diff --git a/DEPS b/DEPS index c540e5bd..15949cf4 100644 --- a/DEPS +++ b/DEPS
@@ -105,11 +105,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': '77552dbce365bdf0f892f29136aa92686019334f', + 'skia_revision': '5dbb0c3db56a6d9895cdcf1210e4fc65bfa84966', # 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': '65ea3e26ace71bfb80f977f5e6876ea2db851b5e', + 'v8_revision': 'd7b61abe7b48928aed739f02bf7695732d359e7e', # 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. @@ -129,7 +129,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling PDFium # and whatever else without interference from each other. - 'pdfium_revision': '89063ecda876e3be7df5935860235eb5f8199ded', + 'pdfium_revision': '30688fb1c434b141380aa224da12e8246a8a78e1', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling openmax_dl # and whatever else without interference from each other. @@ -165,7 +165,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': 'e8dc05ccba6b7aa0f489fa97ffbf81273766b1ee', + 'catapult_revision': 'a824624071cc6850b99a9f29ac75e8905d53e800', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling libFuzzer # and whatever else without interference from each other. @@ -550,7 +550,7 @@ # Build tools for Chrome OS. Note: This depends on third_party/pyelftools. 'src/third_party/chromite': { - 'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '1b00ba71647335cafd439809b84610a8ba068e31', + 'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '764a58f972be81c5a0eb16f2928e1453347e7bfa', 'condition': 'checkout_linux', }, @@ -923,7 +923,7 @@ }, 'src/third_party/perfetto': - Var('android_git') + '/platform/external/perfetto.git' + '@' + 'debca810f01d3bd7c0c3bccce58ad92f6354fbea', + Var('android_git') + '/platform/external/perfetto.git' + '@' + '510bbe089b53f4cb281805670ff7586f2aec2e86', 'src/third_party/perl': { 'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + 'ac0d98b5cee6c024b0cffeb4f8f45b6fc5ccdb78', @@ -1045,7 +1045,7 @@ Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + 'a5c263cc63ffc2cc189b5214074c8792067c1853', 'src/third_party/webrtc': - Var('webrtc_git') + '/src.git' + '@' + '0f5400acfa40b4ecbf71bf44d925ea6ec9d3401d', + Var('webrtc_git') + '/src.git' + '@' + '881fe53d1faefe135c0d6959794da91a25e247f2', 'src/third_party/xdg-utils': { 'url': Var('chromium_git') + '/chromium/deps/xdg-utils.git' + '@' + 'd80274d5869b17b8c9067a1022e4416ee7ed5e0d',
diff --git a/WATCHLISTS b/WATCHLISTS index f5cfb68..c86710e 100644 --- a/WATCHLISTS +++ b/WATCHLISTS
@@ -1736,7 +1736,6 @@ 'ftirelo+autofillwatch@chromium.org', 'mathp+autofillwatch@chromium.org', 'rogerm+autofillwatch@chromium.org', - 'rouslan+autofill@chromium.org', 'sebsg+autofillwatch@chromium.org', 'tmartino+autofillwatch@chromium.org', 'vabr+watchlistautofill@chromium.org'], @@ -1939,8 +1938,7 @@ 'browsing_data': ['dullweber+watch@chromium.org', 'markusheintz@chromium.org', 'msramek+watch@chromium.org'], - 'bubble': ['hcarmona+bubble@chromium.org', - 'rouslan+bubble@chromium.org'], + 'bubble': ['hcarmona+bubble@chromium.org'], 'cache_storage': ['nhiroki@chromium.org'], 'cast': ['imcheng+watch@chromium.org', 'isheriff+watch@chromium.org', @@ -2221,7 +2219,8 @@ 'jennb@chromium.org', 'jianli@chromium.org'], 'password_manager': ['gcasto+watchlist@chromium.org', - 'vabr+watchlistpasswordmanager@chromium.org'], + 'vabr+watchlistpasswordmanager@chromium.org', + 'vasilii+watchlistpasswordmanager@chromium.org'], 'payments': ['rouslan+payments@chromium.org', 'sebsg+paymentswatch@chromium.org', 'gogerald+paymentswatch@chromium.org',
diff --git a/ash/BUILD.gn b/ash/BUILD.gn index d1cfa86..ecc31ac 100644 --- a/ash/BUILD.gn +++ b/ash/BUILD.gn
@@ -431,6 +431,8 @@ "message_center/message_center_button_bar.h", "message_center/message_center_controller.cc", "message_center/message_center_controller.h", + "message_center/message_center_scroll_bar.cc", + "message_center/message_center_scroll_bar.h", "message_center/message_center_style.h", "message_center/message_center_view.cc", "message_center/message_center_view.h",
diff --git a/ash/message_center/message_center_scroll_bar.cc b/ash/message_center/message_center_scroll_bar.cc new file mode 100644 index 0000000..70246ce0 --- /dev/null +++ b/ash/message_center/message_center_scroll_bar.cc
@@ -0,0 +1,56 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ash/message_center/message_center_scroll_bar.h" + +#include "base/metrics/histogram_macros.h" + +namespace { + +enum class ScrollActionReason { + kUnknown, + kByMouseWheel, + kByTouch, + kByArrowKey, + kCount, +}; + +void CollectScrollActionReason(ScrollActionReason reason) { + UMA_HISTOGRAM_ENUMERATION("ChromeOS.MessageCenter.ScrollActionReason", reason, + ScrollActionReason::kCount); +} + +} // namespace + +namespace ash { + +MessageCenterScrollBar::MessageCenterScrollBar() + : views::OverlayScrollBar(false) {} + +bool MessageCenterScrollBar::OnKeyPressed(const ui::KeyEvent& event) { + if (!stats_recorded_ && + (event.key_code() == ui::VKEY_UP || event.key_code() == ui::VKEY_DOWN)) { + CollectScrollActionReason(ScrollActionReason::kByMouseWheel); + stats_recorded_ = true; + } + return views::OverlayScrollBar::OnKeyPressed(event); +} + +bool MessageCenterScrollBar::OnMouseWheel(const ui::MouseWheelEvent& event) { + if (!stats_recorded_) { + CollectScrollActionReason(ScrollActionReason::kByMouseWheel); + stats_recorded_ = true; + } + return views::OverlayScrollBar::OnMouseWheel(event); +} + +void MessageCenterScrollBar::OnGestureEvent(ui::GestureEvent* event) { + if (!stats_recorded_ && (event->type() == ui::ET_GESTURE_SCROLL_BEGIN)) { + CollectScrollActionReason(ScrollActionReason::kByTouch); + stats_recorded_ = true; + } + return views::OverlayScrollBar::OnGestureEvent(event); +} + +} // namespace ash
diff --git a/ash/message_center/message_center_scroll_bar.h b/ash/message_center/message_center_scroll_bar.h new file mode 100644 index 0000000..cd985eb --- /dev/null +++ b/ash/message_center/message_center_scroll_bar.h
@@ -0,0 +1,36 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef ASH_MESSAGE_CENTER_MESSAGE_CENTER_SCROLL_BAR_H_ +#define ASH_MESSAGE_CENTER_MESSAGE_CENTER_SCROLL_BAR_H_ + +#include "ui/events/event.h" +#include "ui/views/controls/scrollbar/overlay_scroll_bar.h" + +namespace ash { + +// The scroll bar for message center. This is basically views::OverlayScrollBar +// but also records the metrics for the type of scrolling. Only the first event +// after the message center opens is recorded. +class MessageCenterScrollBar : public views::OverlayScrollBar { + public: + MessageCenterScrollBar(); + + private: + // View overrides: + bool OnKeyPressed(const ui::KeyEvent& event) override; + bool OnMouseWheel(const ui::MouseWheelEvent& event) override; + + // ui::EventHandler overrides: + void OnGestureEvent(ui::GestureEvent* event) override; + + // False if no event is recorded yet. True if the first event is recorded. + bool stats_recorded_ = false; + + DISALLOW_COPY_AND_ASSIGN(MessageCenterScrollBar); +}; + +} // namespace ash + +#endif // ASH_MESSAGE_CENTER_MESSAGE_CENTER_SCROLL_BAR_H_
diff --git a/ash/message_center/message_center_view.cc b/ash/message_center/message_center_view.cc index 12f4e1be..78e4072 100644 --- a/ash/message_center/message_center_view.cc +++ b/ash/message_center/message_center_view.cc
@@ -8,6 +8,7 @@ #include <map> #include "ash/message_center/message_center_button_bar.h" +#include "ash/message_center/message_center_scroll_bar.h" #include "ash/message_center/message_center_style.h" #include "ash/message_center/notifier_settings_view.h" #include "ash/public/cpp/ash_features.h" @@ -212,8 +213,7 @@ // set the default opaque background color. scroller_->SetBackgroundColor(SK_ColorTRANSPARENT); scroller_->ClipHeightTo(kMinScrollViewHeight, max_scroll_view_height); - scroller_->SetVerticalScrollBar(new views::OverlayScrollBar(false)); - scroller_->SetHorizontalScrollBar(new views::OverlayScrollBar(true)); + scroller_->SetVerticalScrollBar(new MessageCenterScrollBar()); message_list_view_.reset(new MessageListView()); message_list_view_->SetBorderPadding();
diff --git a/ash/shelf/shelf_layout_manager.cc b/ash/shelf/shelf_layout_manager.cc index 67e568e..d526a43 100644 --- a/ash/shelf/shelf_layout_manager.cc +++ b/ash/shelf/shelf_layout_manager.cc
@@ -28,6 +28,7 @@ #include "ash/system/status_area_widget.h" #include "ash/wm/fullscreen_window_finder.h" #include "ash/wm/mru_window_tracker.h" +#include "ash/wm/overview/window_selector_controller.h" #include "ash/wm/screen_pinning_controller.h" #include "ash/wm/tablet_mode/tablet_mode_controller.h" #include "ash/wm/window_state.h" @@ -673,7 +674,9 @@ const display::Display display = display::Screen::GetScreen()->GetDisplayNearestWindow( shelf_widget_->GetNativeWindow()); - if (!state_.IsScreenLocked() && change_work_area && + bool in_overview = + Shell::Get()->window_selector_controller()->IsSelecting(); + if (!in_overview && !state_.IsScreenLocked() && change_work_area && (shelf_->alignment() != SHELF_ALIGNMENT_BOTTOM_LOCKED || display.work_area() == display.bounds())) { gfx::Insets insets;
diff --git a/ash/system/message_center/arc/arc_notification_content_view.cc b/ash/system/message_center/arc/arc_notification_content_view.cc index 9727ae95..044a153 100644 --- a/ash/system/message_center/arc/arc_notification_content_view.cc +++ b/ash/system/message_center/arc/arc_notification_content_view.cc
@@ -259,6 +259,9 @@ // Create a layer as an anchor to insert surface copy during a slide. SetPaintToLayer(); + // SetFillsBoundsOpaquely causes overdraw and has performance implications. + // See the comment in this method and --show-overdraw-feedback for detail. + layer()->SetFillsBoundsOpaquely(false); UpdatePreferredSize(); } @@ -327,6 +330,15 @@ floating_control_buttons_widget_->Hide(); } +void ArcNotificationContentView::UpdateCornerRadius(int top_radius, + int bottom_radius) { + top_radius_ = top_radius; + bottom_radius_ = bottom_radius; + + if (GetWidget()) + InstallMask(); +} + void ArcNotificationContentView::OnSlideChanged() { if (slide_helper_) slide_helper_->Update(); @@ -461,6 +473,8 @@ // (Re-)create the floating buttons after |surface_| is attached to a widget. MaybeCreateFloatingControlButtons(); + + InstallMask(); } void ArcNotificationContentView::ShowCopiedSurface() { @@ -471,16 +485,63 @@ // |surface_copy_| is at (0, 0) in owner_->layer(). surface_copy_->root()->SetBounds(gfx::Rect(surface_copy_->root()->size())); layer()->Add(surface_copy_->root()); + + if (!surface_copy_mask_) { + surface_copy_mask_ = views::Painter::CreatePaintedLayer( + std::make_unique<message_center::NotificationBackgroundPainter>( + top_radius_, bottom_radius_)); + surface_copy_mask_->layer()->SetBounds( + gfx::Rect(surface_copy_->root()->size())); + } + DCHECK(!surface_copy_mask_->layer()->parent()); + surface_copy_->root()->SetMaskLayer(surface_copy_mask_->layer()); + + // Changes the opacity instead of setting the visibility, to keep + // |EventFowarder| working. surface_->GetWindow()->layer()->SetOpacity(0.0f); } void ArcNotificationContentView::HideCopiedSurface() { - if (!surface_) + if (!surface_ || !surface_copy_) return; DCHECK(surface_->GetWindow()); surface_->GetWindow()->layer()->SetOpacity(1.0f); Layout(); surface_copy_.reset(); + + // Re-install the mask since the custom mask is unset by + // |::wm::RecreateLayers()| in |ShowCopiedSurface()| method. + InstallMask(); +} + +void ArcNotificationContentView::InstallMask() { + if (top_radius_ == 0 && bottom_radius_ == 0) { + SetCustomMask(nullptr); + return; + } + + SetCustomMask(views::Painter::CreatePaintedLayer( + std::make_unique<message_center::NotificationBackgroundPainter>( + top_radius_, bottom_radius_))); +} + +void ArcNotificationContentView::AddedToWidget() { + if (attached_widget_) + attached_widget_->RemoveObserver(this); + + attached_widget_ = GetWidget(); + attached_widget_->AddObserver(this); + + // Hide the copied surface since it may be visible by OnWidgetClosing(). + if (surface_copy_) + HideCopiedSurface(); +} + +void ArcNotificationContentView::RemovedFromWidget() { + if (attached_widget_) { + attached_widget_->RemoveObserver(this); + attached_widget_ = nullptr; + } } void ArcNotificationContentView::ViewHierarchyChanged( @@ -513,28 +574,35 @@ void ArcNotificationContentView::Layout() { base::AutoReset<bool> auto_reset_in_layout(&in_layout_, true); - views::NativeViewHost::Layout(); - if (!surface_ || !GetWidget()) return; - const gfx::Rect contents_bounds = GetContentsBounds(); + bool is_surface_visible = (surface_->GetWindow()->layer()->opacity() != 0.0f); + if (is_surface_visible) { + // |views::NativeViewHost::Layout()| can be called only when the hosted + // window is opaque, because that method calls + // |views::NativeViewHostAura::ShowWidget()| and |aura::Window::Show()| + // which has DCHECK the opacity of the window. + views::NativeViewHost::Layout(); - // Scale notification surface if necessary. - gfx::Transform transform; - const gfx::Size surface_size = surface_->GetSize(); - if (!surface_size.IsEmpty()) { - const float factor = - static_cast<float>(message_center::kNotificationWidth) / - surface_size.width(); - transform.Scale(factor, factor); + // Scale notification surface if necessary. + gfx::Transform transform; + const gfx::Size surface_size = surface_->GetSize(); + if (!surface_size.IsEmpty()) { + const float factor = + static_cast<float>(message_center::kNotificationWidth) / + surface_size.width(); + transform.Scale(factor, factor); + } + + // Apply the transform to the surface content so that close button can + // be positioned without the need to consider the transform. + surface_->GetContentWindow()->SetTransform(transform); } - // Apply the transform to the surface content so that close button can - // be positioned without the need to consider the transform. - surface_->GetContentWindow()->SetTransform(transform); - if (floating_control_buttons_widget_) { + const gfx::Rect contents_bounds = GetContentsBounds(); + gfx::Rect control_buttons_bounds(contents_bounds); const gfx::Size button_size = control_buttons_view_.GetPreferredSize(); @@ -550,12 +618,23 @@ UpdateControlButtonsVisibility(); - ::wm::SnapWindowToPixelBoundary(surface_->GetWindow()); + if (is_surface_visible) + ::wm::SnapWindowToPixelBoundary(surface_->GetWindow()); } void ArcNotificationContentView::OnPaint(gfx::Canvas* canvas) { views::NativeViewHost::OnPaint(canvas); + SkScalar radii[8] = {top_radius_, top_radius_, // top-left + top_radius_, top_radius_, // top-right + bottom_radius_, bottom_radius_, // bottom-right + bottom_radius_, bottom_radius_}; // bottom-left + + SkPath path; + path.addRoundRect(gfx::RectToSkRect(GetLocalBounds()), radii, + SkPath::kCCW_Direction); + canvas->ClipPath(path, false); + if (!surface_ && item_ && !item_->GetSnapshot().isNull()) { // Draw the snapshot if there is no surface and the snapshot is available. const gfx::Rect contents_bounds = GetContentsBounds(); @@ -683,6 +762,17 @@ SetSurface(nullptr); } +void ArcNotificationContentView::OnWidgetClosing(views::Widget* widget) { + // Show copied surface, since the mask doesn't work correctly with closing + // animation (fade-out): https://crbug.com/811634. + ShowCopiedSurface(); + + if (attached_widget_) { + attached_widget_->RemoveObserver(this); + attached_widget_ = nullptr; + } +} + void ArcNotificationContentView::OnItemDestroying() { item_->RemoveObserver(this); item_ = nullptr;
diff --git a/ash/system/message_center/arc/arc_notification_content_view.h b/ash/system/message_center/arc/arc_notification_content_view.h index 3b386683..ef329ffc 100644 --- a/ash/system/message_center/arc/arc_notification_content_view.h +++ b/ash/system/message_center/arc/arc_notification_content_view.h
@@ -12,8 +12,10 @@ #include "ash/system/message_center/arc/arc_notification_surface_manager.h" #include "base/macros.h" #include "ui/aura/window_observer.h" +#include "ui/message_center/views/notification_background_painter.h" #include "ui/message_center/views/notification_control_buttons_view.h" #include "ui/views/controls/native/native_view_host.h" +#include "ui/views/widget/widget_observer.h" namespace message_center { class Notification; @@ -39,7 +41,8 @@ : public views::NativeViewHost, public aura::WindowObserver, public ArcNotificationItem::Observer, - public ArcNotificationSurfaceManager::Observer { + public ArcNotificationSurfaceManager::Observer, + public views::WidgetObserver { public: static const char kViewClassName[]; @@ -54,6 +57,7 @@ void Update(const message_center::Notification& notification); message_center::NotificationControlButtonsView* GetControlButtonsView(); void UpdateControlButtonsVisibility(); + void UpdateCornerRadius(int top_radius, int bottom_radius); void OnSlideChanged(); void OnContainerAnimationStarted(); void OnContainerAnimationEnded(); @@ -85,6 +89,9 @@ void ShowCopiedSurface(); void HideCopiedSurface(); + // Generates a mask using |top_radius_| and |bottom_radius_| and installs it. + void InstallMask(); + // views::NativeViewHost void ViewHierarchyChanged( const ViewHierarchyChangedDetails& details) override; @@ -97,6 +104,8 @@ views::FocusTraversable* GetFocusTraversable() override; void GetAccessibleNodeData(ui::AXNodeData* node_data) override; void OnAccessibilityEvent(ax::mojom::Event event) override; + void AddedToWidget() override; + void RemovedFromWidget() override; // aura::WindowObserver void OnWindowBoundsChanged(aura::Window* window, @@ -105,6 +114,9 @@ ui::PropertyChangeReason reason) override; void OnWindowDestroying(aura::Window* window) override; + // views::WidgetObserver: + void OnWidgetClosing(views::Widget* widget) override; + // ArcNotificationItem::Observer void OnItemDestroying() override; void OnItemContentChanged( @@ -158,9 +170,17 @@ // Protects from call loops between Layout and OnWindowBoundsChanged. bool in_layout_ = false; + // Widget which this view tree is currently attached to. + views::Widget* attached_widget_ = nullptr; + base::string16 accessible_name_; + // Radiuses of rounded corners. These values are used in InstallMask(). + int top_radius_ = 0; + int bottom_radius_ = 0; + std::unique_ptr<ui::LayerTreeOwner> surface_copy_; + std::unique_ptr<ui::LayerOwner> surface_copy_mask_; DISALLOW_COPY_AND_ASSIGN(ArcNotificationContentView); };
diff --git a/ash/system/message_center/arc/arc_notification_view.cc b/ash/system/message_center/arc/arc_notification_view.cc index 89702ce..92dfa89 100644 --- a/ash/system/message_center/arc/arc_notification_view.cc +++ b/ash/system/message_center/arc/arc_notification_view.cc
@@ -14,6 +14,7 @@ #include "ui/base/ime/text_input_type.h" #include "ui/gfx/geometry/size.h" #include "ui/message_center/public/cpp/message_center_constants.h" +#include "ui/message_center/views/notification_background_painter.h" #include "ui/message_center/views/notification_control_buttons_view.h" #include "ui/views/background.h" #include "ui/views/controls/button/image_button.h" @@ -83,6 +84,13 @@ message_center::MessageView::SetDrawBackgroundAsActive(active); } +void ArcNotificationView::UpdateCornerRadius(int top_radius, + int bottom_radius) { + MessageView::UpdateCornerRadius(top_radius, bottom_radius); + + content_view_->UpdateCornerRadius(top_radius, bottom_radius); +} + void ArcNotificationView::UpdateControlButtonsVisibility() { content_view_->UpdateControlButtonsVisibility(); }
diff --git a/ash/system/message_center/arc/arc_notification_view.h b/ash/system/message_center/arc/arc_notification_view.h index b820e6a..4c0e9a3 100644 --- a/ash/system/message_center/arc/arc_notification_view.h +++ b/ash/system/message_center/arc/arc_notification_view.h
@@ -53,6 +53,7 @@ void OnContainerAnimationStarted() override; void OnContainerAnimationEnded() override; void OnSnoozeButtonPressed(const ui::Event& event) override; + void UpdateCornerRadius(int top_radius, int bottom_radius) override; // views::SlideOutController::Delegate: void OnSlideChanged() override;
diff --git a/ash/system/unified/unified_message_center_view.cc b/ash/system/unified/unified_message_center_view.cc index b803b04..69e0d26 100644 --- a/ash/system/unified/unified_message_center_view.cc +++ b/ash/system/unified/unified_message_center_view.cc
@@ -4,6 +4,7 @@ #include "ash/system/unified/unified_message_center_view.h" +#include "ash/message_center/message_center_scroll_bar.h" #include "ash/strings/grit/ash_strings.h" #include "ash/system/tray/tray_constants.h" #include "ash/system/unified/sign_out_button.h" @@ -15,7 +16,6 @@ #include "ui/message_center/views/message_view_factory.h" #include "ui/message_center/views/notification_control_buttons_view.h" #include "ui/views/controls/scroll_view.h" -#include "ui/views/controls/scrollbar/overlay_scroll_bar.h" #include "ui/views/layout/box_layout.h" using message_center::MessageCenter; @@ -51,8 +51,7 @@ // Need to set the transparent background explicitly, since ScrollView has // set the default opaque background color. scroller_->SetBackgroundColor(SK_ColorTRANSPARENT); - scroller_->SetVerticalScrollBar(new views::OverlayScrollBar(false)); - scroller_->SetHorizontalScrollBar(new views::OverlayScrollBar(true)); + scroller_->SetVerticalScrollBar(new MessageCenterScrollBar()); scroller_->set_draw_overflow_indicator(false); AddChildView(scroller_);
diff --git a/ash/wm/overview/window_selector_controller.cc b/ash/wm/overview/window_selector_controller.cc index 76de53c..c0c2838 100644 --- a/ash/wm/overview/window_selector_controller.cc +++ b/ash/wm/overview/window_selector_controller.cc
@@ -410,10 +410,10 @@ overview_blur_controller_->Unblur(); is_shutting_down_ = true; Shell::Get()->NotifyOverviewModeEnding(); - window_selector_->Shutdown(); + auto* window_selector = window_selector_.release(); + window_selector->Shutdown(); // Don't delete |window_selector_| yet since the stack is still using it. - base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE, - window_selector_.release()); + base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE, window_selector); last_selection_time_ = base::Time::Now(); Shell::Get()->NotifyOverviewModeEnded(); is_shutting_down_ = false;
diff --git a/ash/wm/overview/window_selector_unittest.cc b/ash/wm/overview/window_selector_unittest.cc index c3286a6..0495e22 100644 --- a/ash/wm/overview/window_selector_unittest.cc +++ b/ash/wm/overview/window_selector_unittest.cc
@@ -949,6 +949,7 @@ // mode correctly applies the transformations to the window and correctly // updates the window bounds on exiting overview mode: http://crbug.com/401664. TEST_F(WindowSelectorTest, FullscreenWindowTabletMode) { + UpdateDisplay("800x600"); const gfx::Rect bounds(400, 400); std::unique_ptr<aura::Window> window1(CreateWindow(bounds)); std::unique_ptr<aura::Window> window2(CreateWindow(bounds)); @@ -961,13 +962,24 @@ gfx::Rect fullscreen_window_bounds(window1->bounds()); EXPECT_NE(normal_window_bounds, fullscreen_window_bounds); EXPECT_EQ(fullscreen_window_bounds, window2->GetTargetBounds()); + + const gfx::Rect fullscreen(800, 600); + const gfx::Rect normal_work_area(800, 552); + display::Screen* screen = display::Screen::GetScreen(); + EXPECT_EQ(gfx::Rect(800, 600), + screen->GetDisplayNearestWindow(window1.get()).work_area()); + ToggleOverview(); + EXPECT_EQ(fullscreen, + screen->GetDisplayNearestWindow(window1.get()).work_area()); + // Window 2 would normally resize to normal window bounds on showing the shelf // for overview but this is deferred until overview is exited. EXPECT_EQ(fullscreen_window_bounds, window2->GetTargetBounds()); EXPECT_FALSE(WindowsOverlapping(window1.get(), window2.get())); ToggleOverview(); - + EXPECT_EQ(fullscreen, + screen->GetDisplayNearestWindow(window1.get()).work_area()); // Since the fullscreen window is still active, window2 will still have the // larger bounds. EXPECT_EQ(fullscreen_window_bounds, window2->GetTargetBounds()); @@ -976,7 +988,19 @@ // the shelf bringing window2 back to the normal bounds. ToggleOverview(); ClickWindow(window2.get()); + // Selecting non fullscreen window should set the work area back to normal. + EXPECT_EQ(normal_work_area, + screen->GetDisplayNearestWindow(window1.get()).work_area()); EXPECT_EQ(normal_window_bounds, window2->GetTargetBounds()); + + ToggleOverview(); + EXPECT_EQ(normal_work_area, + screen->GetDisplayNearestWindow(window1.get()).work_area()); + ClickWindow(window1.get()); + // Selecting fullscreen. The work area should be updated to fullscreen as + // well. + EXPECT_EQ(fullscreen, + screen->GetDisplayNearestWindow(window1.get()).work_area()); } // Tests that beginning window selection hides the app list.
diff --git a/base/BUILD.gn b/base/BUILD.gn index edc501e..f260fe7 100644 --- a/base/BUILD.gn +++ b/base/BUILD.gn
@@ -2021,6 +2021,7 @@ "message_loop/message_loop_perftest.cc", "message_loop/message_loop_task_runner_perftest.cc", "message_loop/message_pump_perftest.cc", + "task/sequence_manager/sequence_manager_perftest.cc", # "test/run_all_unittests.cc", "json/json_perftest.cc", @@ -2389,6 +2390,13 @@ "sys_info_unittest.cc", "system_monitor/system_monitor_unittest.cc", "task/cancelable_task_tracker_unittest.cc", + "task/sequence_manager/intrusive_heap_unittest.cc", + "task/sequence_manager/lazily_deallocated_deque_unittest.cc", + "task/sequence_manager/sequence_manager_impl_unittest.cc", + "task/sequence_manager/task_queue_selector_unittest.cc", + "task/sequence_manager/time_domain_unittest.cc", + "task/sequence_manager/work_queue_sets_unittest.cc", + "task/sequence_manager/work_queue_unittest.cc", "task_runner_util_unittest.cc", "task_scheduler/delayed_task_manager_unittest.cc", "task_scheduler/lazy_task_runner_unittest.cc",
diff --git a/base/fuchsia/component_context.h b/base/fuchsia/component_context.h index 0d395bd7..d7ff9dd 100644 --- a/base/fuchsia/component_context.h +++ b/base/fuchsia/component_context.h
@@ -18,7 +18,7 @@ class InterfacePtr; template <typename Interface> -class Synchronous2InterfacePtr; +class SynchronousInterfacePtr; } // namespace fidl @@ -49,8 +49,8 @@ // Connects to an environment service and returns synchronous interface // implementation. template <typename Interface> - fidl::Synchronous2InterfacePtr<Interface> ConnectToServiceSync() { - fidl::Synchronous2InterfacePtr<Interface> result; + fidl::SynchronousInterfacePtr<Interface> ConnectToServiceSync() { + fidl::SynchronousInterfacePtr<Interface> result; ConnectToService(FidlInterfaceRequest(&result)); return result; }
diff --git a/base/fuchsia/fidl_interface_request.h b/base/fuchsia/fidl_interface_request.h index 5ce4cdc..b3dc3019 100644 --- a/base/fuchsia/fidl_interface_request.h +++ b/base/fuchsia/fidl_interface_request.h
@@ -20,7 +20,7 @@ class InterfacePtr; template <typename Interface> -class Synchronous2InterfacePtr; +class SynchronousInterfacePtr; } // namespace fidl @@ -46,7 +46,7 @@ : FidlInterfaceRequest(stub->NewRequest()) {} template <typename Interface> - explicit FidlInterfaceRequest(fidl::Synchronous2InterfacePtr<Interface>* stub) + explicit FidlInterfaceRequest(fidl::SynchronousInterfacePtr<Interface>* stub) : FidlInterfaceRequest(stub->NewRequest()) {} FidlInterfaceRequest(FidlInterfaceRequest&&);
diff --git a/base/logging_unittest.cc b/base/logging_unittest.cc index 9d37b4c..9b79f98 100644 --- a/base/logging_unittest.cc +++ b/base/logging_unittest.cc
@@ -325,8 +325,8 @@ int death_location = data->death_location; // Register the exception handler on the port. - status = - zx::thread::self().bind_exception_port(*data->port, kExceptionPortKey, 0); + status = zx::thread::self()->bind_exception_port(*data->port, + kExceptionPortKey, 0); if (status != ZX_OK) { data->event->signal(0, ZX_USER_SIGNAL_0); return nullptr; @@ -374,8 +374,8 @@ // Get the crash address. zx::thread zircon_thread; - status = zx::process::self().get_child(packet.exception.tid, - ZX_RIGHT_SAME_RIGHTS, &zircon_thread); + status = zx::process::self()->get_child(packet.exception.tid, + ZX_RIGHT_SAME_RIGHTS, &zircon_thread); ASSERT_EQ(status, ZX_OK); zx_thread_state_general_regs_t buffer; status = zircon_thread.read_state(ZX_THREAD_STATE_GENERAL_REGS, &buffer,
diff --git a/base/memory/shared_memory_fuchsia.cc b/base/memory/shared_memory_fuchsia.cc index 7fb8cee..c22f22b 100644 --- a/base/memory/shared_memory_fuchsia.cc +++ b/base/memory/shared_memory_fuchsia.cc
@@ -88,7 +88,7 @@ if (!read_only_) flags |= ZX_VM_FLAG_PERM_WRITE; uintptr_t addr; - zx_status_t status = zx::vmar::root_self().map( + zx_status_t status = zx::vmar::root_self()->map( 0, *zx::unowned_vmo(shm_.GetHandle()), offset, bytes, flags, &addr); if (status != ZX_OK) { ZX_DLOG(ERROR, status) << "zx_vmar_map"; @@ -109,7 +109,7 @@ SharedMemoryTracker::GetInstance()->DecrementMemoryUsage(*this); uintptr_t addr = reinterpret_cast<uintptr_t>(memory_); - zx_status_t status = zx::vmar::root_self().unmap(addr, mapped_size_); + zx_status_t status = zx::vmar::root_self()->unmap(addr, mapped_size_); if (status != ZX_OK) { ZX_DLOG(ERROR, status) << "zx_vmar_unmap"; return false;
diff --git a/base/memory/shared_memory_unittest.cc b/base/memory/shared_memory_unittest.cc index 3bccf80d..dd76a86 100644 --- a/base/memory/shared_memory_unittest.cc +++ b/base/memory/shared_memory_unittest.cc
@@ -417,7 +417,7 @@ (void)handle; #elif defined(OS_FUCHSIA) uintptr_t addr; - EXPECT_NE(ZX_OK, zx::vmar::root_self().map( + EXPECT_NE(ZX_OK, zx::vmar::root_self()->map( 0, *zx::unowned_vmo(handle.GetHandle()), 0, contents.size(), ZX_VM_FLAG_PERM_WRITE, &addr)) << "Shouldn't be able to map as writable.";
diff --git a/third_party/blink/renderer/platform/scheduler/base/intrusive_heap_unittest.cc b/base/task/sequence_manager/intrusive_heap_unittest.cc similarity index 97% rename from third_party/blink/renderer/platform/scheduler/base/intrusive_heap_unittest.cc rename to base/task/sequence_manager/intrusive_heap_unittest.cc index eb1aecb..3c1323a 100644 --- a/third_party/blink/renderer/platform/scheduler/base/intrusive_heap_unittest.cc +++ b/base/task/sequence_manager/intrusive_heap_unittest.cc
@@ -9,6 +9,7 @@ namespace base { namespace sequence_manager { namespace internal { + namespace { struct TestElement { @@ -364,7 +365,8 @@ TEST_F(IntrusiveHeapTest, CompareNodes) { TestElement five{5, nullptr}, six{6, nullptr}; - // This is the stdlibc++ assertion that fails in http://crbug.com/661080 + // Check that we have a strict comparator, otherwise std::is_heap() + // (used in DCHECK) may fail. See http://crbug.com/661080. EXPECT_FALSE(IntrusiveHeapTest::CompareNodes(six, six)); EXPECT_FALSE(IntrusiveHeapTest::CompareNodes(five, six));
diff --git a/third_party/blink/renderer/platform/scheduler/base/lazily_deallocated_deque_unittest.cc b/base/task/sequence_manager/lazily_deallocated_deque_unittest.cc similarity index 100% rename from third_party/blink/renderer/platform/scheduler/base/lazily_deallocated_deque_unittest.cc rename to base/task/sequence_manager/lazily_deallocated_deque_unittest.cc
diff --git a/third_party/blink/renderer/platform/scheduler/base/sequence_manager_impl_unittest.cc b/base/task/sequence_manager/sequence_manager_impl_unittest.cc similarity index 100% rename from third_party/blink/renderer/platform/scheduler/base/sequence_manager_impl_unittest.cc rename to base/task/sequence_manager/sequence_manager_impl_unittest.cc
diff --git a/third_party/blink/renderer/platform/scheduler/base/sequence_manager_perftest.cc b/base/task/sequence_manager/sequence_manager_perftest.cc similarity index 100% rename from third_party/blink/renderer/platform/scheduler/base/sequence_manager_perftest.cc rename to base/task/sequence_manager/sequence_manager_perftest.cc
diff --git a/third_party/blink/renderer/platform/scheduler/base/task_queue_selector_unittest.cc b/base/task/sequence_manager/task_queue_selector_unittest.cc similarity index 100% rename from third_party/blink/renderer/platform/scheduler/base/task_queue_selector_unittest.cc rename to base/task/sequence_manager/task_queue_selector_unittest.cc
diff --git a/third_party/blink/renderer/platform/scheduler/base/time_domain_unittest.cc b/base/task/sequence_manager/time_domain_unittest.cc similarity index 100% rename from third_party/blink/renderer/platform/scheduler/base/time_domain_unittest.cc rename to base/task/sequence_manager/time_domain_unittest.cc
diff --git a/third_party/blink/renderer/platform/scheduler/base/work_queue_sets_unittest.cc b/base/task/sequence_manager/work_queue_sets_unittest.cc similarity index 99% rename from third_party/blink/renderer/platform/scheduler/base/work_queue_sets_unittest.cc rename to base/task/sequence_manager/work_queue_sets_unittest.cc index d2dc9a1..b849eec 100644 --- a/third_party/blink/renderer/platform/scheduler/base/work_queue_sets_unittest.cc +++ b/base/task/sequence_manager/work_queue_sets_unittest.cc
@@ -12,6 +12,7 @@ namespace base { namespace sequence_manager { + class TimeDomain; namespace internal {
diff --git a/third_party/blink/renderer/platform/scheduler/base/work_queue_unittest.cc b/base/task/sequence_manager/work_queue_unittest.cc similarity index 99% rename from third_party/blink/renderer/platform/scheduler/base/work_queue_unittest.cc rename to base/task/sequence_manager/work_queue_unittest.cc index b984f84..a71cebca 100644 --- a/third_party/blink/renderer/platform/scheduler/base/work_queue_unittest.cc +++ b/base/task/sequence_manager/work_queue_unittest.cc
@@ -16,7 +16,9 @@ namespace base { namespace sequence_manager { namespace internal { + namespace { + void NopTask() {} struct Cancelable { @@ -26,6 +28,7 @@ WeakPtrFactory<Cancelable> weak_ptr_factory; }; + } // namespace class WorkQueueTest : public testing::Test {
diff --git a/base/threading/thread_restrictions.cc b/base/threading/thread_restrictions.cc index 633bcb2..36c64b51 100644 --- a/base/threading/thread_restrictions.cc +++ b/base/threading/thread_restrictions.cc
@@ -31,7 +31,7 @@ "blocking! If this task is running inside the TaskScheduler, it needs " "to have MayBlock() in its TaskTraits. Otherwise, consider making " "this blocking work asynchronous or, as a last resort, you may use " - "ScopedAllowBlocking in a narrow scope."; + "ScopedAllowBlocking (see its documentation for best practices)."; } void DisallowBlocking() {
diff --git a/base/threading/thread_restrictions.h b/base/threading/thread_restrictions.h index e76b162..7453c8e5 100644 --- a/base/threading/thread_restrictions.h +++ b/base/threading/thread_restrictions.h
@@ -214,6 +214,35 @@ // // Avoid using this. Prefer making blocking calls from tasks posted to // base::TaskScheduler with base::MayBlock(). +// +// Where unavoidable, put ScopedAllow* instances in the narrowest scope possible +// in the caller making the blocking call but no further down. That is: if a +// Cleanup() method needs to do a blocking call, document Cleanup() as blocking +// and add a ScopedAllowBlocking instance in callers that can't avoid making +// this call from a context where blocking is banned, as such: +// void Client::MyMethod() { +// (...) +// { +// // Blocking is okay here because XYZ. +// ScopedAllowBlocking allow_blocking; +// my_foo_->Cleanup(); +// } +// (...) +// } +// +// // This method can block. +// void Foo::Cleanup() { +// // Do NOT add the ScopedAllowBlocking in Cleanup() directly as that hides +// // its blocking nature from unknowing callers and defeats the purpose of +// // these checks. +// FlushStateToDisk(); +// } +// +// Note: In rare situations where the blocking call is an implementation detail +// (i.e. the impl makes a call that invokes AssertBlockingAllowed() but it +// somehow knows that in practice this will not block), it might be okay to hide +// the ScopedAllowBlocking instance in the impl with a comment explaining why +// that's okay. class BASE_EXPORT ScopedAllowBlocking { private: // This can only be instantiated by friends. Use ScopedAllowBlockingForTesting
diff --git a/build/config/ios/codesign.py b/build/config/ios/codesign.py index fba34f6..64bd129 100644 --- a/build/config/ios/codesign.py +++ b/build/config/ios/codesign.py
@@ -243,12 +243,15 @@ installed_framework_path = os.path.join( bundle_path, 'Frameworks', os.path.basename(framework_path)) - if os.path.exists(installed_framework_path): - shutil.rmtree(installed_framework_path) + if os.path.isfile(framework_path): + shutil.copy(framework_path, installed_framework_path) + elif os.path.isdir(framework_path): + if os.path.exists(installed_framework_path): + shutil.rmtree(installed_framework_path) + shutil.copytree(framework_path, installed_framework_path) - shutil.copytree(framework_path, installed_framework_path) CodeSignBundle(installed_framework_path, args.identity, - ['--deep', '--preserve-metadata=identifier,entitlements']) + ['--deep', '--preserve-metadata=identifier,entitlements,flags']) def GenerateEntitlements(path, provisioning_profile, bundle_identifier):
diff --git a/build/config/ios/rules.gni b/build/config/ios/rules.gni index 7fdc931..02725177 100644 --- a/build/config/ios/rules.gni +++ b/build/config/ios/rules.gni
@@ -1809,7 +1809,11 @@ [ "$_ios_platform_library/Frameworks/XCTest.framework" ] # TODO: Remove this once support for Xcode 9.x is dropped. - if (xcode_version_int < 1000) { + if (xcode_version_int >= 1000) { + extra_system_frameworks += [ + "$ios_sdk_platform_path/Developer/usr/lib/libXCTestBundleInject.dylib", + ] + } else { extra_system_frameworks += [ "$_ios_platform_library/PrivateFrameworks/IDEBundleInjection.framework", ]
diff --git a/build/split_static_library.gni b/build/split_static_library.gni index ea378e3..60ad4528 100644 --- a/build/split_static_library.gni +++ b/build/split_static_library.gni
@@ -2,13 +2,17 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +import("//build/config/compiler/compiler.gni") + template("split_static_library") { assert(defined(invoker.split_count), "Must define split_count for split_static_library") - # In many conditions the number of inputs will be 1 (because the count will - # be conditional on platform or configuration) so optimize that. - if (invoker.split_count == 1) { + # In many conditions the number of inputs will be 1 (because the + # count will be conditional on platform or configuration) and for + # some build configurations it's unnecessary to split libraries + # since the tooling will never create files of a problematic size. + if (invoker.split_count == 1 || use_lld) { static_library(target_name) { forward_variables_from(invoker, "*") }
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn index 7d3e49a..0545158 100644 --- a/chrome/android/BUILD.gn +++ b/chrome/android/BUILD.gn
@@ -701,23 +701,14 @@ "javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityTestRule.java", "javatests/src/org/chromium/chrome/browser/customtabs/CustomTabsTestUtils.java", "javatests/src/org/chromium/chrome/browser/media/RouterTestUtils.java", - "javatests/src/org/chromium/chrome/browser/vr/mock/MockVrCoreVersionCheckerImpl.java", "javatests/src/org/chromium/chrome/browser/vr/rules/ChromeTabbedActivityXrTestRule.java", "javatests/src/org/chromium/chrome/browser/vr/rules/CustomTabActivityXrTestRule.java", - "javatests/src/org/chromium/chrome/browser/vr/rules/HeadTrackingMode.java", "javatests/src/org/chromium/chrome/browser/vr/rules/XrActivityRestriction.java", "javatests/src/org/chromium/chrome/browser/vr/rules/XrActivityRestrictionRule.java", "javatests/src/org/chromium/chrome/browser/vr/rules/XrTestRule.java", "javatests/src/org/chromium/chrome/browser/vr/rules/WebappActivityXrTestRule.java", - "javatests/src/org/chromium/chrome/browser/vr/util/HeadTrackingUtils.java", - "javatests/src/org/chromium/chrome/browser/vr/util/TransitionUtils.java", - "javatests/src/org/chromium/chrome/browser/vr/util/VrShellDelegateUtils.java", "javatests/src/org/chromium/chrome/browser/vr/util/XrTestRuleUtils.java", - "javatests/src/org/chromium/chrome/browser/vr/util/VrTransitionUtils.java", - "javatests/src/org/chromium/chrome/browser/vr/util/XrTransitionUtils.java", - "javatests/src/org/chromium/chrome/browser/vr/TestFramework.java", - "javatests/src/org/chromium/chrome/browser/vr/TestVrShellDelegate.java", - "javatests/src/org/chromium/chrome/browser/vr/VrTestFramework.java", + "javatests/src/org/chromium/chrome/browser/vr/WebXrTestFramework.java", "javatests/src/org/chromium/chrome/browser/vr/XrTestFramework.java", "javatests/src/org/chromium/chrome/browser/webapps/TestFetchStorageCallback.java", "javatests/src/org/chromium/chrome/browser/webapps/WebappActivityTestRule.java", @@ -739,11 +730,23 @@ java_files = [ "javatests/src/org/chromium/chrome/browser/vr/EmulatedVrController.java", "javatests/src/org/chromium/chrome/browser/vr/mock/MockBrowserKeyboardInterface.java", + "javatests/src/org/chromium/chrome/browser/vr/mock/MockVrCoreVersionCheckerImpl.java", "javatests/src/org/chromium/chrome/browser/vr/mock/MockVrDaydreamApi.java", "javatests/src/org/chromium/chrome/browser/vr/nfc_apk/SimNfcActivity.java", + "javatests/src/org/chromium/chrome/browser/vr/rules/ChromeTabbedActivityVrTestRule.java", + "javatests/src/org/chromium/chrome/browser/vr/rules/CustomTabActivityVrTestRule.java", + "javatests/src/org/chromium/chrome/browser/vr/rules/HeadTrackingMode.java", + "javatests/src/org/chromium/chrome/browser/vr/rules/VrTestRule.java", + "javatests/src/org/chromium/chrome/browser/vr/rules/WebappActivityVrTestRule.java", + "javatests/src/org/chromium/chrome/browser/vr/util/HeadTrackingUtils.java", "javatests/src/org/chromium/chrome/browser/vr/util/NativeUiUtils.java", "javatests/src/org/chromium/chrome/browser/vr/util/NfcSimUtils.java", + "javatests/src/org/chromium/chrome/browser/vr/util/VrBrowserTransitionUtils.java", "javatests/src/org/chromium/chrome/browser/vr/util/VrInfoBarUtils.java", + "javatests/src/org/chromium/chrome/browser/vr/util/VrShellDelegateUtils.java", + "javatests/src/org/chromium/chrome/browser/vr/util/VrTestRuleUtils.java", + "javatests/src/org/chromium/chrome/browser/vr/util/VrTransitionUtils.java", + "javatests/src/org/chromium/chrome/browser/vr/TestVrShellDelegate.java", "javatests/src/org/chromium/chrome/browser/vr/VrFeedbackInfoBarTest.java", "javatests/src/org/chromium/chrome/browser/vr/VrInstallUpdateInfoBarTest.java", "javatests/src/org/chromium/chrome/browser/vr/VrBrowserCompositorViewHolderTest.java", @@ -751,11 +754,14 @@ "javatests/src/org/chromium/chrome/browser/vr/VrBrowserDialogTest.java", "javatests/src/org/chromium/chrome/browser/vr/VrBrowserNativeUiTest.java", "javatests/src/org/chromium/chrome/browser/vr/VrBrowserNavigationTest.java", + "javatests/src/org/chromium/chrome/browser/vr/VrBrowserTestFramework.java", "javatests/src/org/chromium/chrome/browser/vr/VrBrowserTransitionTest.java", "javatests/src/org/chromium/chrome/browser/vr/VrBrowserWebInputEditingTest.java", + "javatests/src/org/chromium/chrome/browser/vr/WebVrTestFramework.java", "javatests/src/org/chromium/chrome/browser/vr/WebXrVrDeviceTest.java", "javatests/src/org/chromium/chrome/browser/vr/WebXrVrInputTest.java", "javatests/src/org/chromium/chrome/browser/vr/WebXrVrTabTest.java", + "javatests/src/org/chromium/chrome/browser/vr/WebXrVrTestFramework.java", "javatests/src/org/chromium/chrome/browser/vr/WebXrVrTransitionTest.java", ] @@ -779,6 +785,7 @@ java_files = [ "javatests/src/org/chromium/chrome/browser/vr/WebXrArSessionTest.java", + "javatests/src/org/chromium/chrome/browser/vr/WebXrArTestFramework.java", ] deps =
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedNewTabPage.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedNewTabPage.java index 0665ce9e..9a409fd9 100644 --- a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedNewTabPage.java +++ b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedNewTabPage.java
@@ -157,9 +157,6 @@ mStream.setHeaderViews(Arrays.asList(mNewTabPageLayout)); - // TODO(skym): This is a work around for outstanding Feed bug. - mStream.triggerRefresh(); - // Listen for layout changes on the NewTabPageView itself to catch changes in scroll // position that are due to layout changes after e.g. device rotation. This contrasts with // regular scrolling, which is observed through an OnScrollListener.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/NotificationCategory.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/NotificationCategory.java new file mode 100644 index 0000000..7305fe3 --- /dev/null +++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/NotificationCategory.java
@@ -0,0 +1,35 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.browser.preferences.website; + +import android.content.Context; +import android.os.Build; +import android.support.v4.app.NotificationManagerCompat; + +import org.chromium.chrome.browser.ChromeFeatureList; + +/** + * Enables custom implementation for the notification site settings category, similar to + * {@link LocationCategory}. + */ +public class NotificationCategory extends SiteSettingsCategory { + NotificationCategory() { + // Android does not treat notifications as a 'permission', i.e. notification status cannot + // be checked via Context#checkPermission(). Hence we pass an empty string here and override + // #enabledForChrome() to use the notification-status checking API instead. + super(Type.NOTIFICATIONS, "" /* androidPermission*/); + } + + @Override + protected boolean enabledForChrome(Context context) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT + || !ChromeFeatureList.isEnabled( + ChromeFeatureList.APP_NOTIFICATION_STATUS_MESSAGING)) { + return super.enabledForChrome(context); + } + NotificationManagerCompat manager = NotificationManagerCompat.from(context); + return manager.areNotificationsEnabled(); + } +}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/SingleWebsitePreferences.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/SingleWebsitePreferences.java index 5c4a192e..71e88261 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/SingleWebsitePreferences.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/SingleWebsitePreferences.java
@@ -517,6 +517,9 @@ if (showWarningFor(SiteSettingsCategory.Type.MICROPHONE)) { return SiteSettingsCategory.createFromType(SiteSettingsCategory.Type.MICROPHONE); } + if (showWarningFor(SiteSettingsCategory.Type.NOTIFICATIONS)) { + return SiteSettingsCategory.createFromType(SiteSettingsCategory.Type.NOTIFICATIONS); + } return null; } @@ -528,6 +531,8 @@ setting = mSite.getPermission(PermissionInfo.Type.CAMERA); } else if (type == SiteSettingsCategory.Type.MICROPHONE) { setting = mSite.getPermission(PermissionInfo.Type.MICROPHONE); + } else if (type == SiteSettingsCategory.Type.NOTIFICATIONS) { + setting = mSite.getPermission(PermissionInfo.Type.NOTIFICATION); } if (setting == null) return false;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/SiteSettingsCategory.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/SiteSettingsCategory.java index be8beca..ec189d0 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/SiteSettingsCategory.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/SiteSettingsCategory.java
@@ -145,6 +145,7 @@ */ public static SiteSettingsCategory createFromType(@Type int type) { if (type == Type.DEVICE_LOCATION) return new LocationCategory(); + if (type == Type.NOTIFICATIONS) return new NotificationCategory(); final String permission; if (type == Type.CAMERA) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/vr/VrDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/vr/VrDelegate.java index beac69d..b81a3723 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/vr/VrDelegate.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/vr/VrDelegate.java
@@ -35,4 +35,6 @@ void maybeHandleVrIntentPreNative(ChromeActivity activity, Intent intent); void setVrModeEnabled(Activity activity, boolean enabled); boolean bootsToVr(); + boolean isDaydreamReadyDevice(); + boolean isDaydreamCurrentViewer(); }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/vr/VrDelegateFallback.java b/chrome/android/java/src/org/chromium/chrome/browser/vr/VrDelegateFallback.java index b8b1c85..9534bb2 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/vr/VrDelegateFallback.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/vr/VrDelegateFallback.java
@@ -113,6 +113,16 @@ } @Override + public boolean isDaydreamReadyDevice() { + return false; + } + + @Override + public boolean isDaydreamCurrentViewer() { + return false; + } + + @Override public boolean isVrIntent(Intent intent) { return false; }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/vr/VrDelegateImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/vr/VrDelegateImpl.java index 2220dfd0..65c2b61 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/vr/VrDelegateImpl.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/vr/VrDelegateImpl.java
@@ -134,6 +134,16 @@ } @Override + public boolean isDaydreamReadyDevice() { + return VrShellDelegate.isDaydreamReadyDevice(); + } + + @Override + public boolean isDaydreamCurrentViewer() { + return VrShellDelegate.isDaydreamCurrentViewer(); + } + + @Override public boolean isVrIntent(Intent intent) { return VrIntentUtils.isVrIntent(intent); }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/vr/VrShellDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/vr/VrShellDelegate.java index 7deeb9e..0d5805a5 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/vr/VrShellDelegate.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/vr/VrShellDelegate.java
@@ -684,6 +684,11 @@ return getVrClassesWrapper() != null && getVrClassesWrapper().isDaydreamReadyDevice(); } + public static boolean isDaydreamCurrentViewer() { + if (sInstance != null) return sInstance.isDaydreamCurrentViewerInternal(); + return getVrDaydreamApi().isDaydreamCurrentViewer(); + } + /** * @return A helper class for creating VR-specific classes that may not be available at compile * time. @@ -797,11 +802,6 @@ return isVrBrowsingSupported(activity) && vrSupportLevel == VrSupportLevel.VR_DAYDREAM; } - /* package */ static boolean isDaydreamCurrentViewer() { - if (sInstance != null) return sInstance.isDaydreamCurrentViewerInternal(); - return getVrDaydreamApi().isDaydreamCurrentViewer(); - } - /* package */ static boolean isInVrSession() { return getVrClassesWrapper() != null && getVrClassesWrapper().isInVrSession(); }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/vr/VrWindowAndroid.java b/chrome/android/java/src/org/chromium/chrome/browser/vr/VrWindowAndroid.java index f539a979..ca51433 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/vr/VrWindowAndroid.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/vr/VrWindowAndroid.java
@@ -15,6 +15,7 @@ import org.chromium.base.ApiCompatibilityUtils; import org.chromium.base.ApplicationStatus; import org.chromium.base.Callback; +import org.chromium.base.ContextUtils; import org.chromium.ui.base.AndroidPermissionDelegate; import org.chromium.ui.base.PermissionCallback; import org.chromium.ui.base.WindowAndroid; @@ -80,8 +81,8 @@ private class ActivityAndroidPermissionDelegate implements AndroidPermissionDelegate { @Override public boolean hasPermission(String permission) { - return ApiCompatibilityUtils.checkPermission( - mApplicationContext, permission, Process.myPid(), Process.myUid()) + return ApiCompatibilityUtils.checkPermission(ContextUtils.getApplicationContext(), + permission, Process.myPid(), Process.myUid()) == PackageManager.PERMISSION_GRANTED; }
diff --git a/chrome/android/java_sources.gni b/chrome/android/java_sources.gni index 953f6e0..6d613493 100644 --- a/chrome/android/java_sources.gni +++ b/chrome/android/java_sources.gni
@@ -1168,6 +1168,7 @@ "java/src/org/chromium/chrome/browser/preferences/website/LocalStorageInfo.java", "java/src/org/chromium/chrome/browser/preferences/website/LocationCategory.java", "java/src/org/chromium/chrome/browser/preferences/website/ManageSpaceActivity.java", + "java/src/org/chromium/chrome/browser/preferences/website/NotificationCategory.java", "java/src/org/chromium/chrome/browser/preferences/website/PermissionInfo.java", "java/src/org/chromium/chrome/browser/preferences/website/SingleCategoryPreferences.java", "java/src/org/chromium/chrome/browser/preferences/website/SingleWebsitePreferences.java",
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/README.md b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/README.md index c2600c5..f203d72 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/README.md +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/README.md
@@ -223,7 +223,7 @@ * Set `@RunWith` to `ParameterizedRunner.class` * Add `@UseRunnerDelegate` and set it to `ChromeJUnit4RunnerDelegate.class` * Define `sClassParams`, annotate it with `@ClassParameter`, and set it to the - value returned by `XrTestRuleUtils.generateDefaultXrTestRuleParameters()` + value returned by `XrTestRuleUtils.generateDefaultTestRuleParameters()` * Define `mRuleChain` as a `RuleChain` and annotate it with `@Rule` * Define `mVrTestRule` as a `ChromeActivityTestRule` * Define `mVrTestFramework` as a `VrTestFramework` and initialize it using
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/TestFramework.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/TestFramework.java deleted file mode 100644 index 3f5f1e0..0000000 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/TestFramework.java +++ /dev/null
@@ -1,305 +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. - -package org.chromium.chrome.browser.vr; - -import android.support.annotation.IntDef; -import android.view.View; - -import org.junit.Assert; - -import org.chromium.base.Log; -import org.chromium.base.ThreadUtils; -import org.chromium.base.test.util.UrlUtils; -import org.chromium.chrome.browser.UrlConstants; -import org.chromium.chrome.browser.tab.Tab; -import org.chromium.chrome.test.ChromeActivityTestRule; -import org.chromium.content.browser.test.util.Criteria; -import org.chromium.content.browser.test.util.CriteriaHelper; -import org.chromium.content.browser.test.util.JavaScriptUtils; -import org.chromium.content_public.browser.WebContents; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.util.concurrent.Callable; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; - -/** - * Class containing most of the test framework for WebVR and WebXR testing, which requires - * back-and-forth communication with JavaScript running in the browser. WebVR and WebXR-specific - * functionality can be found in the VrTestFramework and XrTestFramework subclasses. - * - * The general test flow is: - * - Load the HTML file containing the test, which: - * - Loads the WebVR boilerplate code and some test functions - * - Sets up common elements like the canvas and synchronization variable - * - Sets up any steps that need to be triggered by the Java code - * - Check if any VRDisplay objects were found and fail the test if it doesn't - * match what we expect for that test - * - Repeat: - * - Run any necessary Java-side code, e.g. trigger a user action - * - Trigger the next JavaScript test step and wait for it to finish - * - * The JavaScript code will automatically process test results once all - * testharness.js tests are done, just like in layout tests. Once the results - * are processed, the JavaScript code will automatically signal the Java code, - * which can then grab the results and pass/fail the instrumentation test. - */ -public class TestFramework { - public static final int PAGE_LOAD_TIMEOUT_S = 10; - public static final int POLL_CHECK_INTERVAL_SHORT_MS = 50; - public static final int POLL_CHECK_INTERVAL_LONG_MS = 100; - public static final int POLL_TIMEOUT_SHORT_MS = 1000; - public static final int POLL_TIMEOUT_LONG_MS = 10000; - - public static final String[] NATIVE_URLS_OF_INTEREST = {UrlConstants.BOOKMARKS_FOLDER_URL, - UrlConstants.BOOKMARKS_UNCATEGORIZED_URL, UrlConstants.BOOKMARKS_URL, - UrlConstants.DOWNLOADS_URL, UrlConstants.NATIVE_HISTORY_URL, UrlConstants.NTP_URL, - UrlConstants.RECENT_TABS_URL}; - - private static final String TAG = "TestFramework"; - static final String TEST_DIR = "chrome/test/data/xr/e2e_test_files"; - - // Test status enum - private static final int STATUS_RUNNING = 0; - private static final int STATUS_PASSED = 1; - private static final int STATUS_FAILED = 2; - @Retention(RetentionPolicy.SOURCE) - @IntDef({STATUS_RUNNING, STATUS_PASSED, STATUS_FAILED}) - private @interface TestStatus {} - - private ChromeActivityTestRule mRule; - private WebContents mFirstTabWebContents; - private View mFirstTabContentView; - - /** - * Must be constructed after the rule has been applied (e.g. in whatever method is - * tagged with @Before) - */ - public TestFramework(ChromeActivityTestRule rule) { - mRule = rule; - mFirstTabWebContents = mRule.getWebContents(); - mFirstTabContentView = mRule.getActivity().getActivityTab().getContentView(); - Assert.assertFalse("Test did not start in VR", VrShellDelegate.isInVr()); - } - - public WebContents getFirstTabWebContents() { - return mFirstTabWebContents; - } - - public View getFirstTabContentView() { - return mFirstTabContentView; - } - - public ChromeActivityTestRule getRule() { - return mRule; - } - - public void simulateRendererKilled() { - final Tab tab = getRule().getActivity().getActivityTab(); - ThreadUtils.runOnUiThreadBlocking(() -> tab.simulateRendererKilledForTesting(true)); - - CriteriaHelper.pollUiThread(new Criteria() { - @Override - public boolean isSatisfied() { - return tab.isShowingSadTab(); - } - }); - } - - /** - * Gets the file:// URL to the test file - * @param testName The name of the test whose file will be retrieved. - * @return The file:// URL to the specified test file. - */ - public static String getFileUrlForHtmlTestFile(String testName) { - return "file://" + UrlUtils.getIsolatedTestFilePath(TEST_DIR) + "/html/" + testName - + ".html"; - } - - /** - * Gets the path to pass to an EmbeddedTestServer.getURL to load the given HTML test file. - * @param testName The name of the test whose file will be retrieved. - * @param A path that can be passed to EmbeddedTestServer.getURL to load the test file. - */ - public static String getEmbeddedServerPathForHtmlTestFile(String testName) { - return "/" + TEST_DIR + "/html/" + testName + ".html"; - } - - /** - * Loads the given URL with the given timeout then waits for JavaScript to - * signal that it's ready for testing. - * @param url The URL of the page to load. - * @param timeoutSec The timeout of the page load in seconds. - * @return The return value of ChromeActivityTestRule.loadUrl() - */ - public int loadUrlAndAwaitInitialization(String url, int timeoutSec) - throws InterruptedException { - int result = mRule.loadUrl(url, timeoutSec); - Assert.assertTrue("JavaScript initialization successful", - pollJavaScriptBoolean("isInitializationComplete()", POLL_TIMEOUT_LONG_MS, - mRule.getWebContents())); - return result; - } - - /** - * Helper function to run the given JavaScript, return the return value, - * and fail if a timeout/interrupt occurs so we don't have to catch or - * declare exceptions all the time. - * @param js The JavaScript to run. - * @param timeout The timeout in milliseconds before a failure. - * @param webContents The WebContents object to run the JavaScript in. - * @return The return value of the JavaScript. - */ - public static String runJavaScriptOrFail(String js, int timeout, WebContents webContents) { - try { - return JavaScriptUtils.executeJavaScriptAndWaitForResult( - webContents, js, timeout, TimeUnit.MILLISECONDS); - } catch (InterruptedException | TimeoutException e) { - Assert.fail("Fatal interruption or timeout running JavaScript: " + js); - } - return "Not reached"; - } - - /** - * Retrieves the current status of the JavaScript test and returns an enum corresponding to it. - * @param webContents The WebContents for the tab to check the status in. - * @return A TestStatus integer corresponding to the current state of the JavaScript test - */ - @TestStatus - public static int checkTestStatus(WebContents webContents) { - String resultString = - runJavaScriptOrFail("resultString", POLL_TIMEOUT_SHORT_MS, webContents); - boolean testPassed = Boolean.parseBoolean( - runJavaScriptOrFail("testPassed", POLL_TIMEOUT_SHORT_MS, webContents)); - if (testPassed) { - return STATUS_PASSED; - } else if (!testPassed && resultString.equals("\"\"")) { - return STATUS_RUNNING; - } else { - // !testPassed && !resultString.equals("\"\"") - return STATUS_FAILED; - } - } - - /** - * Helper function to end the test harness test and assert that it passed, - * setting the failure reason as the description if it didn't. - * @param webContents The WebContents for the tab to check test results in. - */ - public static void endTest(WebContents webContents) { - switch (checkTestStatus(webContents)) { - case STATUS_PASSED: - break; - case STATUS_FAILED: - String resultString = - runJavaScriptOrFail("resultString", POLL_TIMEOUT_SHORT_MS, webContents); - Assert.fail("JavaScript testharness failed with result: " + resultString); - break; - case STATUS_RUNNING: - Assert.fail("Attempted to end test in Java without finishing in JavaScript."); - break; - default: - Assert.fail("Received unknown test status."); - } - } - - /** - * Helper function to make sure that the JavaScript test harness did not detect any failures. - * Similar to endTest, but does not fail if the test is still detected as running. This is - * useful because not all tests make use of the test harness' test/assert features (particularly - * simple enter/exit tests), but may still want to ensure that no unexpected JavaScript errors - * were encountered. - * @param webContents The Webcontents for the tab to check for failures in. - */ - public static void assertNoJavaScriptErrors(WebContents webContents) { - Assert.assertNotEquals(checkTestStatus(webContents), STATUS_FAILED); - } - - /** - * Polls the provided JavaScript boolean until the timeout is reached or - * the boolean is true. - * @param boolName The name of the JavaScript boolean or expression to poll. - * @param timeoutMs The polling timeout in milliseconds. - * @param webContents The WebContents to run the JavaScript through. - * @return True if the boolean evaluated to true, false if timed out. - */ - public static boolean pollJavaScriptBoolean( - final String boolName, int timeoutMs, final WebContents webContents) { - try { - CriteriaHelper.pollInstrumentationThread(Criteria.equals(true, new Callable<Boolean>() { - @Override - public Boolean call() { - String result = "false"; - try { - result = JavaScriptUtils.executeJavaScriptAndWaitForResult(webContents, - boolName, POLL_CHECK_INTERVAL_SHORT_MS, TimeUnit.MILLISECONDS); - } catch (InterruptedException | TimeoutException e) { - // Expected to happen regularly, do nothing - } - return Boolean.parseBoolean(result); - } - }), timeoutMs, POLL_CHECK_INTERVAL_LONG_MS); - } catch (AssertionError e) { - Log.d(TAG, "pollJavaScriptBoolean() timed out"); - return false; - } - return true; - } - - /** - * Waits for a JavaScript step to finish, asserting that the step finished - * instead of timing out. - * @param webContents The WebContents for the tab the JavaScript step is in. - */ - public static void waitOnJavaScriptStep(WebContents webContents) { - // Make sure we aren't trying to wait on a JavaScript test step without the code to do so. - Assert.assertTrue("Attempted to wait on a JavaScript step without the code to do so. You " - + "either forgot to import webvr_e2e.js or are incorrectly using a Java " - + "method.", - Boolean.parseBoolean(runJavaScriptOrFail("typeof javascriptDone !== 'undefined'", - POLL_TIMEOUT_SHORT_MS, webContents))); - - // Actually wait for the step to finish - boolean success = - pollJavaScriptBoolean("javascriptDone", POLL_TIMEOUT_LONG_MS, webContents); - - // Check what state we're in to make sure javascriptDone wasn't called because the test - // failed. - int testStatus = checkTestStatus(webContents); - if (!success || testStatus == STATUS_FAILED) { - // Failure states: Either polling failed or polling succeeded, but because the test - // failed. - String reason; - if (!success) { - reason = "Polling JavaScript boolean javascriptDone timed out."; - } else { - reason = "Polling JavaScript boolean javascriptDone succeeded, but test failed."; - } - String resultString = - runJavaScriptOrFail("resultString", POLL_TIMEOUT_SHORT_MS, webContents); - if (resultString.equals("\"\"")) { - reason += " Did not obtain specific reason from testharness."; - } else { - reason += " Testharness reported failure: " + resultString; - } - Assert.fail(reason); - } - - // Reset the synchronization boolean - runJavaScriptOrFail("javascriptDone = false", POLL_TIMEOUT_SHORT_MS, webContents); - } - - /** - * Executes a JavaScript step function using the given WebContents. - * @param stepFunction The JavaScript step function to call. - * @param webContents The WebContents for the tab the JavaScript is in. - */ - public static void executeStepAndWait(String stepFunction, WebContents webContents) { - // Run the step and block - JavaScriptUtils.executeJavaScript(webContents, stepFunction); - waitOnJavaScriptStep(webContents); - } -}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrBrowserCompositorViewHolderTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrBrowserCompositorViewHolderTest.java index 3d4048b..47092d0 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrBrowserCompositorViewHolderTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrBrowserCompositorViewHolderTest.java
@@ -18,7 +18,7 @@ import org.chromium.chrome.R; import org.chromium.chrome.browser.ChromeSwitches; import org.chromium.chrome.browser.compositor.CompositorViewHolder; -import org.chromium.chrome.browser.vr.rules.ChromeTabbedActivityXrTestRule; +import org.chromium.chrome.browser.vr.rules.ChromeTabbedActivityVrTestRule; import org.chromium.chrome.test.ChromeJUnit4ClassRunner; import org.chromium.content.browser.test.util.Criteria; import org.chromium.content.browser.test.util.CriteriaHelper; @@ -37,7 +37,7 @@ // We explicitly instantiate a rule here instead of using parameterization since this class // only ever runs in ChromeTabbedActivity. @Rule - public ChromeTabbedActivityXrTestRule mVrTestRule = new ChromeTabbedActivityXrTestRule(); + public ChromeTabbedActivityVrTestRule mVrTestRule = new ChromeTabbedActivityVrTestRule(); /** * Verify that resizing the CompositorViewHolder does not cause the current tab to resize while
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrBrowserControllerInputTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrBrowserControllerInputTest.java index 9854fc4..4590060a 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrBrowserControllerInputTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrBrowserControllerInputTest.java
@@ -4,10 +4,10 @@ package org.chromium.chrome.browser.vr; -import static org.chromium.chrome.browser.vr.VrTestFramework.PAGE_LOAD_TIMEOUT_S; -import static org.chromium.chrome.browser.vr.VrTestFramework.POLL_CHECK_INTERVAL_LONG_MS; -import static org.chromium.chrome.browser.vr.VrTestFramework.POLL_TIMEOUT_LONG_MS; -import static org.chromium.chrome.browser.vr.VrTestFramework.POLL_TIMEOUT_SHORT_MS; +import static org.chromium.chrome.browser.vr.XrTestFramework.PAGE_LOAD_TIMEOUT_S; +import static org.chromium.chrome.browser.vr.XrTestFramework.POLL_CHECK_INTERVAL_LONG_MS; +import static org.chromium.chrome.browser.vr.XrTestFramework.POLL_TIMEOUT_LONG_MS; +import static org.chromium.chrome.browser.vr.XrTestFramework.POLL_TIMEOUT_SHORT_MS; import static org.chromium.chrome.test.util.ChromeRestriction.RESTRICTION_TYPE_DEVICE_DAYDREAM; import static org.chromium.chrome.test.util.ChromeRestriction.RESTRICTION_TYPE_VIEWER_DAYDREAM; @@ -26,8 +26,8 @@ import org.chromium.base.test.util.RetryOnFailure; import org.chromium.chrome.browser.ChromeSwitches; import org.chromium.chrome.browser.history.HistoryPage; -import org.chromium.chrome.browser.vr.rules.ChromeTabbedActivityXrTestRule; -import org.chromium.chrome.browser.vr.util.VrTransitionUtils; +import org.chromium.chrome.browser.vr.rules.ChromeTabbedActivityVrTestRule; +import org.chromium.chrome.browser.vr.util.VrBrowserTransitionUtils; import org.chromium.chrome.test.ChromeJUnit4ClassRunner; import org.chromium.chrome.test.util.ChromeTabUtils; import org.chromium.content.browser.test.util.Criteria; @@ -48,16 +48,16 @@ // We explicitly instantiate a rule here instead of using parameterization since this class // only ever runs in ChromeTabbedActivity. @Rule - public ChromeTabbedActivityXrTestRule mVrTestRule = new ChromeTabbedActivityXrTestRule(); + public ChromeTabbedActivityVrTestRule mVrTestRule = new ChromeTabbedActivityVrTestRule(); - private VrTestFramework mVrTestFramework; + private VrBrowserTestFramework mVrBrowserTestFramework; private EmulatedVrController mController; @Before public void setUp() throws Exception { - mVrTestFramework = new VrTestFramework(mVrTestRule); - VrTransitionUtils.forceEnterVr(); - VrTransitionUtils.waitForVrEntry(POLL_TIMEOUT_LONG_MS); + mVrBrowserTestFramework = new VrBrowserTestFramework(mVrTestRule); + VrBrowserTransitionUtils.forceEnterVrBrowser(); + VrBrowserTransitionUtils.waitForVrEntry(POLL_TIMEOUT_LONG_MS); mController = new EmulatedVrController(mVrTestRule.getActivity()); mController.recenterView(); } @@ -80,7 +80,8 @@ @Test @MediumTest public void testControllerScrolling() throws InterruptedException { - mVrTestRule.loadUrl(VrTestFramework.getFileUrlForHtmlTestFile("test_controller_scrolling"), + mVrTestRule.loadUrl( + VrBrowserTestFramework.getFileUrlForHtmlTestFile("test_controller_scrolling"), PAGE_LOAD_TIMEOUT_S); final RenderCoordinates coord = RenderCoordinates.fromWebContents(mVrTestRule.getWebContents()); @@ -125,7 +126,8 @@ @Test @MediumTest public void testControllerFlingScrolling() throws InterruptedException { - mVrTestRule.loadUrl(VrTestFramework.getFileUrlForHtmlTestFile("test_controller_scrolling"), + mVrTestRule.loadUrl( + VrBrowserTestFramework.getFileUrlForHtmlTestFile("test_controller_scrolling"), PAGE_LOAD_TIMEOUT_S); final RenderCoordinates coord = RenderCoordinates.fromWebContents(mVrTestRule.getWebContents()); @@ -190,13 +192,13 @@ @Test @MediumTest public void testControllerClicksRegisterOnWebpage() throws InterruptedException { - mVrTestRule.loadUrl(VrTestFramework.getFileUrlForHtmlTestFile( + mVrTestRule.loadUrl(VrBrowserTestFramework.getFileUrlForHtmlTestFile( "test_controller_clicks_register_on_webpage"), PAGE_LOAD_TIMEOUT_S); mController.performControllerClick(); ChromeTabUtils.waitForTabPageLoaded(mVrTestRule.getActivity().getActivityTab(), - VrTestFramework.getFileUrlForHtmlTestFile("test_navigation_2d_page")); + VrBrowserTestFramework.getFileUrlForHtmlTestFile("test_navigation_2d_page")); } /* @@ -206,22 +208,26 @@ @Test @MediumTest public void testControllerScrollingNative() throws InterruptedException { - VrTransitionUtils.forceEnterVr(); - VrTransitionUtils.waitForVrEntry(POLL_TIMEOUT_LONG_MS); + VrBrowserTransitionUtils.forceEnterVrBrowser(); + VrBrowserTransitionUtils.waitForVrEntry(POLL_TIMEOUT_LONG_MS); // Fill history with enough items to scroll - mVrTestRule.loadUrl(VrTestFramework.getFileUrlForHtmlTestFile("test_navigation_2d_page"), + mVrTestRule.loadUrl( + VrBrowserTestFramework.getFileUrlForHtmlTestFile("test_navigation_2d_page"), PAGE_LOAD_TIMEOUT_S); - mVrTestRule.loadUrl(VrTestFramework.getFileUrlForHtmlTestFile("test_controller_scrolling"), + mVrTestRule.loadUrl( + VrBrowserTestFramework.getFileUrlForHtmlTestFile("test_controller_scrolling"), PAGE_LOAD_TIMEOUT_S); - mVrTestRule.loadUrl(VrTestFramework.getFileUrlForHtmlTestFile("generic_webvr_page"), + mVrTestRule.loadUrl(VrBrowserTestFramework.getFileUrlForHtmlTestFile("generic_webvr_page"), PAGE_LOAD_TIMEOUT_S); - mVrTestRule.loadUrl(VrTestFramework.getFileUrlForHtmlTestFile("test_navigation_webvr_page"), + mVrTestRule.loadUrl( + VrBrowserTestFramework.getFileUrlForHtmlTestFile("test_navigation_webvr_page"), PAGE_LOAD_TIMEOUT_S); - mVrTestRule.loadUrl(VrTestFramework.getFileUrlForHtmlTestFile("test_webvr_autopresent"), + mVrTestRule.loadUrl( + VrBrowserTestFramework.getFileUrlForHtmlTestFile("test_webvr_autopresent"), PAGE_LOAD_TIMEOUT_S); - mVrTestRule.loadUrl(VrTestFramework.getFileUrlForHtmlTestFile("generic_webxr_page"), + mVrTestRule.loadUrl(VrBrowserTestFramework.getFileUrlForHtmlTestFile("generic_webxr_page"), PAGE_LOAD_TIMEOUT_S); - mVrTestRule.loadUrl(VrTestFramework.getFileUrlForHtmlTestFile("test_gamepad_button"), + mVrTestRule.loadUrl(VrBrowserTestFramework.getFileUrlForHtmlTestFile("test_gamepad_button"), PAGE_LOAD_TIMEOUT_S); mVrTestRule.loadUrl("chrome://history", PAGE_LOAD_TIMEOUT_S); @@ -255,21 +261,21 @@ @MediumTest @RetryOnFailure(message = "Very rarely, button press not registered (race condition?)") public void testAppButtonExitsFullscreen() throws InterruptedException, TimeoutException { - mVrTestFramework.loadUrlAndAwaitInitialization( - VrTestFramework.getFileUrlForHtmlTestFile("test_navigation_2d_page"), + mVrBrowserTestFramework.loadUrlAndAwaitInitialization( + VrBrowserTestFramework.getFileUrlForHtmlTestFile("test_navigation_2d_page"), PAGE_LOAD_TIMEOUT_S); // Enter fullscreen - DOMUtils.clickNode(mVrTestFramework.getFirstTabWebContents(), "fullscreen", + DOMUtils.clickNode(mVrBrowserTestFramework.getFirstTabWebContents(), "fullscreen", false /* goThroughRootAndroidView */); - VrTestFramework.waitOnJavaScriptStep(mVrTestFramework.getFirstTabWebContents()); - Assert.assertTrue(DOMUtils.isFullscreen(mVrTestFramework.getFirstTabWebContents())); + mVrBrowserTestFramework.waitOnJavaScriptStep(); + Assert.assertTrue(DOMUtils.isFullscreen(mVrBrowserTestFramework.getFirstTabWebContents())); mController.pressReleaseAppButton(); CriteriaHelper.pollInstrumentationThread(new Criteria() { @Override public boolean isSatisfied() { try { - return !DOMUtils.isFullscreen(mVrTestFramework.getFirstTabWebContents()); + return !DOMUtils.isFullscreen(mVrBrowserTestFramework.getFirstTabWebContents()); } catch (InterruptedException | TimeoutException e) { return false; }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrBrowserDialogTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrBrowserDialogTest.java index 1fadc499..d106a96 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrBrowserDialogTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrBrowserDialogTest.java
@@ -4,10 +4,9 @@ package org.chromium.chrome.browser.vr; -import static org.chromium.chrome.browser.vr.TestFramework.POLL_TIMEOUT_LONG_MS; -import static org.chromium.chrome.browser.vr.VrTestFramework.PAGE_LOAD_TIMEOUT_S; -import static org.chromium.chrome.browser.vr.VrTestFramework.POLL_TIMEOUT_LONG_MS; -import static org.chromium.chrome.browser.vr.VrTestFramework.POLL_TIMEOUT_SHORT_MS; +import static org.chromium.chrome.browser.vr.XrTestFramework.PAGE_LOAD_TIMEOUT_S; +import static org.chromium.chrome.browser.vr.XrTestFramework.POLL_TIMEOUT_LONG_MS; +import static org.chromium.chrome.browser.vr.XrTestFramework.POLL_TIMEOUT_SHORT_MS; import static org.chromium.chrome.test.util.ChromeRestriction.RESTRICTION_TYPE_VIEWER_DAYDREAM; import android.graphics.PointF; @@ -26,10 +25,10 @@ import org.chromium.base.test.util.Restriction; import org.chromium.base.test.util.UrlUtils; import org.chromium.chrome.browser.ChromeSwitches; -import org.chromium.chrome.browser.vr.rules.ChromeTabbedActivityXrTestRule; +import org.chromium.chrome.browser.vr.rules.ChromeTabbedActivityVrTestRule; import org.chromium.chrome.browser.vr.rules.HeadTrackingMode; import org.chromium.chrome.browser.vr.util.NativeUiUtils; -import org.chromium.chrome.browser.vr.util.TransitionUtils; +import org.chromium.chrome.browser.vr.util.VrBrowserTransitionUtils; import org.chromium.chrome.test.ChromeJUnit4ClassRunner; import org.chromium.content.browser.test.util.JavaScriptUtils; import org.chromium.net.test.EmbeddedTestServer; @@ -59,14 +58,14 @@ // We explicitly instantiate a rule here instead of using parameterization since this class // only ever runs in ChromeTabbedActivity. @Rule - public ChromeTabbedActivityXrTestRule mVrTestRule = new ChromeTabbedActivityXrTestRule(); + public ChromeTabbedActivityVrTestRule mVrTestRule = new ChromeTabbedActivityVrTestRule(); - private VrTestFramework mVrTestFramework; + private VrBrowserTestFramework mVrBrowserTestFramework; private EmbeddedTestServer mServer; @Before public void setUp() throws Exception { - mVrTestFramework = new VrTestFramework(mVrTestRule); + mVrBrowserTestFramework = new VrBrowserTestFramework(mVrTestRule); // Create UiCapture image directory. if (!sBaseDirectory.exists() && !sBaseDirectory.isDirectory()) { @@ -102,16 +101,16 @@ if (mServer == null) { mServer = EmbeddedTestServer.createAndStartServer(InstrumentationRegistry.getContext()); } - mVrTestFramework.loadUrlAndAwaitInitialization( - mServer.getURL(VrTestFramework.getEmbeddedServerPathForHtmlTestFile(initialPage)), + mVrBrowserTestFramework.loadUrlAndAwaitInitialization( + mServer.getURL( + VrBrowserTestFramework.getEmbeddedServerPathForHtmlTestFile(initialPage)), PAGE_LOAD_TIMEOUT_S); // Display the given permission prompt. - Assert.assertTrue(TransitionUtils.forceEnterVr()); - TransitionUtils.waitForVrEntry(POLL_TIMEOUT_LONG_MS); - VrTestFramework.runJavaScriptOrFail(navigationCommand, POLL_TIMEOUT_SHORT_MS, - mVrTestFramework.getFirstTabWebContents()); - TransitionUtils.waitForNativeUiPrompt(POLL_TIMEOUT_LONG_MS); + Assert.assertTrue(VrBrowserTransitionUtils.forceEnterVrBrowser()); + VrBrowserTransitionUtils.waitForVrEntry(POLL_TIMEOUT_LONG_MS); + mVrBrowserTestFramework.runJavaScriptOrFail(navigationCommand, POLL_TIMEOUT_SHORT_MS); + VrBrowserTransitionUtils.waitForNativeUiPrompt(POLL_TIMEOUT_LONG_MS); // There is currently no way to know whether a dialog has been drawn yet, // so sleep long enough for it to show up. @@ -120,17 +119,17 @@ private void displayJavascriptDialog(String initialPage, String navigationCommand) throws InterruptedException, TimeoutException { - mVrTestFramework.loadUrlAndAwaitInitialization( - VrTestFramework.getFileUrlForHtmlTestFile(initialPage), PAGE_LOAD_TIMEOUT_S); + mVrBrowserTestFramework.loadUrlAndAwaitInitialization( + VrBrowserTestFramework.getFileUrlForHtmlTestFile(initialPage), PAGE_LOAD_TIMEOUT_S); // Display the JavaScript dialog. - Assert.assertTrue(TransitionUtils.forceEnterVr()); - TransitionUtils.waitForVrEntry(POLL_TIMEOUT_LONG_MS); + Assert.assertTrue(VrBrowserTransitionUtils.forceEnterVrBrowser()); + VrBrowserTransitionUtils.waitForVrEntry(POLL_TIMEOUT_LONG_MS); // We can't use runJavaScriptOrFail here because JavaScript execution is blocked while a // JS dialog is visible, so runJavaScriptOrFail will always time out. JavaScriptUtils.executeJavaScript( - mVrTestFramework.getFirstTabWebContents(), navigationCommand); - TransitionUtils.waitForNativeUiPrompt(POLL_TIMEOUT_LONG_MS); + mVrBrowserTestFramework.getFirstTabWebContents(), navigationCommand); + VrBrowserTransitionUtils.waitForNativeUiPrompt(POLL_TIMEOUT_LONG_MS); // There is currently no way to know whether a dialog has been drawn yet, // so sleep long enough for it to show up. @@ -139,10 +138,10 @@ private void clickElement(String initialPage, int elementName) throws InterruptedException, TimeoutException { - mVrTestFramework.loadUrlAndAwaitInitialization( - VrTestFramework.getFileUrlForHtmlTestFile(initialPage), PAGE_LOAD_TIMEOUT_S); - Assert.assertTrue(TransitionUtils.forceEnterVr()); - TransitionUtils.waitForVrEntry(POLL_TIMEOUT_LONG_MS); + mVrBrowserTestFramework.loadUrlAndAwaitInitialization( + VrBrowserTestFramework.getFileUrlForHtmlTestFile(initialPage), PAGE_LOAD_TIMEOUT_S); + Assert.assertTrue(VrBrowserTransitionUtils.forceEnterVrBrowser()); + VrBrowserTransitionUtils.waitForVrEntry(POLL_TIMEOUT_LONG_MS); Thread.sleep(VR_ENTRY_SLEEP_MS); NativeUiUtils.clickElementAndWaitForUiQuiescence(elementName, new PointF(0, 0)); // Technically not necessary, but clicking on native elements causes the laser to originate @@ -262,9 +261,7 @@ NativeUiUtils.revertToRealControllerAndWaitForUiQuiescence(); // Ensure the cancel button was clicked. Assert.assertTrue("Negative button clicked", - VrTestFramework - .runJavaScriptOrFail("c", POLL_TIMEOUT_SHORT_MS, - mVrTestFramework.getFirstTabWebContents()) + mVrBrowserTestFramework.runJavaScriptOrFail("c", POLL_TIMEOUT_SHORT_MS) .equals("false")); Assert.assertTrue(captureScreen("JavaScriptConfirm_Dismissed")); } @@ -290,9 +287,8 @@ // will only be what we expect if the positive button was actually clicked (as opposed to // canceled). Assert.assertTrue("Positive button clicked", - VrTestFramework - .runJavaScriptOrFail("p == '" + expectedString + "'", POLL_TIMEOUT_SHORT_MS, - mVrTestFramework.getFirstTabWebContents()) + mVrBrowserTestFramework + .runJavaScriptOrFail("p == '" + expectedString + "'", POLL_TIMEOUT_SHORT_MS) .equals("true")); Assert.assertTrue(captureScreen("JavaScriptPrompt_Dismissed")); }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrBrowserNativeUiTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrBrowserNativeUiTest.java index 7af70eb..66d0a73 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrBrowserNativeUiTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrBrowserNativeUiTest.java
@@ -4,9 +4,9 @@ package org.chromium.chrome.browser.vr; -import static org.chromium.chrome.browser.vr.VrTestFramework.NATIVE_URLS_OF_INTEREST; -import static org.chromium.chrome.browser.vr.VrTestFramework.PAGE_LOAD_TIMEOUT_S; -import static org.chromium.chrome.browser.vr.VrTestFramework.POLL_TIMEOUT_LONG_MS; +import static org.chromium.chrome.browser.vr.XrTestFramework.NATIVE_URLS_OF_INTEREST; +import static org.chromium.chrome.browser.vr.XrTestFramework.PAGE_LOAD_TIMEOUT_S; +import static org.chromium.chrome.browser.vr.XrTestFramework.POLL_TIMEOUT_LONG_MS; import static org.chromium.chrome.test.util.ChromeRestriction.RESTRICTION_TYPE_VIEWER_DAYDREAM; import android.support.test.filters.MediumTest; @@ -20,8 +20,8 @@ import org.chromium.base.test.util.CommandLineFlags; import org.chromium.base.test.util.Restriction; import org.chromium.chrome.browser.ChromeSwitches; -import org.chromium.chrome.browser.vr.rules.ChromeTabbedActivityXrTestRule; -import org.chromium.chrome.browser.vr.util.VrTransitionUtils; +import org.chromium.chrome.browser.vr.rules.ChromeTabbedActivityVrTestRule; +import org.chromium.chrome.browser.vr.util.VrBrowserTransitionUtils; import org.chromium.chrome.test.ChromeJUnit4ClassRunner; import java.util.concurrent.TimeoutException; @@ -36,15 +36,15 @@ // We explicitly instantiate a rule here instead of using parameterization since this class // only ever runs in ChromeTabbedActivity. @Rule - public ChromeTabbedActivityXrTestRule mVrTestRule = new ChromeTabbedActivityXrTestRule(); + public ChromeTabbedActivityVrTestRule mVrTestRule = new ChromeTabbedActivityVrTestRule(); private static final String TEST_PAGE_2D_URL = - VrTestFramework.getFileUrlForHtmlTestFile("test_navigation_2d_page"); + VrBrowserTestFramework.getFileUrlForHtmlTestFile("test_navigation_2d_page"); @Before public void setUp() throws Exception { - VrTransitionUtils.forceEnterVr(); - VrTransitionUtils.waitForVrEntry(POLL_TIMEOUT_LONG_MS); + VrBrowserTransitionUtils.forceEnterVrBrowser(); + VrBrowserTransitionUtils.waitForVrEntry(POLL_TIMEOUT_LONG_MS); } /**
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrBrowserNavigationTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrBrowserNavigationTest.java index d71b8f91..1b489db 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrBrowserNavigationTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrBrowserNavigationTest.java
@@ -4,10 +4,10 @@ package org.chromium.chrome.browser.vr; -import static org.chromium.chrome.browser.vr.VrTestFramework.NATIVE_URLS_OF_INTEREST; -import static org.chromium.chrome.browser.vr.VrTestFramework.PAGE_LOAD_TIMEOUT_S; -import static org.chromium.chrome.browser.vr.VrTestFramework.POLL_TIMEOUT_LONG_MS; -import static org.chromium.chrome.browser.vr.VrTestFramework.POLL_TIMEOUT_SHORT_MS; +import static org.chromium.chrome.browser.vr.XrTestFramework.NATIVE_URLS_OF_INTEREST; +import static org.chromium.chrome.browser.vr.XrTestFramework.PAGE_LOAD_TIMEOUT_S; +import static org.chromium.chrome.browser.vr.XrTestFramework.POLL_TIMEOUT_LONG_MS; +import static org.chromium.chrome.browser.vr.XrTestFramework.POLL_TIMEOUT_SHORT_MS; import static org.chromium.chrome.test.util.ChromeRestriction.RESTRICTION_TYPE_VIEWER_DAYDREAM; import android.support.test.InstrumentationRegistry; @@ -28,10 +28,9 @@ import org.chromium.chrome.browser.history.HistoryPage; import org.chromium.chrome.browser.tab.Tab; import org.chromium.chrome.browser.tabmodel.TabModel.TabLaunchType; -import org.chromium.chrome.browser.vr.rules.ChromeTabbedActivityXrTestRule; -import org.chromium.chrome.browser.vr.util.TransitionUtils; +import org.chromium.chrome.browser.vr.rules.ChromeTabbedActivityVrTestRule; +import org.chromium.chrome.browser.vr.util.VrBrowserTransitionUtils; import org.chromium.chrome.browser.vr.util.VrInfoBarUtils; -import org.chromium.chrome.browser.vr.util.VrTransitionUtils; import org.chromium.chrome.test.ChromeActivityTestRule; import org.chromium.chrome.test.ChromeJUnit4ClassRunner; import org.chromium.chrome.test.util.ChromeTabUtils; @@ -53,19 +52,20 @@ // We explicitly instantiate a rule here instead of using parameterization since this class // only ever runs in ChromeTabbedActivity. @Rule - public ChromeTabbedActivityXrTestRule mTestRule = new ChromeTabbedActivityXrTestRule(); + public ChromeTabbedActivityVrTestRule mTestRule = new ChromeTabbedActivityVrTestRule(); - private VrTestFramework mVrTestFramework; - private XrTestFramework mXrTestFramework; + private WebXrVrTestFramework mWebXrVrTestFramework; + private WebVrTestFramework mWebVrTestFramework; + private VrBrowserTestFramework mVrBrowserTestFramework; private static final String TEST_PAGE_2D_URL = - VrTestFramework.getFileUrlForHtmlTestFile("test_navigation_2d_page"); + VrBrowserTestFramework.getFileUrlForHtmlTestFile("test_navigation_2d_page"); private static final String TEST_PAGE_2D_2_URL = - VrTestFramework.getFileUrlForHtmlTestFile("test_navigation_2d_page2"); + VrBrowserTestFramework.getFileUrlForHtmlTestFile("test_navigation_2d_page2"); private static final String TEST_PAGE_WEBVR_URL = - VrTestFramework.getFileUrlForHtmlTestFile("test_navigation_webvr_page"); + WebVrTestFramework.getFileUrlForHtmlTestFile("test_navigation_webvr_page"); private static final String TEST_PAGE_WEBXR_URL = - XrTestFramework.getFileUrlForHtmlTestFile("test_navigation_webxr_page"); + WebXrVrTestFramework.getFileUrlForHtmlTestFile("test_navigation_webxr_page"); private enum Page { PAGE_2D, PAGE_2D_2, PAGE_WEBVR, PAGE_WEBXR } private enum PresentationMode { NON_PRESENTING, PRESENTING } @@ -73,10 +73,11 @@ @Before public void setUp() throws Exception { - mVrTestFramework = new VrTestFramework(mTestRule); - mXrTestFramework = new XrTestFramework(mTestRule); - TransitionUtils.forceEnterVr(); - TransitionUtils.waitForVrEntry(POLL_TIMEOUT_LONG_MS); + mWebXrVrTestFramework = new WebXrVrTestFramework(mTestRule); + mWebVrTestFramework = new WebVrTestFramework(mTestRule); + mVrBrowserTestFramework = new VrBrowserTestFramework(mTestRule); + VrBrowserTransitionUtils.forceEnterVrBrowser(); + VrBrowserTransitionUtils.waitForVrEntry(POLL_TIMEOUT_LONG_MS); } private String getUrl(Page page) { @@ -104,9 +105,9 @@ mTestRule.getActivity().getActivityTab(), new Runnable() { @Override public void run() { - VrTestFramework.runJavaScriptOrFail( + mVrBrowserTestFramework.runJavaScriptOrFail( "window.location.href = '" + getUrl(to) + "';", - POLL_TIMEOUT_SHORT_MS, mVrTestFramework.getFirstTabWebContents()); + POLL_TIMEOUT_SHORT_MS); } }, POLL_TIMEOUT_LONG_MS); } @@ -114,7 +115,7 @@ private void enterFullscreenOrFail(WebContents webContents) throws InterruptedException, TimeoutException { DOMUtils.clickNode(webContents, "fullscreen", false /* goThroughRootAndroidView */); - TestFramework.waitOnJavaScriptStep(webContents); + VrBrowserTestFramework.waitOnJavaScriptStep(webContents); Assert.assertTrue(DOMUtils.isFullscreen(webContents)); } @@ -128,7 +129,7 @@ Assert.assertEquals("Browser is in fullscreen", fullscreenMode == FullscreenMode.FULLSCREENED, DOMUtils.isFullscreen(wc)); // Feedback infobar should never show up during navigations. - VrInfoBarUtils.expectInfoBarPresent(mVrTestFramework, false); + VrInfoBarUtils.expectInfoBarPresent(mTestRule, false); } /** @@ -138,11 +139,12 @@ @Test @MediumTest public void test2dTo2d() throws InterruptedException, TimeoutException { - mVrTestFramework.loadUrlAndAwaitInitialization(TEST_PAGE_2D_URL, PAGE_LOAD_TIMEOUT_S); + mVrBrowserTestFramework.loadUrlAndAwaitInitialization( + TEST_PAGE_2D_URL, PAGE_LOAD_TIMEOUT_S); navigateTo(Page.PAGE_2D_2); - assertState(mVrTestFramework.getFirstTabWebContents(), Page.PAGE_2D_2, + assertState(mVrBrowserTestFramework.getFirstTabWebContents(), Page.PAGE_2D_2, PresentationMode.NON_PRESENTING, FullscreenMode.NON_FULLSCREENED); // Test that the navigations were added to history @@ -164,7 +166,7 @@ ThreadUtils.runOnUiThreadBlocking(() -> itemViews.get(0).onClick()); ChromeTabUtils.waitForTabPageLoaded( mTestRule.getActivity().getActivityTab(), getUrl(Page.PAGE_2D_2)); - assertState(mVrTestFramework.getFirstTabWebContents(), Page.PAGE_2D_2, + assertState(mWebXrVrTestFramework.getFirstTabWebContents(), Page.PAGE_2D_2, PresentationMode.NON_PRESENTING, FullscreenMode.NON_FULLSCREENED); } @@ -175,7 +177,7 @@ @MediumTest public void test2dToWebVr() throws IllegalArgumentException, InterruptedException, TimeoutException { - impl2dToWeb(Page.PAGE_WEBVR, mVrTestFramework); + impl2dToWeb(Page.PAGE_WEBVR, mWebVrTestFramework); } /** @@ -188,10 +190,10 @@ @CommandLineFlags.Add({"enable-features=WebXR"}) public void test2dToWebXr() throws IllegalArgumentException, InterruptedException, TimeoutException { - impl2dToWeb(Page.PAGE_WEBXR, mXrTestFramework); + impl2dToWeb(Page.PAGE_WEBXR, mWebXrVrTestFramework); } - private void impl2dToWeb(Page page, TestFramework framework) + private void impl2dToWeb(Page page, WebXrVrTestFramework framework) throws InterruptedException, TimeoutException { framework.loadUrlAndAwaitInitialization(TEST_PAGE_2D_URL, PAGE_LOAD_TIMEOUT_S); @@ -208,7 +210,7 @@ @MediumTest public void test2dFullscreenToWebVr() throws IllegalArgumentException, InterruptedException, TimeoutException { - impl2dFullscreenToWeb(Page.PAGE_WEBVR, mVrTestFramework); + impl2dFullscreenToWeb(Page.PAGE_WEBVR, mWebVrTestFramework); } /** @@ -221,10 +223,10 @@ @CommandLineFlags.Add({"enable-features=WebXR"}) public void test2dFullscreenToWebXr() throws IllegalArgumentException, InterruptedException, TimeoutException { - impl2dFullscreenToWeb(Page.PAGE_WEBXR, mXrTestFramework); + impl2dFullscreenToWeb(Page.PAGE_WEBXR, mWebXrVrTestFramework); } - private void impl2dFullscreenToWeb(Page page, TestFramework framework) + private void impl2dFullscreenToWeb(Page page, WebXrVrTestFramework framework) throws InterruptedException, TimeoutException { framework.loadUrlAndAwaitInitialization(TEST_PAGE_2D_URL, PAGE_LOAD_TIMEOUT_S); enterFullscreenOrFail(framework.getFirstTabWebContents()); @@ -242,7 +244,7 @@ @MediumTest public void testWebVrTo2d() throws IllegalArgumentException, InterruptedException, TimeoutException { - webTo2dImpl(Page.PAGE_WEBVR, mVrTestFramework); + webTo2dImpl(Page.PAGE_WEBVR, mWebVrTestFramework); } /** @@ -255,10 +257,10 @@ @CommandLineFlags.Add({"enable-features=WebXR"}) public void testWebXrTo2d() throws IllegalArgumentException, InterruptedException, TimeoutException { - webTo2dImpl(Page.PAGE_WEBXR, mXrTestFramework); + webTo2dImpl(Page.PAGE_WEBXR, mWebXrVrTestFramework); } - private void webTo2dImpl(Page page, TestFramework framework) + private void webTo2dImpl(Page page, WebXrVrTestFramework framework) throws InterruptedException, TimeoutException { framework.loadUrlAndAwaitInitialization(getUrl(page), PAGE_LOAD_TIMEOUT_S); @@ -275,7 +277,7 @@ @MediumTest public void testWebVrToWebVr() throws IllegalArgumentException, InterruptedException, TimeoutException { - webToWebImpl(Page.PAGE_WEBVR, mVrTestFramework); + webToWebImpl(Page.PAGE_WEBVR, mWebVrTestFramework); } /** @@ -288,10 +290,10 @@ @CommandLineFlags.Add({"enable-features=WebXR"}) public void testWebXrToWebXr() throws IllegalArgumentException, InterruptedException, TimeoutException { - webToWebImpl(Page.PAGE_WEBXR, mXrTestFramework); + webToWebImpl(Page.PAGE_WEBXR, mWebXrVrTestFramework); } - private void webToWebImpl(Page page, TestFramework framework) + private void webToWebImpl(Page page, WebXrVrTestFramework framework) throws InterruptedException, TimeoutException { framework.loadUrlAndAwaitInitialization(getUrl(page), PAGE_LOAD_TIMEOUT_S); @@ -308,7 +310,7 @@ @MediumTest public void testWebVrPresentingTo2d() throws IllegalArgumentException, InterruptedException, TimeoutException { - webPresentingTo2dImpl(Page.PAGE_WEBVR, mVrTestFramework); + webPresentingTo2dImpl(Page.PAGE_WEBVR, mWebVrTestFramework); } /** @@ -321,13 +323,13 @@ @CommandLineFlags.Add({"enable-features=WebXR"}) public void testWebXrPresentingTo2d() throws IllegalArgumentException, InterruptedException, TimeoutException { - webPresentingTo2dImpl(Page.PAGE_WEBXR, mXrTestFramework); + webPresentingTo2dImpl(Page.PAGE_WEBXR, mWebXrVrTestFramework); } - private void webPresentingTo2dImpl(Page page, TestFramework framework) + private void webPresentingTo2dImpl(Page page, WebXrVrTestFramework framework) throws InterruptedException, TimeoutException { framework.loadUrlAndAwaitInitialization(getUrl(page), PAGE_LOAD_TIMEOUT_S); - TransitionUtils.enterPresentationOrFail(framework); + framework.enterSessionWithUserGestureOrFail(); navigateTo(Page.PAGE_2D); @@ -342,7 +344,7 @@ @MediumTest public void testWebVrPresentingToWebVr() throws IllegalArgumentException, InterruptedException, TimeoutException { - webPresentingToWebImpl(Page.PAGE_WEBVR, mVrTestFramework); + webPresentingToWebImpl(Page.PAGE_WEBVR, mWebVrTestFramework); } /** @@ -355,13 +357,13 @@ @CommandLineFlags.Add({"enable-features=WebXR"}) public void testWebXrPresentingToWebXr() throws IllegalArgumentException, InterruptedException, TimeoutException { - webPresentingToWebImpl(Page.PAGE_WEBXR, mXrTestFramework); + webPresentingToWebImpl(Page.PAGE_WEBXR, mWebXrVrTestFramework); } - private void webPresentingToWebImpl(Page page, TestFramework framework) + private void webPresentingToWebImpl(Page page, WebXrVrTestFramework framework) throws InterruptedException, TimeoutException { framework.loadUrlAndAwaitInitialization(getUrl(page), PAGE_LOAD_TIMEOUT_S); - TransitionUtils.enterPresentationOrFail(framework); + framework.enterSessionWithUserGestureOrFail(); navigateTo(page); @@ -376,7 +378,7 @@ @MediumTest public void testWebVrFullscreenTo2d() throws IllegalArgumentException, InterruptedException, TimeoutException { - webFullscreenTo2dImpl(Page.PAGE_WEBVR, mVrTestFramework); + webFullscreenTo2dImpl(Page.PAGE_WEBVR, mWebVrTestFramework); } /** @@ -389,10 +391,10 @@ @CommandLineFlags.Add({"enable-features=WebXR"}) public void testWebXrFullscreenTo2d() throws IllegalArgumentException, InterruptedException, TimeoutException { - webFullscreenTo2dImpl(Page.PAGE_WEBXR, mXrTestFramework); + webFullscreenTo2dImpl(Page.PAGE_WEBXR, mWebXrVrTestFramework); } - private void webFullscreenTo2dImpl(Page page, TestFramework framework) + private void webFullscreenTo2dImpl(Page page, WebXrVrTestFramework framework) throws InterruptedException, TimeoutException { framework.loadUrlAndAwaitInitialization(getUrl(page), PAGE_LOAD_TIMEOUT_S); enterFullscreenOrFail(framework.getFirstTabWebContents()); @@ -410,7 +412,7 @@ @MediumTest public void testWebVrFullscreenToWebVr() throws IllegalArgumentException, InterruptedException, TimeoutException { - webFullscreenToWebImpl(Page.PAGE_WEBVR, mVrTestFramework); + webFullscreenToWebImpl(Page.PAGE_WEBVR, mWebVrTestFramework); } /** @@ -423,10 +425,10 @@ @CommandLineFlags.Add({"enable-features=WebXR"}) public void testWebXrFullscreenToWebXr() throws IllegalArgumentException, InterruptedException, TimeoutException { - webFullscreenToWebImpl(Page.PAGE_WEBXR, mXrTestFramework); + webFullscreenToWebImpl(Page.PAGE_WEBXR, mWebXrVrTestFramework); } - private void webFullscreenToWebImpl(Page page, TestFramework framework) + private void webFullscreenToWebImpl(Page page, WebXrVrTestFramework framework) throws InterruptedException, TimeoutException { framework.loadUrlAndAwaitInitialization(getUrl(page), PAGE_LOAD_TIMEOUT_S); enterFullscreenOrFail(framework.getFirstTabWebContents()); @@ -445,19 +447,23 @@ @MediumTest public void testBackDoesntBackgroundChrome() throws IllegalArgumentException, InterruptedException { - Assert.assertFalse("Back button isn't disabled.", VrTransitionUtils.isBackButtonEnabled()); + Assert.assertFalse( + "Back button isn't disabled.", VrBrowserTransitionUtils.isBackButtonEnabled()); mTestRule.loadUrlInNewTab(getUrl(Page.PAGE_2D), false, TabLaunchType.FROM_CHROME_UI); - Assert.assertFalse("Back button isn't disabled.", VrTransitionUtils.isBackButtonEnabled()); + Assert.assertFalse( + "Back button isn't disabled.", VrBrowserTransitionUtils.isBackButtonEnabled()); final Tab tab = mTestRule.loadUrlInNewTab(getUrl(Page.PAGE_2D), false, TabLaunchType.FROM_LINK); - Assert.assertTrue("Back button isn't enabled.", VrTransitionUtils.isBackButtonEnabled()); + Assert.assertTrue( + "Back button isn't enabled.", VrBrowserTransitionUtils.isBackButtonEnabled()); ThreadUtils.runOnUiThreadBlocking(new Runnable() { @Override public void run() { mTestRule.getActivity().getTabModelSelector().closeTab(tab); } }); - Assert.assertFalse("Back button isn't disabled.", VrTransitionUtils.isBackButtonEnabled()); + Assert.assertFalse( + "Back button isn't disabled.", VrBrowserTransitionUtils.isBackButtonEnabled()); } /** @@ -466,33 +472,38 @@ @Test @MediumTest public void testNavigationButtons() throws IllegalArgumentException, InterruptedException { - Assert.assertFalse("Back button isn't disabled.", VrTransitionUtils.isBackButtonEnabled()); Assert.assertFalse( - "Forward button isn't disabled.", VrTransitionUtils.isForwardButtonEnabled()); + "Back button isn't disabled.", VrBrowserTransitionUtils.isBackButtonEnabled()); + Assert.assertFalse("Forward button isn't disabled.", + VrBrowserTransitionUtils.isForwardButtonEnabled()); // Opening a new tab shouldn't enable the back button mTestRule.loadUrlInNewTab(getUrl(Page.PAGE_2D), false, TabLaunchType.FROM_CHROME_UI); - Assert.assertFalse("Back button isn't disabled.", VrTransitionUtils.isBackButtonEnabled()); Assert.assertFalse( - "Forward button isn't disabled.", VrTransitionUtils.isForwardButtonEnabled()); + "Back button isn't disabled.", VrBrowserTransitionUtils.isBackButtonEnabled()); + Assert.assertFalse("Forward button isn't disabled.", + VrBrowserTransitionUtils.isForwardButtonEnabled()); // Navigating to a new page should enable the back button mTestRule.loadUrl(getUrl(Page.PAGE_WEBVR)); - Assert.assertTrue("Back button isn't enabled.", VrTransitionUtils.isBackButtonEnabled()); - Assert.assertFalse( - "Forward button isn't disabled.", VrTransitionUtils.isForwardButtonEnabled()); + Assert.assertTrue( + "Back button isn't enabled.", VrBrowserTransitionUtils.isBackButtonEnabled()); + Assert.assertFalse("Forward button isn't disabled.", + VrBrowserTransitionUtils.isForwardButtonEnabled()); // Navigating back should disable the back button and enable the forward button - VrTransitionUtils.navigateBack(); + VrBrowserTransitionUtils.navigateBack(); ChromeTabUtils.waitForTabPageLoaded( mTestRule.getActivity().getActivityTab(), getUrl(Page.PAGE_2D)); - Assert.assertFalse("Back button isn't disabled.", VrTransitionUtils.isBackButtonEnabled()); + Assert.assertFalse( + "Back button isn't disabled.", VrBrowserTransitionUtils.isBackButtonEnabled()); Assert.assertTrue( - "Forward button isn't enabled.", VrTransitionUtils.isForwardButtonEnabled()); + "Forward button isn't enabled.", VrBrowserTransitionUtils.isForwardButtonEnabled()); // Navigating forward should disable the forward button and enable the back button - VrTransitionUtils.navigateForward(); + VrBrowserTransitionUtils.navigateForward(); ChromeTabUtils.waitForTabPageLoaded( mTestRule.getActivity().getActivityTab(), getUrl(Page.PAGE_WEBVR)); - Assert.assertTrue("Back button isn't enabled.", VrTransitionUtils.isBackButtonEnabled()); - Assert.assertFalse( - "Forward button isn't disabled.", VrTransitionUtils.isForwardButtonEnabled()); + Assert.assertTrue( + "Back button isn't enabled.", VrBrowserTransitionUtils.isBackButtonEnabled()); + Assert.assertFalse("Forward button isn't disabled.", + VrBrowserTransitionUtils.isForwardButtonEnabled()); } /** @@ -519,19 +530,20 @@ @MediumTest public void testRendererKilledInFullscreenStaysInVr() throws IllegalArgumentException, InterruptedException, TimeoutException { - mVrTestFramework.loadUrlAndAwaitInitialization(TEST_PAGE_2D_URL, PAGE_LOAD_TIMEOUT_S); - enterFullscreenOrFail(mVrTestFramework.getFirstTabWebContents()); + mVrBrowserTestFramework.loadUrlAndAwaitInitialization( + TEST_PAGE_2D_URL, PAGE_LOAD_TIMEOUT_S); + enterFullscreenOrFail(mVrBrowserTestFramework.getFirstTabWebContents()); final Tab tab = mTestRule.getActivity().getActivityTab(); ThreadUtils.runOnUiThreadBlocking(() -> tab.simulateRendererKilledForTesting(true)); - mVrTestFramework.simulateRendererKilled(); + mVrBrowserTestFramework.simulateRendererKilled(); ThreadUtils.runOnUiThreadBlocking(() -> tab.reload()); ChromeTabUtils.waitForTabPageLoaded(tab, TEST_PAGE_2D_URL); ChromeTabUtils.waitForInteractable(tab); - assertState(mVrTestFramework.getFirstTabWebContents(), Page.PAGE_2D, + assertState(mVrBrowserTestFramework.getFirstTabWebContents(), Page.PAGE_2D, PresentationMode.NON_PRESENTING, FullscreenMode.NON_FULLSCREENED); } }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrBrowserTestFramework.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrBrowserTestFramework.java new file mode 100644 index 0000000..f5efb59c --- /dev/null +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrBrowserTestFramework.java
@@ -0,0 +1,20 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.browser.vr; + +import org.junit.Assert; + +import org.chromium.chrome.test.ChromeActivityTestRule; + +/** + * Extension of VrTestFramework containing VR Browser-specific functionality. Mainly exists in order + * to preserve the naming convention of using XYZTestFramework for testing feature XYZ. + */ +public class VrBrowserTestFramework extends XrTestFramework { + public VrBrowserTestFramework(ChromeActivityTestRule rule) { + super(rule); + Assert.assertFalse("Test did not start in VR", VrShellDelegate.isInVr()); + } +}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrBrowserTransitionTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrBrowserTransitionTest.java index e81e58b..8e7654c 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrBrowserTransitionTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrBrowserTransitionTest.java
@@ -4,11 +4,10 @@ package org.chromium.chrome.browser.vr; -import static org.chromium.chrome.browser.vr.TestFramework.POLL_TIMEOUT_LONG_MS; -import static org.chromium.chrome.browser.vr.VrTestFramework.PAGE_LOAD_TIMEOUT_S; -import static org.chromium.chrome.browser.vr.VrTestFramework.POLL_CHECK_INTERVAL_SHORT_MS; -import static org.chromium.chrome.browser.vr.VrTestFramework.POLL_TIMEOUT_LONG_MS; -import static org.chromium.chrome.browser.vr.VrTestFramework.POLL_TIMEOUT_SHORT_MS; +import static org.chromium.chrome.browser.vr.XrTestFramework.PAGE_LOAD_TIMEOUT_S; +import static org.chromium.chrome.browser.vr.XrTestFramework.POLL_CHECK_INTERVAL_SHORT_MS; +import static org.chromium.chrome.browser.vr.XrTestFramework.POLL_TIMEOUT_LONG_MS; +import static org.chromium.chrome.browser.vr.XrTestFramework.POLL_TIMEOUT_SHORT_MS; import static org.chromium.chrome.test.util.ChromeRestriction.RESTRICTION_TYPE_DEVICE_NON_DAYDREAM; import static org.chromium.chrome.test.util.ChromeRestriction.RESTRICTION_TYPE_VIEWER_DAYDREAM; @@ -37,12 +36,11 @@ import org.chromium.chrome.browser.preferences.PreferencesLauncher; import org.chromium.chrome.browser.preferences.website.SingleWebsitePreferences; import org.chromium.chrome.browser.vr.mock.MockVrDaydreamApi; -import org.chromium.chrome.browser.vr.rules.ChromeTabbedActivityXrTestRule; +import org.chromium.chrome.browser.vr.rules.ChromeTabbedActivityVrTestRule; import org.chromium.chrome.browser.vr.util.NativeUiUtils; import org.chromium.chrome.browser.vr.util.NfcSimUtils; -import org.chromium.chrome.browser.vr.util.TransitionUtils; +import org.chromium.chrome.browser.vr.util.VrBrowserTransitionUtils; import org.chromium.chrome.browser.vr.util.VrShellDelegateUtils; -import org.chromium.chrome.browser.vr.util.VrTransitionUtils; import org.chromium.chrome.test.ChromeJUnit4ClassRunner; import org.chromium.chrome.test.util.ActivityUtils; import org.chromium.content.browser.test.util.Criteria; @@ -62,15 +60,17 @@ // We explicitly instantiate a rule here instead of using parameterization since this class // only ever runs in ChromeTabbedActivity. @Rule - public ChromeTabbedActivityXrTestRule mTestRule = new ChromeTabbedActivityXrTestRule(); + public ChromeTabbedActivityVrTestRule mTestRule = new ChromeTabbedActivityVrTestRule(); - private VrTestFramework mVrTestFramework; - private XrTestFramework mXrTestFramework; + private WebXrVrTestFramework mWebXrVrTestFramework; + private WebVrTestFramework mWebVrTestFramework; + private VrBrowserTestFramework mVrBrowserTestFramework; @Before public void setUp() throws Exception { - mVrTestFramework = new VrTestFramework(mTestRule); - mXrTestFramework = new XrTestFramework(mTestRule); + mWebXrVrTestFramework = new WebXrVrTestFramework(mTestRule); + mWebVrTestFramework = new WebVrTestFramework(mTestRule); + mVrBrowserTestFramework = new VrBrowserTestFramework(mTestRule); } private void enterVrShellNfc(boolean supported) { @@ -82,7 +82,7 @@ NfcSimUtils.simNfcScan(mTestRule.getActivity()); Assert.assertFalse(VrShellDelegate.isInVr()); } - TransitionUtils.forceExitVr(); + VrBrowserTransitionUtils.forceExitVr(); } private void enterExitVrShell(boolean supported) { @@ -90,15 +90,15 @@ if (!supported) { VrShellDelegateUtils.getDelegateInstance().overrideDaydreamApiForTesting(mockApi); } - TransitionUtils.forceEnterVr(); + VrBrowserTransitionUtils.forceEnterVrBrowser(); if (supported) { - TransitionUtils.waitForVrEntry(POLL_TIMEOUT_LONG_MS); + VrBrowserTransitionUtils.waitForVrEntry(POLL_TIMEOUT_LONG_MS); Assert.assertTrue(VrShellDelegate.isInVr()); } else { Assert.assertFalse(mockApi.getLaunchInVrCalled()); Assert.assertFalse(VrShellDelegate.isInVr()); } - TransitionUtils.forceExitVr(); + VrBrowserTransitionUtils.forceExitVr(); Assert.assertFalse(VrShellDelegate.isInVr()); } @@ -151,12 +151,13 @@ private String testVrEntryIntentInternal() { // Send a VR intent, which will open the link in a CTA. - final String url = VrTestFramework.getFileUrlForHtmlTestFile("test_navigation_2d_page"); - VrTransitionUtils.sendVrLaunchIntent( + final String url = + VrBrowserTestFramework.getFileUrlForHtmlTestFile("test_navigation_2d_page"); + VrBrowserTransitionUtils.sendVrLaunchIntent( url, false /* autopresent */, true /* avoidRelaunch */); // Wait until we enter VR and have the correct URL. - VrTransitionUtils.waitForVrEntry(POLL_TIMEOUT_LONG_MS); + VrBrowserTransitionUtils.waitForVrEntry(POLL_TIMEOUT_LONG_MS); CriteriaHelper.pollUiThread(new Criteria() { @Override public boolean isSatisfied() { @@ -177,7 +178,7 @@ TestVrShellDelegate.getInstance().setAllow2dIntents(true); String url = testVrEntryIntentInternal(); - VrTransitionUtils.send2dMainIntent(); + VrBrowserTransitionUtils.send2dMainIntent(); CriteriaHelper.pollUiThread(new Criteria() { @Override public boolean isSatisfied() { @@ -205,24 +206,24 @@ @MediumTest public void testExitFullscreenAfterExitingVrFromCinemaMode() throws InterruptedException, TimeoutException { - VrTransitionUtils.forceEnterVr(); - VrTransitionUtils.waitForVrEntry(POLL_TIMEOUT_LONG_MS); - mVrTestFramework.loadUrlAndAwaitInitialization( - VrTestFramework.getFileUrlForHtmlTestFile("test_navigation_2d_page"), + VrBrowserTransitionUtils.forceEnterVrBrowser(); + VrBrowserTransitionUtils.waitForVrEntry(POLL_TIMEOUT_LONG_MS); + mVrBrowserTestFramework.loadUrlAndAwaitInitialization( + VrBrowserTestFramework.getFileUrlForHtmlTestFile("test_navigation_2d_page"), PAGE_LOAD_TIMEOUT_S); - DOMUtils.clickNode(mVrTestFramework.getFirstTabWebContents(), "fullscreen", + DOMUtils.clickNode(mVrBrowserTestFramework.getFirstTabWebContents(), "fullscreen", false /* goThroughRootAndroidView */); - VrTestFramework.waitOnJavaScriptStep(mVrTestFramework.getFirstTabWebContents()); + mVrBrowserTestFramework.waitOnJavaScriptStep(); - Assert.assertTrue(DOMUtils.isFullscreen(mVrTestFramework.getFirstTabWebContents())); - VrTransitionUtils.forceExitVr(); + Assert.assertTrue(DOMUtils.isFullscreen(mVrBrowserTestFramework.getFirstTabWebContents())); + VrBrowserTransitionUtils.forceExitVr(); // The fullscreen exit from exiting VR isn't necessarily instantaneous, so give it // a bit of time. CriteriaHelper.pollInstrumentationThread(new Criteria() { @Override public boolean isSatisfied() { try { - return !DOMUtils.isFullscreen(mVrTestFramework.getFirstTabWebContents()); + return !DOMUtils.isFullscreen(mVrBrowserTestFramework.getFirstTabWebContents()); } catch (InterruptedException | TimeoutException e) { return false; } @@ -241,8 +242,8 @@ public void testExitPresentationWebVrToVrShell() throws IllegalArgumentException, InterruptedException, TimeoutException { exitPresentationToVrShellImpl( - VrTestFramework.getFileUrlForHtmlTestFile("test_navigation_webvr_page"), - mVrTestFramework, "vrDisplay.exitPresent();"); + WebVrTestFramework.getFileUrlForHtmlTestFile("test_navigation_webvr_page"), + mWebVrTestFramework, "vrDisplay.exitPresent();"); } /** @@ -256,33 +257,30 @@ public void testExitPresentationWebXrToVrShell() throws IllegalArgumentException, InterruptedException, TimeoutException { exitPresentationToVrShellImpl( - XrTestFramework.getFileUrlForHtmlTestFile("test_navigation_webxr_page"), - mXrTestFramework, "immersiveSession.end();"); + WebXrVrTestFramework.getFileUrlForHtmlTestFile("test_navigation_webxr_page"), + mWebXrVrTestFramework, "immersiveSession.end();"); } - private void exitPresentationToVrShellImpl(String url, TestFramework framework, + private void exitPresentationToVrShellImpl(String url, WebXrVrTestFramework framework, String exitPresentString) throws InterruptedException { - TransitionUtils.forceEnterVr(); - TransitionUtils.waitForVrEntry(POLL_TIMEOUT_LONG_MS); + VrBrowserTransitionUtils.forceEnterVrBrowser(); + VrBrowserTransitionUtils.waitForVrEntry(POLL_TIMEOUT_LONG_MS); framework.loadUrlAndAwaitInitialization(url, PAGE_LOAD_TIMEOUT_S); VrShellImpl vrShellImpl = (VrShellImpl) TestVrShellDelegate.getVrShellForTesting(); float expectedWidth = vrShellImpl.getContentWidthForTesting(); float expectedHeight = vrShellImpl.getContentHeightForTesting(); - TransitionUtils.enterPresentationOrFail(framework); + framework.enterSessionWithUserGestureOrFail(); // Validate our size is what we expect while in VR. // We aren't comparing for equality because there is some rounding that occurs. String javascript = "Math.abs(screen.width - " + expectedWidth + ") <= 1 && " + "Math.abs(screen.height - " + expectedHeight + ") <= 1"; - Assert.assertTrue(TestFramework.pollJavaScriptBoolean( - javascript, POLL_TIMEOUT_LONG_MS, framework.getFirstTabWebContents())); + Assert.assertTrue(framework.pollJavaScriptBoolean(javascript, POLL_TIMEOUT_LONG_MS)); // Exit presentation through JavaScript. - TestFramework.runJavaScriptOrFail( - exitPresentString, POLL_TIMEOUT_SHORT_MS, framework.getFirstTabWebContents()); + framework.runJavaScriptOrFail(exitPresentString, POLL_TIMEOUT_SHORT_MS); - Assert.assertTrue(TestFramework.pollJavaScriptBoolean( - javascript, POLL_TIMEOUT_LONG_MS, framework.getFirstTabWebContents())); + Assert.assertTrue(framework.pollJavaScriptBoolean(javascript, POLL_TIMEOUT_LONG_MS)); } /** @@ -295,8 +293,8 @@ @MediumTest public void testWebVrReEntryFromVrBrowser() throws InterruptedException, TimeoutException { reEntryFromVrBrowserImpl( - VrTestFramework.getFileUrlForHtmlTestFile("test_webvr_reentry_from_vr_browser"), - mVrTestFramework); + WebVrTestFramework.getFileUrlForHtmlTestFile("test_webvr_reentry_from_vr_browser"), + mWebVrTestFramework); } /** @@ -308,33 +306,30 @@ @Restriction(RESTRICTION_TYPE_VIEWER_DAYDREAM) @MediumTest public void testWebXrReEntryFromVrBrowser() throws InterruptedException, TimeoutException { - reEntryFromVrBrowserImpl( - XrTestFramework.getFileUrlForHtmlTestFile("test_webxr_reentry_from_vr_browser"), - mXrTestFramework); + reEntryFromVrBrowserImpl(WebXrVrTestFramework.getFileUrlForHtmlTestFile( + "test_webxr_reentry_from_vr_browser"), + mWebXrVrTestFramework); } - private void reEntryFromVrBrowserImpl(String url, TestFramework framework) + private void reEntryFromVrBrowserImpl(String url, WebXrVrTestFramework framework) throws InterruptedException { - TransitionUtils.forceEnterVr(); - TransitionUtils.waitForVrEntry(POLL_TIMEOUT_LONG_MS); + VrBrowserTransitionUtils.forceEnterVrBrowser(); + VrBrowserTransitionUtils.waitForVrEntry(POLL_TIMEOUT_LONG_MS); EmulatedVrController controller = new EmulatedVrController(mTestRule.getActivity()); framework.loadUrlAndAwaitInitialization(url, PAGE_LOAD_TIMEOUT_S); - TransitionUtils.enterPresentationOrFail(framework); + framework.enterSessionWithUserGestureOrFail(); - TestFramework.executeStepAndWait( - "stepVerifyFirstPresent()", framework.getFirstTabWebContents()); + framework.executeStepAndWait("stepVerifyFirstPresent()"); // The bug did not reproduce with vrDisplay.exitPresent(), so it might not reproduce with // session.end(). Instead, use the controller to exit. controller.pressReleaseAppButton(); - TestFramework.executeStepAndWait( - "stepVerifyMagicWindow()", framework.getFirstTabWebContents()); + framework.executeStepAndWait("stepVerifyMagicWindow()"); - TransitionUtils.enterPresentationOrFail(framework); - TestFramework.executeStepAndWait( - "stepVerifySecondPresent()", framework.getFirstTabWebContents()); + framework.enterSessionWithUserGestureOrFail(); + framework.executeStepAndWait("stepVerifySecondPresent()"); - TestFramework.endTest(framework.getFirstTabWebContents()); + framework.endTest(); } /** @@ -357,8 +352,8 @@ MockVrDaydreamApi mockApi = new MockVrDaydreamApi(); VrShellDelegateUtils.getDelegateInstance().overrideDaydreamApiForTesting(mockApi); - Assert.assertTrue(VrTransitionUtils.forceEnterVr()); - VrTransitionUtils.waitForVrEntry(POLL_TIMEOUT_LONG_MS); + Assert.assertTrue(VrBrowserTransitionUtils.forceEnterVrBrowser()); + VrBrowserTransitionUtils.waitForVrEntry(POLL_TIMEOUT_LONG_MS); Assert.assertTrue(VrShellDelegateUtils.getDelegateInstance().isVrEntryComplete()); Assert.assertFalse(mockApi.getExitFromVrCalled()); Assert.assertFalse(mockApi.getLaunchVrHomescreenCalled()); @@ -390,8 +385,8 @@ private void testStartActivityTriggersDoffImpl(Context context) throws InterruptedException, TimeoutException { - Assert.assertTrue(TransitionUtils.forceEnterVr()); - TransitionUtils.waitForVrEntry(POLL_TIMEOUT_LONG_MS); + Assert.assertTrue(VrBrowserTransitionUtils.forceEnterVrBrowser()); + VrBrowserTransitionUtils.waitForVrEntry(POLL_TIMEOUT_LONG_MS); Assert.assertTrue(VrShellDelegateUtils.getDelegateInstance().isVrEntryComplete()); MockVrDaydreamApi mockApi = new MockVrDaydreamApi(); @@ -441,8 +436,8 @@ @MediumTest public void testStartActivityIfNeeded() throws InterruptedException, TimeoutException { Activity context = mTestRule.getActivity(); - Assert.assertTrue(TransitionUtils.forceEnterVr()); - TransitionUtils.waitForVrEntry(POLL_TIMEOUT_LONG_MS); + Assert.assertTrue(VrBrowserTransitionUtils.forceEnterVrBrowser()); + VrBrowserTransitionUtils.waitForVrEntry(POLL_TIMEOUT_LONG_MS); Assert.assertTrue(VrShellDelegateUtils.getDelegateInstance().isVrEntryComplete()); ThreadUtils.runOnUiThreadBlocking(new Runnable() { @@ -464,26 +459,26 @@ @LargeTest @CommandLineFlags.Add("enable-features=VrBrowsingNativeAndroidUi") public void testExitVrWithPromptDisplayed() throws InterruptedException, TimeoutException { - mVrTestFramework.loadUrlAndAwaitInitialization( - VrTestFramework.getFileUrlForHtmlTestFile("test_navigation_2d_page"), + mVrBrowserTestFramework.loadUrlAndAwaitInitialization( + VrBrowserTestFramework.getFileUrlForHtmlTestFile("test_navigation_2d_page"), PAGE_LOAD_TIMEOUT_S); // Test JavaScript dialogs. - Assert.assertTrue(TransitionUtils.forceEnterVr()); - TransitionUtils.waitForVrEntry(POLL_TIMEOUT_LONG_MS); + Assert.assertTrue(VrBrowserTransitionUtils.forceEnterVrBrowser()); + VrBrowserTransitionUtils.waitForVrEntry(POLL_TIMEOUT_LONG_MS); // Alerts block JavaScript execution until they're closed, so we can't use the normal // runJavaScriptOrFail, as that will time out. JavaScriptUtils.executeJavaScript( - mVrTestFramework.getFirstTabWebContents(), "alert('Please no crash')"); - TransitionUtils.waitForNativeUiPrompt(POLL_TIMEOUT_LONG_MS); - TransitionUtils.forceExitVr(); + mVrBrowserTestFramework.getFirstTabWebContents(), "alert('Please no crash')"); + VrBrowserTransitionUtils.waitForNativeUiPrompt(POLL_TIMEOUT_LONG_MS); + VrBrowserTransitionUtils.forceExitVr(); // Test permission prompts. - Assert.assertTrue(TransitionUtils.forceEnterVr()); - TransitionUtils.waitForVrEntry(POLL_TIMEOUT_LONG_MS); - VrTestFramework.runJavaScriptOrFail("navigator.getUserMedia({video: true}, ()=>{}, ()=>{})", - POLL_TIMEOUT_SHORT_MS, mVrTestFramework.getFirstTabWebContents()); - TransitionUtils.waitForNativeUiPrompt(POLL_TIMEOUT_LONG_MS); - TransitionUtils.forceExitVr(); + Assert.assertTrue(VrBrowserTransitionUtils.forceEnterVrBrowser()); + VrBrowserTransitionUtils.waitForVrEntry(POLL_TIMEOUT_LONG_MS); + mVrBrowserTestFramework.runJavaScriptOrFail( + "navigator.getUserMedia({video: true}, ()=>{}, ()=>{})", POLL_TIMEOUT_SHORT_MS); + VrBrowserTransitionUtils.waitForNativeUiPrompt(POLL_TIMEOUT_LONG_MS); + VrBrowserTransitionUtils.forceExitVr(); } }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrBrowserWebInputEditingTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrBrowserWebInputEditingTest.java index 58437df1..c4974df0 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrBrowserWebInputEditingTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrBrowserWebInputEditingTest.java
@@ -4,9 +4,9 @@ package org.chromium.chrome.browser.vr; -import static org.chromium.chrome.browser.vr.VrTestFramework.PAGE_LOAD_TIMEOUT_S; -import static org.chromium.chrome.browser.vr.VrTestFramework.POLL_CHECK_INTERVAL_SHORT_MS; -import static org.chromium.chrome.browser.vr.VrTestFramework.POLL_TIMEOUT_LONG_MS; +import static org.chromium.chrome.browser.vr.XrTestFramework.PAGE_LOAD_TIMEOUT_S; +import static org.chromium.chrome.browser.vr.XrTestFramework.POLL_CHECK_INTERVAL_SHORT_MS; +import static org.chromium.chrome.browser.vr.XrTestFramework.POLL_TIMEOUT_LONG_MS; import static org.chromium.chrome.test.util.ChromeRestriction.RESTRICTION_TYPE_DEVICE_DAYDREAM; import static org.chromium.chrome.test.util.ChromeRestriction.RESTRICTION_TYPE_VIEWER_DAYDREAM; @@ -23,9 +23,9 @@ import org.chromium.chrome.browser.ChromeSwitches; import org.chromium.chrome.browser.vr.keyboard.TextEditAction; import org.chromium.chrome.browser.vr.mock.MockBrowserKeyboardInterface; -import org.chromium.chrome.browser.vr.rules.ChromeTabbedActivityXrTestRule; +import org.chromium.chrome.browser.vr.rules.ChromeTabbedActivityVrTestRule; import org.chromium.chrome.browser.vr.util.NativeUiUtils; -import org.chromium.chrome.browser.vr.util.VrTransitionUtils; +import org.chromium.chrome.browser.vr.util.VrBrowserTransitionUtils; import org.chromium.chrome.test.ChromeJUnit4ClassRunner; import org.chromium.content.browser.test.util.Criteria; import org.chromium.content.browser.test.util.CriteriaHelper; @@ -40,14 +40,14 @@ // We explicitly instantiate a rule here instead of using parameterization since this class // only ever runs in ChromeTabbedActivity. @Rule - public ChromeTabbedActivityXrTestRule mVrTestRule = new ChromeTabbedActivityXrTestRule(); + public ChromeTabbedActivityVrTestRule mVrTestRule = new ChromeTabbedActivityVrTestRule(); - private VrTestFramework mVrTestFramework; + private VrBrowserTestFramework mVrBrowserTestFramework; private EmulatedVrController mController; @Before public void setUp() throws Exception { - mVrTestFramework = new VrTestFramework(mVrTestRule); + mVrBrowserTestFramework = new VrBrowserTestFramework(mVrTestRule); } /** @@ -60,10 +60,11 @@ @CommandLineFlags.Add("enable-features=VrLaunchIntents") public void testWebInputFocus() throws InterruptedException { // Load page in VR and make sure the controller is pointed at the content quad. - mVrTestRule.loadUrl(VrTestFramework.getFileUrlForHtmlTestFile("test_web_input_editing"), + mVrTestRule.loadUrl( + VrBrowserTestFramework.getFileUrlForHtmlTestFile("test_web_input_editing"), PAGE_LOAD_TIMEOUT_S); - VrTransitionUtils.forceEnterVr(); - VrTransitionUtils.waitForVrEntry(POLL_TIMEOUT_LONG_MS); + VrBrowserTransitionUtils.forceEnterVrBrowser(); + VrBrowserTransitionUtils.waitForVrEntry(POLL_TIMEOUT_LONG_MS); VrShellImpl vrShellImpl = (VrShellImpl) TestVrShellDelegate.getVrShellForTesting(); MockBrowserKeyboardInterface keyboard = new MockBrowserKeyboardInterface();
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrFeedbackInfoBarTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrFeedbackInfoBarTest.java index 7a1ff6b..b3e7087 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrFeedbackInfoBarTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrFeedbackInfoBarTest.java
@@ -4,9 +4,9 @@ package org.chromium.chrome.browser.vr; -import static org.chromium.chrome.browser.vr.VrTestFramework.PAGE_LOAD_TIMEOUT_S; -import static org.chromium.chrome.browser.vr.VrTestFramework.POLL_TIMEOUT_LONG_MS; -import static org.chromium.chrome.browser.vr.VrTestFramework.POLL_TIMEOUT_SHORT_MS; +import static org.chromium.chrome.browser.vr.XrTestFramework.PAGE_LOAD_TIMEOUT_S; +import static org.chromium.chrome.browser.vr.XrTestFramework.POLL_TIMEOUT_LONG_MS; +import static org.chromium.chrome.browser.vr.XrTestFramework.POLL_TIMEOUT_SHORT_MS; import static org.chromium.chrome.test.util.ChromeRestriction.RESTRICTION_TYPE_DEVICE_DAYDREAM; import static org.chromium.chrome.test.util.ChromeRestriction.RESTRICTION_TYPE_VIEWER_DAYDREAM; @@ -22,8 +22,8 @@ import org.chromium.base.test.util.Restriction; import org.chromium.base.test.util.RetryOnFailure; import org.chromium.chrome.browser.ChromeSwitches; -import org.chromium.chrome.browser.vr.rules.ChromeTabbedActivityXrTestRule; -import org.chromium.chrome.browser.vr.util.TransitionUtils; +import org.chromium.chrome.browser.vr.rules.ChromeTabbedActivityVrTestRule; +import org.chromium.chrome.browser.vr.util.VrBrowserTransitionUtils; import org.chromium.chrome.browser.vr.util.VrInfoBarUtils; import org.chromium.chrome.browser.vr.util.VrShellDelegateUtils; import org.chromium.chrome.test.ChromeJUnit4ClassRunner; @@ -43,35 +43,37 @@ // We explicitly instantiate a rule here instead of using parameterization since this class // only ever runs in ChromeTabbedActivity. @Rule - public ChromeTabbedActivityXrTestRule mTestRule = new ChromeTabbedActivityXrTestRule(); + public ChromeTabbedActivityVrTestRule mTestRule = new ChromeTabbedActivityVrTestRule(); - private VrTestFramework mVrTestFramework; - private XrTestFramework mXrTestFramework; + private WebXrVrTestFramework mWebXrVrTestFramework; + private WebVrTestFramework mWebVrTestFramework; + private VrBrowserTestFramework mVrBrowserTestFramework; private static final String TEST_PAGE_2D_URL = - VrTestFramework.getFileUrlForHtmlTestFile("test_navigation_2d_page"); + VrBrowserTestFramework.getFileUrlForHtmlTestFile("test_navigation_2d_page"); private static final String TEST_PAGE_WEBVR_URL = - VrTestFramework.getFileUrlForHtmlTestFile("generic_webvr_page"); + WebVrTestFramework.getFileUrlForHtmlTestFile("generic_webvr_page"); private static final String TEST_PAGE_WEBXR_URL = - XrTestFramework.getFileUrlForHtmlTestFile("generic_webxr_page"); + WebXrVrTestFramework.getFileUrlForHtmlTestFile("generic_webxr_page"); @Before public void setUp() throws Exception { - mVrTestFramework = new VrTestFramework(mTestRule); - mXrTestFramework = new XrTestFramework(mTestRule); + mWebXrVrTestFramework = new WebXrVrTestFramework(mTestRule); + mWebVrTestFramework = new WebVrTestFramework(mTestRule); + mVrBrowserTestFramework = new VrBrowserTestFramework(mTestRule); Assert.assertFalse(VrFeedbackStatus.getFeedbackOptOut()); } private void assertState(boolean isInVr, boolean isInfobarVisible) { Assert.assertEquals("Browser is in VR", isInVr, VrShellDelegate.isInVr()); - VrInfoBarUtils.expectInfoBarPresent(mVrTestFramework, isInfobarVisible); + VrInfoBarUtils.expectInfoBarPresent(mTestRule, isInfobarVisible); } private void enterThenExitVr() { - TransitionUtils.forceEnterVr(); - TransitionUtils.waitForVrEntry(POLL_TIMEOUT_LONG_MS); + VrBrowserTransitionUtils.forceEnterVrBrowser(); + VrBrowserTransitionUtils.waitForVrEntry(POLL_TIMEOUT_LONG_MS); assertState(true /* isInVr */, false /* isInfobarVisible */); - TransitionUtils.forceExitVr(); + VrBrowserTransitionUtils.forceExitVr(); } /** @@ -81,14 +83,15 @@ @MediumTest @Restriction(RESTRICTION_TYPE_VIEWER_DAYDREAM) public void testFeedbackFrequency() throws InterruptedException, TimeoutException { - mVrTestFramework.loadUrlAndAwaitInitialization(TEST_PAGE_2D_URL, PAGE_LOAD_TIMEOUT_S); + mVrBrowserTestFramework.loadUrlAndAwaitInitialization( + TEST_PAGE_2D_URL, PAGE_LOAD_TIMEOUT_S); // Set frequency of infobar to every 2nd time. VrShellDelegateUtils.getDelegateInstance().setFeedbackFrequencyForTesting(2); // Verify that the Feedback infobar is visible when exiting VR. enterThenExitVr(); assertState(false /* isInVr */, true /* isInfobarVisible */); - VrInfoBarUtils.clickInfobarCloseButton(mVrTestFramework); + VrInfoBarUtils.clickInfobarCloseButton(mTestRule); // Feedback infobar shouldn't show up this time. enterThenExitVr(); @@ -97,7 +100,7 @@ // Feedback infobar should show up again. enterThenExitVr(); assertState(false /* isInVr */, true /* isInfobarVisible */); - VrInfoBarUtils.clickInfobarCloseButton(mVrTestFramework); + VrInfoBarUtils.clickInfobarCloseButton(mTestRule); } /** @@ -107,7 +110,8 @@ @MediumTest @Restriction(RESTRICTION_TYPE_VIEWER_DAYDREAM) public void testFeedbackOptOut() throws InterruptedException, TimeoutException { - mVrTestFramework.loadUrlAndAwaitInitialization(TEST_PAGE_2D_URL, PAGE_LOAD_TIMEOUT_S); + mVrBrowserTestFramework.loadUrlAndAwaitInitialization( + TEST_PAGE_2D_URL, PAGE_LOAD_TIMEOUT_S); // Show infobar every time. VrShellDelegateUtils.getDelegateInstance().setFeedbackFrequencyForTesting(1); @@ -117,7 +121,7 @@ assertState(false /* isInVr */, true /* isInfobarVisible */); // Opt-out of seeing the infobar. - VrInfoBarUtils.clickInfoBarButton(VrInfoBarUtils.Button.SECONDARY, mVrTestFramework); + VrInfoBarUtils.clickInfoBarButton(VrInfoBarUtils.Button.SECONDARY, mTestRule); Assert.assertTrue(VrFeedbackStatus.getFeedbackOptOut()); // The infobar should not show because the user opted out. @@ -131,7 +135,7 @@ @Test @MediumTest public void testFeedbackOnlyOnVrBrowsing() throws InterruptedException, TimeoutException { - feedbackOnlyOnVrBrowsingImpl(TEST_PAGE_WEBVR_URL, mVrTestFramework); + feedbackOnlyOnVrBrowsingImpl(TEST_PAGE_WEBVR_URL, mWebVrTestFramework); } /** @@ -144,19 +148,19 @@ @CommandLineFlags.Add({"enable-features=WebXR"}) public void testFeedbackOnlyOnVrBrowsing_WebXr() throws InterruptedException, TimeoutException { - feedbackOnlyOnVrBrowsingImpl(TEST_PAGE_WEBXR_URL, mXrTestFramework); + feedbackOnlyOnVrBrowsingImpl(TEST_PAGE_WEBXR_URL, mWebXrVrTestFramework); } - private void feedbackOnlyOnVrBrowsingImpl(String url, TestFramework framework) + private void feedbackOnlyOnVrBrowsingImpl(String url, WebXrVrTestFramework framework) throws InterruptedException { // Enter VR presentation mode. framework.loadUrlAndAwaitInitialization(url, PAGE_LOAD_TIMEOUT_S); - TransitionUtils.enterPresentationOrFail(framework); + framework.enterSessionWithUserGestureOrFail(); assertState(true /* isInVr */, false /* isInfobarVisible */); Assert.assertTrue(TestVrShellDelegate.getVrShellForTesting().getWebVrModeEnabled()); // Exiting VR should not prompt for feedback since the no VR browsing was performed. - TransitionUtils.forceExitVr(); + VrBrowserTransitionUtils.forceExitVr(); assertState(false /* isInVr */, false /* isInfobarVisible */); } @@ -168,7 +172,7 @@ @Restriction(RESTRICTION_TYPE_VIEWER_DAYDREAM) public void testExitPresentationInVr() throws InterruptedException, TimeoutException { // Enter VR presentation mode. - exitPresentationInVrImpl(TEST_PAGE_WEBVR_URL, mVrTestFramework); + exitPresentationInVrImpl(TEST_PAGE_WEBVR_URL, mWebVrTestFramework); } /** @@ -183,14 +187,14 @@ @Restriction(RESTRICTION_TYPE_VIEWER_DAYDREAM) public void testExitPresentationInVr_WebXr() throws InterruptedException, TimeoutException { - exitPresentationInVrImpl(TEST_PAGE_WEBXR_URL, mXrTestFramework); + exitPresentationInVrImpl(TEST_PAGE_WEBXR_URL, mWebXrVrTestFramework); } - private void exitPresentationInVrImpl(String url, final TestFramework framework) + private void exitPresentationInVrImpl(String url, final WebXrVrTestFramework framework) throws InterruptedException { // Enter VR presentation mode. framework.loadUrlAndAwaitInitialization(url, PAGE_LOAD_TIMEOUT_S); - TransitionUtils.enterPresentationOrFail(framework); + framework.enterSessionWithUserGestureOrFail(); assertState(true /* isInVr */, false /* isInfobarVisible */); Assert.assertTrue(TestVrShellDelegate.getVrShellForTesting().getWebVrModeEnabled()); @@ -199,14 +203,14 @@ mTestRule.getActivity().getActivityTab(), new Runnable() { @Override public void run() { - TestFramework.runJavaScriptOrFail( + mVrBrowserTestFramework.runJavaScriptOrFail( "window.location.href = '" + TEST_PAGE_2D_URL + "';", - POLL_TIMEOUT_SHORT_MS, framework.getFirstTabWebContents()); + POLL_TIMEOUT_SHORT_MS); } }, POLL_TIMEOUT_LONG_MS); // Exiting VR should prompt for feedback since 2D browsing was performed after. - TransitionUtils.forceExitVr(); + VrBrowserTransitionUtils.forceExitVr(); assertState(false /* isInVr */, true /* isInfobarVisible */); } }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrInstallUpdateInfoBarTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrInstallUpdateInfoBarTest.java index 535b825..74bad02 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrInstallUpdateInfoBarTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrInstallUpdateInfoBarTest.java
@@ -10,7 +10,6 @@ import android.widget.TextView; import org.junit.Assert; -import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.RuleChain; @@ -27,7 +26,7 @@ import org.chromium.chrome.browser.vr.rules.XrActivityRestriction; import org.chromium.chrome.browser.vr.util.VrInfoBarUtils; import org.chromium.chrome.browser.vr.util.VrShellDelegateUtils; -import org.chromium.chrome.browser.vr.util.XrTestRuleUtils; +import org.chromium.chrome.browser.vr.util.VrTestRuleUtils; import org.chromium.chrome.test.ChromeActivityTestRule; import org.chromium.chrome.test.ChromeJUnit4RunnerDelegate; @@ -46,21 +45,15 @@ public class VrInstallUpdateInfoBarTest { @ClassParameter private static List<ParameterSet> sClassParams = - XrTestRuleUtils.generateDefaultXrTestRuleParameters(); + VrTestRuleUtils.generateDefaultTestRuleParameters(); @Rule public RuleChain mRuleChain; private ChromeActivityTestRule mVrTestRule; - private VrTestFramework mVrTestFramework; public VrInstallUpdateInfoBarTest(Callable<ChromeActivityTestRule> callable) throws Exception { mVrTestRule = callable.call(); - mRuleChain = XrTestRuleUtils.wrapRuleInXrActivityRestrictionRule(mVrTestRule); - } - - @Before - public void setUp() throws Exception { - mVrTestFramework = new VrTestFramework(mVrTestRule); + mRuleChain = VrTestRuleUtils.wrapRuleInXrActivityRestrictionRule(mVrTestRule); } /** @@ -73,7 +66,7 @@ VrShellDelegateUtils.setVrCoreCompatibility(checkerReturnCompatibility); View decorView = mVrTestRule.getActivity().getWindow().getDecorView(); if (checkerReturnCompatibility == VrCoreCompatibility.VR_READY) { - VrInfoBarUtils.expectInfoBarPresent(mVrTestFramework, false); + VrInfoBarUtils.expectInfoBarPresent(mVrTestRule, false); } else if (checkerReturnCompatibility == VrCoreCompatibility.VR_OUT_OF_DATE || checkerReturnCompatibility == VrCoreCompatibility.VR_NOT_AVAILABLE) { // Out of date and missing cases are the same, but with different text @@ -89,13 +82,13 @@ expectedButton = mVrTestRule.getActivity().getString( R.string.vr_services_check_infobar_install_button); } - VrInfoBarUtils.expectInfoBarPresent(mVrTestFramework, true); + VrInfoBarUtils.expectInfoBarPresent(mVrTestRule, true); TextView tempView = (TextView) decorView.findViewById(R.id.infobar_message); Assert.assertEquals(expectedMessage, tempView.getText().toString()); tempView = (TextView) decorView.findViewById(R.id.button_primary); Assert.assertEquals(expectedButton, tempView.getText().toString()); } else if (checkerReturnCompatibility == VrCoreCompatibility.VR_NOT_SUPPORTED) { - VrInfoBarUtils.expectInfoBarPresent(mVrTestFramework, false); + VrInfoBarUtils.expectInfoBarPresent(mVrTestRule, false); } else { Assert.fail("Invalid VrCoreVersionChecker compatibility: " + String.valueOf(checkerReturnCompatibility));
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrTestFramework.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrTestFramework.java deleted file mode 100644 index b0019e69..0000000 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrTestFramework.java +++ /dev/null
@@ -1,26 +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. - -package org.chromium.chrome.browser.vr; - -import org.chromium.chrome.test.ChromeActivityTestRule; -import org.chromium.content_public.browser.WebContents; - -/** - * Extension of TestFramework containing WebVR-specific functionality. - */ -public class VrTestFramework extends TestFramework { - public VrTestFramework(ChromeActivityTestRule rule) { - super(rule); - } - - /** - * Checks whether a VRDisplay was actually found. - * @param webContents The WebContents to run the JavaScript through. - * @return Whether a VRDisplay was found. - */ - public static boolean vrDisplayFound(WebContents webContents) { - return !runJavaScriptOrFail("vrDisplay", POLL_TIMEOUT_SHORT_MS, webContents).equals("null"); - } -}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebVrTestFramework.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebVrTestFramework.java new file mode 100644 index 0000000..05cf410 --- /dev/null +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebVrTestFramework.java
@@ -0,0 +1,52 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.browser.vr; + +import org.junit.Assert; + +import org.chromium.chrome.test.ChromeActivityTestRule; +import org.chromium.content_public.browser.WebContents; + +/** + * Extension of WebXrVrTestFramework containing WebVR-specific functionality. + */ +public class WebVrTestFramework extends WebXrVrTestFramework { + public WebVrTestFramework(ChromeActivityTestRule rule) { + super(rule); + } + + /** + * Checks whether a VRDisplay was actually found. Keeps the "xrDeviceFound" naming instead of + * "vrDisplayFound" since WebVR is being deprecated and maintaining consistency with WebXR + * naming is more important in the long run. + * @param webContents The WebContents to run the JavaScript through. + * @return Whether a VRDisplay was found. + */ + @Override + public boolean xrDeviceFound(WebContents webContents) { + return !runJavaScriptOrFail("vrDisplay", POLL_TIMEOUT_SHORT_MS, webContents).equals("null"); + } + + /** + * WebVR-specific implementation of enterSessionWithUserGestureOrFail. + * @param webContents The WebContents of the tab to enter WebVR presentation in. + */ + @Override + public void enterSessionWithUserGestureOrFail(WebContents webContents) { + enterSessionWithUserGesture(webContents); + Assert.assertTrue( + pollJavaScriptBoolean("vrDisplay.isPresenting", POLL_TIMEOUT_LONG_MS, webContents)); + Assert.assertTrue(TestVrShellDelegate.getVrShellForTesting().getWebVrModeEnabled()); + } + + /** + * Exits WebVR presentation. + * @param webContents The WebContents of the tab to exit WebVR presentation in. + */ + @Override + public void endSession(WebContents webContents) { + runJavaScriptOrFail("vrDisplay.exitPresent()", POLL_TIMEOUT_SHORT_MS, webContents); + } +}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrArSessionTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrArSessionTest.java index 635f40d..1f269ec6 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrArSessionTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrArSessionTest.java
@@ -4,8 +4,8 @@ package org.chromium.chrome.browser.vr; -import static org.chromium.chrome.browser.vr.TestFramework.PAGE_LOAD_TIMEOUT_S; -import static org.chromium.chrome.browser.vr.XrTestFramework.POLL_TIMEOUT_LONG_MS; +import static org.chromium.chrome.browser.vr.WebXrArTestFramework.PAGE_LOAD_TIMEOUT_S; +import static org.chromium.chrome.browser.vr.WebXrArTestFramework.POLL_TIMEOUT_LONG_MS; import android.os.Build; import android.support.test.InstrumentationRegistry; @@ -28,7 +28,6 @@ import org.chromium.chrome.browser.ChromeSwitches; import org.chromium.chrome.browser.vr.rules.XrActivityRestriction; import org.chromium.chrome.browser.vr.util.XrTestRuleUtils; -import org.chromium.chrome.browser.vr.util.XrTransitionUtils; import org.chromium.chrome.test.ChromeActivityTestRule; import org.chromium.chrome.test.ChromeJUnit4RunnerDelegate; import org.chromium.net.test.EmbeddedTestServer; @@ -47,12 +46,12 @@ public class WebXrArSessionTest { @ClassParameter private static List<ParameterSet> sClassParams = - XrTestRuleUtils.generateDefaultXrTestRuleParameters(); + XrTestRuleUtils.generateDefaultTestRuleParameters(); @Rule public RuleChain mRuleChain; private ChromeActivityTestRule mTestRule; - private XrTestFramework mXrTestFramework; + private WebXrArTestFramework mWebXrArTestFramework; private EmbeddedTestServer mServer; private boolean mShouldCreateServer; @@ -64,7 +63,7 @@ @Before public void setUp() throws Exception { - mXrTestFramework = new XrTestFramework(mTestRule); + mWebXrArTestFramework = new WebXrArTestFramework(mTestRule); // WebappActivityTestRule automatically creates a test server, and creating multiple causes // it to crash hitting a DCHECK. So, only handle the server ourselves if whatever test rule // we're using doesn't create one itself. @@ -89,12 +88,12 @@ @MediumTest @XrActivityRestriction({XrActivityRestriction.SupportedActivity.ALL}) public void testArRequestSessionSucceeds() throws InterruptedException { - mXrTestFramework.loadUrlAndAwaitInitialization( - mServer.getURL(XrTestFramework.getEmbeddedServerPathForHtmlTestFile( + mWebXrArTestFramework.loadUrlAndAwaitInitialization( + mServer.getURL(WebXrArTestFramework.getEmbeddedServerPathForHtmlTestFile( "test_ar_request_session_succeeds")), PAGE_LOAD_TIMEOUT_S); - XrTransitionUtils.enterArSessionOrFail(mXrTestFramework.getFirstTabWebContents()); - XrTestFramework.assertNoJavaScriptErrors(mXrTestFramework.getFirstTabWebContents()); + mWebXrArTestFramework.enterSessionWithUserGestureOrFail(); + mWebXrArTestFramework.assertNoJavaScriptErrors(); } /** @@ -105,15 +104,15 @@ @MediumTest @XrActivityRestriction({XrActivityRestriction.SupportedActivity.ALL}) public void testRepeatedArSessionsSucceed() throws InterruptedException { - mXrTestFramework.loadUrlAndAwaitInitialization( - mServer.getURL(XrTestFramework.getEmbeddedServerPathForHtmlTestFile( + mWebXrArTestFramework.loadUrlAndAwaitInitialization( + mServer.getURL(WebXrArTestFramework.getEmbeddedServerPathForHtmlTestFile( "test_ar_request_session_succeeds")), PAGE_LOAD_TIMEOUT_S); for (int i = 0; i < 2; i++) { - XrTransitionUtils.enterArSessionOrFail(mXrTestFramework.getFirstTabWebContents()); - XrTransitionUtils.endArSession(mXrTestFramework.getFirstTabWebContents()); + mWebXrArTestFramework.enterSessionWithUserGestureOrFail(); + mWebXrArTestFramework.endSession(); } - XrTestFramework.assertNoJavaScriptErrors(mXrTestFramework.getFirstTabWebContents()); + mWebXrArTestFramework.assertNoJavaScriptErrors(); } /** @@ -124,21 +123,18 @@ @MediumTest @XrActivityRestriction({XrActivityRestriction.SupportedActivity.ALL}) public void testRepeatedArSessionsOnlyPromptPermissionsOnce() throws InterruptedException { - mXrTestFramework.loadUrlAndAwaitInitialization( - mServer.getURL(XrTestFramework.getEmbeddedServerPathForHtmlTestFile( + mWebXrArTestFramework.loadUrlAndAwaitInitialization( + mServer.getURL(WebXrArTestFramework.getEmbeddedServerPathForHtmlTestFile( "test_ar_request_session_succeeds")), PAGE_LOAD_TIMEOUT_S); - Assert.assertTrue(XrTransitionUtils.arSessionRequestWouldTriggerPermissionPrompt( - mXrTestFramework.getFirstTabWebContents())); - XrTransitionUtils.enterArSessionOrFail(mXrTestFramework.getFirstTabWebContents()); - XrTransitionUtils.endArSession(mXrTestFramework.getFirstTabWebContents()); + Assert.assertTrue(mWebXrArTestFramework.arSessionRequestWouldTriggerPermissionPrompt()); + mWebXrArTestFramework.enterSessionWithUserGestureOrFail(); + mWebXrArTestFramework.endSession(); // Manually run through the same steps as enterArSessionOrFail so that we don't trigger // its automatic permission acceptance. - Assert.assertFalse(XrTransitionUtils.arSessionRequestWouldTriggerPermissionPrompt( - mXrTestFramework.getFirstTabWebContents())); - XrTransitionUtils.enterPresentation(mXrTestFramework.getFirstTabWebContents()); - Assert.assertTrue(XrTestFramework.pollJavaScriptBoolean( - "sessionInfos[sessionTypes.AR].currentSession != null", POLL_TIMEOUT_LONG_MS, - mXrTestFramework.getFirstTabWebContents())); + Assert.assertFalse(mWebXrArTestFramework.arSessionRequestWouldTriggerPermissionPrompt()); + mWebXrArTestFramework.enterSessionWithUserGesture(); + Assert.assertTrue(mWebXrArTestFramework.pollJavaScriptBoolean( + "sessionInfos[sessionTypes.AR].currentSession != null", POLL_TIMEOUT_LONG_MS)); } } \ No newline at end of file
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrArTestFramework.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrArTestFramework.java new file mode 100644 index 0000000..f0a69d5c --- /dev/null +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrArTestFramework.java
@@ -0,0 +1,100 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.browser.vr; + +import android.content.DialogInterface; + +import org.junit.Assert; + +import org.chromium.base.ThreadUtils; +import org.chromium.chrome.browser.permissions.PermissionDialogController; +import org.chromium.chrome.test.ChromeActivityTestRule; +import org.chromium.content.browser.test.util.Criteria; +import org.chromium.content.browser.test.util.CriteriaHelper; +import org.chromium.content_public.browser.WebContents; + +/** + * WebXR for AR-specific implementation of the WebXrTestFramework. + */ +public class WebXrArTestFramework extends WebXrTestFramework { + /** + * Must be constructed after the rule has been applied (e.g. in whatever method is + * tagged with @Before) + */ + public WebXrArTestFramework(ChromeActivityTestRule rule) { + super(rule); + } + + /** + * Requests an AR session, automatically accepting the Camera permission prompt if necessary. + * Causes a test failure if it is unable to do so. + * @param webContents The Webcontents to start the AR session in. + */ + @Override + public void enterSessionWithUserGestureOrFail(WebContents webContents) { + runJavaScriptOrFail( + "sessionTypeToRequest = sessionTypes.AR", POLL_TIMEOUT_LONG_MS, webContents); + // Requesting an AR session for the first time on a page will always prompt for camera + // permissions, but not on subsequent requests, so check to see if we'll need to accept it + // after requesting the session. + boolean expectPermissionPrompt = arSessionRequestWouldTriggerPermissionPrompt(webContents); + // TODO(bsheedy): Rename enterPresentation since it's used for both presentation and AR? + enterSessionWithUserGesture(webContents); + if (expectPermissionPrompt) { + // Wait for the permission prompt to appear. + CriteriaHelper.pollUiThread(new Criteria() { + @Override + public boolean isSatisfied() { + return PermissionDialogController.getInstance().getCurrentDialogForTesting() + != null; + } + }); + // Accept the permission prompt. + ThreadUtils.runOnUiThreadBlocking(() -> { + PermissionDialogController.getInstance() + .getCurrentDialogForTesting() + .getButton(DialogInterface.BUTTON_POSITIVE) + .performClick(); + }); + } + Assert.assertTrue( + pollJavaScriptBoolean("sessionInfos[sessionTypes.AR].currentSession != null", + POLL_TIMEOUT_LONG_MS, webContents)); + } + + /** + * Checks whether an AR session request would prompt the user for Camera permissions. + * @param webContents The WebContents to check for the permission in + * @return True if an AR session request will cause a permission prompt, false otherwise. + */ + public static boolean arSessionRequestWouldTriggerPermissionPrompt(WebContents webContents) { + runJavaScriptOrFail("checkIfArSessionWouldTriggerPermissionPrompt()", POLL_TIMEOUT_SHORT_MS, + webContents); + Assert.assertTrue( + pollJavaScriptBoolean("arSessionRequestWouldTriggerPermissionPrompt !== null", + POLL_TIMEOUT_SHORT_MS, webContents)); + return Boolean.valueOf(runJavaScriptOrFail("arSessionRequestWouldTriggerPermissionPrompt", + POLL_TIMEOUT_SHORT_MS, webContents)); + } + + /** + * Helper function to run arSessionRequestWouldTriggerPermissionPrompt with the first tab's + * WebContents. + * @return True if an AR session request will cause a permission prompt, false otherwise + */ + public boolean arSessionRequestWouldTriggerPermissionPrompt() { + return arSessionRequestWouldTriggerPermissionPrompt(mFirstTabWebContents); + } + + /** + * Exits a WebXR AR session. + * @param webcontents The WebContents to exit the AR session in + */ + @Override + public void endSession(WebContents webContents) { + runJavaScriptOrFail("sessionInfos[sessionTypes.AR].currentSession.end()", + POLL_TIMEOUT_SHORT_MS, webContents); + } +} \ No newline at end of file
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrTestFramework.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrTestFramework.java new file mode 100644 index 0000000..6cc8c27 --- /dev/null +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrTestFramework.java
@@ -0,0 +1,105 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.browser.vr; + +import org.junit.Assert; + +import org.chromium.chrome.test.ChromeActivityTestRule; +import org.chromium.content.browser.test.util.DOMUtils; +import org.chromium.content_public.browser.WebContents; + +import java.util.concurrent.TimeoutException; + +/** + * Extension of XrTestFramework meant for testing XR-related web APIs. + */ +public abstract class WebXrTestFramework extends XrTestFramework { + /** + * Must be constructed after the rule has been applied (e.g. in whatever method is + * tagged with @Before) + */ + public WebXrTestFramework(ChromeActivityTestRule rule) { + super(rule); + } + + /** + * Checks whether an XRDevice was actually found. + * @param webContents The WebContents to run the JavaScript through. + * @return Whether an XRDevice was found. + */ + public boolean xrDeviceFound(WebContents webContents) { + return !runJavaScriptOrFail("xrDevice", POLL_TIMEOUT_SHORT_MS, webContents).equals("null"); + } + + /** + * Helper function to run xrDeviceFound with the first tab's WebContents. + * @return Whether an XRDevice was found. + */ + public boolean xrDeviceFound() { + return xrDeviceFound(mFirstTabWebContents); + } + + /** + * Enters a WebXR or WebVR session of some kind by tapping on the canvas on the page. + * @param webContents The WebContents for the tab the canvas is in. + */ + public void enterSessionWithUserGesture(WebContents webContents) { + try { + DOMUtils.clickNode(webContents, "webgl-canvas", false /* goThroughRootAndroidView */); + } catch (InterruptedException | TimeoutException e) { + Assert.fail("Failed to click canvas to enter session: " + e.toString()); + } + } + + /** + * Helper function to run enterSessionWithUserGesture using the first tab's WebContents. + */ + public void enterSessionWithUserGesture() { + enterSessionWithUserGesture(mFirstTabWebContents); + } + + /** + * Enters a WebXR or WebVR session of some kind and waits until the page reports it is finished + * with its JavaScript step. + * @param webContents The WebContents for the tab to enter the session in. + */ + public void enterSessionWithUserGestureAndWait(WebContents webContents) { + enterSessionWithUserGesture(webContents); + waitOnJavaScriptStep(webContents); + } + + /** + * Helper function to run enterSessionWithUserGestureAndWait with the first tab's WebContents. + */ + public void enterSessionWithUserGestureAndWait() { + enterSessionWithUserGestureAndWait(mFirstTabWebContents); + } + + /** + * Attempts to enter a WebXR or WebVR session of some kind, failing if it is unable to. + * @param webContents The WebContents for the tab to enter the session in. + */ + public abstract void enterSessionWithUserGestureOrFail(WebContents webContents); + + /** + * Helper function to run enterSessionWithUserGestureOrFail with the first tab's WebContents. + */ + public void enterSessionWithUserGestureOrFail() { + enterSessionWithUserGestureOrFail(mFirstTabWebContents); + } + + /** + * Ends whatever type of session a subclass enters with enterSessionWithUserGesture. + * @param webContents The WebContents to end the session in + */ + public abstract void endSession(WebContents webContents); + + /** + * Helper function to run endSession with the first tab's WebContents. + */ + public void endSession() { + endSession(mFirstTabWebContents); + } +}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrVrDeviceTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrVrDeviceTest.java index cd525e5a..7ccb1eee 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrVrDeviceTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrVrDeviceTest.java
@@ -4,7 +4,7 @@ package org.chromium.chrome.browser.vr; -import static org.chromium.chrome.browser.vr.VrTestFramework.PAGE_LOAD_TIMEOUT_S; +import static org.chromium.chrome.browser.vr.XrTestFramework.PAGE_LOAD_TIMEOUT_S; import android.os.Build; import android.support.test.filters.MediumTest; @@ -25,7 +25,7 @@ import org.chromium.chrome.browser.ChromeSwitches; import org.chromium.chrome.browser.vr.rules.XrActivityRestriction; import org.chromium.chrome.browser.vr.util.VrShellDelegateUtils; -import org.chromium.chrome.browser.vr.util.XrTestRuleUtils; +import org.chromium.chrome.browser.vr.util.VrTestRuleUtils; import org.chromium.chrome.test.ChromeActivityTestRule; import org.chromium.chrome.test.ChromeJUnit4RunnerDelegate; @@ -43,23 +43,23 @@ public class WebXrVrDeviceTest { @ClassParameter private static List<ParameterSet> sClassParams = - XrTestRuleUtils.generateDefaultXrTestRuleParameters(); + VrTestRuleUtils.generateDefaultTestRuleParameters(); @Rule public RuleChain mRuleChain; private ChromeActivityTestRule mTestRule; - private VrTestFramework mVrTestFramework; - private XrTestFramework mXrTestFramework; + private WebXrVrTestFramework mWebXrVrTestFramework; + private WebVrTestFramework mWebVrTestFramework; public WebXrVrDeviceTest(Callable<ChromeActivityTestRule> callable) throws Exception { mTestRule = callable.call(); - mRuleChain = XrTestRuleUtils.wrapRuleInXrActivityRestrictionRule(mTestRule); + mRuleChain = VrTestRuleUtils.wrapRuleInXrActivityRestrictionRule(mTestRule); } @Before public void setUp() throws Exception { - mVrTestFramework = new VrTestFramework(mTestRule); - mXrTestFramework = new XrTestFramework(mTestRule); + mWebXrVrTestFramework = new WebXrVrTestFramework(mTestRule); + mWebVrTestFramework = new WebVrTestFramework(mTestRule); } /** @@ -70,13 +70,13 @@ @MediumTest @XrActivityRestriction({XrActivityRestriction.SupportedActivity.ALL}) public void testDeviceCapabilitiesMatchExpectations() throws InterruptedException { - mVrTestFramework.loadUrlAndAwaitInitialization( - VrTestFramework.getFileUrlForHtmlTestFile( + mWebVrTestFramework.loadUrlAndAwaitInitialization( + WebVrTestFramework.getFileUrlForHtmlTestFile( "test_device_capabilities_match_expectations"), PAGE_LOAD_TIMEOUT_S); - VrTestFramework.executeStepAndWait("stepCheckDeviceCapabilities('" + Build.DEVICE + "')", - mVrTestFramework.getFirstTabWebContents()); - VrTestFramework.endTest(mVrTestFramework.getFirstTabWebContents()); + mWebVrTestFramework.executeStepAndWait( + "stepCheckDeviceCapabilities('" + Build.DEVICE + "')"); + mWebVrTestFramework.endTest(); } /** @@ -91,15 +91,14 @@ // Make Chrome think that VrCore is not installed VrShellDelegateUtils.setVrCoreCompatibility(VrCoreCompatibility.VR_NOT_AVAILABLE); - mVrTestFramework.loadUrlAndAwaitInitialization( - VrTestFramework.getFileUrlForHtmlTestFile( + mWebVrTestFramework.loadUrlAndAwaitInitialization( + WebVrTestFramework.getFileUrlForHtmlTestFile( "test_device_capabilities_match_expectations"), PAGE_LOAD_TIMEOUT_S); - Assert.assertTrue( - VrTestFramework.vrDisplayFound(mVrTestFramework.getFirstTabWebContents())); - VrTestFramework.executeStepAndWait("stepCheckDeviceCapabilities('VR Orientation Device')", - mVrTestFramework.getFirstTabWebContents()); - VrTestFramework.endTest(mVrTestFramework.getFirstTabWebContents()); + Assert.assertTrue(mWebVrTestFramework.xrDeviceFound()); + mWebVrTestFramework.executeStepAndWait( + "stepCheckDeviceCapabilities('VR Orientation Device')"); + mWebVrTestFramework.endTest(); VrShellDelegateUtils.getDelegateInstance().overrideVrCoreVersionCheckerForTesting(null); } @@ -113,11 +112,10 @@ @CommandLineFlags.Add({"enable-features=WebXR"}) @XrActivityRestriction({XrActivityRestriction.SupportedActivity.ALL}) public void testWebXrCapabilities() throws InterruptedException { - mXrTestFramework.loadUrlAndAwaitInitialization( - XrTestFramework.getFileUrlForHtmlTestFile("test_webxr_capabilities"), + mWebXrVrTestFramework.loadUrlAndAwaitInitialization( + WebXrVrTestFramework.getFileUrlForHtmlTestFile("test_webxr_capabilities"), PAGE_LOAD_TIMEOUT_S); - XrTestFramework.executeStepAndWait( - "stepCheckCapabilities('Daydream')", mXrTestFramework.getFirstTabWebContents()); - XrTestFramework.endTest(mXrTestFramework.getFirstTabWebContents()); + mWebXrVrTestFramework.executeStepAndWait("stepCheckCapabilities('Daydream')"); + mWebXrVrTestFramework.endTest(); } }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrVrInputTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrVrInputTest.java index f7954a1b..d6f32c6 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrVrInputTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrVrInputTest.java
@@ -4,10 +4,10 @@ package org.chromium.chrome.browser.vr; -import static org.chromium.chrome.browser.vr.TestFramework.PAGE_LOAD_TIMEOUT_S; -import static org.chromium.chrome.browser.vr.TestFramework.POLL_CHECK_INTERVAL_SHORT_MS; -import static org.chromium.chrome.browser.vr.TestFramework.POLL_TIMEOUT_LONG_MS; -import static org.chromium.chrome.browser.vr.TestFramework.POLL_TIMEOUT_SHORT_MS; +import static org.chromium.chrome.browser.vr.XrTestFramework.PAGE_LOAD_TIMEOUT_S; +import static org.chromium.chrome.browser.vr.XrTestFramework.POLL_CHECK_INTERVAL_SHORT_MS; +import static org.chromium.chrome.browser.vr.XrTestFramework.POLL_TIMEOUT_LONG_MS; +import static org.chromium.chrome.browser.vr.XrTestFramework.POLL_TIMEOUT_SHORT_MS; import static org.chromium.chrome.test.util.ChromeRestriction.RESTRICTION_TYPE_VIEWER_DAYDREAM; import static org.chromium.chrome.test.util.ChromeRestriction.RESTRICTION_TYPE_VIEWER_NON_DAYDREAM; @@ -39,10 +39,9 @@ import org.chromium.chrome.browser.ChromeSwitches; import org.chromium.chrome.browser.vr.mock.MockVrDaydreamApi; import org.chromium.chrome.browser.vr.rules.XrActivityRestriction; -import org.chromium.chrome.browser.vr.util.TransitionUtils; import org.chromium.chrome.browser.vr.util.VrShellDelegateUtils; +import org.chromium.chrome.browser.vr.util.VrTestRuleUtils; import org.chromium.chrome.browser.vr.util.VrTransitionUtils; -import org.chromium.chrome.browser.vr.util.XrTestRuleUtils; import org.chromium.chrome.test.ChromeActivityTestRule; import org.chromium.chrome.test.ChromeJUnit4RunnerDelegate; import org.chromium.content.browser.test.util.Criteria; @@ -66,32 +65,31 @@ public class WebXrVrInputTest { @ClassParameter private static List<ParameterSet> sClassParams = - XrTestRuleUtils.generateDefaultXrTestRuleParameters(); + VrTestRuleUtils.generateDefaultTestRuleParameters(); @Rule public RuleChain mRuleChain; private ChromeActivityTestRule mTestRule; - private VrTestFramework mVrTestFramework; - private XrTestFramework mXrTestFramework; + private WebXrVrTestFramework mWebXrVrTestFramework; + private WebVrTestFramework mWebVrTestFramework; public WebXrVrInputTest(Callable<ChromeActivityTestRule> callable) throws Exception { mTestRule = callable.call(); - mRuleChain = XrTestRuleUtils.wrapRuleInXrActivityRestrictionRule(mTestRule); + mRuleChain = VrTestRuleUtils.wrapRuleInXrActivityRestrictionRule(mTestRule); } @Before public void setUp() throws Exception { - mVrTestFramework = new VrTestFramework(mTestRule); - mXrTestFramework = new XrTestFramework(mTestRule); + mWebXrVrTestFramework = new WebXrVrTestFramework(mTestRule); + mWebVrTestFramework = new WebVrTestFramework(mTestRule); } - private void assertAppButtonEffect(boolean shouldHaveExited, TestFramework framework) { - String boolExpression = (framework instanceof VrTestFramework) + private void assertAppButtonEffect(boolean shouldHaveExited, WebXrVrTestFramework framework) { + String boolExpression = (framework instanceof WebVrTestFramework) ? "!vrDisplay.isPresenting" : "sessionInfos[sessionTypes.IMMERSIVE].currentSession == null"; Assert.assertEquals("App button exited presentation", shouldHaveExited, - TestFramework.pollJavaScriptBoolean( - boolExpression, POLL_TIMEOUT_SHORT_MS, framework.getFirstTabWebContents())); + mWebXrVrTestFramework.pollJavaScriptBoolean(boolExpression, POLL_TIMEOUT_SHORT_MS)); } /** @@ -104,8 +102,8 @@ @XrActivityRestriction({XrActivityRestriction.SupportedActivity.ALL}) public void testScreenTapsNotRegistered() throws InterruptedException { screenTapsNotRegisteredImpl( - VrTestFramework.getFileUrlForHtmlTestFile("test_screen_taps_not_registered"), - mVrTestFramework); + WebVrTestFramework.getFileUrlForHtmlTestFile("test_screen_taps_not_registered"), + mWebVrTestFramework); } /** @@ -122,18 +120,17 @@ @XrActivityRestriction({XrActivityRestriction.SupportedActivity.ALL}) public void testScreenTapsNotRegistered_WebXr() throws InterruptedException { - screenTapsNotRegisteredImpl( - XrTestFramework.getFileUrlForHtmlTestFile("webxr_test_screen_taps_not_registered"), - mXrTestFramework); + screenTapsNotRegisteredImpl(WebXrVrTestFramework.getFileUrlForHtmlTestFile( + "webxr_test_screen_taps_not_registered"), + mWebXrVrTestFramework); } - private void screenTapsNotRegisteredImpl(String url, final TestFramework framework) + private void screenTapsNotRegisteredImpl(String url, final WebXrVrTestFramework framework) throws InterruptedException { framework.loadUrlAndAwaitInitialization(url, PAGE_LOAD_TIMEOUT_S); - TestFramework.executeStepAndWait( - "stepVerifyNoInitialTaps()", framework.getFirstTabWebContents()); - TransitionUtils.enterPresentationOrFail(framework); - TransitionUtils.waitForOverlayGone(); + framework.executeStepAndWait("stepVerifyNoInitialTaps()"); + framework.enterSessionWithUserGestureOrFail(); + VrTransitionUtils.waitForOverlayGone(); // Wait on VrShellImpl to say that its parent consumed the touch event // Set to 2 because there's an ACTION_DOWN followed by ACTION_UP final CountDownLatch touchRegisteredLatch = new CountDownLatch(2); @@ -148,9 +145,8 @@ TouchCommon.singleClickView(mTestRule.getActivity().getWindow().getDecorView()); Assert.assertTrue("VrShellImpl dispatched touches", touchRegisteredLatch.await(POLL_TIMEOUT_LONG_MS * 10, TimeUnit.MILLISECONDS)); - TestFramework.executeStepAndWait( - "stepVerifyNoAdditionalTaps()", framework.getFirstTabWebContents()); - TestFramework.endTest(framework.getFirstTabWebContents()); + framework.executeStepAndWait("stepVerifyNoAdditionalTaps()"); + framework.endTest(); } /** @@ -162,19 +158,17 @@ @XrActivityRestriction({XrActivityRestriction.SupportedActivity.ALL}) public void testControllerClicksRegisteredOnDaydream() throws InterruptedException { EmulatedVrController controller = new EmulatedVrController(mTestRule.getActivity()); - mVrTestFramework.loadUrlAndAwaitInitialization( - VrTestFramework.getFileUrlForHtmlTestFile("test_gamepad_button"), + mWebVrTestFramework.loadUrlAndAwaitInitialization( + WebVrTestFramework.getFileUrlForHtmlTestFile("test_gamepad_button"), PAGE_LOAD_TIMEOUT_S); // Wait to enter VR - VrTransitionUtils.enterPresentationOrFail(mVrTestFramework.getFirstTabWebContents()); + mWebVrTestFramework.enterSessionWithUserGestureOrFail(); // The Gamepad API can flakily fail to detect the gamepad from a single button press, so // spam it with button presses boolean controllerConnected = false; for (int i = 0; i < 10; i++) { controller.performControllerClick(); - if (VrTestFramework - .runJavaScriptOrFail("index != -1", POLL_TIMEOUT_SHORT_MS, - mVrTestFramework.getFirstTabWebContents()) + if (mWebVrTestFramework.runJavaScriptOrFail("index != -1", POLL_TIMEOUT_SHORT_MS) .equals("true")) { controllerConnected = true; break; @@ -184,18 +178,16 @@ // It's possible for input to get backed up if the emulated controller is being slow, so // ensure that any outstanding output has been received before starting by waiting for // 60 frames (1 second) of not receiving input. - VrTestFramework.pollJavaScriptBoolean("isInputDrained()", POLL_TIMEOUT_LONG_MS, - mVrTestFramework.getFirstTabWebContents()); + mWebVrTestFramework.pollJavaScriptBoolean("isInputDrained()", POLL_TIMEOUT_LONG_MS); // Have a separate start condition so that the above presses/releases don't get // accidentally detected during the actual test - VrTestFramework.runJavaScriptOrFail("canStartTest = true;", POLL_TIMEOUT_SHORT_MS, - mVrTestFramework.getFirstTabWebContents()); + mWebVrTestFramework.runJavaScriptOrFail("canStartTest = true;", POLL_TIMEOUT_SHORT_MS); // Send a controller click and wait for JavaScript to receive it. controller.sendClickButtonToggleEvent(); - VrTestFramework.waitOnJavaScriptStep(mVrTestFramework.getFirstTabWebContents()); + mWebVrTestFramework.waitOnJavaScriptStep(); controller.sendClickButtonToggleEvent(); - VrTestFramework.waitOnJavaScriptStep(mVrTestFramework.getFirstTabWebContents()); - VrTestFramework.endTest(mVrTestFramework.getFirstTabWebContents()); + mWebVrTestFramework.waitOnJavaScriptStep(); + mWebVrTestFramework.endTest(); } /** @@ -211,14 +203,14 @@ public void testControllerClicksRegisteredOnDaydream_WebXr() throws InterruptedException { EmulatedVrController controller = new EmulatedVrController(mTestRule.getActivity()); - mXrTestFramework.loadUrlAndAwaitInitialization( - XrTestFramework.getFileUrlForHtmlTestFile("test_webxr_input"), PAGE_LOAD_TIMEOUT_S); - TransitionUtils.enterPresentationOrFail(mXrTestFramework); + mWebXrVrTestFramework.loadUrlAndAwaitInitialization( + WebXrVrTestFramework.getFileUrlForHtmlTestFile("test_webxr_input"), + PAGE_LOAD_TIMEOUT_S); + mWebXrVrTestFramework.enterSessionWithUserGestureOrFail(); int numIterations = 10; - XrTestFramework.runJavaScriptOrFail( - "stepSetupListeners(" + String.valueOf(numIterations) + ")", POLL_TIMEOUT_SHORT_MS, - mXrTestFramework.getFirstTabWebContents()); + mWebXrVrTestFramework.runJavaScriptOrFail( + "stepSetupListeners(" + String.valueOf(numIterations) + ")", POLL_TIMEOUT_SHORT_MS); // Click the touchpad a bunch of times and make sure they're all registered. for (int i = 0; i < numIterations; i++) { @@ -227,10 +219,10 @@ // The controller emulation can sometimes deliver controller input at weird times such // that we only register 8 or 9 of the 10 press/release pairs. So, send a press/release // and wait for it to register before doing another. - XrTestFramework.waitOnJavaScriptStep(mXrTestFramework.getFirstTabWebContents()); + mWebXrVrTestFramework.waitOnJavaScriptStep(); } - XrTestFramework.endTest(mXrTestFramework.getFirstTabWebContents()); + mWebXrVrTestFramework.endTest(); } private long sendScreenTouchDown(final View view, final int x, final int y) { @@ -276,27 +268,26 @@ @Restriction(RESTRICTION_TYPE_VIEWER_NON_DAYDREAM) @XrActivityRestriction({XrActivityRestriction.SupportedActivity.ALL}) public void testScreenTapsRegisteredOnCardboard() throws InterruptedException { - mVrTestFramework.loadUrlAndAwaitInitialization( - VrTestFramework.getFileUrlForHtmlTestFile("test_gamepad_button"), + mWebVrTestFramework.loadUrlAndAwaitInitialization( + WebVrTestFramework.getFileUrlForHtmlTestFile("test_gamepad_button"), PAGE_LOAD_TIMEOUT_S); // This boolean is used by testControllerClicksRegisteredOnDaydream to prevent some // flakiness, but is unnecessary here, so set immediately - VrTestFramework.runJavaScriptOrFail("canStartTest = true;", POLL_TIMEOUT_SHORT_MS, - mVrTestFramework.getFirstTabWebContents()); + mWebVrTestFramework.runJavaScriptOrFail("canStartTest = true;", POLL_TIMEOUT_SHORT_MS); // Wait to enter VR - VrTransitionUtils.enterPresentationOrFail(mVrTestFramework.getFirstTabWebContents()); - int x = mVrTestFramework.getFirstTabContentView().getWidth() / 2; - int y = mVrTestFramework.getFirstTabContentView().getHeight() / 2; + mWebVrTestFramework.enterSessionWithUserGestureOrFail(); + int x = mWebVrTestFramework.getFirstTabContentView().getWidth() / 2; + int y = mWebVrTestFramework.getFirstTabContentView().getHeight() / 2; // TODO(mthiesse, https://crbug.com/758374): Injecting touch events into the root GvrLayout // (VrShellImpl) is flaky. Sometimes the events just don't get routed to the presentation // view for no apparent reason. We should figure out why this is and see if it's fixable. final View presentationView = ((VrShellImpl) TestVrShellDelegate.getVrShellForTesting()) .getPresentationViewForTesting(); long downTime = sendScreenTouchDown(presentationView, x, y); - VrTestFramework.waitOnJavaScriptStep(mVrTestFramework.getFirstTabWebContents()); + mWebVrTestFramework.waitOnJavaScriptStep(); sendScreenTouchUp(presentationView, x, y, downTime); - VrTestFramework.waitOnJavaScriptStep(mVrTestFramework.getFirstTabWebContents()); - VrTestFramework.endTest(mVrTestFramework.getFirstTabWebContents()); + mWebVrTestFramework.waitOnJavaScriptStep(); + mWebVrTestFramework.endTest(); } /** @@ -311,16 +302,16 @@ @XrActivityRestriction({XrActivityRestriction.SupportedActivity.ALL}) public void testScreenTapsRegisteredOnCardboard_WebXr() throws InterruptedException { EmulatedVrController controller = new EmulatedVrController(mTestRule.getActivity()); - mXrTestFramework.loadUrlAndAwaitInitialization( - XrTestFramework.getFileUrlForHtmlTestFile("test_webxr_input"), PAGE_LOAD_TIMEOUT_S); - TransitionUtils.enterPresentationOrFail(mXrTestFramework); + mWebXrVrTestFramework.loadUrlAndAwaitInitialization( + WebXrVrTestFramework.getFileUrlForHtmlTestFile("test_webxr_input"), + PAGE_LOAD_TIMEOUT_S); + mWebXrVrTestFramework.enterSessionWithUserGestureOrFail(); int numIterations = 10; - XrTestFramework.runJavaScriptOrFail( - "stepSetupListeners(" + String.valueOf(numIterations) + ")", POLL_TIMEOUT_SHORT_MS, - mXrTestFramework.getFirstTabWebContents()); + mWebXrVrTestFramework.runJavaScriptOrFail( + "stepSetupListeners(" + String.valueOf(numIterations) + ")", POLL_TIMEOUT_SHORT_MS); - int x = mXrTestFramework.getFirstTabContentView().getWidth() / 2; - int y = mXrTestFramework.getFirstTabContentView().getHeight() / 2; + int x = mWebXrVrTestFramework.getFirstTabContentView().getWidth() / 2; + int y = mWebXrVrTestFramework.getFirstTabContentView().getHeight() / 2; // TODO(mthiesse, https://crbug.com/758374): Injecting touch events into the root GvrLayout // (VrShellImpl) is flaky. Sometimes the events just don't get routed to the presentation // view for no apparent reason. We should figure out why this is and see if it's fixable. @@ -330,8 +321,8 @@ // Tap the screen a bunch of times and make sure that they're all registered. spamScreenTaps(presentationView, x, y, numIterations); - XrTestFramework.waitOnJavaScriptStep(mXrTestFramework.getFirstTabWebContents()); - XrTestFramework.endTest(mXrTestFramework.getFirstTabWebContents()); + mWebXrVrTestFramework.waitOnJavaScriptStep(); + mWebXrVrTestFramework.endTest(); } /** @@ -342,8 +333,8 @@ @XrActivityRestriction({XrActivityRestriction.SupportedActivity.ALL}) public void testPresentationLocksFocus() throws InterruptedException { presentationLocksFocusImpl( - VrTestFramework.getFileUrlForHtmlTestFile("test_presentation_locks_focus"), - mVrTestFramework); + WebVrTestFramework.getFileUrlForHtmlTestFile("test_presentation_locks_focus"), + mWebVrTestFramework); } /** @@ -357,18 +348,17 @@ @CommandLineFlags.Add({"enable-features=WebXR"}) @XrActivityRestriction({XrActivityRestriction.SupportedActivity.ALL}) public void testPresentationLocksFocus_WebXr() throws InterruptedException { - presentationLocksFocusImpl( - XrTestFramework.getFileUrlForHtmlTestFile("webxr_test_presentation_locks_focus"), - mXrTestFramework); + presentationLocksFocusImpl(WebXrVrTestFramework.getFileUrlForHtmlTestFile( + "webxr_test_presentation_locks_focus"), + mWebXrVrTestFramework); } - private void presentationLocksFocusImpl(String url, TestFramework framework) + private void presentationLocksFocusImpl(String url, WebXrVrTestFramework framework) throws InterruptedException { framework.loadUrlAndAwaitInitialization(url, PAGE_LOAD_TIMEOUT_S); - TransitionUtils.enterPresentationOrFail(framework); - TestFramework.executeStepAndWait( - "stepSetupFocusLoss()", framework.getFirstTabWebContents()); - TestFramework.endTest(framework.getFirstTabWebContents()); + framework.enterSessionWithUserGestureOrFail(); + framework.executeStepAndWait("stepSetupFocusLoss()"); + framework.endTest(); } /** @@ -381,7 +371,8 @@ @RetryOnFailure(message = "Very rarely, button press not registered (race condition?)") public void testAppButtonExitsPresentation() throws InterruptedException { appButtonExitsPresentationImpl( - VrTestFramework.getFileUrlForHtmlTestFile("generic_webvr_page"), mVrTestFramework); + WebVrTestFramework.getFileUrlForHtmlTestFile("generic_webvr_page"), + mWebVrTestFramework); } /** @@ -397,13 +388,14 @@ @RetryOnFailure(message = "Very rarely, button press not registered (race condition?)") public void testAppButtonExitsPresentation_WebXr() throws InterruptedException { appButtonExitsPresentationImpl( - XrTestFramework.getFileUrlForHtmlTestFile("generic_webxr_page"), mXrTestFramework); + WebXrVrTestFramework.getFileUrlForHtmlTestFile("generic_webxr_page"), + mWebXrVrTestFramework); } - private void appButtonExitsPresentationImpl(String url, TestFramework framework) + private void appButtonExitsPresentationImpl(String url, WebXrVrTestFramework framework) throws InterruptedException { framework.loadUrlAndAwaitInitialization(url, PAGE_LOAD_TIMEOUT_S); - TransitionUtils.enterPresentationOrFail(framework); + framework.enterSessionWithUserGestureOrFail(); EmulatedVrController controller = new EmulatedVrController(mTestRule.getActivity()); controller.pressReleaseAppButton(); assertAppButtonEffect(true /* shouldHaveExited */, framework); @@ -419,8 +411,8 @@ @XrActivityRestriction({XrActivityRestriction.SupportedActivity.ALL}) public void testAppButtonNoopsWhenBrowsingDisabled() throws InterruptedException, ExecutionException { - appButtonNoopsTestImpl( - VrTestFramework.getFileUrlForHtmlTestFile("generic_webvr_page"), mVrTestFramework); + appButtonNoopsTestImpl(WebVrTestFramework.getFileUrlForHtmlTestFile("generic_webvr_page"), + mWebVrTestFramework); } /** @@ -434,8 +426,8 @@ XrActivityRestriction.SupportedActivity.CCT}) public void testAppButtonNoopsWhenBrowsingNotSupported() throws InterruptedException, ExecutionException { - appButtonNoopsTestImpl( - VrTestFramework.getFileUrlForHtmlTestFile("generic_webvr_page"), mVrTestFramework); + appButtonNoopsTestImpl(WebVrTestFramework.getFileUrlForHtmlTestFile("generic_webvr_page"), + mWebVrTestFramework); } /** @@ -451,8 +443,8 @@ @CommandLineFlags.Add({"enable-features=WebXR"}) public void testAppButtonNoopsWhenBrowsingDisabled_WebXr() throws InterruptedException, ExecutionException { - appButtonNoopsTestImpl( - XrTestFramework.getFileUrlForHtmlTestFile("generic_webxr_page"), mXrTestFramework); + appButtonNoopsTestImpl(WebXrVrTestFramework.getFileUrlForHtmlTestFile("generic_webxr_page"), + mWebXrVrTestFramework); } /** @@ -470,15 +462,15 @@ public void testAppButtonNoopsWhenBrowsingNotSupported_WebXr() throws InterruptedException, ExecutionException { - appButtonNoopsTestImpl( - XrTestFramework.getFileUrlForHtmlTestFile("generic_webxr_page"), mXrTestFramework); + appButtonNoopsTestImpl(WebXrVrTestFramework.getFileUrlForHtmlTestFile("generic_webxr_page"), + mWebXrVrTestFramework); } - private void appButtonNoopsTestImpl(String url, TestFramework framework) + private void appButtonNoopsTestImpl(String url, WebXrVrTestFramework framework) throws InterruptedException, ExecutionException { VrShellDelegateUtils.getDelegateInstance().setVrBrowsingDisabled(true); framework.loadUrlAndAwaitInitialization(url, PAGE_LOAD_TIMEOUT_S); - TransitionUtils.enterPresentationOrFail(framework); + framework.enterSessionWithUserGestureOrFail(); MockVrDaydreamApi mockApi = new MockVrDaydreamApi(); VrShellDelegateUtils.getDelegateInstance().overrideDaydreamApiForTesting(mockApi); @@ -506,8 +498,8 @@ @Restriction(RESTRICTION_TYPE_VIEWER_DAYDREAM) @XrActivityRestriction({XrActivityRestriction.SupportedActivity.ALL}) public void testFocusUpdatesSynchronously() throws InterruptedException { - mVrTestFramework.loadUrlAndAwaitInitialization( - VrTestFramework.getFileUrlForHtmlTestFile( + mWebVrTestFramework.loadUrlAndAwaitInitialization( + WebVrTestFramework.getFileUrlForHtmlTestFile( "generic_webvr_page_with_activate_listener"), PAGE_LOAD_TIMEOUT_S); @@ -537,8 +529,8 @@ @RetryOnFailure(message = "Very rarely, button press not registered (race condition?)") public void testAppButtonAfterPageStopsSubmitting() throws InterruptedException { appButtonAfterPageStopsSubmittingImpl( - VrTestFramework.getFileUrlForHtmlTestFile("webvr_page_submits_once"), - mVrTestFramework); + WebVrTestFramework.getFileUrlForHtmlTestFile("webvr_page_submits_once"), + mWebVrTestFramework); } /** @@ -554,16 +546,16 @@ @RetryOnFailure(message = "Very rarely, button press not registered (race condition?)") public void testAppButtonAfterPageStopsSubmitting_WebXr() throws InterruptedException { appButtonAfterPageStopsSubmittingImpl( - XrTestFramework.getFileUrlForHtmlTestFile("webxr_page_submits_once"), - mXrTestFramework); + WebXrVrTestFramework.getFileUrlForHtmlTestFile("webxr_page_submits_once"), + mWebXrVrTestFramework); } - private void appButtonAfterPageStopsSubmittingImpl(String url, TestFramework framework) + private void appButtonAfterPageStopsSubmittingImpl(String url, WebXrVrTestFramework framework) throws InterruptedException { framework.loadUrlAndAwaitInitialization(url, PAGE_LOAD_TIMEOUT_S); - TransitionUtils.enterPresentationOrFail(framework); + framework.enterSessionWithUserGestureOrFail(); // Wait for page to stop submitting frames. - TestFramework.waitOnJavaScriptStep(framework.getFirstTabWebContents()); + framework.waitOnJavaScriptStep(); EmulatedVrController controller = new EmulatedVrController(mTestRule.getActivity()); controller.pressReleaseAppButton(); assertAppButtonEffect(true /* shouldHaveExited */, framework); @@ -671,13 +663,14 @@ private void webxrGamepadSupportImpl(int numExpectedGamepads, boolean webxrPresent, boolean daydream) throws InterruptedException { if (webxrPresent) { - mXrTestFramework.loadUrlAndAwaitInitialization( - XrTestFramework.getFileUrlForHtmlTestFile("test_webxr_gamepad_support"), + mWebXrVrTestFramework.loadUrlAndAwaitInitialization( + WebXrVrTestFramework.getFileUrlForHtmlTestFile("test_webxr_gamepad_support"), PAGE_LOAD_TIMEOUT_S); - TransitionUtils.enterPresentationOrFail(mXrTestFramework); + mWebXrVrTestFramework.enterSessionWithUserGestureOrFail(); } else { - mXrTestFramework.loadUrlAndAwaitInitialization( - XrTestFramework.getFileUrlForHtmlTestFile("test_webxr_gamepad_support_nowebxr"), + mWebXrVrTestFramework.loadUrlAndAwaitInitialization( + WebXrVrTestFramework.getFileUrlForHtmlTestFile( + "test_webxr_gamepad_support_nowebxr"), PAGE_LOAD_TIMEOUT_S); } @@ -689,8 +682,8 @@ controller.performControllerClick(); } } else { - int x = mXrTestFramework.getFirstTabContentView().getWidth() / 2; - int y = mXrTestFramework.getFirstTabContentView().getHeight() / 2; + int x = mWebXrVrTestFramework.getFirstTabContentView().getWidth() / 2; + int y = mWebXrVrTestFramework.getFirstTabContentView().getHeight() / 2; View presentationView; if (webxrPresent) { @@ -703,9 +696,8 @@ spamScreenTaps(presentationView, x, y, numIterations); } - XrTestFramework.executeStepAndWait("stepAssertNumGamepadsMatchesExpectation(" - + String.valueOf(numExpectedGamepads) + ")", - mXrTestFramework.getFirstTabWebContents()); - XrTestFramework.endTest(mXrTestFramework.getFirstTabWebContents()); + mWebXrVrTestFramework.executeStepAndWait("stepAssertNumGamepadsMatchesExpectation(" + + String.valueOf(numExpectedGamepads) + ")"); + mWebXrVrTestFramework.endTest(); } }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrVrTabTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrVrTabTest.java index ffcfb6e..6742e3f 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrVrTabTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrVrTabTest.java
@@ -4,7 +4,7 @@ package org.chromium.chrome.browser.vr; -import static org.chromium.chrome.browser.vr.VrTestFramework.PAGE_LOAD_TIMEOUT_S; +import static org.chromium.chrome.browser.vr.XrTestFramework.PAGE_LOAD_TIMEOUT_S; import android.os.Build; import android.support.test.filters.MediumTest; @@ -22,7 +22,7 @@ import org.chromium.base.test.util.CommandLineFlags; import org.chromium.base.test.util.MinAndroidSdkLevel; import org.chromium.chrome.browser.ChromeSwitches; -import org.chromium.chrome.browser.vr.util.XrTestRuleUtils; +import org.chromium.chrome.browser.vr.util.VrTestRuleUtils; import org.chromium.chrome.test.ChromeActivityTestRule; import org.chromium.chrome.test.ChromeJUnit4RunnerDelegate; @@ -39,23 +39,23 @@ public class WebXrVrTabTest { @ClassParameter private static List<ParameterSet> sClassParams = - XrTestRuleUtils.generateDefaultXrTestRuleParameters(); + VrTestRuleUtils.generateDefaultTestRuleParameters(); @Rule public RuleChain mRuleChain; private ChromeActivityTestRule mTestRule; - private VrTestFramework mVrTestFramework; - private XrTestFramework mXrTestFramework; + private WebXrVrTestFramework mWebXrVrTestFramework; + private WebVrTestFramework mWebVrTestFramework; public WebXrVrTabTest(Callable<ChromeActivityTestRule> callable) throws Exception { mTestRule = callable.call(); - mRuleChain = XrTestRuleUtils.wrapRuleInXrActivityRestrictionRule(mTestRule); + mRuleChain = VrTestRuleUtils.wrapRuleInXrActivityRestrictionRule(mTestRule); } @Before public void setUp() throws Exception { - mVrTestFramework = new VrTestFramework(mTestRule); - mXrTestFramework = new XrTestFramework(mTestRule); + mWebXrVrTestFramework = new WebXrVrTestFramework(mTestRule); + mWebVrTestFramework = new WebVrTestFramework(mTestRule); } /** @@ -65,8 +65,8 @@ @MediumTest public void testPoseDataUnfocusedTab() throws InterruptedException { testPoseDataUnfocusedTabImpl( - VrTestFramework.getFileUrlForHtmlTestFile("test_pose_data_unfocused_tab"), - mVrTestFramework); + WebVrTestFramework.getFileUrlForHtmlTestFile("test_pose_data_unfocused_tab"), + mWebVrTestFramework); } /** @@ -78,21 +78,19 @@ .Remove({"enable-webvr"}) @CommandLineFlags.Add({"enable-features=WebXR"}) public void testPoseDataUnfocusedTab_WebXr() throws InterruptedException { - testPoseDataUnfocusedTabImpl( - XrTestFramework.getFileUrlForHtmlTestFile("webxr_test_pose_data_unfocused_tab"), - mXrTestFramework); + testPoseDataUnfocusedTabImpl(WebXrVrTestFramework.getFileUrlForHtmlTestFile( + "webxr_test_pose_data_unfocused_tab"), + mWebXrVrTestFramework); } - private void testPoseDataUnfocusedTabImpl(String url, TestFramework framework) + private void testPoseDataUnfocusedTabImpl(String url, WebXrVrTestFramework framework) throws InterruptedException { framework.loadUrlAndAwaitInitialization(url, PAGE_LOAD_TIMEOUT_S); - TestFramework.executeStepAndWait( - "stepCheckFrameDataWhileFocusedTab()", framework.getFirstTabWebContents()); + framework.executeStepAndWait("stepCheckFrameDataWhileFocusedTab()"); mTestRule.loadUrlInNewTab("about:blank"); - TestFramework.executeStepAndWait( - "stepCheckFrameDataWhileNonFocusedTab()", framework.getFirstTabWebContents()); - TestFramework.endTest(framework.getFirstTabWebContents()); + framework.executeStepAndWait("stepCheckFrameDataWhileNonFocusedTab()"); + framework.endTest(); } }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrVrTestFramework.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrVrTestFramework.java new file mode 100644 index 0000000..cf3e0ef --- /dev/null +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrVrTestFramework.java
@@ -0,0 +1,66 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.browser.vr; + +import org.junit.Assert; + +import org.chromium.base.CommandLine; +import org.chromium.chrome.browser.vr.util.VrShellDelegateUtils; +import org.chromium.chrome.test.ChromeActivityTestRule; +import org.chromium.content_public.browser.WebContents; + +/** + * Extension of VrTestFramework containing WebXR for VR-specific functionality. + */ +public class WebXrVrTestFramework extends WebXrTestFramework { + public WebXrVrTestFramework(ChromeActivityTestRule rule) { + super(rule); + Assert.assertFalse("Test did not start in VR", VrShellDelegate.isInVr()); + } + + /** + * VR-specific implementation of enterSessionWithUserGesture that includes a workaround for + * receiving broadcasts late. + * @param webContents The WebContents for the tab to enter the VR session in. + */ + @Override + public void enterSessionWithUserGesture(WebContents webContents) { + // TODO(https://crbug.com/762724): Remove this workaround when the issue with being resumed + // before receiving the VR broadcast is fixed on VrCore's end. + // However, we don't want to enable the workaround if the DON flow is enabled, as that + // causes issues. Since we don't have a way of actually checking whether the DON flow is + // enabled, check for the presence of the flag that's passed to tests when the DON flow is + // enabled. + if (!CommandLine.getInstance().hasSwitch("don-enabled")) { + VrShellDelegateUtils.getDelegateInstance().setExpectingBroadcast(); + } + super.enterSessionWithUserGesture(webContents); + } + + /** + * WebXR for VR-specific implementation of enterSessionWithUserGestureOrFail. + * @param webContents The WebContents of the tab to enter the immersive session in. + */ + @Override + public void enterSessionWithUserGestureOrFail(WebContents webContents) { + runJavaScriptOrFail( + "sessionTypeToRequest = sessionTypes.IMMERSIVE", POLL_TIMEOUT_LONG_MS, webContents); + enterSessionWithUserGesture(webContents); + Assert.assertTrue( + pollJavaScriptBoolean("sessionInfos[sessionTypes.IMMERSIVE].currentSession != null", + POLL_TIMEOUT_LONG_MS, webContents)); + Assert.assertTrue(TestVrShellDelegate.getVrShellForTesting().getWebVrModeEnabled()); + } + + /** + * Ends a WebXR immersive session. + * @param webContents The WebContents for the tab to end the session in + */ + @Override + public void endSession(WebContents webContents) { + runJavaScriptOrFail("sessionInfos[sessionTypes.IMMERSIVE].currentSession.end()", + POLL_TIMEOUT_SHORT_MS, webContents); + } +}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrVrTransitionTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrVrTransitionTest.java index e75a999..ca7514a 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrVrTransitionTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrVrTransitionTest.java
@@ -4,11 +4,11 @@ package org.chromium.chrome.browser.vr; -import static org.chromium.chrome.browser.vr.TestFramework.PAGE_LOAD_TIMEOUT_S; -import static org.chromium.chrome.browser.vr.TestFramework.POLL_CHECK_INTERVAL_LONG_MS; -import static org.chromium.chrome.browser.vr.TestFramework.POLL_CHECK_INTERVAL_SHORT_MS; -import static org.chromium.chrome.browser.vr.TestFramework.POLL_TIMEOUT_LONG_MS; -import static org.chromium.chrome.browser.vr.TestFramework.POLL_TIMEOUT_SHORT_MS; +import static org.chromium.chrome.browser.vr.XrTestFramework.PAGE_LOAD_TIMEOUT_S; +import static org.chromium.chrome.browser.vr.XrTestFramework.POLL_CHECK_INTERVAL_LONG_MS; +import static org.chromium.chrome.browser.vr.XrTestFramework.POLL_CHECK_INTERVAL_SHORT_MS; +import static org.chromium.chrome.browser.vr.XrTestFramework.POLL_TIMEOUT_LONG_MS; +import static org.chromium.chrome.browser.vr.XrTestFramework.POLL_TIMEOUT_SHORT_MS; import static org.chromium.chrome.test.util.ChromeRestriction.RESTRICTION_TYPE_DON_ENABLED; import static org.chromium.chrome.test.util.ChromeRestriction.RESTRICTION_TYPE_VIEWER_DAYDREAM; @@ -41,9 +41,8 @@ import org.chromium.chrome.browser.ChromeSwitches; import org.chromium.chrome.browser.vr.rules.XrActivityRestriction; import org.chromium.chrome.browser.vr.util.NfcSimUtils; -import org.chromium.chrome.browser.vr.util.TransitionUtils; +import org.chromium.chrome.browser.vr.util.VrTestRuleUtils; import org.chromium.chrome.browser.vr.util.VrTransitionUtils; -import org.chromium.chrome.browser.vr.util.XrTestRuleUtils; import org.chromium.chrome.test.ChromeActivityTestRule; import org.chromium.chrome.test.ChromeJUnit4RunnerDelegate; import org.chromium.content.browser.test.util.Criteria; @@ -67,23 +66,23 @@ public class WebXrVrTransitionTest { @ClassParameter private static List<ParameterSet> sClassParams = - XrTestRuleUtils.generateDefaultXrTestRuleParameters(); + VrTestRuleUtils.generateDefaultTestRuleParameters(); @Rule public RuleChain mRuleChain; private ChromeActivityTestRule mTestRule; - private VrTestFramework mVrTestFramework; - private XrTestFramework mXrTestFramework; + private WebXrVrTestFramework mWebXrVrTestFramework; + private WebVrTestFramework mWebVrTestFramework; public WebXrVrTransitionTest(Callable<ChromeActivityTestRule> callable) throws Exception { mTestRule = callable.call(); - mRuleChain = XrTestRuleUtils.wrapRuleInXrActivityRestrictionRule(mTestRule); + mRuleChain = VrTestRuleUtils.wrapRuleInXrActivityRestrictionRule(mTestRule); } @Before public void setUp() throws Exception { - mVrTestFramework = new VrTestFramework(mTestRule); - mXrTestFramework = new XrTestFramework(mTestRule); + mWebXrVrTestFramework = new WebXrVrTestFramework(mTestRule); + mWebVrTestFramework = new WebVrTestFramework(mTestRule); } /** @@ -94,7 +93,8 @@ @XrActivityRestriction({XrActivityRestriction.SupportedActivity.ALL}) public void testRequestPresentEntersVr() throws InterruptedException { testPresentationEntryImpl( - VrTestFramework.getFileUrlForHtmlTestFile("generic_webvr_page"), mVrTestFramework); + WebVrTestFramework.getFileUrlForHtmlTestFile("generic_webvr_page"), + mWebVrTestFramework); } /** @@ -108,13 +108,14 @@ @XrActivityRestriction({XrActivityRestriction.SupportedActivity.ALL}) public void testRequestSessionEntersVr() throws InterruptedException { testPresentationEntryImpl( - XrTestFramework.getFileUrlForHtmlTestFile("generic_webxr_page"), mXrTestFramework); + WebXrVrTestFramework.getFileUrlForHtmlTestFile("generic_webxr_page"), + mWebXrVrTestFramework); } - private void testPresentationEntryImpl(String url, TestFramework framework) + private void testPresentationEntryImpl(String url, WebXrVrTestFramework framework) throws InterruptedException { framework.loadUrlAndAwaitInitialization(url, PAGE_LOAD_TIMEOUT_S); - TransitionUtils.enterPresentationOrFail(framework); + framework.enterSessionWithUserGestureOrFail(); Assert.assertTrue("VrShellDelegate is in VR", VrShellDelegate.isInVr()); // Initial Pixel Test - Verify that the Canvas is blue. @@ -157,9 +158,9 @@ public void testWebVrDisabledWithoutFlagSet() throws InterruptedException { // TODO(bsheedy): Remove this test once WebVR is on by default without // requiring an origin trial. - apiDisabledWithoutFlagSetImpl( - VrTestFramework.getFileUrlForHtmlTestFile("test_webvr_disabled_without_flag_set"), - mVrTestFramework); + apiDisabledWithoutFlagSetImpl(WebVrTestFramework.getFileUrlForHtmlTestFile( + "test_webvr_disabled_without_flag_set"), + mWebVrTestFramework); } /** @@ -173,16 +174,16 @@ public void testWebXrDisabledWithoutFlagSet() throws InterruptedException { // TODO(bsheedy): Remove this test once WebXR is on by default without // requiring an origin trial. - apiDisabledWithoutFlagSetImpl( - XrTestFramework.getFileUrlForHtmlTestFile("test_webxr_disabled_without_flag_set"), - mXrTestFramework); + apiDisabledWithoutFlagSetImpl(WebXrVrTestFramework.getFileUrlForHtmlTestFile( + "test_webxr_disabled_without_flag_set"), + mWebXrVrTestFramework); } - private void apiDisabledWithoutFlagSetImpl(String url, TestFramework framework) + private void apiDisabledWithoutFlagSetImpl(String url, WebXrVrTestFramework framework) throws InterruptedException { framework.loadUrlAndAwaitInitialization(url, PAGE_LOAD_TIMEOUT_S); - TestFramework.waitOnJavaScriptStep(framework.getFirstTabWebContents()); - TestFramework.endTest(framework.getFirstTabWebContents()); + framework.waitOnJavaScriptStep(); + framework.endTest(); } /** @@ -194,14 +195,13 @@ @Restriction(RESTRICTION_TYPE_VIEWER_DAYDREAM) @XrActivityRestriction({XrActivityRestriction.SupportedActivity.ALL}) public void testNfcFiresVrdisplayactivate() throws InterruptedException { - mVrTestFramework.loadUrlAndAwaitInitialization( - VrTestFramework.getFileUrlForHtmlTestFile("test_nfc_fires_vrdisplayactivate"), + mWebVrTestFramework.loadUrlAndAwaitInitialization( + WebVrTestFramework.getFileUrlForHtmlTestFile("test_nfc_fires_vrdisplayactivate"), PAGE_LOAD_TIMEOUT_S); - VrTestFramework.runJavaScriptOrFail( - "addListener()", POLL_TIMEOUT_LONG_MS, mVrTestFramework.getFirstTabWebContents()); + mWebVrTestFramework.runJavaScriptOrFail("addListener()", POLL_TIMEOUT_LONG_MS); NfcSimUtils.simNfcScanUntilVrEntry(mTestRule.getActivity()); - VrTestFramework.waitOnJavaScriptStep(mVrTestFramework.getFirstTabWebContents()); - VrTestFramework.endTest(mVrTestFramework.getFirstTabWebContents()); + mWebVrTestFramework.waitOnJavaScriptStep(); + mWebVrTestFramework.endTest(); // VrCore has a 2000 ms debounce timeout on NFC scans. When run multiple times in different // activities, it is possible for a latter test to be run in the 2 seconds after the // previous test's NFC scan, causing it to fail flakily. So, wait 2 seconds to ensure that @@ -218,12 +218,10 @@ @Restriction({RESTRICTION_TYPE_VIEWER_DAYDREAM, RESTRICTION_TYPE_DON_ENABLED}) @XrActivityRestriction({XrActivityRestriction.SupportedActivity.ALL}) public void testPresentationPromiseUnresolvedDuringDon() throws InterruptedException { - mVrTestFramework.loadUrlAndAwaitInitialization( - VrTestFramework.getFileUrlForHtmlTestFile( + presentationPromiseUnresolvedDuringDonImpl( + WebVrTestFramework.getFileUrlForHtmlTestFile( "test_presentation_promise_unresolved_during_don"), - PAGE_LOAD_TIMEOUT_S); - VrTransitionUtils.enterPresentationAndWait(mVrTestFramework.getFirstTabWebContents()); - VrTestFramework.endTest(mVrTestFramework.getFirstTabWebContents()); + mWebVrTestFramework); } /** @@ -240,16 +238,16 @@ public void testPresentationPromiseUnresolvedDuringDon_WebXr() throws InterruptedException { presentationPromiseUnresolvedDuringDonImpl( - XrTestFramework.getFileUrlForHtmlTestFile( + WebXrVrTestFramework.getFileUrlForHtmlTestFile( "webxr_test_presentation_promise_unresolved_during_don"), - mXrTestFramework); + mWebXrVrTestFramework); } - private void presentationPromiseUnresolvedDuringDonImpl(String url, TestFramework framework) - throws InterruptedException { + private void presentationPromiseUnresolvedDuringDonImpl( + String url, WebXrVrTestFramework framework) throws InterruptedException { framework.loadUrlAndAwaitInitialization(url, PAGE_LOAD_TIMEOUT_S); - TransitionUtils.enterPresentationAndWait(framework.getFirstTabWebContents()); - TestFramework.endTest(framework.getFirstTabWebContents()); + framework.enterSessionWithUserGestureOrFail(); + framework.endTest(); } /** @@ -261,9 +259,9 @@ @XrActivityRestriction({XrActivityRestriction.SupportedActivity.ALL}) public void testPresentationPromiseRejectedIfDonCanceled() throws InterruptedException { presentationPromiseRejectedIfDonCanceledImpl( - VrTestFramework.getFileUrlForHtmlTestFile( + WebVrTestFramework.getFileUrlForHtmlTestFile( "test_presentation_promise_rejected_if_don_canceled"), - mVrTestFramework); + mWebVrTestFramework); } /** @@ -279,17 +277,17 @@ public void testPresentationPromiseRejectedIfDonCanceled_WebXr() throws InterruptedException { presentationPromiseRejectedIfDonCanceledImpl( - XrTestFramework.getFileUrlForHtmlTestFile( + WebXrVrTestFramework.getFileUrlForHtmlTestFile( "webxr_test_presentation_promise_rejected_if_don_canceled"), - mXrTestFramework); + mWebXrVrTestFramework); } - private void presentationPromiseRejectedIfDonCanceledImpl(String url, TestFramework framework) - throws InterruptedException { + private void presentationPromiseRejectedIfDonCanceledImpl( + String url, WebXrVrTestFramework framework) throws InterruptedException { framework.loadUrlAndAwaitInitialization(url, PAGE_LOAD_TIMEOUT_S); final UiDevice uiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); - TransitionUtils.enterPresentation(framework.getFirstTabWebContents()); + framework.enterSessionWithUserGesture(); // Wait until the DON flow appears to be triggered // TODO(bsheedy): Make this less hacky if there's ever an explicit way to check if the // DON flow is currently active https://crbug.com/758296 @@ -300,8 +298,8 @@ } }, POLL_TIMEOUT_LONG_MS, POLL_CHECK_INTERVAL_SHORT_MS); uiDevice.pressBack(); - TestFramework.waitOnJavaScriptStep(framework.getFirstTabWebContents()); - TestFramework.endTest(framework.getFirstTabWebContents()); + framework.waitOnJavaScriptStep(); + framework.endTest(); } /** @@ -311,7 +309,8 @@ @MediumTest public void testControlsVisibleAfterExitingVr() throws InterruptedException { controlsVisibleAfterExitingVrImpl( - VrTestFramework.getFileUrlForHtmlTestFile("generic_webvr_page"), mVrTestFramework); + WebVrTestFramework.getFileUrlForHtmlTestFile("generic_webvr_page"), + mWebVrTestFramework); } /** @@ -324,14 +323,15 @@ @CommandLineFlags.Add({"enable-features=WebXR"}) public void testControlsVisibleAfterExitingVr_WebXr() throws InterruptedException { controlsVisibleAfterExitingVrImpl( - XrTestFramework.getFileUrlForHtmlTestFile("generic_webxr_page"), mXrTestFramework); + WebXrVrTestFramework.getFileUrlForHtmlTestFile("generic_webxr_page"), + mWebXrVrTestFramework); } - private void controlsVisibleAfterExitingVrImpl(String url, final TestFramework framework) + private void controlsVisibleAfterExitingVrImpl(String url, final WebXrVrTestFramework framework) throws InterruptedException { framework.loadUrlAndAwaitInitialization(url, PAGE_LOAD_TIMEOUT_S); - TransitionUtils.enterPresentationOrFail(framework); - TransitionUtils.forceExitVr(); + framework.enterSessionWithUserGestureOrFail(); + VrTransitionUtils.forceExitVr(); // The hiding of the controls may only propagate after VR has exited, so give it a chance // to propagate. In the worst case this test will erroneously pass, but should never // erroneously fail, and should only be flaky if omnibox showing is broken. @@ -355,9 +355,9 @@ @RetryOnFailure public void testWindowRafStopsFiringWhilePresenting() throws InterruptedException { windowRafStopsFiringWhilePresentingImpl( - VrTestFramework.getFileUrlForHtmlTestFile( + WebVrTestFramework.getFileUrlForHtmlTestFile( "test_window_raf_stops_firing_while_presenting"), - mVrTestFramework); + mWebVrTestFramework); } /** @@ -373,28 +373,25 @@ public void testWindowRafStopsFiringWhilePresenting_WebXr() throws InterruptedException { windowRafStopsFiringWhilePresentingImpl( - XrTestFramework.getFileUrlForHtmlTestFile( + WebXrVrTestFramework.getFileUrlForHtmlTestFile( "webxr_test_window_raf_stops_firing_during_immersive_session"), - mXrTestFramework); + mWebXrVrTestFramework); } - private void windowRafStopsFiringWhilePresentingImpl(String url, TestFramework framework) + private void windowRafStopsFiringWhilePresentingImpl(String url, WebXrVrTestFramework framework) throws InterruptedException { framework.loadUrlAndAwaitInitialization(url, PAGE_LOAD_TIMEOUT_S); - TestFramework.executeStepAndWait( - "stepVerifyBeforePresent()", framework.getFirstTabWebContents()); + framework.executeStepAndWait("stepVerifyBeforePresent()"); // Pausing of window.rAF is done asynchronously, so wait until that's done. final CountDownLatch vsyncPausedLatch = new CountDownLatch(1); TestVrShellDelegate.getInstance().setVrShellOnVSyncPausedCallback( () -> { vsyncPausedLatch.countDown(); }); - TransitionUtils.enterPresentationOrFail(framework); + framework.enterSessionWithUserGestureOrFail(); vsyncPausedLatch.await(POLL_TIMEOUT_SHORT_MS, TimeUnit.MILLISECONDS); - TestFramework.executeStepAndWait( - "stepVerifyDuringPresent()", framework.getFirstTabWebContents()); - TransitionUtils.forceExitVr(); - TestFramework.executeStepAndWait( - "stepVerifyAfterPresent()", framework.getFirstTabWebContents()); - TestFramework.endTest(framework.getFirstTabWebContents()); + framework.executeStepAndWait("stepVerifyDuringPresent()"); + VrTransitionUtils.forceExitVr(); + framework.executeStepAndWait("stepVerifyAfterPresent()"); + framework.endTest(); } /** @@ -406,7 +403,8 @@ public void testRendererKilledInWebVrStaysInVr() throws IllegalArgumentException, InterruptedException, TimeoutException { rendererKilledInVrStaysInVrImpl( - VrTestFramework.getFileUrlForHtmlTestFile("generic_webvr_page"), mVrTestFramework); + WebVrTestFramework.getFileUrlForHtmlTestFile("generic_webvr_page"), + mWebVrTestFramework); } /** @@ -421,13 +419,14 @@ public void testRendererKilledInWebXrStaysInVr() throws IllegalArgumentException, InterruptedException, TimeoutException { rendererKilledInVrStaysInVrImpl( - XrTestFramework.getFileUrlForHtmlTestFile("generic_webxr_page"), mXrTestFramework); + WebXrVrTestFramework.getFileUrlForHtmlTestFile("generic_webxr_page"), + mWebXrVrTestFramework); } - private void rendererKilledInVrStaysInVrImpl(String url, TestFramework framework) + private void rendererKilledInVrStaysInVrImpl(String url, WebXrVrTestFramework framework) throws InterruptedException { framework.loadUrlAndAwaitInitialization(url, PAGE_LOAD_TIMEOUT_S); - TransitionUtils.enterPresentationOrFail(framework); + framework.enterSessionWithUserGestureOrFail(); framework.simulateRendererKilled(); Assert.assertTrue("Browser is in VR", VrShellDelegate.isInVr()); } @@ -442,12 +441,12 @@ @CommandLineFlags.Add({"enable-features=WebXR"}) @XrActivityRestriction({XrActivityRestriction.SupportedActivity.ALL}) public void testWindowRafFiresDuringNonImmersiveSession() throws InterruptedException { - mXrTestFramework.loadUrlAndAwaitInitialization( - XrTestFramework.getFileUrlForHtmlTestFile( + mWebXrVrTestFramework.loadUrlAndAwaitInitialization( + WebXrVrTestFramework.getFileUrlForHtmlTestFile( "test_window_raf_fires_during_non_immersive_session"), PAGE_LOAD_TIMEOUT_S); - XrTestFramework.waitOnJavaScriptStep(mXrTestFramework.getFirstTabWebContents()); - XrTestFramework.endTest(mXrTestFramework.getFirstTabWebContents()); + mWebXrVrTestFramework.waitOnJavaScriptStep(); + mWebXrVrTestFramework.endTest(); } /** @@ -461,18 +460,15 @@ @CommandLineFlags.Add({"enable-features=WebXR"}) @XrActivityRestriction({XrActivityRestriction.SupportedActivity.ALL}) public void testNonImmersiveStopsDuringImmersive() throws InterruptedException { - mXrTestFramework.loadUrlAndAwaitInitialization( - XrTestFramework.getFileUrlForHtmlTestFile( + mWebXrVrTestFramework.loadUrlAndAwaitInitialization( + WebXrVrTestFramework.getFileUrlForHtmlTestFile( "test_non_immersive_stops_during_immersive"), PAGE_LOAD_TIMEOUT_S); - XrTestFramework.executeStepAndWait( - "stepBeforeImmersive()", mXrTestFramework.getFirstTabWebContents()); - TransitionUtils.enterPresentationOrFail(mXrTestFramework); - XrTestFramework.executeStepAndWait( - "stepDuringImmersive()", mXrTestFramework.getFirstTabWebContents()); - TransitionUtils.forceExitVr(); - XrTestFramework.executeStepAndWait( - "stepAfterImmersive()", mXrTestFramework.getFirstTabWebContents()); - XrTestFramework.endTest(mXrTestFramework.getFirstTabWebContents()); + mWebXrVrTestFramework.executeStepAndWait("stepBeforeImmersive()"); + mWebXrVrTestFramework.enterSessionWithUserGestureOrFail(); + mWebXrVrTestFramework.executeStepAndWait("stepDuringImmersive()"); + VrTransitionUtils.forceExitVr(); + mWebXrVrTestFramework.executeStepAndWait("stepAfterImmersive()"); + mWebXrVrTestFramework.endTest(); } }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/XrTestFramework.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/XrTestFramework.java index 22543aa..8f14dcc 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/XrTestFramework.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/XrTestFramework.java
@@ -4,23 +4,360 @@ package org.chromium.chrome.browser.vr; +import android.support.annotation.IntDef; +import android.view.View; + +import org.junit.Assert; + +import org.chromium.base.Log; +import org.chromium.base.ThreadUtils; +import org.chromium.base.test.util.UrlUtils; +import org.chromium.chrome.browser.UrlConstants; +import org.chromium.chrome.browser.tab.Tab; import org.chromium.chrome.test.ChromeActivityTestRule; +import org.chromium.content.browser.test.util.Criteria; +import org.chromium.content.browser.test.util.CriteriaHelper; +import org.chromium.content.browser.test.util.JavaScriptUtils; import org.chromium.content_public.browser.WebContents; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.concurrent.Callable; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + /** - * Extension of TestFramework containing WebXR-specific functionality. + * Class containing the core framework for all XR (VR and AR) testing, which requires + * back-and-forth communication with JavaScript running in the browser. Feature-specific behavior + * can be found in the *TestFramework subclasses. Additional utility methods that don't relate + * to the core framework can be found in the util/ directory. + * + * The general test flow is: + * - Load the HTML file containing the test, which: + * - Loads the WebVR boilerplate code and some test functions + * - Sets up common elements like the canvas and synchronization variable + * - Sets up any steps that need to be triggered by the Java code + * - Check if any VRDisplay objects were found and fail the test if it doesn't + * match what we expect for that test + * - Repeat: + * - Run any necessary Java-side code, e.g. trigger a user action + * - Trigger the next JavaScript test step and wait for it to finish + * + * The JavaScript code will automatically process test results once all + * testharness.js tests are done, just like in layout tests. Once the results + * are processed, the JavaScript code will automatically signal the Java code, + * which can then grab the results and pass/fail the instrumentation test. */ -public class XrTestFramework extends TestFramework { +public abstract class XrTestFramework { + public static final int PAGE_LOAD_TIMEOUT_S = 10; + public static final int POLL_CHECK_INTERVAL_SHORT_MS = 50; + public static final int POLL_CHECK_INTERVAL_LONG_MS = 100; + public static final int POLL_TIMEOUT_SHORT_MS = 1000; + public static final int POLL_TIMEOUT_LONG_MS = 10000; + + public static final String[] NATIVE_URLS_OF_INTEREST = {UrlConstants.BOOKMARKS_FOLDER_URL, + UrlConstants.BOOKMARKS_UNCATEGORIZED_URL, UrlConstants.BOOKMARKS_URL, + UrlConstants.DOWNLOADS_URL, UrlConstants.NATIVE_HISTORY_URL, UrlConstants.NTP_URL, + UrlConstants.RECENT_TABS_URL}; + + private static final String TAG = "XrTestFramework"; + static final String TEST_DIR = "chrome/test/data/xr/e2e_test_files"; + + // Test status enum + private static final int STATUS_RUNNING = 0; + private static final int STATUS_PASSED = 1; + private static final int STATUS_FAILED = 2; + @Retention(RetentionPolicy.SOURCE) + @IntDef({STATUS_RUNNING, STATUS_PASSED, STATUS_FAILED}) + private @interface TestStatus {} + + private ChromeActivityTestRule mRule; + protected WebContents mFirstTabWebContents; + private View mFirstTabContentView; + + /** + * Must be constructed after the rule has been applied (e.g. in whatever method is + * tagged with @Before) + */ public XrTestFramework(ChromeActivityTestRule rule) { - super(rule); + mRule = rule; + mFirstTabWebContents = mRule.getWebContents(); + mFirstTabContentView = mRule.getActivity().getActivityTab().getContentView(); + } + + public WebContents getFirstTabWebContents() { + return mFirstTabWebContents; + } + + public View getFirstTabContentView() { + return mFirstTabContentView; + } + + public ChromeActivityTestRule getRule() { + return mRule; + } + + public void simulateRendererKilled() { + final Tab tab = getRule().getActivity().getActivityTab(); + ThreadUtils.runOnUiThreadBlocking(() -> tab.simulateRendererKilledForTesting(true)); + + CriteriaHelper.pollUiThread(new Criteria() { + @Override + public boolean isSatisfied() { + return tab.isShowingSadTab(); + } + }); } /** - * Checks whether an XRDevice was actually found. - * @param webContents The WebContents to run the JavaScript through. - * @return Whether an XRDevice was found. + * Gets the file:// URL to the test file + * @param testName The name of the test whose file will be retrieved. + * @return The file:// URL to the specified test file. */ - public static boolean xrDeviceFound(WebContents webContents) { - return !runJavaScriptOrFail("xrDevice", POLL_TIMEOUT_SHORT_MS, webContents).equals("null"); + public static String getFileUrlForHtmlTestFile(String testName) { + return "file://" + UrlUtils.getIsolatedTestFilePath(TEST_DIR) + "/html/" + testName + + ".html"; + } + + /** + * Gets the path to pass to an EmbeddedTestServer.getURL to load the given HTML test file. + * @param testName The name of the test whose file will be retrieved. + * @param A path that can be passed to EmbeddedTestServer.getURL to load the test file. + */ + public static String getEmbeddedServerPathForHtmlTestFile(String testName) { + return "/" + TEST_DIR + "/html/" + testName + ".html"; + } + + /** + * Loads the given URL with the given timeout then waits for JavaScript to + * signal that it's ready for testing. + * @param url The URL of the page to load. + * @param timeoutSec The timeout of the page load in seconds. + * @return The return value of ChromeActivityTestRule.loadUrl() + */ + public int loadUrlAndAwaitInitialization(String url, int timeoutSec) + throws InterruptedException { + int result = mRule.loadUrl(url, timeoutSec); + Assert.assertTrue("JavaScript initialization successful", + pollJavaScriptBoolean("isInitializationComplete()", POLL_TIMEOUT_LONG_MS, + mRule.getWebContents())); + return result; + } + + /** + * Helper function to run the given JavaScript, return the return value, + * and fail if a timeout/interrupt occurs so we don't have to catch or + * declare exceptions all the time. + * @param js The JavaScript to run. + * @param timeout The timeout in milliseconds before a failure. + * @param webContents The WebContents object to run the JavaScript in. + * @return The return value of the JavaScript. + */ + public static String runJavaScriptOrFail(String js, int timeout, WebContents webContents) { + try { + return JavaScriptUtils.executeJavaScriptAndWaitForResult( + webContents, js, timeout, TimeUnit.MILLISECONDS); + } catch (InterruptedException | TimeoutException e) { + Assert.fail("Fatal interruption or timeout running JavaScript: " + js); + } + return "Not reached"; + } + + /** + * Helper method to run runJavaScriptOrFail with the first tab's WebContents. + * @param js The JavaScript to run. + * @param timeout The timeout in milliseconds before a failure. + * @return The return value of the JavaScript. + */ + public String runJavaScriptOrFail(String js, int timeout) { + return runJavaScriptOrFail(js, timeout, mFirstTabWebContents); + } + + /** + * Retrieves the current status of the JavaScript test and returns an enum corresponding to it. + * @param webContents The WebContents for the tab to check the status in. + * @return A TestStatus integer corresponding to the current state of the JavaScript test + */ + @TestStatus + public static int checkTestStatus(WebContents webContents) { + String resultString = + runJavaScriptOrFail("resultString", POLL_TIMEOUT_SHORT_MS, webContents); + boolean testPassed = Boolean.parseBoolean( + runJavaScriptOrFail("testPassed", POLL_TIMEOUT_SHORT_MS, webContents)); + if (testPassed) { + return STATUS_PASSED; + } else if (!testPassed && resultString.equals("\"\"")) { + return STATUS_RUNNING; + } else { + // !testPassed && !resultString.equals("\"\"") + return STATUS_FAILED; + } + } + + /** + * Helper method to run checkTestSTatus with the first tab's WebContents. + * @return A TestStatus integer corresponding to the current state of the JavaScript test + */ + @TestStatus + public int checkTestStatus() { + return checkTestStatus(mFirstTabWebContents); + } + + /** + * Helper function to end the test harness test and assert that it passed, + * setting the failure reason as the description if it didn't. + * @param webContents The WebContents for the tab to check test results in. + */ + public static void endTest(WebContents webContents) { + switch (checkTestStatus(webContents)) { + case STATUS_PASSED: + break; + case STATUS_FAILED: + String resultString = + runJavaScriptOrFail("resultString", POLL_TIMEOUT_SHORT_MS, webContents); + Assert.fail("JavaScript testharness failed with result: " + resultString); + break; + case STATUS_RUNNING: + Assert.fail("Attempted to end test in Java without finishing in JavaScript."); + break; + default: + Assert.fail("Received unknown test status."); + } + } + + /** + * Helper function to run endTest with the first tab's WebContents. + */ + public void endTest() { + endTest(mFirstTabWebContents); + } + + /** + * Helper function to make sure that the JavaScript test harness did not detect any failures. + * Similar to endTest, but does not fail if the test is still detected as running. This is + * useful because not all tests make use of the test harness' test/assert features (particularly + * simple enter/exit tests), but may still want to ensure that no unexpected JavaScript errors + * were encountered. + * @param webContents The Webcontents for the tab to check for failures in. + */ + public static void assertNoJavaScriptErrors(WebContents webContents) { + Assert.assertNotEquals(checkTestStatus(webContents), STATUS_FAILED); + } + + /** + * Helper function to run assertNoJavaScriptErrors with the first tab's WebContents. + */ + public void assertNoJavaScriptErrors() { + assertNoJavaScriptErrors(mFirstTabWebContents); + } + + /** + * Polls the provided JavaScript boolean until the timeout is reached or + * the boolean is true. + * @param boolName The name of the JavaScript boolean or expression to poll. + * @param timeoutMs The polling timeout in milliseconds. + * @param webContents The WebContents to run the JavaScript through. + * @return True if the boolean evaluated to true, false if timed out. + */ + public static boolean pollJavaScriptBoolean( + final String boolName, int timeoutMs, final WebContents webContents) { + try { + CriteriaHelper.pollInstrumentationThread(Criteria.equals(true, new Callable<Boolean>() { + @Override + public Boolean call() { + String result = "false"; + try { + result = JavaScriptUtils.executeJavaScriptAndWaitForResult(webContents, + boolName, POLL_CHECK_INTERVAL_SHORT_MS, TimeUnit.MILLISECONDS); + } catch (InterruptedException | TimeoutException e) { + // Expected to happen regularly, do nothing + } + return Boolean.parseBoolean(result); + } + }), timeoutMs, POLL_CHECK_INTERVAL_LONG_MS); + } catch (AssertionError e) { + Log.d(TAG, "pollJavaScriptBoolean() timed out"); + return false; + } + return true; + } + + /** + * Helper function to run pollJavaScriptBoolean with the first tab's WebContents. + * @param boolName The name of the JavaScript boolean or expression to poll. + * @param timeoutMs The polling timeout in milliseconds. + * @return True if the boolean evaluated to true, false if timed out. + */ + public boolean pollJavaScriptBoolean(String boolName, int timeoutMs) { + return pollJavaScriptBoolean(boolName, timeoutMs, mFirstTabWebContents); + } + + /** + * Waits for a JavaScript step to finish, asserting that the step finished + * instead of timing out. + * @param webContents The WebContents for the tab the JavaScript step is in. + */ + public static void waitOnJavaScriptStep(WebContents webContents) { + // Make sure we aren't trying to wait on a JavaScript test step without the code to do so. + Assert.assertTrue("Attempted to wait on a JavaScript step without the code to do so. You " + + "either forgot to import webvr_e2e.js or are incorrectly using a Java " + + "method.", + Boolean.parseBoolean(runJavaScriptOrFail("typeof javascriptDone !== 'undefined'", + POLL_TIMEOUT_SHORT_MS, webContents))); + + // Actually wait for the step to finish + boolean success = + pollJavaScriptBoolean("javascriptDone", POLL_TIMEOUT_LONG_MS, webContents); + + // Check what state we're in to make sure javascriptDone wasn't called because the test + // failed. + int testStatus = checkTestStatus(webContents); + if (!success || testStatus == STATUS_FAILED) { + // Failure states: Either polling failed or polling succeeded, but because the test + // failed. + String reason; + if (!success) { + reason = "Polling JavaScript boolean javascriptDone timed out."; + } else { + reason = "Polling JavaScript boolean javascriptDone succeeded, but test failed."; + } + String resultString = + runJavaScriptOrFail("resultString", POLL_TIMEOUT_SHORT_MS, webContents); + if (resultString.equals("\"\"")) { + reason += " Did not obtain specific reason from testharness."; + } else { + reason += " Testharness reported failure: " + resultString; + } + Assert.fail(reason); + } + + // Reset the synchronization boolean + runJavaScriptOrFail("javascriptDone = false", POLL_TIMEOUT_SHORT_MS, webContents); + } + + /** + * Helper function to run waitOnJavaScriptStep with the first tab's WebContents. + */ + public void waitOnJavaScriptStep() { + waitOnJavaScriptStep(mFirstTabWebContents); + } + + /** + * Executes a JavaScript step function using the given WebContents. + * @param stepFunction The JavaScript step function to call. + * @param webContents The WebContents for the tab the JavaScript is in. + */ + public static void executeStepAndWait(String stepFunction, WebContents webContents) { + // Run the step and block + JavaScriptUtils.executeJavaScript(webContents, stepFunction); + waitOnJavaScriptStep(webContents); + } + + /** + * Helper function to run executeStepAndWait using the first tab's WebContents. + * @param stepFunction The JavaScript step function to call. + */ + public void executeStepAndWait(String stepFunction) { + executeStepAndWait(stepFunction, mFirstTabWebContents); } }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/rules/ChromeTabbedActivityVrTestRule.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/rules/ChromeTabbedActivityVrTestRule.java new file mode 100644 index 0000000..0391f39 --- /dev/null +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/rules/ChromeTabbedActivityVrTestRule.java
@@ -0,0 +1,58 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.browser.vr.rules; + +import org.junit.runner.Description; +import org.junit.runners.model.Statement; + +import org.chromium.chrome.browser.vr.TestVrShellDelegate; +import org.chromium.chrome.browser.vr.rules.XrActivityRestriction.SupportedActivity; +import org.chromium.chrome.browser.vr.util.HeadTrackingUtils; +import org.chromium.chrome.browser.vr.util.VrTestRuleUtils; +import org.chromium.chrome.test.ChromeTabbedActivityTestRule; + +/** + * VR extension of ChromeTabbedActivityTestRule. Applies ChromeTabbedActivityTestRule + * then opens up a ChromeTabbedActivity to a blank page while performing some additional VR-only + * setup. + */ +public class ChromeTabbedActivityVrTestRule + extends ChromeTabbedActivityTestRule implements VrTestRule { + private boolean mTrackerDirty; + + @Override + public Statement apply(final Statement base, final Description desc) { + return super.apply(new Statement() { + @Override + public void evaluate() throws Throwable { + VrTestRuleUtils.ensureNoVrActivitiesDisplayed(); + HeadTrackingUtils.checkForAndApplyHeadTrackingModeAnnotation( + ChromeTabbedActivityVrTestRule.this, desc); + startMainActivityOnBlankPage(); + TestVrShellDelegate.createTestVrShellDelegate(getActivity()); + try { + base.evaluate(); + } finally { + if (isTrackerDirty()) HeadTrackingUtils.revertTracker(); + } + } + }, desc); + } + + @Override + public SupportedActivity getRestriction() { + return SupportedActivity.CTA; + } + + @Override + public boolean isTrackerDirty() { + return mTrackerDirty; + } + + @Override + public void setTrackerDirty() { + mTrackerDirty = true; + } +}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/rules/ChromeTabbedActivityXrTestRule.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/rules/ChromeTabbedActivityXrTestRule.java index 91d82442..d90cbcc 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/rules/ChromeTabbedActivityXrTestRule.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/rules/ChromeTabbedActivityXrTestRule.java
@@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// 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. @@ -7,35 +7,22 @@ import org.junit.runner.Description; import org.junit.runners.model.Statement; -import org.chromium.chrome.browser.vr.TestVrShellDelegate; import org.chromium.chrome.browser.vr.rules.XrActivityRestriction.SupportedActivity; -import org.chromium.chrome.browser.vr.util.HeadTrackingUtils; -import org.chromium.chrome.browser.vr.util.XrTestRuleUtils; import org.chromium.chrome.test.ChromeTabbedActivityTestRule; /** - * VR extension of ChromeTabbedActivityTestRule. Applies ChromeTabbedActivityTestRule + * XR extension of ChromeTabbedActivityTestRule. Applies ChromeTabbedActivityTestRule * then opens up a ChromeTabbedActivity to a blank page. */ public class ChromeTabbedActivityXrTestRule extends ChromeTabbedActivityTestRule implements XrTestRule { - private boolean mTrackerDirty; - @Override public Statement apply(final Statement base, final Description desc) { return super.apply(new Statement() { @Override public void evaluate() throws Throwable { - XrTestRuleUtils.ensureNoVrActivitiesDisplayed(); - HeadTrackingUtils.checkForAndApplyHeadTrackingModeAnnotation( - ChromeTabbedActivityXrTestRule.this, desc); startMainActivityOnBlankPage(); - TestVrShellDelegate.createTestVrShellDelegate(getActivity()); - try { - base.evaluate(); - } finally { - if (isTrackerDirty()) HeadTrackingUtils.revertTracker(); - } + base.evaluate(); } }, desc); } @@ -44,14 +31,4 @@ public SupportedActivity getRestriction() { return SupportedActivity.CTA; } - - @Override - public boolean isTrackerDirty() { - return mTrackerDirty; - } - - @Override - public void setTrackerDirty() { - mTrackerDirty = true; - } }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/rules/CustomTabActivityVrTestRule.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/rules/CustomTabActivityVrTestRule.java new file mode 100644 index 0000000..35c5751d --- /dev/null +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/rules/CustomTabActivityVrTestRule.java
@@ -0,0 +1,60 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.browser.vr.rules; + +import android.support.test.InstrumentationRegistry; + +import org.junit.runner.Description; +import org.junit.runners.model.Statement; + +import org.chromium.chrome.browser.customtabs.CustomTabActivityTestRule; +import org.chromium.chrome.browser.customtabs.CustomTabsTestUtils; +import org.chromium.chrome.browser.vr.TestVrShellDelegate; +import org.chromium.chrome.browser.vr.rules.XrActivityRestriction.SupportedActivity; +import org.chromium.chrome.browser.vr.util.HeadTrackingUtils; +import org.chromium.chrome.browser.vr.util.VrTestRuleUtils; + +/** + * VR extension of CustomTabActivityTestRule. Applies CustomTabActivityTestRule then + * opens up a CustomTabActivity to a blank page while performing some additional VR-only setup. + */ +public class CustomTabActivityVrTestRule extends CustomTabActivityTestRule implements VrTestRule { + private boolean mTrackerDirty; + + @Override + public Statement apply(final Statement base, final Description desc) { + return super.apply(new Statement() { + @Override + public void evaluate() throws Throwable { + VrTestRuleUtils.ensureNoVrActivitiesDisplayed(); + HeadTrackingUtils.checkForAndApplyHeadTrackingModeAnnotation( + CustomTabActivityVrTestRule.this, desc); + startCustomTabActivityWithIntent(CustomTabsTestUtils.createMinimalCustomTabIntent( + InstrumentationRegistry.getTargetContext(), "about:blank")); + TestVrShellDelegate.createTestVrShellDelegate(getActivity()); + try { + base.evaluate(); + } finally { + if (isTrackerDirty()) HeadTrackingUtils.revertTracker(); + } + } + }, desc); + } + + @Override + public SupportedActivity getRestriction() { + return SupportedActivity.CCT; + } + + @Override + public boolean isTrackerDirty() { + return mTrackerDirty; + } + + @Override + public void setTrackerDirty() { + mTrackerDirty = true; + } +}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/rules/CustomTabActivityXrTestRule.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/rules/CustomTabActivityXrTestRule.java index d3657ec..690c725 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/rules/CustomTabActivityXrTestRule.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/rules/CustomTabActivityXrTestRule.java
@@ -11,34 +11,21 @@ import org.chromium.chrome.browser.customtabs.CustomTabActivityTestRule; import org.chromium.chrome.browser.customtabs.CustomTabsTestUtils; -import org.chromium.chrome.browser.vr.TestVrShellDelegate; import org.chromium.chrome.browser.vr.rules.XrActivityRestriction.SupportedActivity; -import org.chromium.chrome.browser.vr.util.HeadTrackingUtils; -import org.chromium.chrome.browser.vr.util.XrTestRuleUtils; /** - * VR extension of CustomTabActivityTestRule. Applies CustomTabActivityTestRule then + * XR extension of CustomTabActivityTestRule. Applies CustomTabActivityTestRule then * opens up a CustomTabActivity to a blank page. */ public class CustomTabActivityXrTestRule extends CustomTabActivityTestRule implements XrTestRule { - private boolean mTrackerDirty; - @Override public Statement apply(final Statement base, final Description desc) { return super.apply(new Statement() { @Override public void evaluate() throws Throwable { - XrTestRuleUtils.ensureNoVrActivitiesDisplayed(); - HeadTrackingUtils.checkForAndApplyHeadTrackingModeAnnotation( - CustomTabActivityXrTestRule.this, desc); startCustomTabActivityWithIntent(CustomTabsTestUtils.createMinimalCustomTabIntent( InstrumentationRegistry.getTargetContext(), "about:blank")); - TestVrShellDelegate.createTestVrShellDelegate(getActivity()); - try { - base.evaluate(); - } finally { - if (isTrackerDirty()) HeadTrackingUtils.revertTracker(); - } + base.evaluate(); } }, desc); } @@ -47,14 +34,4 @@ public SupportedActivity getRestriction() { return SupportedActivity.CCT; } - - @Override - public boolean isTrackerDirty() { - return mTrackerDirty; - } - - @Override - public void setTrackerDirty() { - mTrackerDirty = true; - } }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/rules/VrTestRule.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/rules/VrTestRule.java new file mode 100644 index 0000000..c422ec8 --- /dev/null +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/rules/VrTestRule.java
@@ -0,0 +1,22 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.browser.vr.rules; + +/** + * Interface to be implemented by *VrTestRule rules, which allows them to be + * conditionally skipped when used in conjunction with XrActivityRestrictionRule and properly set/ + * clean up the fake head pose tracker. + */ +public interface VrTestRule extends XrTestRule { + /** + * Whether the head tracking mode has been changed. + */ + public boolean isTrackerDirty(); + + /** + * Tells the rule that the head tracking mode has been changed. + */ + public void setTrackerDirty(); +}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/rules/WebappActivityVrTestRule.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/rules/WebappActivityVrTestRule.java new file mode 100644 index 0000000..79f1ccc --- /dev/null +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/rules/WebappActivityVrTestRule.java
@@ -0,0 +1,56 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.browser.vr.rules; + +import org.junit.runner.Description; +import org.junit.runners.model.Statement; + +import org.chromium.chrome.browser.vr.TestVrShellDelegate; +import org.chromium.chrome.browser.vr.rules.XrActivityRestriction.SupportedActivity; +import org.chromium.chrome.browser.vr.util.HeadTrackingUtils; +import org.chromium.chrome.browser.vr.util.VrTestRuleUtils; +import org.chromium.chrome.browser.webapps.WebappActivityTestRule; + +/** + * VR extension of WebappActivityTestRule. Applies WebappActivityTestRule then opens + * up a WebappActivity to a blank page while performing some additional VR-only setup. + */ +public class WebappActivityVrTestRule extends WebappActivityTestRule implements VrTestRule { + private boolean mTrackerDirty; + + @Override + public Statement apply(final Statement base, final Description desc) { + return super.apply(new Statement() { + @Override + public void evaluate() throws Throwable { + VrTestRuleUtils.ensureNoVrActivitiesDisplayed(); + HeadTrackingUtils.checkForAndApplyHeadTrackingModeAnnotation( + WebappActivityVrTestRule.this, desc); + startWebappActivity(); + TestVrShellDelegate.createTestVrShellDelegate(getActivity()); + try { + base.evaluate(); + } finally { + if (isTrackerDirty()) HeadTrackingUtils.revertTracker(); + } + } + }, desc); + } + + @Override + public SupportedActivity getRestriction() { + return SupportedActivity.WAA; + } + + @Override + public boolean isTrackerDirty() { + return mTrackerDirty; + } + + @Override + public void setTrackerDirty() { + mTrackerDirty = true; + } +}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/rules/WebappActivityXrTestRule.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/rules/WebappActivityXrTestRule.java index 696aca1..57193f4 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/rules/WebappActivityXrTestRule.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/rules/WebappActivityXrTestRule.java
@@ -7,14 +7,11 @@ import org.junit.runner.Description; import org.junit.runners.model.Statement; -import org.chromium.chrome.browser.vr.TestVrShellDelegate; import org.chromium.chrome.browser.vr.rules.XrActivityRestriction.SupportedActivity; -import org.chromium.chrome.browser.vr.util.HeadTrackingUtils; -import org.chromium.chrome.browser.vr.util.XrTestRuleUtils; import org.chromium.chrome.browser.webapps.WebappActivityTestRule; /** - * VR extension of WebappActivityTestRule. Applies WebappActivityTestRule then opens + * XR extension of WebappActivityTestRule. Applies WebappActivityTestRule then opens * up a WebappActivity to a blank page. */ public class WebappActivityXrTestRule extends WebappActivityTestRule implements XrTestRule { @@ -25,16 +22,8 @@ return super.apply(new Statement() { @Override public void evaluate() throws Throwable { - XrTestRuleUtils.ensureNoVrActivitiesDisplayed(); - HeadTrackingUtils.checkForAndApplyHeadTrackingModeAnnotation( - WebappActivityXrTestRule.this, desc); startWebappActivity(); - TestVrShellDelegate.createTestVrShellDelegate(getActivity()); - try { - base.evaluate(); - } finally { - if (isTrackerDirty()) HeadTrackingUtils.revertTracker(); - } + base.evaluate(); } }, desc); } @@ -43,14 +32,4 @@ public SupportedActivity getRestriction() { return SupportedActivity.WAA; } - - @Override - public boolean isTrackerDirty() { - return mTrackerDirty; - } - - @Override - public void setTrackerDirty() { - mTrackerDirty = true; - } }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/rules/XrTestRule.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/rules/XrTestRule.java index 394452f..f95999e8 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/rules/XrTestRule.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/rules/XrTestRule.java
@@ -7,7 +7,7 @@ import org.chromium.chrome.browser.vr.rules.XrActivityRestriction.SupportedActivity; /** - * Interface to be implemented by *VrTestRule rules, which allows them to be + * Interface to be implemented by *XrTestRule rules, which allows them to be * conditionally skipped when used in conjunction with XrActivityRestrictionRule. */ public interface XrTestRule { @@ -15,14 +15,4 @@ * Get the XrActivityRestriction.SupportedActivity that this rule is restricted to running in. */ public SupportedActivity getRestriction(); - - /** - * Whether the head tracking mode has been changed. - */ - public boolean isTrackerDirty(); - - /** - * Tells the rule that the head tracking mode has been changed. - */ - public void setTrackerDirty(); }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/util/HeadTrackingUtils.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/util/HeadTrackingUtils.java index 6b54e34..3068ce10 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/util/HeadTrackingUtils.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/util/HeadTrackingUtils.java
@@ -14,7 +14,7 @@ import org.chromium.chrome.browser.vr.rules.HeadTrackingMode; import org.chromium.chrome.browser.vr.rules.HeadTrackingMode.SupportedMode; -import org.chromium.chrome.browser.vr.rules.XrTestRule; +import org.chromium.chrome.browser.vr.rules.VrTestRule; import java.util.Arrays; @@ -95,11 +95,11 @@ * mode to the specified value. If no annotation is found, the tracking mode is left at whatever * the existing value is. * - * @param rule The XrTestRule used by the current test case. + * @param rule The VrTestRule used by the current test case. * @param desc The JUnit4 Description for the current test case. */ public static void checkForAndApplyHeadTrackingModeAnnotation( - XrTestRule rule, Description desc) { + VrTestRule rule, Description desc) { // Check if the test has a HeadTrackingMode annotation HeadTrackingMode annotation = desc.getAnnotation(HeadTrackingMode.class); if (annotation == null) return; @@ -110,10 +110,10 @@ * Sets the tracker type to the given mode and waits long enough to safely assume that the * service has started. * - * @param rule The XrTestRule used by the current test case. + * @param rule The VrTestRule used by the current test case. * @param mode The HeadTrackingMode.SupportedMode value to set the fake head tracker mode to. */ - public static void applyHeadTrackingMode(XrTestRule rule, SupportedMode mode) { + public static void applyHeadTrackingMode(VrTestRule rule, SupportedMode mode) { applyHeadTrackingModeInternal(rule, mode); // TODO(bsheedy): Remove this sleep if the head tracking service ever exposes a way to be // notified when a setting has been applied. @@ -124,11 +124,11 @@ * Sets the head pose to the pose described by the given FakePose and waits long enough to * safely assume that the pose has taken effect. * - * @param rule The XrTestRule used by the current test case. + * @param rule The VrTestRule used by the current test case. * @param pose The FakePose instance containing the pose data that will be sent to the head * tracking service. */ - public static void setHeadPose(XrTestRule rule, FakePose pose) { + public static void setHeadPose(VrTestRule rule, FakePose pose) { restartHeadTrackingServiceIfNecessary(rule); // Set the head pose to the given value Intent poseIntent = new Intent(ACTION_SET_FAKE_TRACKER_POSE); @@ -175,7 +175,7 @@ } } - private static void applyHeadTrackingModeInternal(XrTestRule rule, SupportedMode mode) { + private static void applyHeadTrackingModeInternal(VrTestRule rule, SupportedMode mode) { restartHeadTrackingServiceIfNecessary(rule); // Set the fake tracker mode to the given value. Intent modeIntent = new Intent(ACTION_SET_FAKE_TRACKER_MODE); @@ -185,7 +185,7 @@ rule.setTrackerDirty(); } - private static void restartHeadTrackingServiceIfNecessary(XrTestRule rule) { + private static void restartHeadTrackingServiceIfNecessary(VrTestRule rule) { // If the tracker has already been dirtied, then we can assume that the tracker type // has already been set to "fake". if (rule.isTrackerDirty()) return;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/util/TransitionUtils.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/util/TransitionUtils.java deleted file mode 100644 index 1b7f6cec..0000000 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/util/TransitionUtils.java +++ /dev/null
@@ -1,267 +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. - -package org.chromium.chrome.browser.vr.util; - -import static org.chromium.chrome.browser.vr.TestFramework.POLL_CHECK_INTERVAL_SHORT_MS; -import static org.chromium.chrome.browser.vr.TestFramework.POLL_TIMEOUT_SHORT_MS; - -import android.content.Intent; -import android.net.Uri; - -import org.junit.Assert; - -import org.chromium.base.CommandLine; -import org.chromium.base.ContextUtils; -import org.chromium.base.ThreadUtils; -import org.chromium.chrome.browser.ChromeTabbedActivity; -import org.chromium.chrome.browser.vr.TestFramework; -import org.chromium.chrome.browser.vr.TestVrShellDelegate; -import org.chromium.chrome.browser.vr.VrIntentUtils; -import org.chromium.chrome.browser.vr.VrMainActivity; -import org.chromium.chrome.browser.vr.VrShellDelegate; -import org.chromium.chrome.browser.vr.VrShellImpl; -import org.chromium.chrome.browser.vr.VrTestFramework; -import org.chromium.content.browser.test.util.Criteria; -import org.chromium.content.browser.test.util.CriteriaHelper; -import org.chromium.content.browser.test.util.DOMUtils; -import org.chromium.content_public.browser.WebContents; - -import java.util.concurrent.Callable; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeoutException; -import java.util.concurrent.atomic.AtomicBoolean; - -/** - * Class containing utility functions for transitioning between different - * states in VR and XR, such as fullscreen, WebVR/WebXR presentation, and the VR browser. - * - * WebVR and WebXR-specific functionality can be found in the VrTransitionUtils and - * XrTransitionUtils subclasses. - * - * All the transitions in this class are performed directly through Chrome, - * as opposed to NFC tag simulation which involves receiving an intent from - * an outside application (VR Services). - */ -public class TransitionUtils { - /** - * Forces the browser into VR mode via a VrShellDelegate call. - */ - public static boolean forceEnterVr() { - Boolean result = false; - try { - result = ThreadUtils.runOnUiThreadBlocking(new Callable<Boolean>() { - @Override - public Boolean call() throws Exception { - return VrShellDelegate.enterVrIfNecessary(); - } - }); - } catch (ExecutionException e) { - } - return result; - } - - /** - * Forces the browser out of VR mode via a VrShellDelegate call. - */ - public static void forceExitVr() { - ThreadUtils.runOnUiThreadBlocking(new Runnable() { - @Override - public void run() { - VrShellDelegateUtils.getDelegateInstance().shutdownVr( - true /* disableVrMode */, true /* stayingInChrome */); - } - }); - } - - /** - * Waits until the given VrShellDelegate's isInVR() returns true. Should - * only be used when VR support is expected. - * @param timeout How long to wait before giving up, in milliseconds - */ - public static void waitForVrEntry(final int timeout) { - // If VR Shell is supported, mInVr should eventually go to true - // Relatively long timeout because sometimes GVR takes a while to enter VR - CriteriaHelper.pollUiThread(Criteria.equals(true, new Callable<Boolean>() { - @Override - public Boolean call() { - return VrShellDelegateUtils.getDelegateInstance().isVrEntryComplete(); - } - }), timeout, POLL_CHECK_INTERVAL_SHORT_MS); - } - - /** - * Sends a click event directly to the WebGL canvas, triggering its onclick - * that by default calls WebVR's requestPresent. Will have a similar result to - * CardboardUtils.sendCardboardClick if not already presenting, but less prone - * to errors, e.g. if there's dialog in the center of the screen blocking the canvas. - * - * Only meant to be used alongside the test framework from VrTestFramework. - * @param webContents The WebContents for the tab the canvas is in. - */ - public static void enterPresentation(WebContents webContents) { - // TODO(https://crbug.com/762724): Remove this workaround when the issue with being resumed - // before receiving the VR broadcast is fixed on VrCore's end. - // However, we don't want to enable the workaround if the DON flow is enabled, as that - // causes issues. Since we don't have a way of actually checking whether the DON flow is - // enabled, check for the presence of the flag that's passed to tests when the DON flow is - // enabled. - if (!CommandLine.getInstance().hasSwitch("don-enabled")) { - VrShellDelegateUtils.getDelegateInstance().setExpectingBroadcast(); - } - try { - DOMUtils.clickNode(webContents, "webgl-canvas", false /* goThroughRootAndroidView */); - } catch (InterruptedException | TimeoutException e) { - Assert.fail("Failed to click canvas to enter presentation: " + e.toString()); - } - } - - /** - * Sends a click event directly to the WebGL canvas then waits for the - * JavaScript step to finish. - * - * Only meant to be used alongside the test framework from VrTestFramework. - * @param webContents The WebContents for the tab the JavaScript step is in. - */ - public static void enterPresentationAndWait(WebContents webContents) { - enterPresentation(webContents); - TestFramework.waitOnJavaScriptStep(webContents); - } - - /** - * Allows the use of enterPresentationOrFail for shared WebVR and WebXR tests without having to - * check whether we need to use the WebVR or WebXR version every time. - */ - public static void enterPresentationOrFail(TestFramework framework) { - if (framework instanceof VrTestFramework) { - VrTransitionUtils.enterPresentationOrFail(framework.getFirstTabWebContents()); - } else { - XrTransitionUtils.enterPresentationOrFail(framework.getFirstTabWebContents()); - } - } - - /** - * Waits for the black overlay that shows during VR entry to be gone. - */ - public static void waitForOverlayGone() { - CriteriaHelper.pollInstrumentationThread(new Criteria() { - @Override - public boolean isSatisfied() { - return !TestVrShellDelegate.getInstance().isBlackOverlayVisible(); - } - }, POLL_TIMEOUT_SHORT_MS, POLL_CHECK_INTERVAL_SHORT_MS); - } - - /** - * @return Whether the VR back button is enabled. - */ - public static Boolean isBackButtonEnabled() { - final AtomicBoolean isBackButtonEnabled = new AtomicBoolean(); - ThreadUtils.runOnUiThreadBlocking(new Runnable() { - @Override - public void run() { - isBackButtonEnabled.set( - TestVrShellDelegate.getVrShellForTesting().isBackButtonEnabled()); - } - }); - return isBackButtonEnabled.get(); - } - - /** - * @return Whether the VR forward button is enabled. - */ - public static Boolean isForwardButtonEnabled() { - final AtomicBoolean isForwardButtonEnabled = new AtomicBoolean(); - ThreadUtils.runOnUiThreadBlocking(new Runnable() { - @Override - public void run() { - isForwardButtonEnabled.set( - TestVrShellDelegate.getVrShellForTesting().isForwardButtonEnabled()); - } - }); - return isForwardButtonEnabled.get(); - } - - /** - * Navigates VrShell back. - */ - public static void navigateBack() { - ThreadUtils.runOnUiThreadBlocking(new Runnable() { - @Override - public void run() { - TestVrShellDelegate.getVrShellForTesting().navigateBack(); - } - }); - } - - /** - * Navigates VrShell forward. - */ - public static void navigateForward() { - ThreadUtils.runOnUiThreadBlocking(new Runnable() { - @Override - public void run() { - TestVrShellDelegate.getVrShellForTesting().navigateForward(); - } - }); - } - - /** - * Sends an intent to Chrome telling it to launch in VR mode. If the given autopresent param is - * true, this is expected to fail unless the trusted intent check is disabled in - * VrShellDelegate. - * - * @param url String containing the URL to open - * @param autopresent If this intent is expected to auto-present WebVR - */ - public static void sendVrLaunchIntent(String url, boolean autopresent, boolean avoidRelaunch) { - // Create an intent that will launch Chrome at the specified URL. - final Intent intent = - new Intent(ContextUtils.getApplicationContext(), VrMainActivity.class); - intent.setData(Uri.parse(url)); - intent.addCategory(VrIntentUtils.DAYDREAM_CATEGORY); - VrIntentUtils.setupVrIntent(intent); - - if (autopresent) { - // Daydream removes this category for deep-linked URLs for legacy reasons. - intent.removeCategory(VrIntentUtils.DAYDREAM_CATEGORY); - intent.putExtra(VrIntentUtils.AUTOPRESENT_WEVBVR_EXTRA, true); - } - if (avoidRelaunch) intent.putExtra(VrIntentUtils.AVOID_RELAUNCH_EXTRA, true); - - // TODO(https://crbug.com/854327): Remove this workaround once the issue with launchInVr - // sometimes launching the given intent before entering VR is fixed. - intent.putExtra(VrIntentUtils.ENABLE_TEST_RELAUNCH_WORKAROUND_EXTRA, true); - - ThreadUtils.runOnUiThreadBlocking(new Runnable() { - @Override - public void run() { - VrShellDelegate.getVrDaydreamApi().launchInVr(intent); - } - }); - } - - public static void send2dMainIntent() { - final Intent intent = - new Intent(ContextUtils.getApplicationContext(), ChromeTabbedActivity.class); - intent.setAction(Intent.ACTION_MAIN); - intent.addCategory(Intent.CATEGORY_LAUNCHER); - ThreadUtils.runOnUiThreadBlocking( - () -> { ContextUtils.getApplicationContext().startActivity(intent); }); - } - - /** - * Waits until either a JavaScript dialog or permission prompt is being displayed using the - * Android native UI in the VR browser. - */ - public static void waitForNativeUiPrompt(final int timeout) { - CriteriaHelper.pollInstrumentationThread(new Criteria() { - @Override - public boolean isSatisfied() { - VrShellImpl vrShell = (VrShellImpl) TestVrShellDelegate.getVrShellForTesting(); - return vrShell.isDisplayingDialogView(); - } - }, timeout, POLL_CHECK_INTERVAL_SHORT_MS); - } -}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/util/VrBrowserTransitionUtils.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/util/VrBrowserTransitionUtils.java new file mode 100644 index 0000000..0719c6d --- /dev/null +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/util/VrBrowserTransitionUtils.java
@@ -0,0 +1,144 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.browser.vr.util; + +import static org.chromium.chrome.browser.vr.VrBrowserTestFramework.POLL_CHECK_INTERVAL_SHORT_MS; + +import android.content.Intent; +import android.net.Uri; + +import org.chromium.base.ContextUtils; +import org.chromium.base.ThreadUtils; +import org.chromium.chrome.browser.ChromeTabbedActivity; +import org.chromium.chrome.browser.vr.TestVrShellDelegate; +import org.chromium.chrome.browser.vr.VrIntentUtils; +import org.chromium.chrome.browser.vr.VrMainActivity; +import org.chromium.chrome.browser.vr.VrShellDelegate; +import org.chromium.chrome.browser.vr.VrShellImpl; +import org.chromium.content.browser.test.util.Criteria; +import org.chromium.content.browser.test.util.CriteriaHelper; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * Class containing utility functions for transitioning between different + * states in the VR Browser. + */ +public class VrBrowserTransitionUtils extends VrTransitionUtils { + /** + * Forces Chrome into the VR Browser. + * @return True if the request to enter the VR Browser succeeded, false otherwise. + */ + public static boolean forceEnterVrBrowser() { + Boolean result = false; + try { + result = ThreadUtils.runOnUiThreadBlocking( + () -> { return VrShellDelegate.enterVrIfNecessary(); }); + } catch (ExecutionException e) { + } + return result; + } + + /** + * @return Whether the VR Browser's back button is enabled. + */ + public static Boolean isBackButtonEnabled() { + final AtomicBoolean isBackButtonEnabled = new AtomicBoolean(); + ThreadUtils.runOnUiThreadBlocking(() -> { + isBackButtonEnabled.set( + TestVrShellDelegate.getVrShellForTesting().isBackButtonEnabled()); + }); + return isBackButtonEnabled.get(); + } + + /** + * @return Whether the VR Browers's forward button is enabled. + */ + public static Boolean isForwardButtonEnabled() { + final AtomicBoolean isForwardButtonEnabled = new AtomicBoolean(); + ThreadUtils.runOnUiThreadBlocking(() -> { + isForwardButtonEnabled.set( + TestVrShellDelegate.getVrShellForTesting().isForwardButtonEnabled()); + }); + return isForwardButtonEnabled.get(); + } + + /** + * Navigates the VR Browser back. + */ + public static void navigateBack() { + ThreadUtils.runOnUiThreadBlocking( + () -> { TestVrShellDelegate.getVrShellForTesting().navigateBack(); }); + } + + /** + * Navigates the VR Browser forward. + */ + public static void navigateForward() { + ThreadUtils.runOnUiThreadBlocking( + () -> { TestVrShellDelegate.getVrShellForTesting().navigateForward(); }); + } + + /** + * Sends an intent to Chrome telling it to launch in VR mode. If the given autopresent param is + * true, this is expected to fail unless the trusted intent check is disabled in + * VrShellDelegate. + * + * @param url String containing the URL to open + * @param autopresent If this intent is expected to auto-present WebVR + * @param avoidRelaunch Include an extra that prevents relaunching Chrome once the intent is + * received + */ + public static void sendVrLaunchIntent(String url, boolean autopresent, boolean avoidRelaunch) { + // Create an intent that will launch Chrome at the specified URL. + final Intent intent = + new Intent(ContextUtils.getApplicationContext(), VrMainActivity.class); + intent.setData(Uri.parse(url)); + intent.addCategory(VrIntentUtils.DAYDREAM_CATEGORY); + VrIntentUtils.setupVrIntent(intent); + + if (autopresent) { + // Daydream removes this category for deep-linked URLs for legacy reasons. + intent.removeCategory(VrIntentUtils.DAYDREAM_CATEGORY); + intent.putExtra(VrIntentUtils.AUTOPRESENT_WEVBVR_EXTRA, true); + } + if (avoidRelaunch) intent.putExtra(VrIntentUtils.AVOID_RELAUNCH_EXTRA, true); + + // TODO(https://crbug.com/854327): Remove this workaround once the issue with launchInVr + // sometimes launching the given intent before entering VR is fixed. + intent.putExtra(VrIntentUtils.ENABLE_TEST_RELAUNCH_WORKAROUND_EXTRA, true); + + ThreadUtils.runOnUiThreadBlocking( + () -> { VrShellDelegate.getVrDaydreamApi().launchInVr(intent); }); + } + + /** + * Sends an intent to Chrome telling it to open a tab in 2D mode. + */ + public static void send2dMainIntent() { + final Intent intent = + new Intent(ContextUtils.getApplicationContext(), ChromeTabbedActivity.class); + intent.setAction(Intent.ACTION_MAIN); + intent.addCategory(Intent.CATEGORY_LAUNCHER); + ThreadUtils.runOnUiThreadBlocking( + () -> { ContextUtils.getApplicationContext().startActivity(intent); }); + } + + /** + * Waits until either a JavaScript dialog or permission prompt is being displayed using the + * Android native UI in the VR browser. + * @param timeout How long in milliseconds to wait before timing out and failing. + */ + public static void waitForNativeUiPrompt(final int timeout) { + CriteriaHelper.pollInstrumentationThread(new Criteria() { + @Override + public boolean isSatisfied() { + VrShellImpl vrShell = (VrShellImpl) TestVrShellDelegate.getVrShellForTesting(); + return vrShell.isDisplayingDialogView(); + } + }, timeout, POLL_CHECK_INTERVAL_SHORT_MS); + } +}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/util/VrInfoBarUtils.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/util/VrInfoBarUtils.java index a2082e2..a8597400 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/util/VrInfoBarUtils.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/util/VrInfoBarUtils.java
@@ -4,12 +4,12 @@ package org.chromium.chrome.browser.vr.util; -import static org.chromium.chrome.browser.vr.VrTestFramework.POLL_CHECK_INTERVAL_SHORT_MS; -import static org.chromium.chrome.browser.vr.VrTestFramework.POLL_TIMEOUT_SHORT_MS; +import static org.chromium.chrome.browser.vr.XrTestFramework.POLL_CHECK_INTERVAL_SHORT_MS; +import static org.chromium.chrome.browser.vr.XrTestFramework.POLL_TIMEOUT_SHORT_MS; import org.chromium.base.ThreadUtils; import org.chromium.chrome.browser.infobar.InfoBar; -import org.chromium.chrome.browser.vr.TestFramework; +import org.chromium.chrome.test.ChromeActivityTestRule; import org.chromium.chrome.test.util.InfoBarUtil; import org.chromium.content.browser.test.util.Criteria; import org.chromium.content.browser.test.util.CriteriaHelper; @@ -27,12 +27,12 @@ /** * Determines whether InfoBars are present in the current activity. - * @param framework The TestFramework to get the current activity from + * @param rule The ChromeActivityTestRule to get the InfoBars from * @return True if there are any InfoBars present, false otherwise */ @SuppressWarnings("unchecked") - public static boolean isInfoBarPresent(TestFramework framework) { - List<InfoBar> infoBars = framework.getRule().getInfoBars(); + public static boolean isInfoBarPresent(ChromeActivityTestRule rule) { + List<InfoBar> infoBars = rule.getInfoBars(); return infoBars != null && !infoBars.isEmpty(); } @@ -40,12 +40,12 @@ * Clicks on either the primary or secondary button of the first InfoBar * in the activity. * @param button Which button to click - * @param framework The TestFramework to get the current activity from + * @param rule The ChromeActivityTestRule to get the InfoBars from */ @SuppressWarnings("unchecked") - public static void clickInfoBarButton(final Button button, TestFramework framework) { - if (!isInfoBarPresent(framework)) return; - final List<InfoBar> infoBars = framework.getRule().getInfoBars(); + public static void clickInfoBarButton(final Button button, ChromeActivityTestRule rule) { + if (!isInfoBarPresent(rule)) return; + final List<InfoBar> infoBars = rule.getInfoBars(); ThreadUtils.runOnUiThreadBlocking(new Runnable() { @Override public void run() { @@ -58,36 +58,36 @@ } } }); - InfoBarUtil.waitUntilNoInfoBarsExist(framework.getRule().getInfoBars()); + InfoBarUtil.waitUntilNoInfoBarsExist(rule.getInfoBars()); } /** * Clicks on the close button of the first InfoBar in the activity. - * @param framework The TestFramework to get the current activity from + * @param rule The ChromeActivityTestRule to get the InfoBars from */ @SuppressWarnings("unchecked") - public static void clickInfobarCloseButton(TestFramework framework) { - if (!isInfoBarPresent(framework)) return; - final List<InfoBar> infoBars = framework.getRule().getInfoBars(); + public static void clickInfobarCloseButton(ChromeActivityTestRule rule) { + if (!isInfoBarPresent(rule)) return; + final List<InfoBar> infoBars = rule.getInfoBars(); ThreadUtils.runOnUiThreadBlocking(new Runnable() { @Override public void run() { InfoBarUtil.clickCloseButton(infoBars.get(0)); } }); - InfoBarUtil.waitUntilNoInfoBarsExist(framework.getRule().getInfoBars()); + InfoBarUtil.waitUntilNoInfoBarsExist(rule.getInfoBars()); } /** * Determines is there is any InfoBar present in the given View hierarchy. - * @param framework The TestFramework that will be used to get the current activity/view from + * @param rule The ChromeActivityTestRule to get the InfoBars from * @param present Whether an InfoBar should be present. */ - public static void expectInfoBarPresent(final TestFramework framework, boolean present) { + public static void expectInfoBarPresent(final ChromeActivityTestRule rule, boolean present) { CriteriaHelper.pollUiThread(Criteria.equals(present, new Callable<Boolean>() { @Override public Boolean call() { - return isInfoBarPresent(framework); + return isInfoBarPresent(rule); } }), POLL_TIMEOUT_SHORT_MS, POLL_CHECK_INTERVAL_SHORT_MS); }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/util/VrTestRuleUtils.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/util/VrTestRuleUtils.java new file mode 100644 index 0000000..3fe03923 --- /dev/null +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/util/VrTestRuleUtils.java
@@ -0,0 +1,82 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.browser.vr.util; + +import android.os.SystemClock; +import android.support.test.InstrumentationRegistry; +import android.support.test.uiautomator.UiDevice; + +import org.chromium.base.test.params.ParameterSet; +import org.chromium.chrome.browser.vr.rules.ChromeTabbedActivityVrTestRule; +import org.chromium.chrome.browser.vr.rules.CustomTabActivityVrTestRule; +import org.chromium.chrome.browser.vr.rules.WebappActivityVrTestRule; + +import java.util.ArrayList; +import java.util.concurrent.Callable; + +/** + * Utility class for interacting with VR-specific Rules, i.e. ChromeActivityTestRules that implement + * the VrTestRule interface. + */ +public class VrTestRuleUtils extends XrTestRuleUtils { + // VrCore waits this amount of time after exiting VR before actually unregistering a registered + // Daydream intent, meaning that it still thinks VR is active until this amount of time has + // passed. + private static final int VRCORE_UNREGISTER_DELAY_MS = 500; + /** + * Creates the list of VrTestRules that are currently supported for use in test + * parameterization. + */ + public static ArrayList<ParameterSet> generateDefaultTestRuleParameters() { + ArrayList<ParameterSet> parameters = new ArrayList<ParameterSet>(); + parameters.add(new ParameterSet() + .value(new Callable<ChromeTabbedActivityVrTestRule>() { + @Override + public ChromeTabbedActivityVrTestRule call() { + return new ChromeTabbedActivityVrTestRule(); + } + }) + .name("ChromeTabbedActivity")); + + parameters.add(new ParameterSet() + .value(new Callable<CustomTabActivityVrTestRule>() { + @Override + public CustomTabActivityVrTestRule call() { + return new CustomTabActivityVrTestRule(); + } + }) + .name("CustomTabActivity")); + + parameters.add(new ParameterSet() + .value(new Callable<WebappActivityVrTestRule>() { + @Override + public WebappActivityVrTestRule call() { + return new WebappActivityVrTestRule(); + } + }) + .name("WebappActivity")); + + return parameters; + } + + /** + * Ensures that no VR-related activity is currently being displayed. This is meant to be used + * by TestRules before starting any activities. Having a VR activity in the foreground (e.g. + * Daydream Home) has the potential to affect test results, as it often means that we are in VR + * at the beginning of the test, which we don't want. This is most commonly caused by VrCore + * automatically launching Daydream Home when Chrome gets closed after a test, but can happen + * for other reasons as well. + */ + public static void ensureNoVrActivitiesDisplayed() { + UiDevice uiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); + String currentPackageName = uiDevice.getCurrentPackageName(); + if (currentPackageName != null && currentPackageName.contains("vr")) { + uiDevice.pressHome(); + // Chrome startup would likely be slow enough that this sleep is unnecessary, but sleep + // to be sure since this will be hit relatively infrequently. + SystemClock.sleep(VRCORE_UNREGISTER_DELAY_MS); + } + } +}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/util/VrTransitionUtils.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/util/VrTransitionUtils.java index dd63877..6ac32dd 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/util/VrTransitionUtils.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/util/VrTransitionUtils.java
@@ -4,34 +4,57 @@ package org.chromium.chrome.browser.vr.util; -import static org.chromium.chrome.browser.vr.VrTestFramework.POLL_TIMEOUT_LONG_MS; +import static org.chromium.chrome.browser.vr.XrTestFramework.POLL_CHECK_INTERVAL_SHORT_MS; +import static org.chromium.chrome.browser.vr.XrTestFramework.POLL_TIMEOUT_SHORT_MS; -import org.junit.Assert; - +import org.chromium.base.ThreadUtils; import org.chromium.chrome.browser.vr.TestVrShellDelegate; -import org.chromium.chrome.browser.vr.VrTestFramework; -import org.chromium.content_public.browser.WebContents; +import org.chromium.chrome.browser.vr.VrShellDelegate; +import org.chromium.content.browser.test.util.Criteria; +import org.chromium.content.browser.test.util.CriteriaHelper; /** * Class containing utility functions for transitioning between different * states in VR, such as fullscreen, WebVR presentation, and the VR browser. * + * Methods in this class are applicable to any form of VR, e.g. they will work for both WebXR for VR + * and the VR Browser. + * * All the transitions in this class are performed directly through Chrome, * as opposed to NFC tag simulation which involves receiving an intent from * an outside application (VR Services). */ -public class VrTransitionUtils extends TransitionUtils { +public class VrTransitionUtils { /** - * Sends a click event directly to the WebGL canvas then waits for WebVR to - * think that it is presenting, failing if this does not occur within the - * allotted time. - * - * @param cvc The ContentViewCore for the tab the canvas is in. + * Forces Chrome out of VR mode. */ - public static void enterPresentationOrFail(WebContents webContents) { - enterPresentation(webContents); - Assert.assertTrue(VrTestFramework.pollJavaScriptBoolean( - "vrDisplay.isPresenting", POLL_TIMEOUT_LONG_MS, webContents)); - Assert.assertTrue(TestVrShellDelegate.getVrShellForTesting().getWebVrModeEnabled()); + public static void forceExitVr() { + ThreadUtils.runOnUiThreadBlocking(() -> { VrShellDelegate.forceExitVrImmediately(); }); + } + + /** + * Waits until Chrome believes it is in VR. + * @param timeout How long to wait for VR entry before timing out and failing. + */ + public static void waitForVrEntry(final int timeout) { + // Relatively long timeout because sometimes GVR takes a while to enter VR + CriteriaHelper.pollUiThread(new Criteria() { + @Override + public boolean isSatisfied() { + return VrShellDelegateUtils.getDelegateInstance().isVrEntryComplete(); + } + }, timeout, POLL_CHECK_INTERVAL_SHORT_MS); + } + + /** + * Waits for the black overlay that shows during VR Browser entry to be gone. + */ + public static void waitForOverlayGone() { + CriteriaHelper.pollInstrumentationThread(new Criteria() { + @Override + public boolean isSatisfied() { + return !TestVrShellDelegate.getInstance().isBlackOverlayVisible(); + } + }, POLL_TIMEOUT_SHORT_MS, POLL_CHECK_INTERVAL_SHORT_MS); } }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/util/XrTestRuleUtils.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/util/XrTestRuleUtils.java index d8b590ad..388f96c 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/util/XrTestRuleUtils.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/util/XrTestRuleUtils.java
@@ -4,10 +4,6 @@ package org.chromium.chrome.browser.vr.util; -import android.os.SystemClock; -import android.support.test.InstrumentationRegistry; -import android.support.test.uiautomator.UiDevice; - import org.junit.Assert; import org.junit.rules.RuleChain; import org.junit.rules.TestRule; @@ -24,18 +20,15 @@ import java.util.concurrent.Callable; /** - * Utility class for interacting with VR-specific Rules, particularly XrActivityRestrictionRule. + * Utility class for interacting with XR-specific Rules, i.e. ChromeActivityTestRules that implement + * the XrTestRule interface. */ public class XrTestRuleUtils { - // VrCore waits this amount of time after exiting VR before actually unregistering a registered - // Daydream intent, meaning that it still thinks VR is active until this amount of time has - // passed. - private static final int VRCORE_UNREGISTER_DELAY_MS = 500; /** * Creates the list of XrTestRules that are currently supported for use in test * parameterization. */ - public static ArrayList<ParameterSet> generateDefaultXrTestRuleParameters() { + public static ArrayList<ParameterSet> generateDefaultTestRuleParameters() { ArrayList<ParameterSet> parameters = new ArrayList<ParameterSet>(); parameters.add(new ParameterSet() .value(new Callable<ChromeTabbedActivityXrTestRule>() { @@ -94,23 +87,4 @@ return "UnknownActivity"; } } - - /** - * Ensures that no VR-related activity is currently being displayed. This is meant to be used - * by TestRules before starting any activities. Having a VR activity in the foreground (e.g. - * Daydream Home) has the potential to affect test results, as it often means that we are in VR - * at the beginning of the test, which we don't want. This is most commonly caused by VrCore - * automatically launching Daydream Home when Chrome gets closed after a test, but can happen - * for other reasons as well. - */ - public static void ensureNoVrActivitiesDisplayed() { - UiDevice uiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); - String currentPackageName = uiDevice.getCurrentPackageName(); - if (currentPackageName != null && currentPackageName.contains("vr")) { - uiDevice.pressHome(); - // Chrome startup would likely be slow enough that this sleep is unnecessary, but sleep - // to be sure since this will be hit relatively infrequently. - SystemClock.sleep(VRCORE_UNREGISTER_DELAY_MS); - } - } }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/util/XrTransitionUtils.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/util/XrTransitionUtils.java deleted file mode 100644 index 14d8d32e..0000000 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/util/XrTransitionUtils.java +++ /dev/null
@@ -1,100 +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. - -package org.chromium.chrome.browser.vr.util; - -import static org.chromium.chrome.browser.vr.XrTestFramework.POLL_TIMEOUT_LONG_MS; -import static org.chromium.chrome.browser.vr.XrTestFramework.POLL_TIMEOUT_SHORT_MS; - -import android.content.DialogInterface; - -import org.junit.Assert; - -import org.chromium.base.ThreadUtils; -import org.chromium.chrome.browser.permissions.PermissionDialogController; -import org.chromium.chrome.browser.vr.TestVrShellDelegate; -import org.chromium.chrome.browser.vr.XrTestFramework; -import org.chromium.content.browser.test.util.Criteria; -import org.chromium.content.browser.test.util.CriteriaHelper; -import org.chromium.content_public.browser.WebContents; - -/** - * Class containing utility functions for transitioning between different - * states in WebXR, e.g. entering immersive or AR sessions. - */ -public class XrTransitionUtils extends TransitionUtils { - /** - * WebXR version of enterPresentationOrFail since the condition to check is different between - * the two APIs. - */ - public static void enterPresentationOrFail(WebContents webContents) { - XrTestFramework.runJavaScriptOrFail( - "sessionTypeToRequest = sessionTypes.IMMERSIVE", POLL_TIMEOUT_LONG_MS, webContents); - enterPresentation(webContents); - Assert.assertTrue(XrTestFramework.pollJavaScriptBoolean( - "sessionInfos[sessionTypes.IMMERSIVE].currentSession != null", POLL_TIMEOUT_LONG_MS, - webContents)); - Assert.assertTrue(TestVrShellDelegate.getVrShellForTesting().getWebVrModeEnabled()); - } - - /** - * Requests that WebXR start an AR session, failing the test if it does not succeed. - * @param webContents The WebContents to run start the AR session in. - */ - public static void enterArSessionOrFail(WebContents webContents) { - XrTestFramework.runJavaScriptOrFail( - "sessionTypeToRequest = sessionTypes.AR", POLL_TIMEOUT_LONG_MS, webContents); - // Requesting an AR session for the first time on a page will always prompt for camera - // permissions, but not on subsequent requests, so check to see if we'll need to accept it - // after requesting the session. - boolean expectPermissionPrompt = arSessionRequestWouldTriggerPermissionPrompt(webContents); - // TODO(bsheedy): Rename enterPresentation since it's used for both presentation and AR? - enterPresentation(webContents); - if (expectPermissionPrompt) { - // Wait for the permission prompt to appear. - CriteriaHelper.pollUiThread(new Criteria() { - @Override - public boolean isSatisfied() { - return PermissionDialogController.getInstance().getCurrentDialogForTesting() - != null; - } - }); - // Accept the permission prompt. - ThreadUtils.runOnUiThreadBlocking(() -> { - PermissionDialogController.getInstance() - .getCurrentDialogForTesting() - .getButton(DialogInterface.BUTTON_POSITIVE) - .performClick(); - }); - } - Assert.assertTrue(XrTestFramework.pollJavaScriptBoolean( - "sessionInfos[sessionTypes.AR].currentSession != null", POLL_TIMEOUT_LONG_MS, - webContents)); - } - - /** - * Ends the current AR session. - * @param webContents The WebContents to end the AR session in. - */ - public static void endArSession(WebContents webContents) { - XrTestFramework.runJavaScriptOrFail("sessionInfos[sessionTypes.AR].currentSession.end()", - POLL_TIMEOUT_SHORT_MS, webContents); - } - - /** - * Checks whether an AR session request would trigger a Camera permission prompt. - * @param webContents The WebContents to check permissions in. - * @return True if an AR session request will cause a permission prompt, false otherwise. - */ - public static boolean arSessionRequestWouldTriggerPermissionPrompt(WebContents webContents) { - XrTestFramework.runJavaScriptOrFail("checkIfArSessionWouldTriggerPermissionPrompt()", - POLL_TIMEOUT_SHORT_MS, webContents); - Assert.assertTrue(XrTestFramework.pollJavaScriptBoolean( - "arSessionRequestWouldTriggerPermissionPrompt !== null", POLL_TIMEOUT_SHORT_MS, - webContents)); - return Boolean.valueOf( - XrTestFramework.runJavaScriptOrFail("arSessionRequestWouldTriggerPermissionPrompt", - POLL_TIMEOUT_SHORT_MS, webContents)); - } -}
diff --git a/chrome/android/profiles/newest.txt b/chrome/android/profiles/newest.txt index 54d9dcb..ce62c5b 100644 --- a/chrome/android/profiles/newest.txt +++ b/chrome/android/profiles/newest.txt
@@ -1 +1 @@ -chromeos-chrome-amd64-69.0.3494.0_rc-r1.afdo.bz2 \ No newline at end of file +chromeos-chrome-amd64-69.0.3495.0_rc-r1.afdo.bz2 \ No newline at end of file
diff --git a/chrome/app/profiles_strings.grdp b/chrome/app/profiles_strings.grdp index 5737a67..4a3802e 100644 --- a/chrome/app/profiles_strings.grdp +++ b/chrome/app/profiles_strings.grdp
@@ -20,13 +20,13 @@ <message name="IDS_AVATAR_BUTTON_INCOGNITO_TOOLTIP" desc="Tooltip for the inactive avatar button when the user is in incognito mode."> You're incognito </message> - <message name="IDS_AVATAR_BUTTON_SYNC_ERROR" desc="Short label for the avatar button when there are sync errors for the current profile."> + <message name="IDS_AVATAR_BUTTON_SYNC_ERROR" desc="Short label for the avatar button when there are sync errors for the current profile." meaning="An error has occurred during sync"> Error </message> <message name="IDS_AVATAR_BUTTON_SYNC_ERROR_TOOLTIP" desc="Tooltip for the avatar button when there are sync errors for the current profile."> <ph name="PROFILE_NAME">$1<ex>User</ex></ph>: Sync isn't working </message> - <message name="IDS_AVATAR_BUTTON_SYNC_PAUSED" desc="Short label for the avatar button when sync is paused for the current profile."> + <message name="IDS_AVATAR_BUTTON_SYNC_PAUSED" desc="Short label for the avatar button when sync is paused for the current profile." meaning="Sync is paused"> Paused </message> <message name="IDS_AVATAR_BUTTON_SYNC_PAUSED_TOOLTIP" desc="Tooltip for the avatar button when sync is paused for the current profile.">
diff --git a/chrome/app/profiles_strings_grdp/IDS_AVATAR_BUTTON_SYNC_ERROR.png.sha1 b/chrome/app/profiles_strings_grdp/IDS_AVATAR_BUTTON_SYNC_ERROR.png.sha1 new file mode 100644 index 0000000..5fcf5bd6 --- /dev/null +++ b/chrome/app/profiles_strings_grdp/IDS_AVATAR_BUTTON_SYNC_ERROR.png.sha1
@@ -0,0 +1 @@ +53d014b5779f098f1394c65ba505af104bc3f59a \ No newline at end of file
diff --git a/chrome/app/profiles_strings_grdp/IDS_AVATAR_BUTTON_SYNC_PAUSED.png.sha1 b/chrome/app/profiles_strings_grdp/IDS_AVATAR_BUTTON_SYNC_PAUSED.png.sha1 new file mode 100644 index 0000000..b2b804a --- /dev/null +++ b/chrome/app/profiles_strings_grdp/IDS_AVATAR_BUTTON_SYNC_PAUSED.png.sha1
@@ -0,0 +1 @@ +463bd01935c7af0ee39c736d3add7cdc4f4b06a9 \ No newline at end of file
diff --git a/chrome/app/profiles_strings_grd/OWNERS b/chrome/app/profiles_strings_grdp/OWNERS similarity index 100% rename from chrome/app/profiles_strings_grd/OWNERS rename to chrome/app/profiles_strings_grdp/OWNERS
diff --git a/chrome/app/profiles_strings_grd/README.md b/chrome/app/profiles_strings_grdp/README.md similarity index 100% rename from chrome/app/profiles_strings_grd/README.md rename to chrome/app/profiles_strings_grdp/README.md
diff --git a/chrome/app/settings_strings.grdp b/chrome/app/settings_strings.grdp index 97dc316..c35df467 100644 --- a/chrome/app/settings_strings.grdp +++ b/chrome/app/settings_strings.grdp
@@ -3561,6 +3561,9 @@ <message name="IDS_SETTINGS_MANAGE_SYNCED_DATA_TITLE" desc="Title for the link to manage Chrome Sync data via Google Dashboard."> Manage synced data on Google Dashboard </message> + <message name="IDS_SETTINGS_EXISTING_PASSPHRASE_TITLE" desc="Title for the section where the user enters the passphrase."> + Encryption options + </message> <message name="IDS_SETTINGS_ENCRYPTION_OPTIONS" desc="Title for the section which includes options for encrypting sync settings."> Encryption options </message>
diff --git a/chrome/app/vector_icons/BUILD.gn b/chrome/app/vector_icons/BUILD.gn index 3a2fa5c..974a79f 100644 --- a/chrome/app/vector_icons/BUILD.gn +++ b/chrome/app/vector_icons/BUILD.gn
@@ -67,7 +67,6 @@ "navigate_stop.icon", "navigate_stop_touch.icon", "new_tab_button_incognito.icon", - "new_tab_button_plus.icon", "nfc.icon", "overflow_chevron.icon", "page_info_content_paste.icon",
diff --git a/chrome/app/vector_icons/new_tab_button_plus.icon b/chrome/app/vector_icons/new_tab_button_plus.icon deleted file mode 100644 index 69484e674..0000000 --- a/chrome/app/vector_icons/new_tab_button_plus.icon +++ /dev/null
@@ -1,18 +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. - -CANVAS_DIMENSIONS, 12, -MOVE_TO, 7, 5, -R_H_LINE_TO, 5, -R_V_LINE_TO, 2, -H_LINE_TO, 7, -R_V_LINE_TO, 5, -H_LINE_TO, 5, -V_LINE_TO, 7, -H_LINE_TO, 0, -V_LINE_TO, 5, -R_H_LINE_TO, 5, -V_LINE_TO, 0, -R_H_LINE_TO, 2, -CLOSE
diff --git a/chrome/app_shim/app_mode-Info.plist b/chrome/app_shim/app_mode-Info.plist index cdacd922..9ac68981 100644 --- a/chrome/app_shim/app_mode-Info.plist +++ b/chrome/app_shim/app_mode-Info.plist
@@ -26,6 +26,11 @@ <string>@APP_MODE_SHORTCUT_URL@</string> <key>CrBundleIdentifier</key> <string>@APP_MODE_BROWSER_BUNDLE_ID@</string> + <key>LSEnvironment</key> + <dict> + <key>MallocNanoZone</key> + <string>0</string> + </dict> <key>LSMinimumSystemVersion</key> <string>${CHROMIUM_MIN_SYSTEM_VERSION}</string> <key>NSAppleScriptEnabled</key>
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn index d6b0316c..fe3779e 100644 --- a/chrome/browser/BUILD.gn +++ b/chrome/browser/BUILD.gn
@@ -1060,6 +1060,8 @@ "plugins/pdf_plugin_placeholder_observer.h", "policy/browser_dm_token_storage.cc", "policy/browser_dm_token_storage.h", + "policy/browser_dm_token_storage_linux.cc", + "policy/browser_dm_token_storage_linux.h", "policy/browser_dm_token_storage_win.cc", "policy/browser_dm_token_storage_win.h", "policy/chrome_browser_policy_connector.cc",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc index 9b1a6d30..1eec3a5 100644 --- a/chrome/browser/about_flags.cc +++ b/chrome/browser/about_flags.cc
@@ -4104,6 +4104,13 @@ FEATURE_VALUE_TYPE(device::kWebAuthTouchId)}, #endif + {"enable-autofill-account-wallet-storage", + flag_descriptions::kEnableAutofillAccountWalletStorageName, + flag_descriptions::kEnableAutofillAccountWalletStorageDescription, + kOsDesktop, + FEATURE_VALUE_TYPE( + autofill::features::kAutofillEnableAccountWalletStorage)}, + // NOTE: Adding a new flag requires adding a corresponding entry to enum // "LoginCustomFlags" in tools/metrics/histograms/enums.xml. See "Flag // Histograms" in tools/metrics/histograms/README.md (run the
diff --git a/chrome/browser/android/feed/feed_host_service_factory.cc b/chrome/browser/android/feed/feed_host_service_factory.cc index b4a1007..40d9749 100644 --- a/chrome/browser/android/feed/feed_host_service_factory.cc +++ b/chrome/browser/android/feed/feed_host_service_factory.cc
@@ -10,6 +10,7 @@ #include "base/files/file_path.h" #include "base/time/default_clock.h" +#include "chrome/browser/browser_process.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/search/suggestions/image_decoder_impl.h" #include "chrome/browser/signin/identity_manager_factory.h" @@ -84,7 +85,8 @@ std::move(image_fetcher), std::move(image_database)); auto scheduler_host = std::make_unique<FeedSchedulerHost>( - profile->GetPrefs(), base::DefaultClock::GetInstance()); + profile->GetPrefs(), g_browser_process->local_state(), + base::DefaultClock::GetInstance()); auto storage_database = std::make_unique<FeedStorageDatabase>(feed_dir);
diff --git a/chrome/browser/autofill/autofill_save_card_infobar_delegate_mobile_unittest.cc b/chrome/browser/autofill/autofill_save_card_infobar_delegate_mobile_unittest.cc index 971d87f..742db2e 100644 --- a/chrome/browser/autofill/autofill_save_card_infobar_delegate_mobile_unittest.cc +++ b/chrome/browser/autofill/autofill_save_card_infobar_delegate_mobile_unittest.cc
@@ -70,12 +70,7 @@ PersonalDataManagerFactory::GetInstance()->SetTestingFactory(profile(), NULL); - ChromeAutofillClient::CreateForWebContents(web_contents()); - ChromeAutofillClient* autofill_client = - ChromeAutofillClient::FromWebContents(web_contents()); - personal_data_.reset(new TestPersonalDataManager()); - personal_data_->set_database(autofill_client->GetDatabase()); personal_data_->SetPrefService(profile()->GetPrefs()); profile()->GetPrefs()->SetInteger(
diff --git a/chrome/browser/autofill/personal_data_manager_factory.cc b/chrome/browser/autofill/personal_data_manager_factory.cc index 6ded2204..e89d027 100644 --- a/chrome/browser/autofill/personal_data_manager_factory.cc +++ b/chrome/browser/autofill/personal_data_manager_factory.cc
@@ -12,6 +12,7 @@ #include "chrome/browser/web_data_service_factory.h" #include "components/autofill/core/browser/personal_data_manager.h" #include "components/autofill/core/browser/webdata/autofill_webdata_service.h" +#include "components/autofill/core/common/autofill_features.h" #include "components/keyed_service/content/browser_context_dependency_manager.h" namespace autofill { @@ -44,11 +45,22 @@ Profile* profile = Profile::FromBrowserContext(context); PersonalDataManager* service = new PersonalDataManager(g_browser_process->GetApplicationLocale()); - service->Init(WebDataServiceFactory::GetAutofillWebDataForProfile( - profile, ServiceAccessType::EXPLICIT_ACCESS), - profile->GetPrefs(), + auto local_storage = WebDataServiceFactory::GetAutofillWebDataForProfile( + profile, ServiceAccessType::EXPLICIT_ACCESS); + auto account_storage = + base::FeatureList::IsEnabled( + features::kAutofillEnableAccountWalletStorage) + ? WebDataServiceFactory::GetAutofillWebDataForAccount( + profile, ServiceAccessType::EXPLICIT_ACCESS) + : nullptr; + service->Init(local_storage, account_storage, profile->GetPrefs(), IdentityManagerFactory::GetForProfile(profile), profile->IsOffTheRecord()); + // For now, just tell the PersonalDataManager to use the account storage if + // the feature flag is enabled. + // TODO(feuunk): Set this based on whether we're in lightweight sync mode. + service->SetUseAccountStorageForServerCards(base::FeatureList::IsEnabled( + features::kAutofillEnableAccountWalletStorage)); return service; }
diff --git a/chrome/browser/browsing_data/browsing_data_remover_browsertest.cc b/chrome/browser/browsing_data/browsing_data_remover_browsertest.cc index c18cb81..e5e4af4a 100644 --- a/chrome/browser/browsing_data/browsing_data_remover_browsertest.cc +++ b/chrome/browser/browsing_data/browsing_data_remover_browsertest.cc
@@ -12,6 +12,7 @@ #include "base/files/file_path.h" #include "base/path_service.h" #include "base/run_loop.h" +#include "base/strings/string_util.h" #include "base/test/bind_test_util.h" #include "base/test/scoped_feature_list.h" #include "base/threading/thread_restrictions.h" @@ -59,6 +60,7 @@ #include "services/network/public/cpp/features.h" #include "services/network/public/cpp/simple_url_loader.h" #include "testing/gtest/include/gtest/gtest.h" +#include "third_party/leveldatabase/env_chromium.h" #include "third_party/re2/src/re2/re2.h" using content::BrowserThread; @@ -109,6 +111,37 @@ } } + // Check leveldb content. + if (path.BaseName().AsUTF8Unsafe() == "CURRENT") { + // LevelDB instances consist of a folder where most files have variable + // names that contain a revision number. + // All leveldb folders have a "CURRENT" file that points to the current + // manifest. We consider all folders with a CURRENT file to be leveldb + // instances and try to open them. + std::unique_ptr<leveldb::DB> db; + std::string db_file = path.DirName().AsUTF8Unsafe(); + auto status = leveldb_env::OpenDB(leveldb_env::Options(), db_file, &db); + if (status.ok()) { + std::unique_ptr<leveldb::Iterator> it( + db->NewIterator(leveldb::ReadOptions())); + for (it->SeekToFirst(); it->Valid(); it->Next()) { + std::string entry = + it->key().ToString() + ":" + it->value().ToString(); + if (entry.find(hostname) != std::string::npos) { + LOG(WARNING) << "Found leveldb entry: " << file << " " << entry; + found++; + } + } + } else { + // TODO(crbug.com/846297): Some databases are already open and the LOCK + // prevents us from accessing them. + LOG(INFO) << "Could not open: " << file << " " << status.ToString(); + } + } + + // TODO(crbug.com/846297): Add support for sqlite and other formats that + // possibly contain non-plaintext data. + // Check file content. if (enumerator.GetInfo().IsDirectory()) continue; @@ -762,9 +795,10 @@ // Test that storage doesn't leave any traces on disk. IN_PROC_BROWSER_TEST_F(BrowsingDataRemoverBrowserTest, PRE_PRE_StorageRemovedFromDisk) { + ASSERT_EQ(0, CheckUserDirectoryForString(kLocalHost, {})); ASSERT_EQ(0, GetSiteDataCount()); ExpectCookieTreeModelCount(0); - ASSERT_EQ(0, CheckUserDirectoryForString(kLocalHost, {})); + // To use secure-only features on a host name, we need an https server. net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS); https_server.SetSSLConfig(
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc index 64b570f..a7c4c463 100644 --- a/chrome/browser/chrome_content_browser_client.cc +++ b/chrome/browser/chrome_content_browser_client.cc
@@ -3212,6 +3212,13 @@ return user_data_dir.Append(FILE_PATH_LITERAL("ShaderCache")); } +base::FilePath ChromeContentBrowserClient::GetGrShaderDiskCacheDirectory() { + base::FilePath user_data_dir; + base::PathService::Get(chrome::DIR_USER_DATA, &user_data_dir); + DCHECK(!user_data_dir.empty()); + return user_data_dir.Append(FILE_PATH_LITERAL("GrShaderCache")); +} + void ChromeContentBrowserClient::DidCreatePpapiPlugin( content::BrowserPpapiHost* browser_host) { #if BUILDFLAG(ENABLE_PLUGINS)
diff --git a/chrome/browser/chrome_content_browser_client.h b/chrome/browser/chrome_content_browser_client.h index ce52ee0..ca83d2de 100644 --- a/chrome/browser/chrome_content_browser_client.h +++ b/chrome/browser/chrome_content_browser_client.h
@@ -290,6 +290,7 @@ base::FilePath GetDefaultDownloadDirectory() override; std::string GetDefaultDownloadName() override; base::FilePath GetShaderDiskCacheDirectory() override; + base::FilePath GetGrShaderDiskCacheDirectory() override; void DidCreatePpapiPlugin(content::BrowserPpapiHost* browser_host) override; content::BrowserPpapiHost* GetExternalBrowserPpapiHost( int plugin_process_id) override;
diff --git a/chrome/browser/chromeos/app_mode/arc/arc_kiosk_app_launcher.cc b/chrome/browser/chromeos/app_mode/arc/arc_kiosk_app_launcher.cc index 91df0309..3c106e58 100644 --- a/chrome/browser/chromeos/app_mode/arc/arc_kiosk_app_launcher.cc +++ b/chrome/browser/chromeos/app_mode/arc/arc_kiosk_app_launcher.cc
@@ -24,7 +24,8 @@ prefs_->AddObserver(this); aura::Env::GetInstance()->AddObserver(this); // Launching the app by app id in landscape mode and in non-touch mode. - arc::LaunchApp(context, app_id_, ui::EF_NONE); + arc::LaunchApp(context, app_id_, ui::EF_NONE, + arc::UserInteractionType::NOT_USER_INITIATED); } ArcKioskAppLauncher::~ArcKioskAppLauncher() {
diff --git a/chrome/browser/chromeos/arc/arc_session_manager.cc b/chrome/browser/chromeos/arc/arc_session_manager.cc index b7fb627..94f1729 100644 --- a/chrome/browser/chromeos/arc/arc_session_manager.cc +++ b/chrome/browser/chromeos/arc/arc_session_manager.cc
@@ -342,7 +342,8 @@ profile_, kPlayStoreAppId, GetLaunchIntent(kPlayStorePackage, kPlayStoreActivity, {kInitialStartParam}), - false /* deferred_launch_allowed */, display::kInvalidDisplayId); + false /* deferred_launch_allowed */, display::kInvalidDisplayId, + arc::UserInteractionType::NOT_USER_INITIATED); } for (auto& observer : observer_list_)
diff --git a/chrome/browser/chromeos/arc/notification/arc_supervision_transition_notification_unittest.cc b/chrome/browser/chromeos/arc/notification/arc_supervision_transition_notification_unittest.cc index 8667857..8f147c98 100644 --- a/chrome/browser/chromeos/arc/notification/arc_supervision_transition_notification_unittest.cc +++ b/chrome/browser/chromeos/arc/notification/arc_supervision_transition_notification_unittest.cc
@@ -76,7 +76,8 @@ static_cast<int>(GetParam())); // Attempt to launch ARC++ app triggers notification. - LaunchApp(profile(), app_id, 0 /* event_flags */); + LaunchApp(profile(), app_id, 0 /* event_flags */, + UserInteractionType::NOT_USER_INITIATED); std::unique_ptr<ArcAppListPrefs::AppInfo> app_info = arc_app_test()->arc_app_list_prefs()->GetApp(app_id);
diff --git a/chrome/browser/chromeos/upgrade_detector_chromeos.cc b/chrome/browser/chromeos/upgrade_detector_chromeos.cc index 7fffeef..fc1a76f 100644 --- a/chrome/browser/chromeos/upgrade_detector_chromeos.cc +++ b/chrome/browser/chromeos/upgrade_detector_chromeos.cc
@@ -145,9 +145,15 @@ if (status.status == UpdateEngineClient::UPDATE_STATUS_UPDATED_NEED_REBOOT) { set_upgrade_detected_time(tick_clock()->NowTicks()); - ChannelsRequester::Begin( - base::Bind(&UpgradeDetectorChromeos::OnChannelsReceived, - weak_factory_.GetWeakPtr())); + if (status.is_rollback) { + set_is_factory_reset_required(true); + NotifyOnUpgrade(); + } else { + // Determine whether powerwash is required based on the channel. + ChannelsRequester::Begin( + base::Bind(&UpgradeDetectorChromeos::OnChannelsReceived, + weak_factory_.GetWeakPtr())); + } } else if (status.status == UpdateEngineClient::UPDATE_STATUS_NEED_PERMISSION_TO_UPDATE) { // Update engine broadcasts this state only when update is available but
diff --git a/chrome/browser/dom_distiller/dom_distiller_service_factory.cc b/chrome/browser/dom_distiller/dom_distiller_service_factory.cc index 4bf8a6c..7c13015 100644 --- a/chrome/browser/dom_distiller/dom_distiller_service_factory.cc +++ b/chrome/browser/dom_distiller/dom_distiller_service_factory.cc
@@ -19,6 +19,7 @@ #include "components/leveldb_proto/proto_database_impl.h" #include "content/public/browser/browser_context.h" #include "content/public/browser/storage_partition.h" +#include "services/network/public/cpp/shared_url_loader_factory.h" namespace dom_distiller { @@ -73,7 +74,7 @@ std::unique_ptr<DistillerURLFetcherFactory> distiller_url_fetcher_factory( new DistillerURLFetcherFactory( content::BrowserContext::GetDefaultStoragePartition(profile) - ->GetURLRequestContext())); + ->GetURLLoaderFactoryForBrowserProcess())); dom_distiller::proto::DomDistillerOptions options; if (VLOG_IS_ON(1)) {
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc index 39111d2..61cef21 100644 --- a/chrome/browser/flag_descriptions.cc +++ b/chrome/browser/flag_descriptions.cc
@@ -355,6 +355,11 @@ "Enable Web Payments to skip showing its UI if the developer specifies a " "single app."; +const char kEnableAutofillAccountWalletStorageName[] = + "Enable the account data storage for autofill"; +const char kEnableAutofillAccountWalletStorageDescription[] = + "Enable the ephemeral storage for account data for autofill."; + const char kEnableAutofillCreditCardAblationExperimentDisplayName[] = "Credit card autofill ablation experiment."; const char kEnableAutofillCreditCardAblationExperimentDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h index 0468d41..8affec33 100644 --- a/chrome/browser/flag_descriptions.h +++ b/chrome/browser/flag_descriptions.h
@@ -246,6 +246,9 @@ extern const char kEnableAsmWasmName[]; extern const char kEnableAsmWasmDescription[]; +extern const char kEnableAutofillAccountWalletStorageName[]; +extern const char kEnableAutofillAccountWalletStorageDescription[]; + extern const char kEnableAutofillCreditCardAblationExperimentDisplayName[]; extern const char kEnableAutofillCreditCardAblationExperimentDescription[];
diff --git a/chrome/browser/password_manager/password_manager_interactive_test_base.cc b/chrome/browser/password_manager/password_manager_interactive_test_base.cc index 2365888e..cc15f49 100644 --- a/chrome/browser/password_manager/password_manager_interactive_test_base.cc +++ b/chrome/browser/password_manager/password_manager_interactive_test_base.cc
@@ -36,8 +36,6 @@ ui::DomCodeToUsLayoutKeyboardCode(dom_code), false, shift, false, false); } - // Execute an empty script to sync the pressed keys for an upcoming JS. - ASSERT_TRUE(content::ExecuteScript(RenderFrameHost(), ";")); } void PasswordManagerInteractiveTestBase::VerifyPasswordIsSavedAndFilled(
diff --git a/chrome/browser/password_manager/password_manager_interactive_uitest.cc b/chrome/browser/password_manager/password_manager_interactive_uitest.cc index f7ee8ab..fd376b5 100644 --- a/chrome/browser/password_manager/password_manager_interactive_uitest.cc +++ b/chrome/browser/password_manager/password_manager_interactive_uitest.cc
@@ -281,7 +281,7 @@ // Tests that if a site embeds the login and signup forms into one <form>, the // login form still gets autofilled. IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithConditionalPopupViews, - AutofillLoginSignupForm) { + DISABLED_AutofillLoginSignupForm) { std::string submit = "document.getElementById('submit').click();"; VerifyPasswordIsSavedAndFilled("/password/login_signup_form.html", "username", "password", submit); @@ -291,7 +291,7 @@ // Tests that password suggestions still work if the fields have the // "autocomplete" attribute set to off. IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithConditionalPopupViews, - AutofillPasswordFormWithAutocompleteOff) { + DISABLED_AutofillPasswordFormWithAutocompleteOff) { std::string submit = "document.getElementById('submit').click();"; VerifyPasswordIsSavedAndFilled( "/password/password_autocomplete_off_test.html", "username", "password", @@ -300,7 +300,7 @@ // Disabled for flakiness crbug.com/849582. IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithConditionalPopupViews, - AutofillPasswordNoFormElement) { + DISABLED_AutofillPasswordNoFormElement) { VerifyPasswordIsSavedAndFilled("/password/no_form_element.html", "username_field", "password_field", "send_xhr();"); @@ -310,7 +310,7 @@ // Check that we can fill in cases where <base href> is set and the action of // the form is not set. Regression test for https://crbug.com/360230. IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithConditionalPopupViews, - AutofillBaseTagWithNoActionTest) { + DISABLED_AutofillBaseTagWithNoActionTest) { std::string submit = "document.getElementById('submit_button').click();"; VerifyPasswordIsSavedAndFilled("/password/password_xhr_submit.html", "username_field", "password_field", submit);
diff --git a/chrome/browser/policy/browser_dm_token_storage.cc b/chrome/browser/policy/browser_dm_token_storage.cc index 4289609f..f1a6c3b 100644 --- a/chrome/browser/policy/browser_dm_token_storage.cc +++ b/chrome/browser/policy/browser_dm_token_storage.cc
@@ -30,7 +30,7 @@ // Static function that can't be overridden. Implementation is only compiled for // non-supported platforms. -#if !defined(OS_WIN) +#if !defined(OS_WIN) && !defined(OS_LINUX) // static BrowserDMTokenStorage* BrowserDMTokenStorage::Get() { if (storage_for_testing_) @@ -95,6 +95,8 @@ return; is_initialized_ = true; + + // Only supported in official builds. client_id_ = InitClientId(); DVLOG(1) << "Client ID = " << client_id_; if (client_id_.empty())
diff --git a/chrome/browser/policy/browser_dm_token_storage_linux.cc b/chrome/browser/policy/browser_dm_token_storage_linux.cc new file mode 100644 index 0000000..5872937 --- /dev/null +++ b/chrome/browser/policy/browser_dm_token_storage_linux.cc
@@ -0,0 +1,152 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/policy/browser_dm_token_storage_linux.h" + +#include <string> + +#include "base/base64url.h" +#include "base/bind.h" +#include "base/bind_helpers.h" +#include "base/callback.h" +#include "base/files/file_util.h" +#include "base/files/important_file_writer.h" +#include "base/logging.h" +#include "base/no_destructor.h" +#include "base/path_service.h" +#include "base/sha1.h" +#include "base/strings/string16.h" +#include "base/strings/string_util.h" +#include "base/strings/utf_string_conversions.h" +#include "base/syslog_logging.h" +#include "base/task_runner_util.h" +#include "base/task_scheduler/post_task.h" +#include "base/threading/scoped_blocking_call.h" +#include "base/threading/sequenced_task_runner_handle.h" +#include "chrome/common/chrome_paths.h" + +namespace policy { + +namespace { + +const char kDmTokenBaseDir[] = FILE_PATH_LITERAL("Policy/Enrollment/"); +const char kEnrollmentTokenFilename[] = + FILE_PATH_LITERAL("enrollment/enrollment_token"); +const char kMachineIdFilename[] = FILE_PATH_LITERAL("/etc/machine-id"); + +bool GetDmTokenFilePath(base::FilePath* token_file_path, + const std::string& client_id, + bool create_dir) { + if (!base::PathService::Get(chrome::DIR_USER_DATA, token_file_path)) + return false; + + *token_file_path = token_file_path->Append(kDmTokenBaseDir); + + if (create_dir && !base::CreateDirectory(*token_file_path)) + return false; + + *token_file_path = token_file_path->Append(FILE_PATH_LITERAL(client_id)); + + return true; +} + +bool StoreDMTokenInUserDataDir(const std::string& token, + const std::string& client_id) { + base::FilePath token_file_path; + if (!GetDmTokenFilePath(&token_file_path, client_id, true)) { + NOTREACHED(); + return false; + } + + return base::ImportantFileWriter::WriteFileAtomically(token_file_path, token); +} + +} // namespace + +// static +BrowserDMTokenStorage* BrowserDMTokenStorage::Get() { + if (storage_for_testing_) + return storage_for_testing_; + + static base::NoDestructor<BrowserDMTokenStorageLinux> storage; + return storage.get(); +} + +BrowserDMTokenStorageLinux::BrowserDMTokenStorageLinux() + : weak_factory_(this) {} + +BrowserDMTokenStorageLinux::~BrowserDMTokenStorageLinux() {} + +std::string BrowserDMTokenStorageLinux::InitClientId() { + // The client ID is derived from /etc/machine-id + // (https://www.freedesktop.org/software/systemd/man/machine-id.html). As per + // guidelines, this ID must not be transmitted outside of the machine, which + // is why we hash it first and then encode it in base64 before transmitting + // it. + const int machine_id_size = 32; + std::string machine_id; + machine_id = ReadMachineIdFile(); + base::StringPiece machine_id_trimmed = + base::TrimWhitespaceASCII(machine_id, base::TRIM_TRAILING); + if (machine_id_trimmed.size() != machine_id_size) { + SYSLOG(ERROR) << "Error: /etc/machine-id contains " << machine_id_size + << " characters (" << machine_id_size << " were expected)."; + return std::string(); + } + + std::string machine_id_base64; + base::Base64UrlEncode(base::SHA1HashString(std::string(machine_id_trimmed)), + base::Base64UrlEncodePolicy::OMIT_PADDING, + &machine_id_base64); + return machine_id_base64; +} + +std::string BrowserDMTokenStorageLinux::InitEnrollmentToken() { + std::string enrollment_token; + base::FilePath dir_policy_files_path; + + if (!base::PathService::Get(chrome::DIR_POLICY_FILES, + &dir_policy_files_path)) { + return std::string(); + } + + base::FilePath token_file_path = + dir_policy_files_path.Append(kEnrollmentTokenFilename); + + if (!base::ReadFileToString(token_file_path, &enrollment_token)) + return std::string(); + + return std::string( + base::TrimWhitespaceASCII(enrollment_token, base::TRIM_TRAILING)); +} + +std::string BrowserDMTokenStorageLinux::InitDMToken() { + base::FilePath token_file_path; + if (!GetDmTokenFilePath(&token_file_path, RetrieveClientId(), true)) + return std::string(); + + std::string token; + if (!base::ReadFileToString(token_file_path, &token)) + return std::string(); + + return token; +} + +void BrowserDMTokenStorageLinux::SaveDMToken(const std::string& token) { + std::string client_id = RetrieveClientId(); + base::PostTaskWithTraitsAndReplyWithResult( + FROM_HERE, {base::MayBlock()}, + base::BindOnce(&StoreDMTokenInUserDataDir, token, client_id), + base::BindOnce(&BrowserDMTokenStorage::OnDMTokenStored, + weak_factory_.GetWeakPtr())); +} + +std::string BrowserDMTokenStorageLinux::ReadMachineIdFile() { + std::string machine_id; + if (!base::ReadFileToString(base::FilePath(kMachineIdFilename), &machine_id)) + return std::string(); + return machine_id; +} + +} // namespace policy
diff --git a/chrome/browser/policy/browser_dm_token_storage_linux.h b/chrome/browser/policy/browser_dm_token_storage_linux.h new file mode 100644 index 0000000..de10f26 --- /dev/null +++ b/chrome/browser/policy/browser_dm_token_storage_linux.h
@@ -0,0 +1,51 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_POLICY_BROWSER_DM_TOKEN_STORAGE_LINUX_H_ +#define CHROME_BROWSER_POLICY_BROWSER_DM_TOKEN_STORAGE_LINUX_H_ + +#include "chrome/browser/policy/browser_dm_token_storage.h" + +#include <string> + +#include "base/gtest_prod_util.h" +#include "base/macros.h" +#include "base/memory/scoped_refptr.h" +#include "base/memory/weak_ptr.h" +#include "base/sequence_checker.h" +#include "base/single_thread_task_runner.h" + +namespace policy { + +// Implementation of BrowserDMTokenStorage for Linux. The global singleton +// instance can be retrieved by calling BrowserDMTokenStorage::Get(). +class BrowserDMTokenStorageLinux : public BrowserDMTokenStorage { + public: + // Get the global singleton instance by calling BrowserDMTokenStorage::Get(). + BrowserDMTokenStorageLinux(); + ~BrowserDMTokenStorageLinux() override; + + private: + // override BrowserDMTokenStorage + std::string InitClientId() override; + std::string InitEnrollmentToken() override; + std::string InitDMToken() override; + void SaveDMToken(const std::string& token) override; + + // Returns the content of "/etc/machine-id". Virtual for tests. + virtual std::string ReadMachineIdFile(); + + // This should always be the last member of the class. + base::WeakPtrFactory<BrowserDMTokenStorageLinux> weak_factory_; + + FRIEND_TEST_ALL_PREFIXES(BrowserDMTokenStorageLinuxTest, InitClientId); + FRIEND_TEST_ALL_PREFIXES(BrowserDMTokenStorageLinuxTest, InitEnrollmentToken); + FRIEND_TEST_ALL_PREFIXES(BrowserDMTokenStorageLinuxTest, InitDMToken); + FRIEND_TEST_ALL_PREFIXES(BrowserDMTokenStorageLinuxTest, SaveDMToken); + + DISALLOW_COPY_AND_ASSIGN(BrowserDMTokenStorageLinux); +}; + +} // namespace policy +#endif // CHROME_BROWSER_POLICY_BROWSER_DM_TOKEN_STORAGE_LINUX_H_
diff --git a/chrome/browser/policy/browser_dm_token_storage_linux_unittest.cc b/chrome/browser/policy/browser_dm_token_storage_linux_unittest.cc new file mode 100644 index 0000000..0f012cf --- /dev/null +++ b/chrome/browser/policy/browser_dm_token_storage_linux_unittest.cc
@@ -0,0 +1,162 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/policy/browser_dm_token_storage_linux.h" + +#include <iostream> + +#include "base/bind.h" +#include "base/bind_helpers.h" +#include "base/files/file_util.h" +#include "base/macros.h" +#include "base/path_service.h" +#include "base/run_loop.h" +#include "base/strings/utf_string_conversions.h" +#include "base/test/scoped_path_override.h" +#include "chrome/common/chrome_paths.h" +#include "content/public/test/test_browser_thread_bundle.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using ::testing::IsEmpty; + +namespace policy { + +namespace { + +const char kDmTokenBaseDir[] = FILE_PATH_LITERAL("Policy/Enrollment/"); +const char kEnrollmentTokenFilename[] = + FILE_PATH_LITERAL("enrollment/enrollment_token"); + +const char kMachineId[] = "a1254c624234b270985170c3549725f1"; +const char kExpectedClientId[] = + "JXduKRDItaY72B6vHikFl9U95m8"; // Corresponds to kMachineId. +const char kEnrollmentToken[] = "fake-enrollment-token"; +const char kDMToken[] = "fake-dm-token"; + +} // namespace + +class MockBrowserDMTokenStorageLinux : public BrowserDMTokenStorageLinux { + public: + std::string ReadMachineIdFile() override { return kMachineId; } +}; + +class BrowserDMTokenStorageLinuxTest : public testing::Test { + private: + content::TestBrowserThreadBundle thread_bundle_; +}; + +TEST_F(BrowserDMTokenStorageLinuxTest, InitClientId) { + MockBrowserDMTokenStorageLinux storage; + EXPECT_EQ(kExpectedClientId, storage.InitClientId()); +} + +TEST_F(BrowserDMTokenStorageLinuxTest, InitEnrollmentToken) { + std::unique_ptr<base::ScopedPathOverride> path_override; + base::ScopedTempDir fake_policy_dir; + + ASSERT_TRUE(fake_policy_dir.CreateUniqueTempDir()); + path_override.reset(new base::ScopedPathOverride(chrome::DIR_POLICY_FILES, + fake_policy_dir.GetPath())); + + base::FilePath dir_policy_files_path; + ASSERT_TRUE( + base::PathService::Get(chrome::DIR_POLICY_FILES, &dir_policy_files_path)); + base::FilePath enrollment_token_file_path = + dir_policy_files_path.Append(kEnrollmentTokenFilename); + + ASSERT_TRUE(base::CreateDirectory(enrollment_token_file_path.DirName())); + + int bytes_written = + base::WriteFile(base::FilePath(enrollment_token_file_path), + kEnrollmentToken, strlen(kEnrollmentToken)); + ASSERT_EQ(static_cast<int>(strlen(kEnrollmentToken)), bytes_written); + + MockBrowserDMTokenStorageLinux storage; + EXPECT_EQ(kEnrollmentToken, storage.InitEnrollmentToken()); +} + +TEST_F(BrowserDMTokenStorageLinuxTest, InitDMToken) { + std::unique_ptr<base::ScopedPathOverride> path_override; + base::ScopedTempDir fake_user_data_dir; + + ASSERT_TRUE(fake_user_data_dir.CreateUniqueTempDir()); + path_override.reset(new base::ScopedPathOverride( + chrome::DIR_USER_DATA, fake_user_data_dir.GetPath())); + + base::FilePath dir_user_data_path; + ASSERT_TRUE( + base::PathService::Get(chrome::DIR_USER_DATA, &dir_user_data_path)); + + base::FilePath dm_token_dir_path = dir_user_data_path.Append(kDmTokenBaseDir); + ASSERT_TRUE(base::CreateDirectory(dm_token_dir_path)); + + base::FilePath dm_token_file_path = + dm_token_dir_path.Append(FILE_PATH_LITERAL(kExpectedClientId)); + int bytes_written = base::WriteFile(base::FilePath(dm_token_file_path), + kDMToken, strlen(kDMToken)); + ASSERT_EQ(static_cast<int>(strlen(kDMToken)), bytes_written); + + MockBrowserDMTokenStorageLinux storage; + EXPECT_EQ(kDMToken, storage.InitDMToken()); +} + +class TestStoreDMTokenDelegate { + public: + TestStoreDMTokenDelegate() : called_(false), success_(false) {} + ~TestStoreDMTokenDelegate() {} + + void OnDMTokenStored(bool success) { + run_loop_.Quit(); + called_ = true; + success_ = success; + } + + bool WasCalled() { + bool was_called = called_; + called_ = false; + return was_called; + } + + bool success() { return success_; } + + void Wait() { run_loop_.Run(); } + + private: + bool called_; + bool success_; + base::RunLoop run_loop_; +}; + +TEST_F(BrowserDMTokenStorageLinuxTest, SaveDMToken) { + TestStoreDMTokenDelegate delegate; + std::unique_ptr<base::ScopedPathOverride> path_override; + base::ScopedTempDir fake_user_data_dir; + + ASSERT_TRUE(fake_user_data_dir.CreateUniqueTempDir()); + path_override.reset(new base::ScopedPathOverride( + chrome::DIR_USER_DATA, fake_user_data_dir.GetPath())); + + MockBrowserDMTokenStorageLinux storage; + storage.StoreDMToken( + kDMToken, base::BindOnce(&TestStoreDMTokenDelegate::OnDMTokenStored, + base::Unretained(&delegate))); + + delegate.Wait(); + ASSERT_TRUE(delegate.WasCalled()); + ASSERT_TRUE(delegate.success()); + + base::FilePath dir_user_data_path; + ASSERT_TRUE( + base::PathService::Get(chrome::DIR_USER_DATA, &dir_user_data_path)); + base::FilePath dm_token_dir_path = dir_user_data_path.Append(kDmTokenBaseDir); + base::FilePath dm_token_file_path = + dm_token_dir_path.Append(FILE_PATH_LITERAL(kExpectedClientId)); + + std::string dm_token; + ASSERT_TRUE(base::ReadFileToString(dm_token_file_path, &dm_token)); + EXPECT_EQ(kDMToken, dm_token); +} + +} // namespace policy
diff --git a/chrome/browser/policy/configuration_policy_handler_list_factory.cc b/chrome/browser/policy/configuration_policy_handler_list_factory.cc index 8c0ff5ef..18f059e 100644 --- a/chrome/browser/policy/configuration_policy_handler_list_factory.cc +++ b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
@@ -61,6 +61,7 @@ #include "components/sync/base/pref_names.h" #include "components/sync/driver/sync_policy_handler.h" #include "components/translate/core/browser/translate_pref_names.h" +#include "components/unified_consent/pref_names.h" #include "components/variations/pref_names.h" #include "extensions/buildflags/buildflags.h" #include "media/media_buildflags.h" @@ -154,6 +155,9 @@ { key::kSafeBrowsingForTrustedSourcesEnabled, prefs::kSafeBrowsingForTrustedSourcesEnabled, base::Value::Type::BOOLEAN }, + { key::kUrlKeyedAnonymizedDataCollectionEnabled, + unified_consent::prefs::kUrlKeyedAnonymizedDataCollectionEnabled, + base::Value::Type::BOOLEAN }, { key::kDownloadRestrictions, prefs::kDownloadRestrictions, base::Value::Type::INTEGER },
diff --git a/chrome/browser/policy/policy_browsertest.cc b/chrome/browser/policy/policy_browsertest.cc index 2294c71..69adad3 100644 --- a/chrome/browser/policy/policy_browsertest.cc +++ b/chrome/browser/policy/policy_browsertest.cc
@@ -150,6 +150,7 @@ #include "components/strings/grit/components_strings.h" #include "components/translate/core/browser/language_state.h" #include "components/translate/core/browser/translate_infobar_delegate.h" +#include "components/unified_consent/pref_names.h" #include "components/update_client/update_client.h" #include "components/update_client/update_client_errors.h" #include "components/update_client/url_loader_post_interceptor.h" @@ -3214,6 +3215,24 @@ CheckURLIsBlocked(browser(), file_path2); } +IN_PROC_BROWSER_TEST_F(PolicyTest, UrlKeyedAnonymizedDataCollection) { + PrefService* prefs = browser()->profile()->GetPrefs(); + prefs->SetBoolean( + unified_consent::prefs::kUrlKeyedAnonymizedDataCollectionEnabled, true); + EXPECT_TRUE(prefs->GetBoolean( + unified_consent::prefs::kUrlKeyedAnonymizedDataCollectionEnabled)); + + // Disable by policy. + PolicyMap policies; + policies.Set(key::kUrlKeyedAnonymizedDataCollectionEnabled, + POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD, + std::make_unique<base::Value>(false), nullptr); + UpdateProviderPolicy(policies); + + EXPECT_FALSE(prefs->GetBoolean( + unified_consent::prefs::kUrlKeyedAnonymizedDataCollectionEnabled)); +} + #if !defined(OS_MACOSX) IN_PROC_BROWSER_TEST_F(PolicyTest, FullscreenAllowedBrowser) { PolicyMap policies;
diff --git a/chrome/browser/resource_coordinator/tab_lifecycle_unit.cc b/chrome/browser/resource_coordinator/tab_lifecycle_unit.cc index 5c7a811..1a6fdeb 100644 --- a/chrome/browser/resource_coordinator/tab_lifecycle_unit.cc +++ b/chrome/browser/resource_coordinator/tab_lifecycle_unit.cc
@@ -239,6 +239,14 @@ DCHECK(observers_); DCHECK(GetWebContents()); DCHECK(tab_strip_model_); + + // Visible tabs are treated as having been immediately focused, while + // non-visible tabs have their focus set to the last active time (the time at + // which they stopped being the active tab in a tabstrip). + if (GetVisibility() == content::Visibility::VISIBLE) + last_focused_time_ = NowTicks(); + else + last_focused_time_ = web_contents->GetLastActiveTime(); } TabLifecycleUnitSource::TabLifecycleUnit::~TabLifecycleUnit() {
diff --git a/chrome/browser/resource_coordinator/tab_lifecycle_unit.h b/chrome/browser/resource_coordinator/tab_lifecycle_unit.h index 6f75a4c..496b0cc 100644 --- a/chrome/browser/resource_coordinator/tab_lifecycle_unit.h +++ b/chrome/browser/resource_coordinator/tab_lifecycle_unit.h
@@ -172,13 +172,14 @@ int discard_count_ = 0; // Last time at which this tab was focused, or TimeTicks::Max() if it is - // currently focused. - // - // TODO(fdoray): To keep old behavior (sort order and protection of recently - // focused tabs), this is initialized with NowTicks(). Consider initializing - // this with a null TimeTicks when the tab isn't initially focused. - // https://crbug.com/800885 - base::TimeTicks last_focused_time_ = NowTicks(); + // currently focused. For tabs that aren't currently focused this is + // initialized using WebContents::GetLastActiveTime, which causes use times + // from previous browsing sessions to persist across session restore + // events. + // TODO(chrisha): Migrate |last_active_time| to actually track focus time, + // instead of the time that focus was lost. This is a more meaninful number + // for all of the clients of |last_active_time|. + base::TimeTicks last_focused_time_; // When this is false, CanDiscard() always returns false. bool auto_discardable_ = true;
diff --git a/chrome/browser/resource_coordinator/tab_lifecycle_unit_unittest.cc b/chrome/browser/resource_coordinator/tab_lifecycle_unit_unittest.cc index 0b2d807..a06e1c4 100644 --- a/chrome/browser/resource_coordinator/tab_lifecycle_unit_unittest.cc +++ b/chrome/browser/resource_coordinator/tab_lifecycle_unit_unittest.cc
@@ -93,10 +93,11 @@ std::unique_ptr<content::WebContents> test_web_contents = CreateTestWebContents(); web_contents_ = test_web_contents.get(); + auto* tester = content::WebContentsTester::For(web_contents_); + tester->SetLastActiveTime(NowTicks()); ResourceCoordinatorTabHelper::CreateForWebContents(web_contents_); // Commit an URL to allow discarding. - content::WebContentsTester::For(web_contents_) - ->NavigateAndCommit(GURL("https://www.example.com")); + tester->NavigateAndCommit(GURL("https://www.example.com")); tab_strip_model_ = std::make_unique<TabStripModel>(&tab_strip_model_delegate_, profile());
diff --git a/chrome/browser/resources/local_ntp/most_visited_single.css b/chrome/browser/resources/local_ntp/most_visited_single.css index b01d0447..46d8668 100644 --- a/chrome/browser/resources/local_ntp/most_visited_single.css +++ b/chrome/browser/resources/local_ntp/most_visited_single.css
@@ -489,10 +489,6 @@ width: var(--md-fallback-letter-size); } -.md-title-container { - overflow: hidden; -} - .md-title { color: rgba(33, 32, 36, 0.86); font-family: 'Roboto', arial, sans-serif; @@ -511,7 +507,7 @@ } body.background-image .md-title { - text-shadow: 0 0 8px rgba(0, 0, 0, 0.5); + text-shadow: 0 0 16px rgba(0, 0, 0, 0.3); } .md-menu {
diff --git a/chrome/browser/resources/settings/people_page/sync_page.html b/chrome/browser/resources/settings/people_page/sync_page.html index 8b3d7e54..35be65b1 100644 --- a/chrome/browser/resources/settings/people_page/sync_page.html +++ b/chrome/browser/resources/settings/people_page/sync_page.html
@@ -91,6 +91,40 @@ </settings-sync-account-control> </template> </if> + + <template is="dom-if" if="[[shouldShowExistingPassphraseBelowAccount_( + unifiedConsentEnabled, syncPrefs.passphraseRequired)]]"> + <div id="existingPassphrase" class="list-frame"> + <div id="existingPassphraseTitle" class="list-item"> + <div class="start"> + <div>$i18n{existingPassphraseTitle}</div> + <div class="secondary"> + [[enterPassphrasePrompt_(syncPrefs.passphraseTypeIsCustom)]] + <a href="$i18nRaw{syncErrorHelpUrl}" target="_blank"> + $i18n{learnMore} + </a> + </div> + </div> + </div> + <div id="existingPassphraseContainer" class="list-item"> + <cr-input id="existingPassphraseInput" type="password" + value="{{existingPassphrase_}}" + placeholder="$i18n{passphrasePlaceholder}" + error-message="$i18n{incorrectPassphraseError}" + on-keypress="onSubmitExistingPassphraseTap_"> + <paper-button id="submitExistingPassphrase" slot="suffix" + on-click="onSubmitExistingPassphraseTap_" + class="action-button" disabled="[[!existingPassphrase_]]"> + $i18n{submitPassphraseButton} + </paper-button> + </cr-input> + </div> + <div id="passphraseRecoverHint" class="list-item"> + <span>$i18nRaw{passphraseRecover}</span> + </div> + </div> + </template> + <!-- A change of the unified consent toggle state is automatically handled in the C++ code after a change in the pref is observed. --> <settings-toggle-button id="unifiedConsentToggle" class="first" @@ -125,7 +159,10 @@ </div> <div id="[[pages_.CONFIGURE]]" hidden$="[[!isStatus_(pages_.CONFIGURE, pageStatus_)]]"> - <template is="dom-if" if="[[syncPrefs.passphraseRequired]]"> + <!-- TODO(http://crbug.com/862983) Remove this section once the Unified + Consent feature is launched. --> + <template is="dom-if" if="[[shouldShowExistingPassphraseInSyncSection_( + unifiedConsentEnabled, syncPrefs.passphraseRequired)]]"> <div id="existingPassphrase" class="list-frame"> <div class="list-item"> <span>
diff --git a/chrome/browser/resources/settings/people_page/sync_page.js b/chrome/browser/resources/settings/people_page/sync_page.js index 5cbfd36..bbb117c 100644 --- a/chrome/browser/resources/settings/people_page/sync_page.js +++ b/chrome/browser/resources/settings/people_page/sync_page.js
@@ -581,6 +581,22 @@ * @return {boolean} * @private */ + shouldShowExistingPassphraseBelowAccount_: function() { + return !!this.unifiedConsentEnabled && !!this.syncPrefs.passphraseRequired; + }, + + /** + * @return {boolean} + * @private + */ + shouldShowExistingPassphraseInSyncSection_: function() { + return !this.unifiedConsentEnabled && !!this.syncPrefs.passphraseRequired; + }, + + /** + * @return {boolean} + * @private + */ shouldShowSyncControls_: function() { return !!this.unifiedConsentEnabled && !this.syncStatus.disabled; },
diff --git a/chrome/browser/search/background/ntp_background_service.cc b/chrome/browser/search/background/ntp_background_service.cc index a6ed42d..a6f78e5 100644 --- a/chrome/browser/search/background/ntp_background_service.cc +++ b/chrome/browser/search/background/ntp_background_service.cc
@@ -273,6 +273,10 @@ if (token_fetcher_ || albums_loader_) return; + // Clear any stale data that may have been fetched with a previous token. + // This is particularly important if the current token fetch results in an + // auth error because the user has since signed out. + album_info_.clear(); OAuth2TokenService::ScopeSet scopes{kScopePhotos}; token_fetcher_ = std::make_unique<identity::PrimaryAccountAccessTokenFetcher>( "ntp_backgrounds_service", identity_manager_, scopes, @@ -342,7 +346,6 @@ void NtpBackgroundService::OnAlbumInfoFetchComplete( std::unique_ptr<std::string> response_body) { - album_info_.clear(); // The loader will be deleted when the request is handled. std::unique_ptr<network::SimpleURLLoader> loader_deleter( std::move(albums_loader_)); @@ -378,6 +381,10 @@ if (token_fetcher_ || albums_photo_info_loader_) return; + // Clear any stale data that may have been fetched with a previous token. + // This is particularly important if the current token fetch results in an + // auth error because the user has since signed out. + album_photos_.clear(); requested_album_id_ = album_id; requested_photo_container_id_ = photo_container_id; OAuth2TokenService::ScopeSet scopes{kScopePhotos}; @@ -449,7 +456,6 @@ void NtpBackgroundService::OnAlbumPhotosFetchComplete( std::unique_ptr<std::string> response_body) { - album_photos_.clear(); // The loader will be deleted when the request is handled. std::unique_ptr<network::SimpleURLLoader> loader_deleter( std::move(albums_photo_info_loader_));
diff --git a/chrome/browser/search/background/ntp_background_service_unittest.cc b/chrome/browser/search/background/ntp_background_service_unittest.cc index 337b7a63..fbc98dc 100644 --- a/chrome/browser/search/background/ntp_background_service_unittest.cc +++ b/chrome/browser/search/background/ntp_background_service_unittest.cc
@@ -58,6 +58,12 @@ test_url_loader_factory_.AddResponse(load_url.spec(), response); } + // This can be used to revoke a token issued by + // SetAutomaticIssueOfAccessTokens above. + void RemoveRefreshTokenForPrimaryAccount() { + identity_env_.RemoveRefreshTokenForPrimaryAccount(); + } + void SetUpResponseWithNetworkError(const GURL& load_url) { test_url_loader_factory_.AddResponse( load_url, network::ResourceResponseHead(), std::string(), @@ -242,6 +248,46 @@ EXPECT_TRUE(service()->album_info().empty()); } +TEST_F(NtpBackgroundServiceTest, AlbumInfoAuthError) { + RemoveRefreshTokenForPrimaryAccount(); + + ASSERT_TRUE(service()->album_info().empty()); + + service()->FetchAlbumInfo(); + base::RunLoop().RunUntilIdle(); + + EXPECT_TRUE(service()->album_info().empty()); +} + +TEST_F(NtpBackgroundServiceTest, AlbumInfoAuthErrorClearsCache) { + ntp::background::AlbumMetaData album; + album.set_album_id(12345); + album.set_album_name("Travel"); + album.set_banner_image_url("https://wallpapers.co/some_image"); + album.set_photo_container_id("AnIdentifierForThePhotoContainer"); + ntp::background::PersonalAlbumsResponse response; + *response.add_album_meta_data() = album; + std::string response_string; + response.SerializeToString(&response_string); + + SetUpResponseWithData(service()->GetAlbumsURLForTesting(), response_string); + + ASSERT_TRUE(service()->album_info().empty()); + + service()->FetchAlbumInfo(); + base::RunLoop().RunUntilIdle(); + + RemoveRefreshTokenForPrimaryAccount(); + + // Stale data fetched with previous token. + ASSERT_FALSE(service()->album_info().empty()); + + service()->FetchAlbumInfo(); + base::RunLoop().RunUntilIdle(); + + EXPECT_TRUE(service()->album_info().empty()); +} + TEST_F(NtpBackgroundServiceTest, BadAlbumsResponse) { SetUpResponseWithData(service()->GetAlbumsURLForTesting(), "bad serialized PersonalAlbumsResponse"); @@ -294,6 +340,45 @@ EXPECT_TRUE(service()->album_photos().empty()); } +TEST_F(NtpBackgroundServiceTest, AlbumPhotosAuthError) { + RemoveRefreshTokenForPrimaryAccount(); + + ASSERT_TRUE(service()->album_info().empty()); + + service()->FetchAlbumPhotos("album_id", "photo_container_id"); + base::RunLoop().RunUntilIdle(); + + EXPECT_TRUE(service()->album_info().empty()); +} + +TEST_F(NtpBackgroundServiceTest, AlbumPhotosAuthErrorClearsCache) { + ntp::background::SettingPreviewResponse::Preview preview; + preview.set_preview_url("https://wallpapers.co/some_image"); + ntp::background::SettingPreviewResponse response; + *response.add_preview() = preview; + std::string response_string; + response.SerializeToString(&response_string); + + SetUpResponseWithData(service()->GetAlbumPhotosApiUrlForTesting( + "album_id", "photo_container_id"), + response_string); + + ASSERT_TRUE(service()->album_photos().empty()); + + service()->FetchAlbumPhotos("album_id", "photo_container_id"); + base::RunLoop().RunUntilIdle(); + + RemoveRefreshTokenForPrimaryAccount(); + + // Stale data fetched with previous token. + ASSERT_FALSE(service()->album_photos().empty()); + + service()->FetchAlbumPhotos("album_id", "photo_container_id"); + base::RunLoop().RunUntilIdle(); + + EXPECT_TRUE(service()->album_photos().empty()); +} + TEST_F(NtpBackgroundServiceTest, BadAlbumPhotosResponse) { SetUpResponseWithData(service()->GetAlbumPhotosApiUrlForTesting( "album_id", "photo_container_id"),
diff --git a/chrome/browser/sessions/session_restore.cc b/chrome/browser/sessions/session_restore.cc index 0cb1cc7d..8a89c0c 100644 --- a/chrome/browser/sessions/session_restore.cc +++ b/chrome/browser/sessions/session_restore.cc
@@ -657,10 +657,6 @@ tab.extension_app_id, is_selected_tab, tab.pinned, true, last_active_time, session_storage_namespace.get(), tab.user_agent_override, true /* from_session_restore */); - // Regression check: if the current tab |is_selected_tab|, it should load - // immediately, otherwise, tabs should not start loading right away. The - // focused tab will be loaded by Browser, and TabLoader will load the rest. - DCHECK(is_selected_tab || web_contents->GetController().NeedsReload()); // RestoreTab can return nullptr if |tab| doesn't have valid data. if (!web_contents)
diff --git a/chrome/browser/sessions/tab_loader.cc b/chrome/browser/sessions/tab_loader.cc index 52f54e5..3cb9a66 100644 --- a/chrome/browser/sessions/tab_loader.cc +++ b/chrome/browser/sessions/tab_loader.cc
@@ -207,7 +207,7 @@ } } - AddTab(restored_tab.contents(), restored_tab.is_active()); + AddTab(restored_tab.contents()); } StartTimerIfNeeded(); @@ -347,7 +347,7 @@ return tabs_to_load; } -void TabLoader::AddTab(WebContents* contents, bool loading_initiated) { +void TabLoader::AddTab(WebContents* contents) { DCHECK(reentry_depth_ > 0); // This can only be called internally. // Handle tabs that have already started or finished loading. @@ -360,15 +360,7 @@ return; } - // Otherwise place it in one of the |tabs_load_initiated_| or - // |tabs_to_load_| containers. - if (loading_initiated) { - delegate_->NotifyTabLoadStarted(); - ++scheduled_to_load_count_; - tabs_load_initiated_.insert(contents); - } else { - tabs_to_load_.push_back(contents); - } + tabs_to_load_.push_back(contents); } void TabLoader::RemoveTab(WebContents* contents) {
diff --git a/chrome/browser/sessions/tab_loader.h b/chrome/browser/sessions/tab_loader.h index 6ddcdf9..b83ad0f 100644 --- a/chrome/browser/sessions/tab_loader.h +++ b/chrome/browser/sessions/tab_loader.h
@@ -127,7 +127,7 @@ // Adds a tab that we are responsible for to one of the |tabs_*| containers. // Can invalidate self-destroy and timer invariants. - void AddTab(content::WebContents* contents, bool loading_initiated); + void AddTab(content::WebContents* contents); // Removes the tab from the set of tabs to load and list of tabs we're waiting // to get a load from. Can invalidate self-destroy and timer invariants.
diff --git a/chrome/browser/signin/identity_manager_factory.cc b/chrome/browser/signin/identity_manager_factory.cc index 423d520..6c3241a 100644 --- a/chrome/browser/signin/identity_manager_factory.cc +++ b/chrome/browser/signin/identity_manager_factory.cc
@@ -7,6 +7,7 @@ #include "build/build_config.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/signin/account_tracker_service_factory.h" +#include "chrome/browser/signin/gaia_cookie_manager_service_factory.h" #include "chrome/browser/signin/profile_oauth2_token_service_factory.h" #include "chrome/browser/signin/signin_manager_factory.h" #include "components/keyed_service/content/browser_context_dependency_manager.h" @@ -27,7 +28,8 @@ : identity::IdentityManager( SigninManagerFactory::GetForProfile(profile), ProfileOAuth2TokenServiceFactory::GetForProfile(profile), - AccountTrackerServiceFactory::GetForProfile(profile)) {} + AccountTrackerServiceFactory::GetForProfile(profile), + GaiaCookieManagerServiceFactory::GetForProfile(profile)) {} }; IdentityManagerFactory::IdentityManagerFactory() @@ -35,6 +37,7 @@ "IdentityManager", BrowserContextDependencyManager::GetInstance()) { DependsOn(AccountTrackerServiceFactory::GetInstance()); + DependsOn(GaiaCookieManagerServiceFactory::GetInstance()); DependsOn(ProfileOAuth2TokenServiceFactory::GetInstance()); DependsOn(SigninManagerFactory::GetInstance()); }
diff --git a/chrome/browser/sync/test/integration/single_client_bookmarks_sync_test.cc b/chrome/browser/sync/test/integration/single_client_bookmarks_sync_test.cc index 228bee6..b6a1a25 100644 --- a/chrome/browser/sync/test/integration/single_client_bookmarks_sync_test.cc +++ b/chrome/browser/sync/test/integration/single_client_bookmarks_sync_test.cc
@@ -110,9 +110,20 @@ DISALLOW_COPY_AND_ASSIGN(SingleClientBookmarksSyncTestIncludingUssTests); }; -IN_PROC_BROWSER_TEST_F(SingleClientBookmarksSyncTest, Sanity) { +IN_PROC_BROWSER_TEST_P(SingleClientBookmarksSyncTestIncludingUssTests, Sanity) { ASSERT_TRUE(SetupClients()) << "SetupClients() failed."; + // TODO(crbug.com/516866): Move the following block after adding the bookmark + // nodes to the model upon implementing the merge logic in USS. This is here + // to make sure that when sync starts, the model is completely empty and + // doesn't require any merge logic which isn't the general case obviously. + + // Setup sync, wait for its completion, and make sure changes were synced. + ASSERT_TRUE(SetupSync()) << "SetupSync() failed."; + ASSERT_TRUE( + UpdatedProgressMarkerChecker(GetSyncService(kSingleProfileIndex)).Wait()); + ASSERT_TRUE(ModelMatchesVerifier(kSingleProfileIndex)); + // Starting state: // other_node // -> top @@ -141,11 +152,8 @@ kSingleProfileIndex, tier1_b, 0, "tier1_b_url0", GURL("http://www.nhl.com")); - // Setup sync, wait for its completion, and make sure changes were synced. - ASSERT_TRUE(SetupSync()) << "SetupSync() failed."; - ASSERT_TRUE( - UpdatedProgressMarkerChecker(GetSyncService(kSingleProfileIndex)).Wait()); - ASSERT_TRUE(ModelMatchesVerifier(kSingleProfileIndex)); + // TODO(crbug.com/516866): Call SetupSync() here upon implementing the merge + // logic in USS. Refer the TODO above for details. // Ultimately we want to end up with the following model; but this test is // more about the journey than the destination.
diff --git a/chrome/browser/ui/app_list/arc/arc_app_item.cc b/chrome/browser/ui/app_list/arc/arc_app_item.cc index 8862c269..15db38cb 100644 --- a/chrome/browser/ui/app_list/arc/arc_app_item.cc +++ b/chrome/browser/ui/app_list/arc/arc_app_item.cc
@@ -8,7 +8,6 @@ #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/app_list/app_list_controller_delegate.h" #include "chrome/browser/ui/app_list/arc/arc_app_context_menu.h" -#include "chrome/browser/ui/app_list/arc/arc_app_utils.h" #include "components/arc/arc_bridge_service.h" #include "content/public/browser/browser_thread.h" #include "extensions/browser/app_sorting.h" @@ -48,14 +47,13 @@ } void ArcAppItem::Activate(int event_flags) { - if (!arc::LaunchApp(profile(), id(), event_flags, - GetController()->GetAppListDisplayId())) { - return; - } + Launch(event_flags, arc::UserInteractionType::APP_STARTED_FROM_LAUNCHER); } void ArcAppItem::ExecuteLaunchCommand(int event_flags) { - PerformActivate(event_flags); + Launch(event_flags, + arc::UserInteractionType::APP_STARTED_FROM_LAUNCHER_CONTEXT_MENU); + MaybeDismissAppList(); } void ArcAppItem::SetName(const std::string& name) { @@ -77,6 +75,11 @@ context_menu_->GetMenuModel(std::move(callback)); } +void ArcAppItem::Launch(int event_flags, arc::UserInteractionType interaction) { + arc::LaunchApp(profile(), id(), event_flags, interaction, + GetController()->GetAppListDisplayId()); +} + app_list::AppContextMenu* ArcAppItem::GetAppContextMenu() { return context_menu_.get(); }
diff --git a/chrome/browser/ui/app_list/arc/arc_app_item.h b/chrome/browser/ui/app_list/arc/arc_app_item.h index 1974c0f..f25b1abd 100644 --- a/chrome/browser/ui/app_list/arc/arc_app_item.h +++ b/chrome/browser/ui/app_list/arc/arc_app_item.h
@@ -11,6 +11,7 @@ #include "base/macros.h" #include "chrome/browser/ui/app_list/app_context_menu_delegate.h" #include "chrome/browser/ui/app_list/arc/arc_app_icon.h" +#include "chrome/browser/ui/app_list/arc/arc_app_utils.h" #include "chrome/browser/ui/app_list/chrome_app_list_item.h" class ArcAppContextMenu; @@ -52,6 +53,8 @@ // ChromeAppListItem overrides: app_list::AppContextMenu* GetAppContextMenu() override; + void Launch(int event_flags, arc::UserInteractionType interaction); + std::unique_ptr<ArcAppIcon> arc_app_icon_; std::unique_ptr<ArcAppContextMenu> context_menu_;
diff --git a/chrome/browser/ui/app_list/arc/arc_app_launcher.cc b/chrome/browser/ui/app_list/arc/arc_app_launcher.cc index f45f12c..6366281 100644 --- a/chrome/browser/ui/app_list/arc/arc_app_launcher.cc +++ b/chrome/browser/ui/app_list/arc/arc_app_launcher.cc
@@ -6,19 +6,20 @@ #include <memory> -#include "chrome/browser/ui/app_list/arc/arc_app_utils.h" #include "ui/events/event_constants.h" ArcAppLauncher::ArcAppLauncher(content::BrowserContext* context, const std::string& app_id, const base::Optional<std::string>& launch_intent, bool deferred_launch_allowed, - int64_t display_id) + int64_t display_id, + arc::UserInteractionType interaction) : context_(context), app_id_(app_id), launch_intent_(launch_intent), deferred_launch_allowed_(deferred_launch_allowed), - display_id_(display_id) { + display_id_(display_id), + interaction_(interaction) { ArcAppListPrefs* prefs = ArcAppListPrefs::Get(context_); DCHECK(prefs); @@ -58,7 +59,7 @@ prefs->RemoveObserver(this); if (!arc::LaunchAppWithIntent(context_, app_id_, launch_intent_, ui::EF_NONE, - display_id_)) { + interaction_, display_id_)) { VLOG(2) << "Failed to launch app: " + app_id_ + "."; }
diff --git a/chrome/browser/ui/app_list/arc/arc_app_launcher.h b/chrome/browser/ui/app_list/arc/arc_app_launcher.h index 1753988..03d36138 100644 --- a/chrome/browser/ui/app_list/arc/arc_app_launcher.h +++ b/chrome/browser/ui/app_list/arc/arc_app_launcher.h
@@ -12,6 +12,7 @@ #include "base/macros.h" #include "base/optional.h" #include "chrome/browser/ui/app_list/arc/arc_app_list_prefs.h" +#include "chrome/browser/ui/app_list/arc/arc_app_utils.h" namespace content { class BrowserContext; @@ -25,7 +26,8 @@ const std::string& app_id, const base::Optional<std::string>& launch_intent, bool deferred_launch_allowed, - int64_t display_id); + int64_t display_id, + arc::UserInteractionType interaction); ~ArcAppLauncher() override; bool app_launched() const { return app_launched_; } @@ -35,6 +37,10 @@ const ArcAppListPrefs::AppInfo& app_info) override; void OnAppReadyChanged(const std::string& app_id, bool ready) override; + // Launches the app if ready to launch, recording the input user interaction. + // If not ready to launch, + void LaunchAppIfReady(arc::UserInteractionType interaction); + private: void LaunchApp(); @@ -53,6 +59,8 @@ const int64_t display_id_; // Flag indicating that ARC app was launched. bool app_launched_ = false; + // Enum that indicates what type of metric to record to UMA on launch. + arc::UserInteractionType interaction_; DISALLOW_COPY_AND_ASSIGN(ArcAppLauncher); };
diff --git a/chrome/browser/ui/app_list/arc/arc_app_unittest.cc b/chrome/browser/ui/app_list/arc/arc_app_unittest.cc index 85ef5067..8e1bc7f 100644 --- a/chrome/browser/ui/app_list/arc/arc_app_unittest.cc +++ b/chrome/browser/ui/app_list/arc/arc_app_unittest.cc
@@ -1189,7 +1189,8 @@ EXPECT_EQ(base::Time(), app_info->last_launch_time); base::Time time_before = base::Time::Now(); - arc::LaunchApp(profile(), id2, ui::EF_NONE); + arc::LaunchApp(profile(), id2, ui::EF_NONE, + arc::UserInteractionType::NOT_USER_INITIATED); const base::Time time_after = base::Time::Now(); app_info = prefs->GetApp(id2); @@ -1314,7 +1315,8 @@ ASSERT_TRUE(app_info); EXPECT_FALSE(app_info->ready); - arc::LaunchApp(profile(), arc::kPlayStoreAppId, ui::EF_NONE); + arc::LaunchApp(profile(), arc::kPlayStoreAppId, ui::EF_NONE, + arc::UserInteractionType::NOT_USER_INITIATED); EXPECT_TRUE(arc::IsArcPlayStoreEnabledForProfile(profile())); } @@ -1907,12 +1909,14 @@ const std::string id3 = ArcAppTest::GetAppId(app3); ArcAppLauncher launcher1(profile(), id1, base::Optional<std::string>(), false, - display::kInvalidDisplayId); + display::kInvalidDisplayId, + arc::UserInteractionType::NOT_USER_INITIATED); EXPECT_FALSE(launcher1.app_launched()); EXPECT_TRUE(prefs->HasObserver(&launcher1)); ArcAppLauncher launcher3(profile(), id3, base::Optional<std::string>(), false, - display::kInvalidDisplayId); + display::kInvalidDisplayId, + arc::UserInteractionType::NOT_USER_INITIATED); EXPECT_FALSE(launcher1.app_launched()); EXPECT_TRUE(prefs->HasObserver(&launcher1)); EXPECT_FALSE(launcher3.app_launched()); @@ -1934,7 +1938,8 @@ const std::string launch_intent2 = arc::GetLaunchIntent( app2.package_name, app2.activity, std::vector<std::string>()); ArcAppLauncher launcher2(profile(), id2, launch_intent2, false, - display::kInvalidDisplayId); + display::kInvalidDisplayId, + arc::UserInteractionType::NOT_USER_INITIATED); EXPECT_TRUE(launcher2.app_launched()); EXPECT_FALSE(prefs->HasObserver(&launcher2)); EXPECT_EQ(1u, app_instance()->launch_requests().size()); @@ -2131,10 +2136,12 @@ // Launch when app is registered and ready. ArcAppLauncher launcher1(profile(), id1, base::Optional<std::string>(), false, - display::kInvalidDisplayId); + display::kInvalidDisplayId, + arc::UserInteractionType::NOT_USER_INITIATED); // Launch when app is registered. ArcAppLauncher launcher2(profile(), id2, base::Optional<std::string>(), true, - display::kInvalidDisplayId); + display::kInvalidDisplayId, + arc::UserInteractionType::NOT_USER_INITIATED); EXPECT_FALSE(launcher1.app_launched());
diff --git a/chrome/browser/ui/app_list/arc/arc_app_utils.cc b/chrome/browser/ui/app_list/arc/arc_app_utils.cc index a400f55..b1215b0a 100644 --- a/chrome/browser/ui/app_list/arc/arc_app_utils.cc +++ b/chrome/browser/ui/app_list/arc/arc_app_utils.cc
@@ -8,6 +8,7 @@ #include <string> #include "base/json/json_writer.h" +#include "base/metrics/histogram_macros.h" #include "base/stl_util.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_split.h" @@ -193,7 +194,8 @@ bool LaunchAndroidSettingsApp(content::BrowserContext* context, int event_flags, int64_t display_id) { - return LaunchApp(context, kSettingsAppId, event_flags, display_id); + return LaunchApp(context, kSettingsAppId, event_flags, + UserInteractionType::APP_STARTED_FROM_SETTINGS, display_id); } bool LaunchPlayStoreWithUrl(const std::string& url) { @@ -210,24 +212,31 @@ bool LaunchApp(content::BrowserContext* context, const std::string& app_id, - int event_flags) { - return LaunchApp(context, app_id, event_flags, display::kInvalidDisplayId); + int event_flags, + arc::UserInteractionType user_action) { + return LaunchApp(context, app_id, event_flags, user_action, + display::kInvalidDisplayId); } bool LaunchApp(content::BrowserContext* context, const std::string& app_id, int event_flags, + arc::UserInteractionType user_action, int64_t display_id) { return LaunchAppWithIntent(context, app_id, base::nullopt /* launch_intent */, - event_flags, display_id); + event_flags, user_action, display_id); } bool LaunchAppWithIntent(content::BrowserContext* context, const std::string& app_id, const base::Optional<std::string>& launch_intent, int event_flags, + arc::UserInteractionType user_action, int64_t display_id) { DCHECK(!launch_intent.has_value() || !launch_intent->empty()); + if (user_action != UserInteractionType::NOT_USER_INITIATED) + UMA_HISTOGRAM_ENUMERATION("Arc.UserInteraction", user_action, + UserInteractionType::SIZE); Profile* const profile = Profile::FromBrowserContext(context); @@ -351,8 +360,9 @@ int64_t display_id) { const std::string launch_intent = GetLaunchIntent( kSettingsAppPackage, activity, std::vector<std::string>()); - return LaunchAppWithIntent(context, kSettingsAppId, launch_intent, - event_flags, display_id); + return LaunchAppWithIntent( + context, kSettingsAppId, launch_intent, event_flags, + UserInteractionType::APP_STARTED_FROM_SETTINGS, display_id); } void SetTaskActive(int task_id) {
diff --git a/chrome/browser/ui/app_list/arc/arc_app_utils.h b/chrome/browser/ui/app_list/arc/arc_app_utils.h index a00bef7..db2e31e0 100644 --- a/chrome/browser/ui/app_list/arc/arc_app_utils.h +++ b/chrome/browser/ui/app_list/arc/arc_app_utils.h
@@ -79,6 +79,55 @@ DISALLOW_COPY_AND_ASSIGN(Intent); }; +// Defines ARC App user interaction types to track how users use ARC apps. +// These enums are used to define the buckets for an enumerated UMA histogram +// and need to be synced with tools/metrics/histograms/enums.xml. +enum class UserInteractionType { + // Default to not user-initiated. + // Can be used temporarily for a new action path or to denote an action + // that was not directly user-initiated. + NOT_USER_INITIATED = 0, + + // User started an app from the launcher. + APP_STARTED_FROM_LAUNCHER = 1, + + // User started an app from a context menu click in the launcher. + APP_STARTED_FROM_LAUNCHER_CONTEXT_MENU = 2, + + // User started an app from a launcher search result. + APP_STARTED_FROM_LAUNCHER_SEARCH = 3, + + // User started an app from a a context menu click on a search result. + APP_STARTED_FROM_LAUNCHER_SEARCH_CONTEXT_MENU = 4, + + // User started a suggested app in the launcher. + APP_STARTED_FROM_LAUNCHER_SUGGESTED_APP = 5, + + // User started a suggested app using the context menu in the launcher. + APP_STARTED_FROM_LAUNCHER_SUGGESTED_APP_CONTEXT_MENU = 6, + + // User started an app from the shelf. + APP_STARTED_FROM_SHELF = 7, + + // User started an app from the shelf using the context menu. + // TODO(crbug.com/862901): Record this separately from APP_STARTED_FROM_SHELF + APP_STARTED_FROM_SHELF_CONTEXT_MENU = 8, + + // User started an app from settings. + APP_STARTED_FROM_SETTINGS = 9, + + // User interacted with an ARC++ notification. + // TODO(crbug.com/862001): Record this. + NOTIFICATION_INTERACTION = 10, + + // User interacted with the content window. + // TODO(crbug.com/855381): Record this. + APP_CONTENT_WINDOW_INTERACTION = 11, + + // The size of this enum; keep last. + SIZE, +}; + // Checks if a given app should be hidden in launcher. bool ShouldShowInLauncher(const std::string& app_id); @@ -93,16 +142,19 @@ // Launches an ARC app. bool LaunchApp(content::BrowserContext* context, const std::string& app_id, - int event_flags); + int event_flags, + UserInteractionType user_action); bool LaunchApp(content::BrowserContext* context, const std::string& app_id, int event_flags, + UserInteractionType user_action, int64_t display_id); bool LaunchAppWithIntent(content::BrowserContext* context, const std::string& app_id, const base::Optional<std::string>& launch_intent, int event_flags, + UserInteractionType user_action, int64_t display_id); // Launches App Shortcut that was published by Android's ShortcutManager.
diff --git a/chrome/browser/ui/app_list/chrome_app_list_item.cc b/chrome/browser/ui/app_list/chrome_app_list_item.cc index 5152a74..41cb0e9c 100644 --- a/chrome/browser/ui/app_list/chrome_app_list_item.cc +++ b/chrome/browser/ui/app_list/chrome_app_list_item.cc
@@ -93,11 +93,7 @@ void ChromeAppListItem::PerformActivate(int event_flags) { Activate(event_flags); - - // Launching apps can take some time. It looks nicer to dismiss the app list. - // Do not close app list for home launcher. - if (!GetController()->IsHomeLauncherEnabledInTabletMode()) - GetController()->DismissView(); + MaybeDismissAppList(); } void ChromeAppListItem::Activate(int event_flags) {} @@ -118,6 +114,13 @@ return nullptr; } +void ChromeAppListItem::MaybeDismissAppList() { + // Launching apps can take some time. It looks nicer to dismiss the app list. + // Do not close app list for home launcher. + if (!GetController()->IsHomeLauncherEnabledInTabletMode()) + GetController()->DismissView(); +} + void ChromeAppListItem::ContextMenuItemSelected(int command_id, int event_flags) { if (GetAppContextMenu())
diff --git a/chrome/browser/ui/app_list/chrome_app_list_item.h b/chrome/browser/ui/app_list/chrome_app_list_item.h index 176f657..4118bdd 100644 --- a/chrome/browser/ui/app_list/chrome_app_list_item.h +++ b/chrome/browser/ui/app_list/chrome_app_list_item.h
@@ -137,6 +137,8 @@ // different kinds of items. virtual app_list::AppContextMenu* GetAppContextMenu(); + void MaybeDismissAppList(); + private: ash::mojom::AppListItemMetadataPtr metadata_; Profile* profile_;
diff --git a/chrome/browser/ui/app_list/internal_app/internal_app_metadata.cc b/chrome/browser/ui/app_list/internal_app/internal_app_metadata.cc index d680563c..d5b758f 100644 --- a/chrome/browser/ui/app_list/internal_app/internal_app_metadata.cc +++ b/chrome/browser/ui/app_list/internal_app/internal_app_metadata.cc
@@ -115,6 +115,7 @@ if (arc_enabled && (!extension || media_consolidated)) { // Open ARC++ camera app. arc::LaunchApp(profile, kAndroidCameraAppId, event_flags, + arc::UserInteractionType::APP_STARTED_FROM_LAUNCHER, controller->GetAppListDisplayId()); } else if (extension) { // Open Chrome camera app.
diff --git a/chrome/browser/ui/app_list/search/arc_app_result.cc b/chrome/browser/ui/app_list/search/arc_app_result.cc index 60486c6..bb9d004b 100644 --- a/chrome/browser/ui/app_list/search/arc_app_result.cc +++ b/chrome/browser/ui/app_list/search/arc_app_result.cc
@@ -11,7 +11,6 @@ #include "chrome/browser/ui/app_list/app_list_controller_delegate.h" #include "chrome/browser/ui/app_list/arc/arc_app_context_menu.h" #include "chrome/browser/ui/app_list/arc/arc_app_icon_loader.h" -#include "chrome/browser/ui/app_list/arc/arc_app_utils.h" #include "chrome/browser/ui/app_list/search/search_util.h" namespace { @@ -43,18 +42,11 @@ } void ArcAppResult::ExecuteLaunchCommand(int event_flags) { - Open(event_flags); + Launch(event_flags, GetContextMenuAppLaunchInteraction()); } void ArcAppResult::Open(int event_flags) { - // Record the search metric if the result is not a suggested app. - if (display_type() != ash::SearchResultDisplayType::kRecommendation) - RecordHistogram(APP_SEARCH_RESULT); - - if (!arc::LaunchApp(profile(), app_id(), event_flags, - controller()->GetAppListDisplayId())) { - return; - } + Launch(event_flags, GetAppLaunchInteraction()); } void ArcAppResult::GetContextMenuModel(GetMenuModelCallback callback) { @@ -67,4 +59,28 @@ return context_menu_.get(); } +void ArcAppResult::Launch(int event_flags, + arc::UserInteractionType interaction) { + // Record the search metric if the result is not a suggested app. + if (display_type() != ash::SearchResultDisplayType::kRecommendation) + RecordHistogram(APP_SEARCH_RESULT); + + arc::LaunchApp(profile(), app_id(), event_flags, interaction, + controller()->GetAppListDisplayId()); +} + +arc::UserInteractionType ArcAppResult::GetAppLaunchInteraction() { + return display_type() == ash::SearchResultDisplayType::kRecommendation + ? arc::UserInteractionType::APP_STARTED_FROM_LAUNCHER_SUGGESTED_APP + : arc::UserInteractionType::APP_STARTED_FROM_LAUNCHER_SEARCH; +} + +arc::UserInteractionType ArcAppResult::GetContextMenuAppLaunchInteraction() { + return display_type() == ash::SearchResultDisplayType::kRecommendation + ? arc::UserInteractionType:: + APP_STARTED_FROM_LAUNCHER_SUGGESTED_APP_CONTEXT_MENU + : arc::UserInteractionType:: + APP_STARTED_FROM_LAUNCHER_SEARCH_CONTEXT_MENU; +} + } // namespace app_list
diff --git a/chrome/browser/ui/app_list/search/arc_app_result.h b/chrome/browser/ui/app_list/search/arc_app_result.h index bbef4dd..295b11fa 100644 --- a/chrome/browser/ui/app_list/search/arc_app_result.h +++ b/chrome/browser/ui/app_list/search/arc_app_result.h
@@ -10,6 +10,7 @@ #include "base/macros.h" #include "chrome/browser/ui/app_icon_loader_delegate.h" +#include "chrome/browser/ui/app_list/arc/arc_app_utils.h" #include "chrome/browser/ui/app_list/search/app_result.h" class AppListControllerDelegate; @@ -43,6 +44,10 @@ // ChromeSearchResult overrides: AppContextMenu* GetAppContextMenu() override; + void Launch(int event_flags, arc::UserInteractionType interaction); + arc::UserInteractionType GetAppLaunchInteraction(); + arc::UserInteractionType GetContextMenuAppLaunchInteraction(); + std::unique_ptr<ArcAppIconLoader> icon_loader_; std::unique_ptr<ArcAppContextMenu> context_menu_;
diff --git a/chrome/browser/ui/ash/launcher/arc_app_launcher_browsertest.cc b/chrome/browser/ui/ash/launcher/arc_app_launcher_browsertest.cc index 902926e9..eacf3d2 100644 --- a/chrome/browser/ui/ash/launcher/arc_app_launcher_browsertest.cc +++ b/chrome/browser/ui/ash/launcher/arc_app_launcher_browsertest.cc
@@ -406,7 +406,8 @@ SelectShelfItem(shelf_id, ui::ET_MOUSE_PRESSED, display::kInvalidDisplayId)); } else { - arc::LaunchApp(profile(), app_id, ui::EF_LEFT_MOUSE_BUTTON); + arc::LaunchApp(profile(), app_id, ui::EF_LEFT_MOUSE_BUTTON, + arc::UserInteractionType::NOT_USER_INITIATED); } const ash::ShelfItem* item = controller->GetItem(shelf_id); @@ -494,7 +495,8 @@ AppListClientImpl* client = AppListClientImpl::GetInstance(); AppListControllerDelegate* delegate = client; EXPECT_FALSE(delegate->IsAppOpen(app_id)); - arc::LaunchApp(profile(), app_id, ui::EF_LEFT_MOUSE_BUTTON); + arc::LaunchApp(profile(), app_id, ui::EF_LEFT_MOUSE_BUTTON, + arc::UserInteractionType::NOT_USER_INITIATED); EXPECT_FALSE(delegate->IsAppOpen(app_id)); // Simulate task creation so the app is marked as running/open. std::unique_ptr<ArcAppListPrefs::AppInfo> info = app_prefs()->GetApp(app_id);
diff --git a/chrome/browser/ui/ash/launcher/arc_playstore_shortcut_launcher_item_controller.cc b/chrome/browser/ui/ash/launcher/arc_playstore_shortcut_launcher_item_controller.cc index c150c29..f1a8a440 100644 --- a/chrome/browser/ui/ash/launcher/arc_playstore_shortcut_launcher_item_controller.cc +++ b/chrome/browser/ui/ash/launcher/arc_playstore_shortcut_launcher_item_controller.cc
@@ -31,7 +31,8 @@ ChromeLauncherController::instance()->profile(), arc::kPlayStoreAppId, base::Optional<std::string>() /* launch_intent */, - true /* deferred_launch_allowed */, display_id); + true /* deferred_launch_allowed */, display_id, + arc::UserInteractionType::APP_STARTED_FROM_SHELF); // ArcAppLauncher may launch Play Store in case it exists already. In this // case this instance of ArcPlaystoreShortcutLauncherItemController may be // deleted. If Play Store does not exist at this moment, then let
diff --git a/chrome/browser/ui/ash/launcher/arc_shelf_spinner_item_controller.cc b/chrome/browser/ui/ash/launcher/arc_shelf_spinner_item_controller.cc index 2fa9417..cfbe4f13 100644 --- a/chrome/browser/ui/ash/launcher/arc_shelf_spinner_item_controller.cc +++ b/chrome/browser/ui/ash/launcher/arc_shelf_spinner_item_controller.cc
@@ -46,7 +46,8 @@ return; // Close() destroys this object, so start launching the app first. - arc::LaunchApp(observed_profile_, changed_app_id, event_flags_, display_id_); + arc::LaunchApp(observed_profile_, changed_app_id, event_flags_, + arc::UserInteractionType::APP_STARTED_FROM_SHELF, display_id_); Close(); }
diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc index e39eb91..144d608 100644 --- a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc +++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc
@@ -1903,11 +1903,16 @@ EXPECT_FALSE(launcher_controller_->GetItem(shelf_id_app_3)); EXPECT_FALSE(launcher_controller_->GetItem(shelf_id_shortcut)); - arc::LaunchApp(profile(), arc_app_id1, ui::EF_LEFT_MOUSE_BUTTON); - arc::LaunchApp(profile(), arc_app_id1, ui::EF_LEFT_MOUSE_BUTTON); - arc::LaunchApp(profile(), arc_app_id2, ui::EF_LEFT_MOUSE_BUTTON); - arc::LaunchApp(profile(), arc_app_id3, ui::EF_LEFT_MOUSE_BUTTON); - arc::LaunchApp(profile(), arc_shortcut_id, ui::EF_LEFT_MOUSE_BUTTON); + arc::LaunchApp(profile(), arc_app_id1, ui::EF_LEFT_MOUSE_BUTTON, + arc::UserInteractionType::NOT_USER_INITIATED); + arc::LaunchApp(profile(), arc_app_id1, ui::EF_LEFT_MOUSE_BUTTON, + arc::UserInteractionType::NOT_USER_INITIATED); + arc::LaunchApp(profile(), arc_app_id2, ui::EF_LEFT_MOUSE_BUTTON, + arc::UserInteractionType::NOT_USER_INITIATED); + arc::LaunchApp(profile(), arc_app_id3, ui::EF_LEFT_MOUSE_BUTTON, + arc::UserInteractionType::NOT_USER_INITIATED); + arc::LaunchApp(profile(), arc_shortcut_id, ui::EF_LEFT_MOUSE_BUTTON, + arc::UserInteractionType::NOT_USER_INITIATED); EXPECT_TRUE(launcher_controller_->GetItem(shelf_id_app_1)); EXPECT_TRUE(launcher_controller_->GetItem(shelf_id_app_2)); @@ -1979,7 +1984,8 @@ launcher_controller_->SetItemStatus(shelf_id, ash::STATUS_RUNNING); // This launch request should be ignored in case of active app. - arc::LaunchApp(profile(), app_id, ui::EF_LEFT_MOUSE_BUTTON); + arc::LaunchApp(profile(), app_id, ui::EF_LEFT_MOUSE_BUTTON, + arc::UserInteractionType::NOT_USER_INITIATED); EXPECT_FALSE( launcher_controller_->GetShelfSpinnerController()->HasApp(app_id)); @@ -1991,7 +1997,8 @@ EXPECT_EQ(ash::TYPE_PINNED_APP, item->type); // Now launch request should not be ignored. - arc::LaunchApp(profile(), app_id, ui::EF_LEFT_MOUSE_BUTTON); + arc::LaunchApp(profile(), app_id, ui::EF_LEFT_MOUSE_BUTTON, + arc::UserInteractionType::NOT_USER_INITIATED); EXPECT_TRUE( launcher_controller_->GetShelfSpinnerController()->HasApp(app_id)); } @@ -3964,7 +3971,8 @@ const std::string app_id = ArcAppTest::GetAppId(arc_test_.fake_default_apps()[0]); EXPECT_FALSE(launcher_controller_->GetItem(ash::ShelfID(app_id))); - EXPECT_TRUE(arc::LaunchApp(profile(), app_id, ui::EF_LEFT_MOUSE_BUTTON)); + EXPECT_TRUE(arc::LaunchApp(profile(), app_id, ui::EF_LEFT_MOUSE_BUTTON, + arc::UserInteractionType::NOT_USER_INITIATED)); EXPECT_TRUE(arc::IsArcPlayStoreEnabledForProfile(profile())); EXPECT_TRUE(launcher_controller_->GetItem(ash::ShelfID(app_id))); @@ -3972,7 +3980,8 @@ EnablePlayStore(false); EXPECT_FALSE(launcher_controller_->GetItem(ash::ShelfID(app_id))); - EXPECT_TRUE(arc::LaunchApp(profile(), app_id, ui::EF_LEFT_MOUSE_BUTTON)); + EXPECT_TRUE(arc::LaunchApp(profile(), app_id, ui::EF_LEFT_MOUSE_BUTTON, + arc::UserInteractionType::NOT_USER_INITIATED)); EXPECT_TRUE(arc::IsArcPlayStoreEnabledForProfile(profile())); EXPECT_TRUE(launcher_controller_->GetItem(ash::ShelfID(app_id)));
diff --git a/chrome/browser/ui/ash/launcher/launcher_context_menu_unittest.cc b/chrome/browser/ui/ash/launcher/launcher_context_menu_unittest.cc index 20a6881..152d3a4 100644 --- a/chrome/browser/ui/ash/launcher/launcher_context_menu_unittest.cc +++ b/chrome/browser/ui/ash/launcher/launcher_context_menu_unittest.cc
@@ -362,8 +362,10 @@ EXPECT_TRUE(controller()->GetItem(shelf_id1)); EXPECT_FALSE(controller()->GetItem(shelf_id2)); - arc::LaunchApp(profile(), app_id1, ui::EF_LEFT_MOUSE_BUTTON); - arc::LaunchApp(profile(), app_id2, ui::EF_LEFT_MOUSE_BUTTON); + arc::LaunchApp(profile(), app_id1, ui::EF_LEFT_MOUSE_BUTTON, + arc::UserInteractionType::NOT_USER_INITIATED); + arc::LaunchApp(profile(), app_id2, ui::EF_LEFT_MOUSE_BUTTON, + arc::UserInteractionType::NOT_USER_INITIATED); EXPECT_TRUE(controller()->GetItem(shelf_id1)); EXPECT_TRUE(controller()->GetItem(shelf_id2));
diff --git a/chrome/browser/ui/ash/launcher/launcher_controller_helper.cc b/chrome/browser/ui/ash/launcher/launcher_controller_helper.cc index 59327e4..b627a97c 100644 --- a/chrome/browser/ui/ash/launcher/launcher_controller_helper.cc +++ b/chrome/browser/ui/ash/launcher/launcher_controller_helper.cc
@@ -203,7 +203,9 @@ const std::string& app_id = id.app_id; const ArcAppListPrefs* arc_prefs = GetArcAppListPrefs(); if (arc_prefs && arc_prefs->IsRegistered(app_id)) { - arc::LaunchApp(profile_, app_id, event_flags, display_id); + arc::LaunchApp(profile_, app_id, event_flags, + arc::UserInteractionType::APP_STARTED_FROM_SHELF, + display_id); return; }
diff --git a/chrome/browser/ui/ash/system_tray_client.cc b/chrome/browser/ui/ash/system_tray_client.cc index 4ac868e..a89af16 100644 --- a/chrome/browser/ui/ash/system_tray_client.cc +++ b/chrome/browser/ui/ash/system_tray_client.cc
@@ -374,7 +374,8 @@ if (!profile) return; - arc::LaunchApp(profile, app_id, ui::EF_NONE); + arc::LaunchApp(profile, app_id, ui::EF_NONE, + arc::UserInteractionType::APP_STARTED_FROM_SETTINGS); } void SystemTrayClient::ShowNetworkSettings(const std::string& network_id) {
diff --git a/chrome/browser/ui/cocoa/tab_contents/sad_tab_mac.mm b/chrome/browser/ui/cocoa/tab_contents/sad_tab_mac.mm index 3e739e6..f5254b8 100644 --- a/chrome/browser/ui/cocoa/tab_contents/sad_tab_mac.mm +++ b/chrome/browser/ui/cocoa/tab_contents/sad_tab_mac.mm
@@ -15,7 +15,7 @@ : SadTab(web_contents, kind) { NSView* web_contents_view = web_contents->GetNativeView(); sad_tab_view_ = - [[SabTabViewCocoa alloc] initWithFrame:web_contents_view.bounds + [[SadTabViewCocoa alloc] initWithFrame:web_contents_view.bounds sadTab:this]; [web_contents_view addSubview:sad_tab_view_]; [sad_tab_view_ release]; @@ -25,7 +25,7 @@ private: // Owned by web_contents - SabTabViewCocoa* sad_tab_view_; + SadTabViewCocoa* sad_tab_view_; }; } // namespace
diff --git a/chrome/browser/ui/cocoa/tab_contents/sad_tab_mac_unittest.mm b/chrome/browser/ui/cocoa/tab_contents/sad_tab_mac_unittest.mm index 43aae24..cb90ac4 100644 --- a/chrome/browser/ui/cocoa/tab_contents/sad_tab_mac_unittest.mm +++ b/chrome/browser/ui/cocoa/tab_contents/sad_tab_mac_unittest.mm
@@ -26,12 +26,12 @@ class SadTabViewTest : public CocoaTest { public: SadTabViewTest() { - base::scoped_nsobject<SabTabViewCocoa> view([SabTabViewCocoa new]); + base::scoped_nsobject<SadTabViewCocoa> view([SadTabViewCocoa new]); view_ = view; [[test_window() contentView] addSubview:view_]; } - SabTabViewCocoa* view_; // Weak. Owned by the view hierarchy. + SadTabViewCocoa* view_; // Weak. Owned by the view hierarchy. }; TEST_VIEW(SadTabViewTest, view_); @@ -48,8 +48,8 @@ MockSadTab sadTab; - base::scoped_nsobject<SabTabViewCocoa> view( - [[SabTabViewCocoa alloc] initWithFrame:NSZeroRect sadTab:&sadTab]); + base::scoped_nsobject<SadTabViewCocoa> view( + [[SadTabViewCocoa alloc] initWithFrame:NSZeroRect sadTab:&sadTab]); EXPECT_CALL(sadTab, RecordFirstPaint()); EXPECT_CALL(sadTab, PerformAction(testing::TypedEq<Action>(Action::BUTTON)));
diff --git a/chrome/browser/ui/cocoa/tab_contents/sad_tab_view_cocoa.h b/chrome/browser/ui/cocoa/tab_contents/sad_tab_view_cocoa.h index dd33af12..f71f83e 100644 --- a/chrome/browser/ui/cocoa/tab_contents/sad_tab_view_cocoa.h +++ b/chrome/browser/ui/cocoa/tab_contents/sad_tab_view_cocoa.h
@@ -10,7 +10,7 @@ #include "chrome/browser/ui/sad_tab.h" // A view that displays the "sad tab" (aka crash page). -@interface SabTabViewCocoa : NSView +@interface SadTabViewCocoa : NSView - (instancetype)initWithFrame:(NSRect)frame sadTab:(SadTab*)sadTab;
diff --git a/chrome/browser/ui/cocoa/tab_contents/sad_tab_view_cocoa.mm b/chrome/browser/ui/cocoa/tab_contents/sad_tab_view_cocoa.mm index 5fda44f..d55639c8 100644 --- a/chrome/browser/ui/cocoa/tab_contents/sad_tab_view_cocoa.mm +++ b/chrome/browser/ui/cocoa/tab_contents/sad_tab_view_cocoa.mm
@@ -43,10 +43,10 @@ } @end -@interface SabTabViewCocoa ()<NSTextViewDelegate> +@interface SadTabViewCocoa ()<NSTextViewDelegate> @end -@implementation SabTabViewCocoa { +@implementation SadTabViewCocoa { NSView* container_; NSTextView* message_; HyperlinkTextView* help_;
diff --git a/chrome/browser/ui/views/autofill/OWNERS b/chrome/browser/ui/views/autofill/OWNERS index f930a56..1e8a600 100644 --- a/chrome/browser/ui/views/autofill/OWNERS +++ b/chrome/browser/ui/views/autofill/OWNERS
@@ -1,3 +1,4 @@ estade@chromium.org +vasilii@chromium.org # COMPONENT: UI>Browser>Autofill
diff --git a/chrome/browser/ui/views/autofill/autofill_popup_base_view.cc b/chrome/browser/ui/views/autofill/autofill_popup_base_view.cc index 1ac29cbe..4c90a98 100644 --- a/chrome/browser/ui/views/autofill/autofill_popup_base_view.cc +++ b/chrome/browser/ui/views/autofill/autofill_popup_base_view.cc
@@ -40,7 +40,7 @@ if (delegate_) { delegate_->ViewDestroyed(); - RemoveObserver(); + RemoveWidgetObservers(); } } @@ -65,6 +65,7 @@ widget->Init(params); widget->SetContentsView(CreateWrapperView().release()); + widget->AddObserver(this); // No animation for popup appearance (too distracting). widget->SetVisibilityAnimationTransition(views::Widget::ANIMATE_HIDE); @@ -93,7 +94,7 @@ // The controller is no longer valid after it hides us. delegate_ = NULL; - RemoveObserver(); + RemoveWidgetObservers(); if (GetWidget()) { // Don't call CloseNow() because some of the functions higher up the stack @@ -108,17 +109,22 @@ void AutofillPopupBaseView::OnWidgetBoundsChanged(views::Widget* widget, const gfx::Rect& new_bounds) { - DCHECK_EQ(widget, parent_widget_); + DCHECK(widget == parent_widget_ || widget == GetWidget()); + if (widget != parent_widget_) + return; + HideController(); } void AutofillPopupBaseView::OnWidgetDestroying(views::Widget* widget) { - DCHECK_EQ(widget, parent_widget_); + // On Windows, widgets can be destroyed in any order. Regardless of which + // widget is destroyed first, remove all observers and hide the popup. + DCHECK(widget == parent_widget_ || widget == GetWidget()); // Normally this happens at destruct-time or hide-time, but because it depends // on |parent_widget_| (which is about to go away), it needs to happen sooner // in this case. - RemoveObserver(); + RemoveWidgetObservers(); // Because the parent widget is about to be destroyed, we null out the weak // reference to it and protect against possibly accessing it during @@ -128,9 +134,10 @@ HideController(); } -void AutofillPopupBaseView::RemoveObserver() { +void AutofillPopupBaseView::RemoveWidgetObservers() { if (parent_widget_) parent_widget_->RemoveObserver(this); + GetWidget()->RemoveObserver(this); views::WidgetFocusManager::GetInstance()->RemoveFocusChangeListener(this); }
diff --git a/chrome/browser/ui/views/autofill/autofill_popup_base_view.h b/chrome/browser/ui/views/autofill/autofill_popup_base_view.h index 068f5da..23ca49b1 100644 --- a/chrome/browser/ui/views/autofill/autofill_popup_base_view.h +++ b/chrome/browser/ui/views/autofill/autofill_popup_base_view.h
@@ -89,7 +89,7 @@ void OnWidgetDestroying(views::Widget* widget) override; // Stop observing the widget. - void RemoveObserver(); + void RemoveWidgetObservers(); void SetSelection(const gfx::Point& point); void AcceptSelection(const gfx::Point& point);
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_bar_view.cc b/chrome/browser/ui/views/bookmarks/bookmark_bar_view.cc index d3b51e3..58383c5 100644 --- a/chrome/browser/ui/views/bookmarks/bookmark_bar_view.cc +++ b/chrome/browser/ui/views/bookmarks/bookmark_bar_view.cc
@@ -178,6 +178,20 @@ views::EMPHASIS_MAXIMUM, host_view->size()); } +// Create a SkPath matching the bookmark inkdrops to be used for the focus ring. +// TODO(pbos): Consolidate inkdrop effects, highlights and ripples along with +// focus rings so that they are derived from the same actual SkPath or other +// shared primitive. +SkPath CreateBookmarkFocusRingPath(views::InkDropHostView* host_view) { + gfx::Rect rect(host_view->size()); + rect.Inset(GetInkDropInsets()); + + SkPath path; + const int radius = GetInkDropCornerRadius(host_view); + path.addRoundRect(gfx::RectToSkRect(rect), radius, radius); + return path; +} + std::unique_ptr<views::InkDrop> CreateBookmarkButtonInkDrop( std::unique_ptr<views::InkDropImpl> ink_drop) { ink_drop->SetShowHighlightOnFocus(!views::PlatformStyle::kPreferFocusRings); @@ -257,6 +271,12 @@ } // LabelButton: + void OnBoundsChanged(const gfx::Rect& previous_bounds) override { + if (focus_ring()) + focus_ring()->SetPath(CreateBookmarkFocusRingPath(this)); + LabelButton::OnBoundsChanged(previous_bounds); + } + std::unique_ptr<views::InkDrop> CreateInkDrop() override { return CreateBookmarkButtonInkDrop(CreateDefaultFloodFillInkDropImpl()); } @@ -380,6 +400,12 @@ } // MenuButton: + void OnBoundsChanged(const gfx::Rect& previous_bounds) override { + if (focus_ring()) + focus_ring()->SetPath(CreateBookmarkFocusRingPath(this)); + MenuButton::OnBoundsChanged(previous_bounds); + } + std::unique_ptr<views::InkDrop> CreateInkDrop() override { return CreateBookmarkButtonInkDrop(CreateDefaultFloodFillInkDropImpl()); }
diff --git a/chrome/browser/ui/views/location_bar/icon_label_bubble_view.cc b/chrome/browser/ui/views/location_bar/icon_label_bubble_view.cc index 0936b19..1630c95 100644 --- a/chrome/browser/ui/views/location_bar/icon_label_bubble_view.cc +++ b/chrome/browser/ui/views/location_bar/icon_label_bubble_view.cc
@@ -241,10 +241,9 @@ if (focus_ring() && !ink_drop_bounds.IsEmpty()) { focus_ring()->Layout(); int radius = ink_drop_bounds.height() / 2; - SkRRect rect = - SkRRect::MakeRectXY(gfx::RectToSkRect(ink_drop_bounds), radius, radius); SkPath path; - path.addRRect(rect); + path.addRoundRect(gfx::RectToSkRect(GetMirroredRect(ink_drop_bounds)), + radius, radius); focus_ring()->SetPath(path); } }
diff --git a/chrome/browser/ui/views/page_action/page_action_icon_view.cc b/chrome/browser/ui/views/page_action/page_action_icon_view.cc index 4761fde9..25203cb 100644 --- a/chrome/browser/ui/views/page_action/page_action_icon_view.cc +++ b/chrome/browser/ui/views/page_action/page_action_icon_view.cc
@@ -119,7 +119,8 @@ focus_ring_->Layout(); if (LocationBarView::IsRounded()) { SkPath path; - path.addOval(gfx::RectToSkRect(GetLocalBounds())); + const float radius = height() / 2.f; + path.addRoundRect(gfx::RectToSkRect(GetLocalBounds()), radius, radius); focus_ring_->SetPath(path); } }
diff --git a/chrome/browser/ui/views/tabs/new_tab_button.cc b/chrome/browser/ui/views/tabs/new_tab_button.cc index a492387..e98cc806 100644 --- a/chrome/browser/ui/views/tabs/new_tab_button.cc +++ b/chrome/browser/ui/views/tabs/new_tab_button.cc
@@ -161,10 +161,8 @@ } void NewTabButton::FrameColorsChanged() { - if (MD::IsRefreshUi()) { - InitButtonIcons(); + if (MD::IsRefreshUi()) UpdateInkDropBaseColor(); - } } void NewTabButton::AnimateInkDropToStateForTesting(views::InkDropState state) { @@ -276,17 +274,18 @@ } if (MD::IsNewerMaterialUi()) { + const int plus_icon_size = MD::IsTouchOptimizedUiEnabled() ? 14 : 12; + const int plus_icon_offset = GetCornerRadius() - (plus_icon_size / 2); + + PaintPlusIcon(canvas, plus_icon_offset, plus_icon_size); + cc::PaintFlags paint_flags; paint_flags.setAntiAlias(true); - - const int plus_icon_offset = GetCornerRadius() - (plus_icon_.width() / 2); - canvas->DrawImageInt(plus_icon_, plus_icon_offset, plus_icon_offset, - paint_flags); if (ShouldDrawIncognitoIcon()) { DCHECK(!incognito_icon_.isNull()); canvas->DrawImageInt( incognito_icon_, - plus_icon_offset + plus_icon_.width() + kDistanceBetweenIcons, + plus_icon_offset + plus_icon_size + kDistanceBetweenIcons, plus_icon_offset, paint_flags); } @@ -333,10 +332,8 @@ ImageButton::Layout(); if (MD::IsNewerMaterialUi()) { - // If icons are not initialized, initialize them now. Icons are always - // initialized together so it's enough to check the |plus_icon_|. - if (plus_icon_.isNull()) - InitButtonIcons(); + if (ShouldDrawIncognitoIcon() && incognito_icon_.isNull()) + InitIncognitoIcon(); // TODO(pkasting): Instead of setting this bounds rect, maybe have the // container match the view bounds, then undo the coordinate transforms in @@ -356,7 +353,8 @@ if (!MD::IsNewerMaterialUi()) return; - InitButtonIcons(); + if (ShouldDrawIncognitoIcon()) + InitIncognitoIcon(); UpdateInkDropBaseColor(); } @@ -513,6 +511,30 @@ } } +void NewTabButton::PaintPlusIcon(gfx::Canvas* canvas, int offset, int size) { + constexpr int kStrokeWidth = 2; + constexpr int kCapRadius = kStrokeWidth / 2; + + cc::PaintFlags paint_flags; + paint_flags.setAntiAlias(true); + paint_flags.setColor(GetIconColor()); + paint_flags.setStrokeCap(cc::PaintFlags::kRound_Cap); + paint_flags.setStrokeWidth(kStrokeWidth); + + // With a round end-cap, the apparent line length will extend past the end + // points by one radius of the cap. Reduce the specified length to take this + // into account. + const int start = offset + kCapRadius; + const int end = offset + size - kCapRadius; + const int center = offset + size / 2; + // Draw the horizontal leg of the plus (+) icon + canvas->DrawLine(gfx::PointF(start, center), gfx::PointF(end, center), + paint_flags); + // Draw the vertical leg of the plus (+) icon + canvas->DrawLine(gfx::PointF(center, start), gfx::PointF(center, end), + paint_flags); +} + SkColor NewTabButton::GetButtonFillColor() const { if (new_tab_promo_observer_.IsObservingSources()) { return GetNativeTheme()->GetSystemColor( @@ -522,19 +544,16 @@ return tab_strip_->GetTabBackgroundColor(TAB_INACTIVE); } -void NewTabButton::InitButtonIcons() { - DCHECK(MD::IsNewerMaterialUi()); - const SkColor icon_color = - color_utils::IsDark(tab_strip_->GetTabForegroundColor(TAB_INACTIVE)) - ? gfx::kChromeIconGrey - : SK_ColorWHITE; - // TODO(b/851041): is there a better source for these icon sizes? - const int size = MD::IsTouchOptimizedUiEnabled() ? 14 : 12; - plus_icon_ = gfx::CreateVectorIcon(kNewTabButtonPlusIcon, size, icon_color); - if (ShouldDrawIncognitoIcon()) { - incognito_icon_ = - gfx::CreateVectorIcon(kNewTabButtonIncognitoIcon, icon_color); - } +SkColor NewTabButton::GetIconColor() const { + return color_utils::IsDark(tab_strip_->GetTabForegroundColor(TAB_INACTIVE)) + ? gfx::kChromeIconGrey + : SK_ColorWHITE; +} + +void NewTabButton::InitIncognitoIcon() { + DCHECK(ShouldDrawIncognitoIcon()); + incognito_icon_ = + gfx::CreateVectorIcon(kNewTabButtonIncognitoIcon, GetIconColor()); } SkPath NewTabButton::GetTouchOptimizedButtonPath(float button_y,
diff --git a/chrome/browser/ui/views/tabs/new_tab_button.h b/chrome/browser/ui/views/tabs/new_tab_button.h index a2d01dfa..c35c84a 100644 --- a/chrome/browser/ui/views/tabs/new_tab_button.h +++ b/chrome/browser/ui/views/tabs/new_tab_button.h
@@ -107,10 +107,14 @@ const SkPath& fill, gfx::Canvas* canvas) const; - SkColor GetButtonFillColor() const; + // Paints a properly sized plus (+) icon into the center of the button. + void PaintPlusIcon(gfx::Canvas* canvas, int offset, int size); - // In the touch-optimized UI, initializes the needed button icons. - void InitButtonIcons(); + SkColor GetButtonFillColor() const; + SkColor GetIconColor() const; + + // In the touch-optimized UI, initializes the incognito button icon. + void InitIncognitoIcon(); // Returns the path for the touch-optimized new tab button for the given // |scale|. |button_y| is the button's top y-cordinate. If |for_fill| is true, @@ -148,7 +152,6 @@ // In the touch-optimized UI, the new tab button has a plus icon, and an // incognito icon if is in incognito mode. - gfx::ImageSkia plus_icon_; gfx::ImageSkia incognito_icon_; // In touch-optimized UI, this view holds the ink drop layer so that it's
diff --git a/chrome/browser/ui/webui/settings/chromeos/internet_handler.cc b/chrome/browser/ui/webui/settings/chromeos/internet_handler.cc index 685e59e5..8dc3a46 100644 --- a/chrome/browser/ui/webui/settings/chromeos/internet_handler.cc +++ b/chrome/browser/ui/webui/settings/chromeos/internet_handler.cc
@@ -152,7 +152,8 @@ // Request to launch Arc VPN provider. const auto* arc_app_list_prefs = ArcAppListPrefs::Get(profile_); if (arc_app_list_prefs && arc_app_list_prefs->GetApp(app_id)) { - arc::LaunchApp(profile_, app_id, ui::EF_NONE); + arc::LaunchApp(profile_, app_id, ui::EF_NONE, + arc::UserInteractionType::APP_STARTED_FROM_SETTINGS); return; }
diff --git a/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc b/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc index 67d6626..81ff758 100644 --- a/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc +++ b/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
@@ -1683,6 +1683,7 @@ {"submitPassphraseButton", IDS_SETTINGS_SUBMIT_PASSPHRASE}, {"personalizeGoogleServicesTitle", IDS_SETTINGS_PERSONALIZE_GOOGLE_SERVICES_TITLE}, + {"existingPassphraseTitle", IDS_SETTINGS_EXISTING_PASSPHRASE_TITLE}, }; AddLocalizedStringsBulk(html_source, localized_strings, arraysize(localized_strings));
diff --git a/chrome/installer/setup/user_experiment.cc b/chrome/installer/setup/user_experiment.cc index 6e33a91..4e8a103d 100644 --- a/chrome/installer/setup/user_experiment.cc +++ b/chrome/installer/setup/user_experiment.cc
@@ -224,7 +224,7 @@ if (base::win::GetVersion() < base::win::VERSION_WIN10) return false; - // Enterprise brand codes and domain joined machines are excluded. + // Installs originating from the MSI and domain joined machines are excluded. if (IsEnterpriseInstall(installer_state)) return false;
diff --git a/chrome/installer/setup/user_experiment.h b/chrome/installer/setup/user_experiment.h index 3a72d78..baa2c37 100644 --- a/chrome/installer/setup/user_experiment.h +++ b/chrome/installer/setup/user_experiment.h
@@ -47,9 +47,6 @@ void WriteInitialState(ExperimentStorage* storage, ExperimentMetrics::State state); -// Returns true if the install is associated with an enterprise brand code. -bool IsEnterpriseBrand(); - // Returns true if the machine is joined to a Windows domain. bool IsDomainJoined();
diff --git a/chrome/renderer/autofill/password_autofill_agent_browsertest.cc b/chrome/renderer/autofill/password_autofill_agent_browsertest.cc index ba7798a..39c631c 100644 --- a/chrome/renderer/autofill/password_autofill_agent_browsertest.cc +++ b/chrome/renderer/autofill/password_autofill_agent_browsertest.cc
@@ -3641,4 +3641,31 @@ CheckTextFieldsDOMState(kAliceUsername, true, kAlicePassword, true); } +TEST_F(PasswordAutofillAgentTest, MayUsePlaceholderNoPlaceholder) { + fill_data_.username_may_use_prefilled_placeholder = true; + UpdateRendererIDs(); + SimulateOnFillPasswordForm(fill_data_); + + CheckTextFieldsSuggestedState(kAliceUsername, true, kAlicePassword, true); +} + +TEST_F(PasswordAutofillAgentTest, MayUsePlaceholderAndPlaceholderOnForm) { + username_element_.SetValue(WebString::FromUTF8("placeholder")); + UpdateRendererIDs(); + fill_data_.username_may_use_prefilled_placeholder = true; + SimulateOnFillPasswordForm(fill_data_); + + CheckTextFieldsSuggestedState(kAliceUsername, true, kAlicePassword, true); +} + +TEST_F(PasswordAutofillAgentTest, NoMayUsePlaceholderAndPlaceholderOnForm) { + username_element_.SetValue(WebString::FromUTF8("placeholder")); + UpdateRendererIDs(); + fill_data_.username_may_use_prefilled_placeholder = false; + + SimulateOnFillPasswordForm(fill_data_); + + CheckTextFieldsDOMState("placeholder", false, "", false); +} + } // namespace autofill
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn index 8fb0b5c..89536728 100644 --- a/chrome/test/BUILD.gn +++ b/chrome/test/BUILD.gn
@@ -2537,6 +2537,7 @@ "../browser/permissions/permission_request_manager_unittest.cc", "../browser/permissions/permission_util_unittest.cc", "../browser/plugins/pdf_iframe_navigation_throttle_unittest.cc", + "../browser/policy/browser_dm_token_storage_linux_unittest.cc", "../browser/policy/browser_dm_token_storage_unittest.cc", "../browser/policy/browser_dm_token_storage_win_unittest.cc", "../browser/policy/cloud/cloud_policy_invalidator_unittest.cc",
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/ChromeJUnit4ClassRunner.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/ChromeJUnit4ClassRunner.java index afe05366..55744e4 100644 --- a/chrome/test/android/javatests/src/org/chromium/chrome/test/ChromeJUnit4ClassRunner.java +++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/ChromeJUnit4ClassRunner.java
@@ -20,8 +20,7 @@ import org.chromium.base.test.util.RestrictionSkipCheck; import org.chromium.base.test.util.SkipCheck; import org.chromium.chrome.browser.ChromeVersionInfo; -import org.chromium.chrome.browser.vr.VrDaydreamApi; -import org.chromium.chrome.browser.vr.VrShellDelegate; +import org.chromium.chrome.browser.vr.VrModuleProvider; import org.chromium.chrome.test.util.ChromeRestriction; import org.chromium.chrome.test.util.browser.ChromeModernDesign; import org.chromium.chrome.test.util.browser.Features; @@ -68,19 +67,19 @@ } private boolean isDaydreamReady() { - return VrShellDelegate.isDaydreamReadyDevice(); + return VrModuleProvider.getDelegate().isDaydreamReadyDevice(); } private boolean isDaydreamViewPaired() { - final VrDaydreamApi daydreamApi = VrShellDelegate.getVrDaydreamApi(); - if (daydreamApi == null) { + if (!isDaydreamReady()) { return false; } // isDaydreamCurrentViewer() creates a concrete instance of DaydreamApi, // which can only be done on the main thread try { - return ThreadUtils.runOnUiThreadBlocking(daydreamApi::isDaydreamCurrentViewer); + return ThreadUtils.runOnUiThreadBlocking( + VrModuleProvider.getDelegate()::isDaydreamCurrentViewer); } catch (CancellationException | ExecutionException | IllegalArgumentException e) { return false; }
diff --git a/chrome/test/data/policy/policy_test_cases.json b/chrome/test/data/policy/policy_test_cases.json index dea5507..ad07bf9 100644 --- a/chrome/test/data/policy/policy_test_cases.json +++ b/chrome/test/data/policy/policy_test_cases.json
@@ -3180,6 +3180,17 @@ ] }, + "UrlKeyedAnonymizedDataCollectionEnabled": { + "os": ["win", "linux", "mac", "chromeos", "android"], + "test_policy": { "UrlKeyedAnonymizedDataCollectionEnabled": false }, + "pref_mappings": [ + { "pref": "url_keyed_anonymized_data_collection.enabled", + "indicator_tests": [ + { "policy": { "url_keyed_anonymized_data_collection.enabled": false } } + ] + } + ] + }, "----- Chrome OS device policies ---------------------------------------": {},
diff --git a/chrome/test/data/vr/webvr_info b/chrome/test/data/vr/webvr_info deleted file mode 160000 index c58ae99..0000000 --- a/chrome/test/data/vr/webvr_info +++ /dev/null
@@ -1 +0,0 @@ -Subproject commit c58ae99b9ff9e2aa4c524633519570bf33536248
diff --git a/chrome/test/data/vr/webxr_samples b/chrome/test/data/vr/webxr_samples deleted file mode 160000 index cf02f19..0000000 --- a/chrome/test/data/vr/webxr_samples +++ /dev/null
@@ -1 +0,0 @@ -Subproject commit cf02f19c4ff6894705a9407722ab52551e010c60
diff --git a/chrome/test/data/webui/settings/people_page_sync_page_interactive_test.js b/chrome/test/data/webui/settings/people_page_sync_page_interactive_test.js index 5e4205a..3e71ffa 100644 --- a/chrome/test/data/webui/settings/people_page_sync_page_interactive_test.js +++ b/chrome/test/data/webui/settings/people_page_sync_page_interactive_test.js
@@ -3,24 +3,25 @@ // found in the LICENSE file. suite('sync-page-test', function() { - /** @type {SyncPageElement} */ let testElement; + /** @type {SyncPageElement} */ let syncPage; setup(function() { PolymerTest.clearBody(); - testElement = document.createElement('settings-sync-page'); - document.body.appendChild(testElement); + syncPage = document.createElement('settings-sync-page'); + document.body.appendChild(syncPage); }); test('autofocus correctly after container is shown', function() { cr.webUIListenerCallback('sync-prefs-changed', {passphraseRequired: true}); + syncPage.unifiedConsentEnabled = false; Polymer.dom.flush(); // Simulate event normally fired by main_page_behavior after subpage // animation ends. - testElement.fire('show-container'); + syncPage.fire('show-container'); assertEquals( - testElement.$$('#existingPassphraseInput').inputElement, - testElement.$$('#existingPassphraseInput').shadowRoot.activeElement); + syncPage.$$('#existingPassphraseInput').inputElement, + syncPage.$$('#existingPassphraseInput').shadowRoot.activeElement); }); });
diff --git a/chrome/test/data/webui/settings/people_page_sync_page_test.js b/chrome/test/data/webui/settings/people_page_sync_page_test.js index 0423e02..e05b4fa5 100644 --- a/chrome/test/data/webui/settings/people_page_sync_page_test.js +++ b/chrome/test/data/webui/settings/people_page_sync_page_test.js
@@ -441,6 +441,7 @@ prefs.passphraseRequired = true; cr.webUIListenerCallback('sync-prefs-changed', prefs); + syncPage.unifiedConsentEnabled = false; Polymer.dom.flush(); const existingPassphraseInput = @@ -461,6 +462,7 @@ prefs.passphraseRequired = true; cr.webUIListenerCallback('sync-prefs-changed', prefs); + syncPage.unifiedConsentEnabled = false; Polymer.dom.flush(); const existingPassphraseInput = syncPage.$$('#existingPassphraseInput'); @@ -492,6 +494,7 @@ prefs.passphraseRequired = true; cr.webUIListenerCallback('sync-prefs-changed', prefs); + syncPage.unifiedConsentEnabled = false; Polymer.dom.flush(); const existingPassphraseInput = syncPage.$$('#existingPassphraseInput');
diff --git a/chrome/test/data/xr/e2e_test_files/html/test_ar_request_session_succeeds.html b/chrome/test/data/xr/e2e_test_files/html/test_ar_request_session_succeeds.html index 3e05164..149f9df 100644 --- a/chrome/test/data/xr/e2e_test_files/html/test_ar_request_session_succeeds.html +++ b/chrome/test/data/xr/e2e_test_files/html/test_ar_request_session_succeeds.html
@@ -10,7 +10,7 @@ <canvas id="webgl-canvas"></canvas> <script src="../../../../../../third_party/WebKit/LayoutTests/resources/testharness.js"></script> <script src="../resources/webxr_e2e.js"></script> - <script>var shouldAutoCreateNonExclusiveSession = false;</script> + <script>var shouldAutoCreateNonImmersiveSession = false;</script> <script src="../resources/webxr_boilerplate.js"></script> </body> </html>
diff --git a/chromeos/dbus/update_engine_client.cc b/chromeos/dbus/update_engine_client.cc index 3e787e33..29af542 100644 --- a/chromeos/dbus/update_engine_client.cc +++ b/chromeos/dbus/update_engine_client.cc
@@ -18,6 +18,7 @@ #include "base/strings/string_util.h" #include "base/threading/thread_task_runner_handle.h" #include "chromeos/chromeos_switches.h" +#include "chromeos/system/version_loader.h" #include "dbus/bus.h" #include "dbus/message.h" #include "dbus/object_path.h" @@ -48,6 +49,9 @@ // |kStateTransitionDownloadingDelayMs| during fake AU. const int64_t kDownloadSizeDelta = 1 << 19; +// Version number of the image being installed during fake AU. +const char kStubVersion[] = "1234.0.0.0"; + // Returns UPDATE_STATUS_ERROR on error. UpdateEngineClient::UpdateStatusOperation UpdateStatusFromString( const std::string& str) { @@ -497,6 +501,14 @@ status.download_progress = progress; status.status = UpdateStatusFromString(current_operation); status.new_version = new_version; + // TODO(hunyadym, https://crbug.com/864672): Add a new DBus call to + // determine this based on the Omaha response, and not version comparison. + status.is_rollback = version_loader::IsRollback( + version_loader::GetVersion(version_loader::VERSION_SHORT), + status.new_version); + if (status.is_rollback) + VLOG(1) << "New image is a rollback."; + status.new_size = new_size; last_status_ = status; @@ -563,7 +575,9 @@ last_status_.status = UPDATE_STATUS_CHECKING_FOR_UPDATE; last_status_.download_progress = 0.0; last_status_.last_checked_time = 0; + last_status_.new_version = "0.0.0.0"; last_status_.new_size = 0; + last_status_.is_rollback = false; base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( FROM_HERE, base::BindOnce(&UpdateEngineClientStubImpl::StateTransition, @@ -636,6 +650,7 @@ } else { next_status = UPDATE_STATUS_DOWNLOADING; last_status_.download_progress += 0.01; + last_status_.new_version = kStubVersion; last_status_.new_size = kDownloadSizeDelta; delay_ms = kStateTransitionDownloadingDelayMs; } @@ -644,7 +659,7 @@ next_status = UPDATE_STATUS_FINALIZING; break; case UPDATE_STATUS_FINALIZING: - next_status = UPDATE_STATUS_IDLE; + next_status = UPDATE_STATUS_UPDATED_NEED_REBOOT; break; } last_status_.status = next_status;
diff --git a/chromeos/dbus/update_engine_client.h b/chromeos/dbus/update_engine_client.h index f942536..aa616cc 100644 --- a/chromeos/dbus/update_engine_client.h +++ b/chromeos/dbus/update_engine_client.h
@@ -55,10 +55,16 @@ } UpdateStatusOperation status; - double download_progress; // 0.0 - 1.0 - int64_t last_checked_time; // As reported by std::time(). + // 0.0 - 1.0 + double download_progress; + // As reported by std::time(). + int64_t last_checked_time; std::string new_version; - int64_t new_size; // Valid during DOWNLOADING, in bytes. + // Valid during DOWNLOADING, in bytes. + int64_t new_size; + // True if the update is actually a rollback and the device will be wiped + // when rebooted. + bool is_rollback; }; // The result code used for RequestUpdateCheck().
diff --git a/chromeos/system/version_loader.cc b/chromeos/system/version_loader.cc index 92d35d7..a9ae29b 100644 --- a/chromeos/system/version_loader.cc +++ b/chromeos/system/version_loader.cc
@@ -14,6 +14,7 @@ #include "base/files/file_util.h" #include "base/location.h" #include "base/optional.h" +#include "base/strings/string_number_conversions.h" #include "base/strings/string_split.h" #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" @@ -120,5 +121,39 @@ return std::string(); } +bool IsRollback(const std::string& current_version, + const std::string& new_version) { + VLOG(1) << "Current version: " << current_version; + VLOG(1) << "New version: " << new_version; + + if (new_version == "0.0.0.0") { + // No update available. + return false; + } + + std::vector<std::string> current_version_parts = base::SplitString( + current_version, ".", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL); + std::vector<std::string> new_version_parts = base::SplitString( + new_version, ".", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL); + + for (size_t i = 0; + i < current_version_parts.size() && i < new_version_parts.size(); ++i) { + if (current_version_parts[i] == new_version_parts[i]) + continue; + + unsigned int current_part, new_part; + if (!base::StringToUint(current_version_parts[i], ¤t_part) || + !base::StringToUint(new_version_parts[i], &new_part)) { + // One of the parts is not a number (e.g. date in test builds), compare + // strings. + return current_version_parts[i] > new_version_parts[i]; + } + return current_part > new_part; + } + + // Return true if new version is prefix of current version, false otherwise. + return new_version_parts.size() < current_version_parts.size(); +} + } // namespace version_loader } // namespace chromeos
diff --git a/chromeos/system/version_loader.h b/chromeos/system/version_loader.h index 773f5f7..08b42ed 100644 --- a/chromeos/system/version_loader.h +++ b/chromeos/system/version_loader.h
@@ -44,6 +44,14 @@ // Extracts the firmware from the file. CHROMEOS_EXPORT std::string ParseFirmware(const std::string& contents); +// Returns true if |new_version| is older than |current_version|. +// Version numbers should be dot separated. The sections are compared as +// numbers if possible, as strings otherwise. Earlier sections have +// precedence. If one version is prefix of another, the shorter one is +// considered older. (See test for examples.) +CHROMEOS_EXPORT bool IsRollback(const std::string& current_version, + const std::string& new_version); + } // namespace version_loader } // namespace chromeos
diff --git a/chromeos/system/version_loader_unittest.cc b/chromeos/system/version_loader_unittest.cc index ab96935..89117d08 100644 --- a/chromeos/system/version_loader_unittest.cc +++ b/chromeos/system/version_loader_unittest.cc
@@ -28,4 +28,54 @@ EXPECT_EQ("0.2.3.3", version_loader::ParseFirmware(kTest15)); } +TEST_F(VersionLoaderTest, IsRollback) { + EXPECT_FALSE(version_loader::IsRollback("1.2.3.4", "1.2.3.4")); + EXPECT_FALSE(version_loader::IsRollback("1.2.3.4", "1.2.3.5")); + EXPECT_FALSE(version_loader::IsRollback("1.2.3.4", "1.2.4.5")); + EXPECT_FALSE(version_loader::IsRollback("1.2.3.4", "1.3.0.0")); + EXPECT_FALSE(version_loader::IsRollback("1.2.3.4", "2.0.0.0")); + EXPECT_FALSE(version_loader::IsRollback("1.2.3.4", "2.3.4.5")); + EXPECT_FALSE(version_loader::IsRollback("1.0.0.0", "2.0.0.0")); + EXPECT_TRUE(version_loader::IsRollback("1.2.3.4", "1.2.3.3")); + EXPECT_TRUE(version_loader::IsRollback("1.2.3.4", "1.1.1.1")); + EXPECT_TRUE(version_loader::IsRollback("1.0.0.0", "0.9.0.0")); + + // If possible, use number comparison, otherwise string comparison. + EXPECT_FALSE(version_loader::IsRollback("999.0.0.0", "1000.0.0.0")); + EXPECT_TRUE(version_loader::IsRollback("1000.0.0.0", "999.0.0.0")); + EXPECT_FALSE(version_loader::IsRollback("1000x.0.0.0", "999x.0.0.0")); + EXPECT_TRUE(version_loader::IsRollback("999x.0.0.0", "1000x.0.0.0")); + + EXPECT_FALSE(version_loader::IsRollback("1.0", "1.1")); + EXPECT_FALSE(version_loader::IsRollback("1.0", "1.0.1")); + EXPECT_FALSE(version_loader::IsRollback("1", "1.1")); + EXPECT_TRUE(version_loader::IsRollback("1.0", "0.9")); + EXPECT_TRUE(version_loader::IsRollback("3", "2.9")); + EXPECT_TRUE(version_loader::IsRollback("3.1", "3")); + EXPECT_TRUE(version_loader::IsRollback("3.0.1", "3")); + EXPECT_TRUE(version_loader::IsRollback("1.0", "0.0")); + + EXPECT_FALSE(version_loader::IsRollback("", "")); + EXPECT_FALSE(version_loader::IsRollback("invalid", "invalid")); + EXPECT_FALSE(version_loader::IsRollback("alpha", "beta")); + EXPECT_TRUE(version_loader::IsRollback("beta", "alpha")); + EXPECT_FALSE(version_loader::IsRollback("10.alpha", "10.beta")); + EXPECT_TRUE(version_loader::IsRollback("10.beta", "10.alpha")); + EXPECT_FALSE(version_loader::IsRollback("10.beta", "11.alpha")); + EXPECT_TRUE(version_loader::IsRollback("11.alpha", "10.beta")); + + // 0.0.0.0 means update is not available, it is not a rollback. + EXPECT_FALSE(version_loader::IsRollback("1.2.3.4", "0.0.0.0")); + EXPECT_FALSE(version_loader::IsRollback("1.0", "0.0.0.0")); + EXPECT_FALSE(version_loader::IsRollback("0.0.0.0", "0.0.0.0")); + + // If there are string parts, there are compared as strings. + EXPECT_FALSE(version_loader::IsRollback("1.2.2018_01_01", "1.2.2018_01_02")); + EXPECT_FALSE(version_loader::IsRollback("1.2.2018_01_01", "1.2.2018_01_01")); + EXPECT_TRUE(version_loader::IsRollback("1.2.2018_01_02", "1.2.2018_01_01")); + EXPECT_FALSE(version_loader::IsRollback("1.2018_01_01.2", "1.2018_01_01.3")); + EXPECT_FALSE(version_loader::IsRollback("1.2018_01_01.2", "1.2018_01_01.2")); + EXPECT_TRUE(version_loader::IsRollback("1.2018_01_01.2", "1.2018_01_01.1")); +} + } // namespace chromeos
diff --git a/components/autofill/content/common/autofill_types.mojom b/components/autofill/content/common/autofill_types.mojom index 60a764da..58ca06b 100644 --- a/components/autofill/content/common/autofill_types.mojom +++ b/components/autofill/content/common/autofill_types.mojom
@@ -185,6 +185,7 @@ url.mojom.Url action; FormFieldData username_field; FormFieldData password_field; + bool username_may_use_prefilled_placeholder; string preferred_realm; map<mojo_base.mojom.String16, PasswordAndRealm> additional_logins; bool wait_for_username;
diff --git a/components/autofill/content/common/autofill_types_struct_traits.cc b/components/autofill/content/common/autofill_types_struct_traits.cc index 1c9b60a..ef9a2c1 100644 --- a/components/autofill/content/common/autofill_types_struct_traits.cc +++ b/components/autofill/content/common/autofill_types_struct_traits.cc
@@ -725,6 +725,8 @@ out->is_possible_change_password_form = data.is_possible_change_password_form(); out->has_renderer_ids = data.has_renderer_ids(); + out->username_may_use_prefilled_placeholder = + data.username_may_use_prefilled_placeholder(); return true; }
diff --git a/components/autofill/content/common/autofill_types_struct_traits.h b/components/autofill/content/common/autofill_types_struct_traits.h index 3c98b08e..7d6b10e 100644 --- a/components/autofill/content/common/autofill_types_struct_traits.h +++ b/components/autofill/content/common/autofill_types_struct_traits.h
@@ -387,6 +387,11 @@ return r.password_field; } + static bool username_may_use_prefilled_placeholder( + const autofill::PasswordFormFillData& r) { + return r.username_may_use_prefilled_placeholder; + } + static const std::string& preferred_realm( const autofill::PasswordFormFillData& r) { return r.preferred_realm;
diff --git a/components/autofill/content/renderer/password_autofill_agent.cc b/components/autofill/content/renderer/password_autofill_agent.cc index 253820f..e62ca34 100644 --- a/components/autofill/content/renderer/password_autofill_agent.cc +++ b/components/autofill/content/renderer/password_autofill_agent.cc
@@ -1748,7 +1748,7 @@ blink::WebInputElement* password_element, const PasswordFormFillData& fill_data, bool exact_username_match, - bool set_selection, + bool username_may_use_prefilled_placeholder, FieldValueAndPropertiesMaskMap* field_value_and_properties_map, RendererSavePasswordProgressLogger* logger) { if (logger) @@ -1769,23 +1769,28 @@ // not autocompletable (no username case). base::string16 current_username; - // Whether the username element was prefilled with content that was not on a - // list of known placeholder texts (e.g. "username or email"). - bool prefilled_not_placeholder_username = false; + // Whether the username element was prefilled with content that was on a + // list of known placeholder texts that should be overridden (e.g. "username + // or email" or there is a server hint that it is just a placeholder). + bool prefilled_placeholder_username = false; if (!username_element->IsNull()) { + prefilled_placeholder_username = + !username_element->Value().IsEmpty() && + (PossiblePrefilledUsernameValue(username_element->Value().Utf8()) || + username_may_use_prefilled_placeholder); if (!username_element->Value().IsEmpty() && - !PossiblePrefilledUsernameValue(username_element->Value().Utf8())) { + !prefilled_placeholder_username) { // Username is filled with content that was not on a list of known - // placeholder texts (e.g. "username or email"). + // placeholder texts (e.g. "username or email") nor there is server-side + // data that this value is placeholder. current_username = username_element->Value().Utf16(); - prefilled_not_placeholder_username = true; } else if (IsElementAutocompletable(*username_element)) { current_username = fill_data.username_field.value; } } - // username and password will contain the match found if any. + // |username| and |password| will contain the match found if any. base::string16 username; base::string16 password; @@ -1793,7 +1798,8 @@ logger, &username, &password); if (password.empty()) { - if (prefilled_not_placeholder_username) { + if (!username_element->IsNull() && !username_element->Value().IsEmpty() && + !prefilled_placeholder_username) { LogPrefilledUsernameFillOutcome( PrefilledUsernameFillOutcome::kPrefilledUsernameNotOverridden); } @@ -1808,34 +1814,24 @@ // Input matches the username, fill in required values. if (!username_element->IsNull() && IsElementAutocompletable(*username_element)) { - // Fill a non-empty username if it is safe to override the value of the - // username element. It is safe to override if the value is empty or a known - // placeholder value. - if (!username.empty()) { - if (username_element->Value().IsEmpty()) { - username_element->SetSuggestedValue( - blink::WebString::FromUTF16(username)); - gatekeeper_.RegisterElement(username_element); - } else if (PossiblePrefilledUsernameValue( - username_element->Value().Utf8())) { - username_element->SetSuggestedValue( - blink::WebString::FromUTF16(username)); - gatekeeper_.RegisterElement(username_element); + if (!username.empty() && (username_element->Value().IsEmpty() || + prefilled_placeholder_username)) { + username_element->SetSuggestedValue( + blink::WebString::FromUTF16(username)); + gatekeeper_.RegisterElement(username_element); + if (prefilled_placeholder_username) { LogPrefilledUsernameFillOutcome( PrefilledUsernameFillOutcome:: kPrefilledPlaceholderUsernameOverridden); } } + UpdateFieldValueAndPropertiesMaskMap(*username_element, &username, FieldPropertiesFlags::AUTOFILLED, field_value_and_properties_map); username_element->SetAutofillState(WebAutofillState::kAutofilled); if (logger) logger->LogElementName(Logger::STRING_USERNAME_FILLED, *username_element); - if (set_selection) { - form_util::PreviewSuggestion(username, current_username, - username_element); - } } // Wait to fill in the password until a user gesture occurs. This is to make @@ -1897,7 +1893,8 @@ // match for read-only username fields. return FillUserNameAndPassword( &username_element, &password_element, fill_data, exact_username_match, - false /* set_selection */, field_value_and_properties_map, logger); + fill_data.username_may_use_prefilled_placeholder, + field_value_and_properties_map, logger); } void PasswordAutofillAgent::OnProvisionallySaveForm(
diff --git a/components/autofill/content/renderer/password_autofill_agent.h b/components/autofill/content/renderer/password_autofill_agent.h index 23d97473..a284a03 100644 --- a/components/autofill/content/renderer/password_autofill_agent.h +++ b/components/autofill/content/renderer/password_autofill_agent.h
@@ -309,12 +309,14 @@ // will only have the suggestedValue set. If a match is found, return true and // |field_value_and_properties_map| will be modified with the autofilled // credentials and |FieldPropertiesFlags::AUTOFILLED| flag. + // If |username_may_use_prefilled_placeholder| then this function may + // overwrite the value of username field. bool FillUserNameAndPassword( blink::WebInputElement* username_element, blink::WebInputElement* password_element, const PasswordFormFillData& fill_data, bool exact_username_match, - bool set_selection, + bool username_may_use_prefilled_placeholder, FieldValueAndPropertiesMaskMap* field_value_and_properties_map, RendererSavePasswordProgressLogger* logger);
diff --git a/components/autofill/core/browser/autofill_manager_unittest.cc b/components/autofill/core/browser/autofill_manager_unittest.cc index bc5ed88..4db3f8b 100644 --- a/components/autofill/core/browser/autofill_manager_unittest.cc +++ b/components/autofill/core/browser/autofill_manager_unittest.cc
@@ -290,7 +290,8 @@ void SetUp() override { autofill_client_.SetPrefs(test::PrefServiceForTesting()); - personal_data_.set_database(autofill_client_.GetDatabase()); + personal_data_.Init(autofill_client_.GetDatabase(), nullptr, + autofill_client_.GetPrefs(), nullptr, false); personal_data_.SetPrefService(autofill_client_.GetPrefs()); autofill_driver_ = std::make_unique<testing::NiceMock<MockAutofillDriver>>(); @@ -369,9 +370,6 @@ autofill_manager_.reset(); autofill_driver_.reset(); - // Remove the AutofillWebDataService so TestPersonalDataManager does not - // need to care about removing self as an observer in destruction. - personal_data_.set_database(scoped_refptr<AutofillWebDataService>(nullptr)); personal_data_.SetPrefService(nullptr); personal_data_.ClearCreditCards();
diff --git a/components/autofill/core/browser/autofill_metrics_unittest.cc b/components/autofill/core/browser/autofill_metrics_unittest.cc index c22089b..8052cca1 100644 --- a/components/autofill/core/browser/autofill_metrics_unittest.cc +++ b/components/autofill/core/browser/autofill_metrics_unittest.cc
@@ -275,7 +275,6 @@ autofill_client_.SetPrefs(test::PrefServiceForTesting()); personal_data_ = std::make_unique<TestPersonalDataManager>(); - personal_data_->set_database(autofill_client_.GetDatabase()); personal_data_->SetPrefService(autofill_client_.GetPrefs()); personal_data_->SetSyncServiceForTest(&sync_service_); autofill_driver_ = std::make_unique<TestAutofillDriver>(); @@ -2791,96 +2790,12 @@ "Autofill.DaysSinceLastUse.StoredCreditCard.Server.Unmasked", 200, 3); } -// Test that the profile count is logged correctly. -TEST_F(AutofillMetricsTest, StoredProfileCount) { - // The metric should be logged when the profiles are first loaded. - { - base::HistogramTester histogram_tester; - personal_data_->LoadProfiles(); - histogram_tester.ExpectUniqueSample("Autofill.StoredProfileCount", 2, 1); - } - - // The metric should only be logged once. - { - base::HistogramTester histogram_tester; - personal_data_->LoadProfiles(); - histogram_tester.ExpectTotalCount("Autofill.StoredProfileCount", 0); - } -} - -// Test that the local credit card count is logged correctly. -TEST_F(AutofillMetricsTest, StoredLocalCreditCardCount) { - // The metric should be logged when the credit cards are first loaded. - { - base::HistogramTester histogram_tester; - RecreateCreditCards(true /* include_local_credit_card */, - false /* include_masked_server_credit_card */, - false /* include_full_server_credit_card */); - histogram_tester.ExpectUniqueSample("Autofill.StoredLocalCreditCardCount", - 1, 1); - } - - // The metric should only be logged once. - { - base::HistogramTester histogram_tester; - RecreateCreditCards(true /* include_local_credit_card */, - false /* include_masked_server_credit_card */, - false /* include_full_server_credit_card */); - histogram_tester.ExpectTotalCount("Autofill.StoredLocalCreditCardCount", 0); - } -} - -// Test that the masked server credit card counts are logged correctly. -TEST_F(AutofillMetricsTest, StoredServerCreditCardCounts_Masked) { - // The metrics should be logged when the credit cards are first loaded. - { - base::HistogramTester histogram_tester; - RecreateCreditCards(false /* include_local_credit_card */, - true /* include_masked_server_credit_card */, - false /* include_full_server_credit_card */); - histogram_tester.ExpectUniqueSample( - "Autofill.StoredServerCreditCardCount.Masked", 1, 1); - } - - // The metrics should only be logged once. - { - base::HistogramTester histogram_tester; - RecreateCreditCards(false /* include_local_credit_card */, - true /* include_masked_server_credit_card */, - true /* include_full_server_credit_card */); - histogram_tester.ExpectTotalCount( - "Autofill.StoredServerCreditCardCount.Masked", 0); - } -} - -// Test that the unmasked (full) server credit card counts are logged correctly. -TEST_F(AutofillMetricsTest, StoredServerCreditCardCounts_Unmasked) { - // The metrics should be logged when the credit cards are first loaded. - { - base::HistogramTester histogram_tester; - RecreateCreditCards(false /* include_local_credit_card */, - false /* include_masked_server_credit_card */, - true /* include_full_server_credit_card */); - histogram_tester.ExpectUniqueSample( - "Autofill.StoredServerCreditCardCount.Unmasked", 1, 1); - } - - // The metrics should only be logged once. - { - base::HistogramTester histogram_tester; - RecreateCreditCards(false /* include_local_credit_card */, - false /* include_masked_server_credit_card */, - true /* include_full_server_credit_card */); - histogram_tester.ExpectTotalCount( - "Autofill.StoredServerCreditCardCount.Unmasked", 0); - } -} - // Test that we correctly log when Autofill is enabled. TEST_F(AutofillMetricsTest, AutofillIsEnabledAtStartup) { base::HistogramTester histogram_tester; personal_data_->SetAutofillEnabled(true); personal_data_->Init(autofill_client_.GetDatabase(), + /*account_database=*/nullptr, autofill_client_.GetPrefs(), /*identity_manager=*/nullptr, /*is_off_the_record=*/false); @@ -2892,6 +2807,7 @@ base::HistogramTester histogram_tester; personal_data_->SetAutofillEnabled(false); personal_data_->Init(autofill_client_.GetDatabase(), + /*account_database=*/nullptr, autofill_client_.GetPrefs(), /*identity_manager=*/nullptr, /*is_off_the_record=*/false);
diff --git a/components/autofill/core/browser/credit_card_save_manager_unittest.cc b/components/autofill/core/browser/credit_card_save_manager_unittest.cc index 3b36a80..51bdb565 100644 --- a/components/autofill/core/browser/credit_card_save_manager_unittest.cc +++ b/components/autofill/core/browser/credit_card_save_manager_unittest.cc
@@ -100,8 +100,8 @@ public: void SetUp() override { autofill_client_.SetPrefs(test::PrefServiceForTesting()); - personal_data_.set_database(autofill_client_.GetDatabase()); - personal_data_.SetPrefService(autofill_client_.GetPrefs()); + personal_data_.Init(autofill_client_.GetDatabase(), nullptr, + autofill_client_.GetPrefs(), nullptr, false); personal_data_.SetSyncServiceForTest(&sync_service_); autofill_driver_.reset(new TestAutofillDriver()); request_context_ = new net::TestURLRequestContextGetter( @@ -130,9 +130,6 @@ autofill_manager_.reset(); autofill_driver_.reset(); - // Remove the AutofillWebDataService so TestPersonalDataManager does not - // need to care about removing self as an observer in destruction. - personal_data_.set_database(scoped_refptr<AutofillWebDataService>(nullptr)); personal_data_.SetPrefService(nullptr); personal_data_.ClearCreditCards();
diff --git a/components/autofill/core/browser/form_data_importer_unittest.cc b/components/autofill/core/browser/form_data_importer_unittest.cc index d1f7062..3c0c3300 100644 --- a/components/autofill/core/browser/form_data_importer_unittest.cc +++ b/components/autofill/core/browser/form_data_importer_unittest.cc
@@ -118,7 +118,7 @@ personal_data_manager_.reset(new PersonalDataManager("en")); personal_data_manager_->Init( scoped_refptr<AutofillWebDataService>(autofill_database_service_), - prefs_.get(), nullptr, is_incognito); + nullptr, prefs_.get(), nullptr, is_incognito); personal_data_manager_->AddObserver(&personal_data_observer_); personal_data_manager_->OnSyncServiceInitialized(nullptr);
diff --git a/components/autofill/core/browser/local_card_migration_manager_unittest.cc b/components/autofill/core/browser/local_card_migration_manager_unittest.cc index 57ac4433..cb6124b 100644 --- a/components/autofill/core/browser/local_card_migration_manager_unittest.cc +++ b/components/autofill/core/browser/local_card_migration_manager_unittest.cc
@@ -60,7 +60,6 @@ public: void SetUp() override { autofill_client_.SetPrefs(test::PrefServiceForTesting()); - personal_data_.set_database(autofill_client_.GetDatabase()); personal_data_.SetPrefService(autofill_client_.GetPrefs()); personal_data_.SetSyncServiceForTest(&sync_service_); autofill_driver_.reset(new TestAutofillDriver()); @@ -93,9 +92,6 @@ autofill_manager_.reset(); autofill_driver_.reset(); - // Remove the AutofillWebDataService so TestPersonalDataManager does not - // need to care about removing self as an observer in destruction. - personal_data_.set_database(scoped_refptr<AutofillWebDataService>(nullptr)); personal_data_.SetPrefService(nullptr); personal_data_.ClearCreditCards();
diff --git a/components/autofill/core/browser/personal_data_manager.cc b/components/autofill/core/browser/personal_data_manager.cc index 9ccf695..f7b77e22 100644 --- a/components/autofill/core/browser/personal_data_manager.cc +++ b/components/autofill/core/browser/personal_data_manager.cc
@@ -347,9 +347,116 @@ } // namespace +// Helper class to abstract the switching between account and profile storage +// for server cards away from the rest of PersonalDataManager. +class PersonalDatabaseHelper + : public AutofillWebDataServiceObserverOnUISequence { + public: + PersonalDatabaseHelper(PersonalDataManager* personal_data_manager) + : personal_data_manager_(personal_data_manager) {} + + void Init(scoped_refptr<AutofillWebDataService> profile_database, + scoped_refptr<AutofillWebDataService> account_database) { + profile_database_ = profile_database; + account_database_ = account_database; + + if (!profile_database_) { + // In some tests, there are no dbs. + return; + } + + // Start observing the profile database. Don't observe the account database + // until we know that we should use it. + profile_database_->AddObserver(personal_data_manager_); + + // If we don't have an account_database , we always use the profile database + // for server data. + if (!account_database_) { + server_database_ = profile_database_; + } else { + // Wait for the call to SetUseAccountStorageForServerCards to decide + // which database to use for server cards. + server_database_ = nullptr; + } + } + + ~PersonalDatabaseHelper() override { + if (profile_database_) { + profile_database_->RemoveObserver(personal_data_manager_); + } + + // If we have a different server database, also remove its observer. + if (server_database_ && server_database_ != profile_database_) { + server_database_->RemoveObserver(personal_data_manager_); + } + } + + // Returns the database that should be used for storing local data. + // Until server addresses are using the server database, this should also + // be used for server addresses. + scoped_refptr<AutofillWebDataService> GetLocalDatabase() { + return profile_database_; + } + + // Returns the database that should be used for storing server data. + // Until server addresses are using the server database, this should *not* + // be used for server addresses. + scoped_refptr<AutofillWebDataService> GetServerDatabase() { + return server_database_; + } + + // Set whether this should use the passed in account storage for server + // addresses. If false, this will use the profile_storage. + // It's an error to call this if no account storage was passed in at + // construction time. + void SetUseAccountStorageForServerCards( + bool use_account_storage_for_server_cards) { + if (!profile_database_) { + // In some tests, there are no dbs. + return; + } + scoped_refptr<AutofillWebDataService> new_server_database = + use_account_storage_for_server_cards ? account_database_ + : profile_database_; + DCHECK(new_server_database != nullptr) + << "SetUseAccountStorageForServerCards(" + << use_account_storage_for_server_cards << "): storage not available."; + + if (new_server_database == server_database_) { + // Nothing to do :) + return; + } + + if (server_database_ != nullptr && server_database_ != profile_database_) { + // Remove the previous observer if we had any. + server_database_->RemoveObserver(personal_data_manager_); + personal_data_manager_->CancelPendingServerQueries(); + } + server_database_ = new_server_database; + // We don't need to add an observer if server_database_ is equal to + // profile_database_, because we're already observing that. + if (server_database_ != profile_database_) { + server_database_->AddObserver(personal_data_manager_); + } + // Notify the manager that the database changed. + personal_data_manager_->Refresh(); + } + + private: + scoped_refptr<AutofillWebDataService> profile_database_; + scoped_refptr<AutofillWebDataService> account_database_; + + // The database that should be used for server data. This will always be equal + // to either profile_database_, or account_database_. + scoped_refptr<AutofillWebDataService> server_database_; + + PersonalDataManager* personal_data_manager_; + + DISALLOW_COPY_AND_ASSIGN(PersonalDatabaseHelper); +}; + PersonalDataManager::PersonalDataManager(const std::string& app_locale) - : database_(nullptr), - is_data_loaded_(false), + : is_data_loaded_(false), pending_profiles_query_(0), pending_server_profiles_query_(0), pending_creditcards_query_(0), @@ -360,15 +467,18 @@ sync_service_(nullptr), is_off_the_record_(false), has_logged_stored_profile_metrics_(false), - has_logged_stored_credit_card_metrics_(false) {} + has_logged_stored_credit_card_metrics_(false) { + database_helper_ = std::make_unique<PersonalDatabaseHelper>(this); +} -void PersonalDataManager::Init(scoped_refptr<AutofillWebDataService> database, - PrefService* pref_service, - identity::IdentityManager* identity_manager, - bool is_off_the_record) { +void PersonalDataManager::Init( + scoped_refptr<AutofillWebDataService> profile_database, + scoped_refptr<AutofillWebDataService> account_database, + PrefService* pref_service, + identity::IdentityManager* identity_manager, + bool is_off_the_record) { CountryNames::SetLocaleString(app_locale_); - - database_ = database; + database_helper_->Init(profile_database, account_database); SetPrefService(pref_service); identity_manager_ = identity_manager; is_off_the_record_ = is_off_the_record; @@ -377,13 +487,12 @@ AutofillMetrics::LogIsAutofillEnabledAtStartup(IsAutofillEnabled()); // WebDataService may not be available in tests. - if (!database_) + if (!database_helper_->GetLocalDatabase()) { return; - + } LoadProfiles(); LoadCreditCards(); - database_->AddObserver(this); // Check if profile cleanup has already been performed this major version. is_autofill_profile_cleanup_pending_ = @@ -396,13 +505,10 @@ } PersonalDataManager::~PersonalDataManager() { - CancelPendingQuery(&pending_profiles_query_); - CancelPendingQuery(&pending_server_profiles_query_); - CancelPendingQuery(&pending_creditcards_query_); - CancelPendingQuery(&pending_server_creditcards_query_); - - if (database_) - database_->RemoveObserver(this); + CancelPendingLocalQuery(&pending_profiles_query_); + CancelPendingLocalQuery(&pending_server_profiles_query_); + CancelPendingLocalQuery(&pending_creditcards_query_); + CancelPendingServerQuery(&pending_server_creditcards_query_); } void PersonalDataManager::Shutdown() { @@ -462,6 +568,12 @@ } } +void PersonalDataManager::SetUseAccountStorageForServerCards( + bool use_account_storage_for_server_cards) { + database_helper_->SetUseAccountStorageForServerCards( + use_account_storage_for_server_cards); +} + void PersonalDataManager::OnWebDataServiceRequestDone( WebDataServiceBase::Handle h, std::unique_ptr<WDTypedResult> result) { @@ -485,6 +597,8 @@ ReceiveLoadedDbValues(h, result.get(), &pending_profiles_query_, &web_profiles_); } else { + DCHECK_EQ(h, pending_server_profiles_query_) + << "received profiles from invalid request."; ReceiveLoadedDbValues(h, result.get(), &pending_server_profiles_query_, &server_profiles_); @@ -495,6 +609,8 @@ ReceiveLoadedDbValues(h, result.get(), &pending_creditcards_query_, &local_credit_cards_); } else { + DCHECK_EQ(h, pending_server_creditcards_query_) + << "received creditcards from invalid request."; ReceiveLoadedDbValues(h, result.get(), &pending_server_creditcards_query_, &server_credit_cards_); @@ -511,9 +627,12 @@ } // If all requests have responded, then all personal data is loaded. + // We need to check if the server database is set here, because we won't have + // the server cards yet if we don't have the database. if (pending_profiles_query_ == 0 && pending_creditcards_query_ == 0 && pending_server_profiles_query_ == 0 && - pending_server_creditcards_query_ == 0) { + pending_server_creditcards_query_ == 0 && + database_helper_->GetServerDatabase()) { is_data_loaded_ = true; LogStoredProfileMetrics(); LogStoredCreditCardMetrics(); @@ -577,17 +696,25 @@ } void PersonalDataManager::RecordUseOf(const AutofillDataModel& data_model) { - if (is_off_the_record_ || !database_) + if (is_off_the_record_) return; CreditCard* credit_card = GetCreditCardByGUID(data_model.guid()); if (credit_card) { credit_card->RecordAndLogUse(); - if (credit_card->record_type() == CreditCard::LOCAL_CARD) - database_->UpdateCreditCard(*credit_card); - else - database_->UpdateServerCardMetadata(*credit_card); + if (credit_card->record_type() == CreditCard::LOCAL_CARD) { + // Fail silently if there's no local database, because we need to support + // this for tests. + if (database_helper_->GetLocalDatabase()) { + database_helper_->GetLocalDatabase()->UpdateCreditCard(*credit_card); + } + } else { + DCHECK(database_helper_->GetServerDatabase()) + << "Recording use of server card without server storage."; + database_helper_->GetServerDatabase()->UpdateServerCardMetadata( + *credit_card); + } Refresh(); return; @@ -597,10 +724,14 @@ if (profile) { profile->RecordAndLogUse(); - if (profile->record_type() == AutofillProfile::LOCAL_PROFILE) - database_->UpdateAutofillProfile(*profile); - else if (profile->record_type() == AutofillProfile::SERVER_PROFILE) - database_->UpdateServerAddressMetadata(*profile); + if (profile->record_type() == AutofillProfile::LOCAL_PROFILE) { + database_helper_->GetLocalDatabase()->UpdateAutofillProfile(*profile); + } else if (profile->record_type() == AutofillProfile::SERVER_PROFILE) { + // TODO(crbug.com/864519): Update this once addresses support account + // storage, and also use the server database. + database_helper_->GetLocalDatabase()->UpdateServerAddressMetadata( + *profile); + } Refresh(); } @@ -617,7 +748,7 @@ if (FindByGUID<AutofillProfile>(web_profiles_, profile.guid())) return; - if (!database_) + if (!database_helper_->GetLocalDatabase()) return; // Don't add a duplicate. @@ -625,7 +756,7 @@ return; // Add the new profile to the web database. - database_->AddAutofillProfile(profile); + database_helper_->GetLocalDatabase()->AddAutofillProfile(profile); // Refresh our local cache and send notifications to observers. Refresh(); @@ -648,11 +779,11 @@ return; } - if (!database_) + if (!database_helper_->GetLocalDatabase()) return; // Make the update. - database_->UpdateAutofillProfile(profile); + database_helper_->GetLocalDatabase()->UpdateAutofillProfile(profile); // Refresh our local cache and send notifications to observers. Refresh(); @@ -682,7 +813,7 @@ if (FindByGUID<CreditCard>(local_credit_cards_, credit_card.guid())) return; - if (!database_) + if (!database_helper_->GetLocalDatabase()) return; // Don't add a duplicate. @@ -690,7 +821,7 @@ return; // Add the new credit card to the web database. - database_->AddCreditCard(credit_card); + database_helper_->GetLocalDatabase()->AddCreditCard(credit_card); // Refresh our local cache and send notifications to observers. Refresh(); @@ -717,11 +848,11 @@ // Update the cached version. *existing_credit_card = credit_card; - if (!database_) + if (!database_helper_->GetLocalDatabase()) return; // Make the update. - database_->UpdateCreditCard(credit_card); + database_helper_->GetLocalDatabase()->UpdateCreditCard(credit_card); // Refresh our local cache and send notifications to observers. Refresh(); @@ -733,16 +864,19 @@ DCHECK(!credit_card.IsEmpty(app_locale_)); DCHECK(!credit_card.server_id().empty()); - if (is_off_the_record_ || !database_) + if (is_off_the_record_) return; + DCHECK(database_helper_->GetServerDatabase()) + << "Adding server card without server storage."; + // Don't add a duplicate. if (FindByGUID<CreditCard>(server_credit_cards_, credit_card.guid()) || FindByContents(server_credit_cards_, credit_card)) return; // Add the new credit card to the web database. - database_->AddFullServerCreditCard(credit_card); + database_helper_->GetServerDatabase()->AddFullServerCreditCard(credit_card); // Refresh our local cache and send notifications to observers. Refresh(); @@ -752,7 +886,7 @@ const CreditCard& credit_card) { DCHECK_NE(CreditCard::LOCAL_CARD, credit_card.record_type()); - if (is_off_the_record_ || !database_) + if (is_off_the_record_ || !database_helper_->GetServerDatabase()) return; // Look up by server id, not GUID. @@ -769,9 +903,11 @@ DCHECK_NE(existing_credit_card->record_type(), credit_card.record_type()); DCHECK_EQ(existing_credit_card->Label(), credit_card.Label()); if (existing_credit_card->record_type() == CreditCard::MASKED_SERVER_CARD) { - database_->UnmaskServerCreditCard(credit_card, credit_card.number()); + database_helper_->GetServerDatabase()->UnmaskServerCreditCard( + credit_card, credit_card.number()); } else { - database_->MaskServerCreditCard(credit_card.server_id()); + database_helper_->GetServerDatabase()->MaskServerCreditCard( + credit_card.server_id()); } Refresh(); @@ -781,10 +917,13 @@ const CreditCard& credit_card) { DCHECK_NE(CreditCard::LOCAL_CARD, credit_card.record_type()); - if (is_off_the_record_ || !database_) + if (is_off_the_record_) return; - database_->UpdateServerCardMetadata(credit_card); + DCHECK(database_helper_->GetServerDatabase()) + << "Updating server card metadata without server storage."; + + database_helper_->GetServerDatabase()->UpdateServerCardMetadata(credit_card); Refresh(); } @@ -830,7 +969,17 @@ // database on startup, and it could get called when the wallet pref is // off (meaning this class won't even query for the server data) so don't // check the server_credit_cards_/profiles_ before posting to the DB. - database_->ClearAllServerData(); + DCHECK(database_helper_->GetServerDatabase()) + << "Updating server card metadata without server storage."; + + database_helper_->GetServerDatabase()->ClearAllServerData(); + + // TODO(crbug.com/864519): Remove this call once addresses support account + // storage, and also use the database_helper_->GetServerDatabase() + if (database_helper_->GetServerDatabase() != + database_helper_->GetLocalDatabase()) { + database_helper_->GetLocalDatabase()->ClearAllServerData(); + } // The above call will eventually clear our server data by notifying us // that the data changed and then this class will re-fetch. Preemptively @@ -840,7 +989,7 @@ } void PersonalDataManager::ClearAllLocalData() { - database_->ClearAllLocalData(); + database_helper_->GetLocalDatabase()->ClearAllLocalData(); local_credit_cards_.clear(); web_profiles_.clear(); } @@ -864,7 +1013,7 @@ void PersonalDataManager:: RemoveAutofillProfileByGUIDAndBlankCreditCardReferecne( const std::string& guid) { - database_->RemoveAutofillProfile(guid); + database_helper_->GetLocalDatabase()->RemoveAutofillProfile(guid); // Reset the billing_address_id of any card that refered to this profile. for (CreditCard* credit_card : GetCreditCards()) { @@ -872,9 +1021,13 @@ credit_card->set_billing_address_id(""); if (credit_card->record_type() == CreditCard::LOCAL_CARD) - database_->UpdateCreditCard(*credit_card); - else - database_->UpdateServerCardMetadata(*credit_card); + database_helper_->GetLocalDatabase()->UpdateCreditCard(*credit_card); + else { + DCHECK(database_helper_->GetServerDatabase()) + << "Updating metadata on null server db."; + database_helper_->GetServerDatabase()->UpdateServerCardMetadata( + *credit_card); + } } } } @@ -889,11 +1042,11 @@ if (!is_credit_card && !is_profile) return; - if (!database_) + if (!database_helper_->GetLocalDatabase()) return; if (is_credit_card) { - database_->RemoveCreditCard(guid); + database_helper_->GetLocalDatabase()->RemoveCreditCard(guid); } else { RemoveAutofillProfileByGUIDAndBlankCreditCardReferecne(guid); } @@ -1306,7 +1459,7 @@ for (AutofillProfile* profile : GetProfiles()) { if (profile->origin() != kSettingsOrigin && !profile->origin().empty()) { profile->set_origin(std::string()); - database_->UpdateAutofillProfile(*profile); + database_helper_->GetLocalDatabase()->UpdateAutofillProfile(*profile); has_updated = true; } } @@ -1323,7 +1476,7 @@ for (CreditCard* card : GetLocalCreditCards()) { if (card->origin() != kSettingsOrigin && !card->origin().empty()) { card->set_origin(std::string()); - database_->UpdateCreditCard(*card); + database_helper_->GetLocalDatabase()->UpdateCreditCard(*card); has_updated = true; } } @@ -1477,27 +1630,27 @@ IsEmptyFunctor<AutofillProfile>(app_locale_)), profiles->end()); - if (!database_) + if (!database_helper_->GetLocalDatabase()) return; // Any profiles that are not in the new profile list should be removed from // the web database. for (const auto& it : web_profiles_) { if (!FindByGUID<AutofillProfile>(*profiles, it->guid())) - database_->RemoveAutofillProfile(it->guid()); + database_helper_->GetLocalDatabase()->RemoveAutofillProfile(it->guid()); } // Update the web database with the existing profiles. for (const AutofillProfile& it : *profiles) { if (FindByGUID<AutofillProfile>(web_profiles_, it.guid())) - database_->UpdateAutofillProfile(it); + database_helper_->GetLocalDatabase()->UpdateAutofillProfile(it); } // Add the new profiles to the web database. Don't add a duplicate. for (const AutofillProfile& it : *profiles) { if (!FindByGUID<AutofillProfile>(web_profiles_, it.guid()) && !FindByContents(web_profiles_, it)) - database_->AddAutofillProfile(it); + database_helper_->GetLocalDatabase()->AddAutofillProfile(it); } // Copy in the new profiles. @@ -1520,27 +1673,27 @@ IsEmptyFunctor<CreditCard>(app_locale_)), credit_cards->end()); - if (!database_) + if (!database_helper_->GetLocalDatabase()) return; // Any credit cards that are not in the new credit card list should be // removed. for (const auto& card : local_credit_cards_) { if (!FindByGUID<CreditCard>(*credit_cards, card->guid())) - database_->RemoveCreditCard(card->guid()); + database_helper_->GetLocalDatabase()->RemoveCreditCard(card->guid()); } // Update the web database with the existing credit cards. for (const CreditCard& card : *credit_cards) { if (FindByGUID<CreditCard>(local_credit_cards_, card.guid())) - database_->UpdateCreditCard(card); + database_helper_->GetLocalDatabase()->UpdateCreditCard(card); } // Add the new credit cards to the web database. Don't add a duplicate. for (const CreditCard& card : *credit_cards) { if (!FindByGUID<CreditCard>(local_credit_cards_, card.guid()) && !FindByContents(local_credit_cards_, card)) - database_->AddCreditCard(card); + database_helper_->GetLocalDatabase()->AddCreditCard(card); } // Copy in the new credit cards. @@ -1553,43 +1706,69 @@ } void PersonalDataManager::LoadProfiles() { - if (!database_) { + if (!database_helper_->GetLocalDatabase()) { NOTREACHED(); return; } - CancelPendingQuery(&pending_profiles_query_); - CancelPendingQuery(&pending_server_profiles_query_); + CancelPendingLocalQuery(&pending_profiles_query_); + CancelPendingLocalQuery(&pending_server_profiles_query_); - pending_profiles_query_ = database_->GetAutofillProfiles(this); - pending_server_profiles_query_ = database_->GetServerProfiles(this); + pending_profiles_query_ = + database_helper_->GetLocalDatabase()->GetAutofillProfiles(this); + pending_server_profiles_query_ = + database_helper_->GetLocalDatabase()->GetServerProfiles(this); } void PersonalDataManager::LoadCreditCards() { - if (!database_) { + if (!database_helper_->GetLocalDatabase()) { NOTREACHED(); return; } - CancelPendingQuery(&pending_creditcards_query_); - CancelPendingQuery(&pending_server_creditcards_query_); + CancelPendingLocalQuery(&pending_creditcards_query_); + CancelPendingServerQuery(&pending_server_creditcards_query_); - pending_creditcards_query_ = database_->GetCreditCards(this); - pending_server_creditcards_query_ = database_->GetServerCreditCards(this); + pending_creditcards_query_ = + database_helper_->GetLocalDatabase()->GetCreditCards(this); + if (database_helper_->GetServerDatabase()) { + pending_server_creditcards_query_ = + database_helper_->GetServerDatabase()->GetServerCreditCards(this); + } } -void PersonalDataManager::CancelPendingQuery( +void PersonalDataManager::CancelPendingLocalQuery( WebDataServiceBase::Handle* handle) { if (*handle) { - if (!database_) { + if (!database_helper_->GetLocalDatabase()) { NOTREACHED(); return; } - database_->CancelRequest(*handle); + database_helper_->GetLocalDatabase()->CancelRequest(*handle); } *handle = 0; } +void PersonalDataManager::CancelPendingServerQuery( + WebDataServiceBase::Handle* handle) { + if (*handle) { + if (!database_helper_->GetServerDatabase()) { + NOTREACHED(); + return; + } + database_helper_->GetServerDatabase()->CancelRequest(*handle); + } + *handle = 0; +} + +void PersonalDataManager::CancelPendingServerQueries() { + if (pending_server_creditcards_query_) { + CancelPendingServerQuery(&pending_server_creditcards_query_); + } + // TODO(crbug.com/864519): also cancel the server addresses query once they + // use the account storage. +} + std::string PersonalDataManager::SaveImportedProfile( const AutofillProfile& imported_profile) { if (is_off_the_record_) @@ -1825,10 +2004,10 @@ if (pref_service_->GetBoolean(prefs::kAutofillOrphanRowsRemoved)) return; - if (!database_) + if (!database_helper_->GetLocalDatabase()) return; - database_->RemoveOrphanAutofillTableRows(); + database_helper_->GetLocalDatabase()->RemoveOrphanAutofillTableRows(); // Set the pref so that this fix is never run again. pref_service_->SetBoolean(prefs::kAutofillOrphanRowsRemoved, true); @@ -1869,10 +2048,11 @@ for (const auto& profile : web_profiles_) { // If the profile was set to be deleted, remove it from the database. if (profiles_to_delete.count(profile.get())) { - database_->RemoveAutofillProfile(profile->guid()); + database_helper_->GetLocalDatabase()->RemoveAutofillProfile( + profile->guid()); } else { // Otherwise, update the profile in the database. - database_->UpdateAutofillProfile(*profile); + database_helper_->GetLocalDatabase()->UpdateAutofillProfile(*profile); } } @@ -2008,9 +2188,10 @@ // If the card was modified, apply the changes to the database. if (was_modified) { if (credit_card->record_type() == CreditCard::LOCAL_CARD) - database_->UpdateCreditCard(*credit_card); + database_helper_->GetLocalDatabase()->UpdateCreditCard(*credit_card); else - database_->UpdateServerCardMetadata(*credit_card); + database_helper_->GetServerDatabase()->UpdateServerCardMetadata( + *credit_card); } } } @@ -2078,7 +2259,8 @@ // Update the wallet addresses metadata to record the conversion. wallet_address->set_has_converted(true); - database_->UpdateServerAddressMetadata(*wallet_address); + database_helper_->GetLocalDatabase()->UpdateServerAddressMetadata( + *wallet_address); has_converted_addresses = true; } @@ -2251,7 +2433,7 @@ size_t num_deleted_cards = guid_to_delete.size(); for (auto const guid : guid_to_delete) { - database_->RemoveCreditCard(guid); + database_helper_->GetLocalDatabase()->RemoveCreditCard(guid); } if (num_deleted_cards > 0) {
diff --git a/components/autofill/core/browser/personal_data_manager.h b/components/autofill/core/browser/personal_data_manager.h index f9792b2..43ba7af 100644 --- a/components/autofill/core/browser/personal_data_manager.h +++ b/components/autofill/core/browser/personal_data_manager.h
@@ -38,6 +38,7 @@ class AutofillInteractiveTest; class PersonalDataManagerObserver; class PersonalDataManagerFactory; +class PersonalDatabaseHelper; } // namespace autofill namespace autofill_helper { @@ -71,10 +72,16 @@ ~PersonalDataManager() override; // Kicks off asynchronous loading of profiles and credit cards. - // |pref_service| must outlive this instance. |is_off_the_record| informs - // this instance whether the user is currently operating in an off-the-record + // |profile_database| is a profile-scoped database that will be used to save + // local cards. |account_database| is scoped to the currently signed-in + // account, and is wiped on signout and browser exit. This can be a nullptr + // if personal_data_manager should use |profile_database| for all data. + // If passed in, the |account_database| is used by default for server cards. + // |pref_service| must outlive this instance. |is_off_the_record| informs this + // instance whether the user is currently operating in an off-the-record // context. - void Init(scoped_refptr<AutofillWebDataService> database, + void Init(scoped_refptr<AutofillWebDataService> profile_database, + scoped_refptr<AutofillWebDataService> account_database, PrefService* pref_service, identity::IdentityManager* identity_manager, bool is_off_the_record); @@ -86,6 +93,13 @@ // not be started, but it's preferences can be queried. virtual void OnSyncServiceInitialized(syncer::SyncService* sync_service); + // Set whether this should use the passed in account storage for server + // cards. If false, this will use the profile_storage. + // It's an error to call this if no account storage was passed in at + // initialization time. + void SetUseAccountStorageForServerCards( + bool use_account_storage_for_server_cards); + // WebDataServiceConsumer: void OnWebDataServiceRequestDone( WebDataServiceBase::Handle h, @@ -312,6 +326,9 @@ // Notifies test observers that personal data has changed. void NotifyPersonalDataChangedForTest() { NotifyPersonalDataChanged(); } + // Cancels any pending queries to the server web database. + void CancelPendingServerQueries(); + // This function assumes |credit_card| contains the full PAN. Returns |true| // if the card number of |credit_card| is equal to any local card or any // unmasked server card known by the browser, or |TypeAndLastFourDigits| of @@ -437,9 +454,13 @@ // Loads the saved credit cards from the web database. virtual void LoadCreditCards(); - // Cancels a pending query to the web database. |handle| is a pointer to the - // query handle. - void CancelPendingQuery(WebDataServiceBase::Handle* handle); + // Cancels a pending query to the local web database. |handle| is a pointer + // to the query handle. + void CancelPendingLocalQuery(WebDataServiceBase::Handle* handle); + + // Cancels a pending query to the server web database. |handle| is a pointer + // to the query handle. + void CancelPendingServerQuery(WebDataServiceBase::Handle* handle); // Notifies observers that personal data has changed. void NotifyPersonalDataChanged(); @@ -479,12 +500,8 @@ void ClearProfileNonSettingsOrigins(); void ClearCreditCardNonSettingsOrigins(); - void set_database(scoped_refptr<AutofillWebDataService> database) { - database_ = database; - } - - // The backing database that this PersonalDataManager uses. - scoped_refptr<AutofillWebDataService> database_; + // Decides which database type to use for server and local cards. + std::unique_ptr<PersonalDatabaseHelper> database_helper_; // True if personal data has been loaded from the web database. bool is_data_loaded_;
diff --git a/components/autofill/core/browser/personal_data_manager_unittest.cc b/components/autofill/core/browser/personal_data_manager_unittest.cc index 72057d6..c5710e2 100644 --- a/components/autofill/core/browser/personal_data_manager_unittest.cc +++ b/components/autofill/core/browser/personal_data_manager_unittest.cc
@@ -112,16 +112,71 @@ class PersonalDataManagerTestBase { protected: - PersonalDataManagerTestBase() : autofill_table_(nullptr) {} + PersonalDataManagerTestBase() : profile_autofill_table_(nullptr) {} - void ResetPersonalDataManager(UserMode user_mode) { + void SetUpTest() { + OSCryptMocker::SetUp(); + prefs_ = test::PrefServiceForTesting(); + ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); + base::FilePath path = temp_dir_.GetPath().AppendASCII("TestWebDB"); + profile_web_database_ = + new WebDatabaseService(path, base::ThreadTaskRunnerHandle::Get(), + base::ThreadTaskRunnerHandle::Get()); + + // Hacky: hold onto a pointer but pass ownership. + profile_autofill_table_ = new AutofillTable; + profile_web_database_->AddTable( + std::unique_ptr<WebDatabaseTable>(profile_autofill_table_)); + profile_web_database_->LoadDatabase(); + profile_database_service_ = new AutofillWebDataService( + profile_web_database_, base::ThreadTaskRunnerHandle::Get(), + base::ThreadTaskRunnerHandle::Get(), + WebDataServiceBase::ProfileErrorCallback()); + profile_database_service_->Init(); + + account_web_database_ = + new WebDatabaseService(base::FilePath(WebDatabase::kInMemoryPath), + base::ThreadTaskRunnerHandle::Get(), + base::ThreadTaskRunnerHandle::Get()); + account_autofill_table_ = new AutofillTable; + account_web_database_->AddTable( + std::unique_ptr<WebDatabaseTable>(account_autofill_table_)); + account_web_database_->LoadDatabase(); + account_database_service_ = new AutofillWebDataService( + account_web_database_, base::ThreadTaskRunnerHandle::Get(), + base::ThreadTaskRunnerHandle::Get(), + WebDataServiceBase::ProfileErrorCallback()); + account_database_service_->Init(); + + test::DisableSystemServices(prefs_.get()); + ResetPersonalDataManager(USER_MODE_NORMAL); + + // Reset the deduping pref to its default value. + personal_data_->pref_service_->SetInteger( + prefs::kAutofillLastVersionDeduped, 0); + } + + void TearDownTest() { + // Order of destruction is important as AutofillManager relies on + // PersonalDataManager to be around when it gets destroyed. + test::ReenableSystemServices(); + OSCryptMocker::TearDown(); + } + + void ResetPersonalDataManager(UserMode user_mode, + bool use_account_server_storage) { bool is_incognito = (user_mode == USER_MODE_INCOGNITO); personal_data_.reset(new PersonalDataManager("en")); personal_data_->Init( - scoped_refptr<AutofillWebDataService>(autofill_database_service_), + scoped_refptr<AutofillWebDataService>(profile_database_service_), + use_account_server_storage + ? scoped_refptr<AutofillWebDataService>(account_database_service_) + : nullptr, prefs_.get(), identity_test_env_.identity_manager(), is_incognito); personal_data_->AddObserver(&personal_data_observer_); personal_data_->OnSyncServiceInitialized(&sync_service_); + personal_data_->SetUseAccountStorageForServerCards( + use_account_server_storage); // Verify that the web database has been updated and the notification sent. EXPECT_CALL(personal_data_observer_, OnPersonalDataChanged()) @@ -129,6 +184,10 @@ base::RunLoop().Run(); } + void ResetPersonalDataManager(UserMode user_mode) { + ResetPersonalDataManager(user_mode, /*use_account_server_storage=*/true); + } + void ResetProfiles() { std::vector<AutofillProfile> empty_profiles; personal_data_->SetProfiles(&empty_profiles); @@ -283,6 +342,10 @@ EXPECT_EQ(1U, personal_data_->GetProfiles().size()); } + void SetServerCards(std::vector<CreditCard> server_cards) { + test::SetServerCreditCards(account_autofill_table_, server_cards); + } + // Verifies that the web database has been updated and the notification sent. void WaitForOnPersonalDataChanged() { EXPECT_CALL(personal_data_observer_, OnPersonalDataChanged()) @@ -297,48 +360,21 @@ std::unique_ptr<PrefService> prefs_; identity::IdentityTestEnvironment identity_test_env_; TestSyncService sync_service_; - scoped_refptr<AutofillWebDataService> autofill_database_service_; - scoped_refptr<WebDatabaseService> web_database_; - AutofillTable* autofill_table_; // weak ref + scoped_refptr<AutofillWebDataService> profile_database_service_; + scoped_refptr<AutofillWebDataService> account_database_service_; + scoped_refptr<WebDatabaseService> profile_web_database_; + scoped_refptr<WebDatabaseService> account_web_database_; + AutofillTable* profile_autofill_table_; // weak ref + AutofillTable* account_autofill_table_; // weak ref PersonalDataLoadedObserverMock personal_data_observer_; std::unique_ptr<PersonalDataManager> personal_data_; }; class PersonalDataManagerTest : public PersonalDataManagerTestBase, public testing::Test { - void SetUp() override { - OSCryptMocker::SetUp(); - prefs_ = test::PrefServiceForTesting(); - ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); - base::FilePath path = temp_dir_.GetPath().AppendASCII("TestWebDB"); - web_database_ = - new WebDatabaseService(path, base::ThreadTaskRunnerHandle::Get(), - base::ThreadTaskRunnerHandle::Get()); + void SetUp() override { SetUpTest(); } - // Hacky: hold onto a pointer but pass ownership. - autofill_table_ = new AutofillTable; - web_database_->AddTable(std::unique_ptr<WebDatabaseTable>(autofill_table_)); - web_database_->LoadDatabase(); - autofill_database_service_ = new AutofillWebDataService( - web_database_, base::ThreadTaskRunnerHandle::Get(), - base::ThreadTaskRunnerHandle::Get(), - WebDataServiceBase::ProfileErrorCallback()); - autofill_database_service_->Init(); - - test::DisableSystemServices(prefs_.get()); - ResetPersonalDataManager(USER_MODE_NORMAL); - - // Reset the deduping pref to its default value. - personal_data_->pref_service_->SetInteger( - prefs::kAutofillLastVersionDeduped, 0); - } - - void TearDown() override { - // Order of destruction is important as AutofillManager relies on - // PersonalDataManager to be around when it gets destroyed. - test::ReenableSystemServices(); - OSCryptMocker::TearDown(); - } + void TearDown() override { TearDownTest(); } }; TEST_F(PersonalDataManagerTest, AddProfile) { @@ -901,7 +937,7 @@ test::SetCreditCardInfo(&server_cards.back(), "Clyde Barrow", "378282246310005" /* American Express */, "04", "2999", "1"); - test::SetServerCreditCards(autofill_table_, server_cards); + SetServerCards(server_cards); personal_data_->Refresh(); WaitForOnPersonalDataChanged(); @@ -972,7 +1008,7 @@ "378282246310005" /* American Express */, "04", "2999", "1"); - test::SetServerCreditCards(autofill_table_, server_cards); + SetServerCards(server_cards); personal_data_->Refresh(); WaitForOnPersonalDataChanged(); @@ -1027,7 +1063,7 @@ server_cards.back().set_card_type(CreditCard::CARD_TYPE_DEBIT); - test::SetServerCreditCards(autofill_table_, server_cards); + SetServerCards(server_cards); personal_data_->Refresh(); WaitForOnPersonalDataChanged(); auto cards = personal_data_->GetCreditCards(); @@ -1222,7 +1258,7 @@ "joewayne@me.xyz", "Fox", "1212 Center.", "Bld. 5", "Orlando", "FL", "32801", "US", "19482937549"); - autofill_database_service_->AddAutofillProfile(profile2); + profile_database_service_->AddAutofillProfile(profile2); personal_data_->Refresh(); @@ -1234,8 +1270,8 @@ profiles.push_back(&profile2); ExpectSameElements(profiles, personal_data_->GetProfiles()); - autofill_database_service_->RemoveAutofillProfile(profile1.guid()); - autofill_database_service_->RemoveAutofillProfile(profile2.guid()); + profile_database_service_->RemoveAutofillProfile(profile1.guid()); + profile_database_service_->RemoveAutofillProfile(profile2.guid()); // Before telling the PDM to refresh, simulate an edit to one of the deleted // profiles via a SetProfile update (this would happen if the Autofill window @@ -1969,7 +2005,7 @@ // will be ignored when the profile is written to the DB. GetServerProfiles.back().SetRawInfo(NAME_FULL, base::ASCIIToUTF16("John Doe")); - autofill_table_->SetServerProfiles(GetServerProfiles); + profile_autofill_table_->SetServerProfiles(GetServerProfiles); // Disable Profile autofill. personal_data_->pref_service_->SetBoolean(prefs::kAutofillProfileEnabled, @@ -2014,7 +2050,7 @@ // will be ignored when the profile is written to the DB. GetServerProfiles.back().SetRawInfo(NAME_FULL, base::ASCIIToUTF16("John Doe")); - autofill_table_->SetServerProfiles(GetServerProfiles); + profile_autofill_table_->SetServerProfiles(GetServerProfiles); personal_data_->Refresh(); WaitForOnPersonalDataChanged(); @@ -2065,7 +2101,7 @@ "2110" /* last 4 digits */, "12", "2999", "1"); server_cards.back().SetNetworkForMaskedCard(kVisaCard); - test::SetServerCreditCards(autofill_table_, server_cards); + SetServerCards(server_cards); // Make sure everything is set up correctly. personal_data_->Refresh(); @@ -2084,7 +2120,7 @@ test::SetCreditCardInfo(&server_cards.back(), "Emmet Dalton", "4234567890122110" /* Visa */, "12", "2999", "1"); - test::SetServerCreditCards(autofill_table_, server_cards); + SetServerCards(server_cards); // Make sure everything is set up correctly. personal_data_->Refresh(); @@ -2167,7 +2203,7 @@ "2999", "1"); server_cards.back().SetNetworkForMaskedCard(kVisaCard); - test::SetServerCreditCards(autofill_table_, server_cards); + SetServerCards(server_cards); // Make sure everything is set up correctly. personal_data_->Refresh(); @@ -2231,7 +2267,7 @@ server_cards.back().set_use_date(AutofillClock::Now() - base::TimeDelta::FromDays(1)); - test::SetServerCreditCards(autofill_table_, server_cards); + SetServerCards(server_cards); // Make sure everything is set up correctly. personal_data_->Refresh(); @@ -2277,7 +2313,7 @@ server_cards.back().set_use_date(AutofillClock::Now() - base::TimeDelta::FromDays(1)); - test::SetServerCreditCards(autofill_table_, server_cards); + SetServerCards(server_cards); // Disable Credit card autofill. personal_data_->pref_service_->SetBoolean(prefs::kAutofillCreditCardEnabled, @@ -2320,7 +2356,7 @@ server_cards.back().set_use_date(AutofillClock::Now() - base::TimeDelta::FromDays(1)); - test::SetServerCreditCards(autofill_table_, server_cards); + SetServerCards(server_cards); personal_data_->Refresh(); WaitForOnPersonalDataChanged(); @@ -2430,7 +2466,7 @@ std::vector<CreditCard> server_cards; server_cards.push_back(credit_card1); server_cards.push_back(credit_card2); - test::SetServerCreditCards(autofill_table_, server_cards); + SetServerCards(server_cards); personal_data_->UpdateServerCardMetadata(credit_card1); personal_data_->UpdateServerCardMetadata(credit_card2); @@ -2614,7 +2650,7 @@ server_cards.back().set_use_date(AutofillClock::Now() - base::TimeDelta::FromDays(15)); - test::SetServerCreditCards(autofill_table_, server_cards); + SetServerCards(server_cards); // Make sure everything is set up correctly. personal_data_->Refresh(); @@ -2665,7 +2701,7 @@ "378282246310005" /* American Express */, "04", "2999", "1"); - test::SetServerCreditCards(autofill_table_, server_cards); + SetServerCards(server_cards); // Make sure everything is set up correctly. personal_data_->Refresh(); @@ -2731,7 +2767,7 @@ server_cards.back().SetNetworkForMaskedCard(kVisaCard); server_cards.back().set_bank_name("Chase"); - test::SetServerCreditCards(autofill_table_, server_cards); + SetServerCards(server_cards); // Make sure everything is set up correctly. personal_data_->Refresh(); @@ -2970,7 +3006,7 @@ TestAutofillClock test_clock; test_clock.SetNow(kArbitraryTime); - test::SetServerCreditCards(autofill_table_, server_cards); + SetServerCards(server_cards); // Make sure everything is set up correctly. personal_data_->Refresh(); @@ -3070,8 +3106,9 @@ test::SetCreditCardInfo(&server_cards.back(), "John Dillinger", "3456" /* Visa */, "01", "2999", "1"); server_cards.back().SetNetworkForMaskedCard(kVisaCard); - test::SetServerCreditCards(autofill_table_, server_cards); + SetServerCards(server_cards); personal_data_->Refresh(); + WaitForOnPersonalDataChanged(); // Need to set the google services username EnableWalletCardImport(); @@ -3134,37 +3171,9 @@ SaveImportedProfileTest() {} ~SaveImportedProfileTest() override {} - void SetUp() override { - OSCryptMocker::SetUp(); - prefs_ = test::PrefServiceForTesting(); - ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); - base::FilePath path = temp_dir_.GetPath().AppendASCII("TestWebDB"); - web_database_ = - new WebDatabaseService(path, base::ThreadTaskRunnerHandle::Get(), - base::ThreadTaskRunnerHandle::Get()); + void SetUp() override { SetUpTest(); } - // Hacky: hold onto a pointer but pass ownership. - autofill_table_ = new AutofillTable; - web_database_->AddTable(std::unique_ptr<WebDatabaseTable>(autofill_table_)); - web_database_->LoadDatabase(); - autofill_database_service_ = new AutofillWebDataService( - web_database_, base::ThreadTaskRunnerHandle::Get(), - base::ThreadTaskRunnerHandle::Get(), - WebDataServiceBase::ProfileErrorCallback()); - autofill_database_service_->Init(); - - test::DisableSystemServices(prefs_.get()); - ResetPersonalDataManager(USER_MODE_NORMAL); - - // Reset the deduping pref to its default value. - personal_data_->pref_service_->SetInteger( - prefs::kAutofillLastVersionDeduped, 0); - } - - void TearDown() override { - test::DisableSystemServices(prefs_.get()); - OSCryptMocker::TearDown(); - } + void TearDown() override { TearDownTest(); } }; TEST_P(SaveImportedProfileTest, SaveImportedProfile) { @@ -4727,7 +4736,7 @@ std::vector<CreditCard> server_cards; server_cards.push_back(credit_card5); server_cards.push_back(credit_card6); - test::SetServerCreditCards(autofill_table_, server_cards); + SetServerCards(server_cards); personal_data_->UpdateServerCardMetadata(credit_card5); personal_data_->UpdateServerCardMetadata(credit_card6); @@ -4792,7 +4801,7 @@ GetServerProfiles.back().SetRawInfo(NAME_FULL, base::ASCIIToUTF16("John Doe")); GetServerProfiles.back().set_use_count(100); - autofill_table_->SetServerProfiles(GetServerProfiles); + profile_autofill_table_->SetServerProfiles(GetServerProfiles); // Add a server and a local card that have the server address as billing // address. @@ -4811,7 +4820,7 @@ "1111" /* Visa */, "01", "2999", "1"); server_cards.back().SetNetworkForMaskedCard(kVisaCard); server_cards.back().set_billing_address_id(kServerAddressId); - test::SetServerCreditCards(autofill_table_, server_cards); + SetServerCards(server_cards); // Make sure everything is set up correctly. personal_data_->Refresh(); @@ -4896,7 +4905,7 @@ GetServerProfiles.back().SetRawInfo(NAME_FULL, base::ASCIIToUTF16("John Doe")); GetServerProfiles.back().set_use_count(100); - autofill_table_->SetServerProfiles(GetServerProfiles); + profile_autofill_table_->SetServerProfiles(GetServerProfiles); // Add a server and a local card that have the server address as billing // address. @@ -4915,7 +4924,7 @@ "1111" /* Visa */, "01", "2999", "1"); server_cards.back().SetNetworkForMaskedCard(kVisaCard); server_cards.back().set_billing_address_id(kServerAddressId); - test::SetServerCreditCards(autofill_table_, server_cards); + SetServerCards(server_cards); // Make sure everything is set up correctly. personal_data_->Refresh(); @@ -4944,7 +4953,7 @@ // The conversion should be recorded in the Wallet address. EXPECT_TRUE(personal_data_->GetServerProfiles().back()->has_converted()); - // Get the profiles, sorted by frecency to have a deterministic order. + // Get the profiles, sorted by frequency to have a deterministic order. std::vector<AutofillProfile*> profiles = personal_data_->GetProfilesToSuggest(); @@ -4987,7 +4996,7 @@ GetServerProfiles.back().set_has_converted(true); // Wallet only provides a full name, so the above first and last names // will be ignored when the profile is written to the DB. - autofill_table_->SetServerProfiles(GetServerProfiles); + profile_autofill_table_->SetServerProfiles(GetServerProfiles); // Make sure everything is set up correctly. personal_data_->Refresh(); @@ -5063,7 +5072,7 @@ GetServerProfiles.back().SetRawInfo(NAME_FULL, base::ASCIIToUTF16("John Doe")); GetServerProfiles.back().set_use_count(200); - autofill_table_->SetServerProfiles(GetServerProfiles); + profile_autofill_table_->SetServerProfiles(GetServerProfiles); // Add a server and a local card that have the first and second Wallet address // as a billing address. @@ -5082,7 +5091,7 @@ "1111" /* Visa */, "01", "2999", "1"); server_cards.back().SetNetworkForMaskedCard(kVisaCard); server_cards.back().set_billing_address_id(kServerAddressId2); - test::SetServerCreditCards(autofill_table_, server_cards); + SetServerCards(server_cards); // Make sure everything is set up correctly. personal_data_->Refresh(); @@ -5165,7 +5174,7 @@ GetServerProfiles.back().SetRawInfo(NAME_FULL, base::ASCIIToUTF16("John Doe")); GetServerProfiles.back().set_use_count(100); - autofill_table_->SetServerProfiles(GetServerProfiles); + profile_autofill_table_->SetServerProfiles(GetServerProfiles); // Add a server card that have the server address as billing address. std::vector<CreditCard> server_cards; @@ -5175,7 +5184,7 @@ "1111" /* Visa */, "01", "2999", "1"); server_cards.back().SetNetworkForMaskedCard(kVisaCard); server_cards.back().set_billing_address_id(kServerAddressId); - test::SetServerCreditCards(autofill_table_, server_cards); + SetServerCards(server_cards); // Make sure everything is set up correctly. personal_data_->Refresh(); @@ -5209,7 +5218,7 @@ "1112" /* Visa */, "01", "2888", "1"); server_cards.back().SetNetworkForMaskedCard(kVisaCard); server_cards.back().set_billing_address_id(kServerAddressId); - test::SetServerCreditCards(autofill_table_, server_cards); + test::SetServerCreditCards(account_autofill_table_, server_cards); // Make sure everything is set up correctly. personal_data_->Refresh(); @@ -5289,7 +5298,7 @@ personal_data_->AddProfile(profile1); personal_data_->AddCreditCard(local_card0); personal_data_->AddCreditCard(local_card1); - test::SetServerCreditCards(autofill_table_, server_cards); + SetServerCards(server_cards); // Verify that the web database has been updated and the notification sent. personal_data_->Refresh(); @@ -5558,13 +5567,13 @@ } } - test::SetServerCreditCards(autofill_table_, server_cards); + SetServerCards(server_cards); - // test::SetServerCreditCards modifies the metadata (use_count and use_date) + // SetServerCards modifies the metadata (use_count and use_date) // of unmasked cards. Reset the server card metadata to match the data set // up above. for (const auto& card : server_cards) - autofill_table_->UpdateServerCardMetadata(card); + account_autofill_table_->UpdateServerCardMetadata(card); personal_data_->Refresh(); WaitForOnPersonalDataChanged(); @@ -6288,4 +6297,82 @@ EXPECT_TRUE(personal_data_->GetCreditCards()[0]->origin().empty()); } +// Sanity check that the mode where we use the regular, persistent storage for +// cards still works. +TEST_F(PersonalDataManagerTest, UsePersistentServerStorage) { + ResetPersonalDataManager(USER_MODE_NORMAL, + /*use_account_server_storage=*/false); + SetUpThreeCardTypes(); + + // include_server_cards is set to false, therefore no server cards should be + // available for suggestion, but that the other calls to get the credit cards + // are unaffected. + EXPECT_EQ(3U, personal_data_->GetCreditCards().size()); + EXPECT_EQ(1U, personal_data_ + ->GetCreditCardsToSuggest(/*include_server_cards=*/false) + .size()); + EXPECT_EQ(1U, personal_data_->GetLocalCreditCards().size()); + EXPECT_EQ(2U, personal_data_->GetServerCreditCards().size()); +} + +// Sanity check that the mode where we use the regular, persistent storage for +// cards still works. +TEST_F(PersonalDataManagerTest, UseCorrectStorageForDifferentCards) { + ResetPersonalDataManager(USER_MODE_NORMAL); + + // Add a server card + CreditCard server_card; + test::SetCreditCardInfo(&server_card, "Server Card", + "4234567890123456", // Visa + "04", "2999", "1"); + server_card.set_guid("00000000-0000-0000-0000-000000000007"); + server_card.set_record_type(CreditCard::FULL_SERVER_CARD); + server_card.set_server_id("server_id"); + personal_data_->AddFullServerCreditCard(server_card); + + // Set server card metadata. + server_card.set_use_count(15); + personal_data_->UpdateServerCardMetadata(server_card); + + WaitForOnPersonalDataChanged(); + + // Expect that the server card is stored in the account autofill table. + std::vector<std::unique_ptr<CreditCard>> cards; + account_autofill_table_->GetServerCreditCards(&cards); + EXPECT_EQ(1U, cards.size()); + EXPECT_EQ(server_card.LastFourDigits(), cards[0]->LastFourDigits()); + + // Add a local card + CreditCard local_card; + test::SetCreditCardInfo(&local_card, "Freddy Mercury", + "4234567890123463", // Visa + "08", "2999", "1"); + local_card.set_guid("00000000-0000-0000-0000-000000000009"); + local_card.set_record_type(CreditCard::LOCAL_CARD); + local_card.set_use_date(AutofillClock::Now() - base::TimeDelta::FromDays(5)); + personal_data_->AddCreditCard(local_card); + + WaitForOnPersonalDataChanged(); + + // Expect that the local card is stored in the profile autofill table. + profile_autofill_table_->GetCreditCards(&cards); + EXPECT_EQ(1U, cards.size()); + EXPECT_EQ(local_card.LastFourDigits(), cards[0]->LastFourDigits()); + + // Add a local profile + AutofillProfile profile(base::GenerateGUID(), "https://www.example.com"); + test::SetProfileInfo(&profile, "Marion", "Mitchell", "Morrison", + "johnwayne@me.xyz", "Fox", "123 Zoo St", "unit 5", + "Hollywood", "CA", "91601", "US", "12345678910"); + personal_data_->AddProfile(profile); + + WaitForOnPersonalDataChanged(); + + std::vector<std::unique_ptr<AutofillProfile>> profiles; + // Expect that a profile is stored in the profile autofill table. + profile_autofill_table_->GetAutofillProfiles(&profiles); + EXPECT_EQ(1U, profiles.size()); + EXPECT_EQ(profile, *profiles[0]); +} + } // namespace autofill
diff --git a/components/autofill/core/browser/test_personal_data_manager.h b/components/autofill/core/browser/test_personal_data_manager.h index ca9ddfb..91c18748 100644 --- a/components/autofill/core/browser/test_personal_data_manager.h +++ b/components/autofill/core/browser/test_personal_data_manager.h
@@ -20,7 +20,6 @@ TestPersonalDataManager(); ~TestPersonalDataManager() override; - using PersonalDataManager::set_database; using PersonalDataManager::SetPrefService; // PersonalDataManager overrides. These functions are overridden as needed
diff --git a/components/autofill/core/common/autofill_features.cc b/components/autofill/core/common/autofill_features.cc index c36bd9d..e46a2b4 100644 --- a/components/autofill/core/common/autofill_features.cc +++ b/components/autofill/core/common/autofill_features.cc
@@ -27,6 +27,11 @@ const base::Feature kAutofillDynamicForms{"AutofillDynamicForms", base::FEATURE_DISABLED_BY_DEFAULT}; +// Controls whether the server credit cards are saved only in the ephemeral +// account-based storage, instead of the persistent local storage. +const base::Feature kAutofillEnableAccountWalletStorage{ + "AutofillEnableAccountWalletStorage", base::FEATURE_DISABLED_BY_DEFAULT}; + // Controls whether the server credit cards are offered to be filled and // uploaded to Google Pay if the sync service is in auth error. const base::Feature kAutofillEnablePaymentsInteractionsOnAuthError{
diff --git a/components/autofill/core/common/autofill_features.h b/components/autofill/core/common/autofill_features.h index bf444da..25d04873 100644 --- a/components/autofill/core/common/autofill_features.h +++ b/components/autofill/core/common/autofill_features.h
@@ -15,6 +15,7 @@ extern const base::Feature kAutofillCacheQueryResponses; extern const base::Feature kAutofillDownstreamUseGooglePayBrandingOniOS; extern const base::Feature kAutofillDynamicForms; +extern const base::Feature kAutofillEnableAccountWalletStorage; extern const base::Feature kAutofillEnablePaymentsInteractionsOnAuthError; extern const base::Feature kAutofillEnforceMinRequiredFieldsForHeuristics; extern const base::Feature kAutofillEnforceMinRequiredFieldsForQuery;
diff --git a/components/autofill/core/common/password_form.h b/components/autofill/core/common/password_form.h index b3d9b14..cc168863 100644 --- a/components/autofill/core/common/password_form.h +++ b/components/autofill/core/common/password_form.h
@@ -177,6 +177,11 @@ uint32_t username_element_renderer_id = FormFieldData::kNotSetFormControlRendererId; + // True if the server-side classification believes that the field may be + // pre-filled with a placeholder in the value attribute. It is set during + // form parsing and not persisted. + bool username_may_use_prefilled_placeholder = false; + // Whether the |username_element| has an autocomplete=username attribute. This // is only used in parsed HTML forms. bool username_marked_by_site;
diff --git a/components/autofill/core/common/password_form_fill_data.cc b/components/autofill/core/common/password_form_fill_data.cc index 8764ce5c..bcc0766 100644 --- a/components/autofill/core/common/password_form_fill_data.cc +++ b/components/autofill/core/common/password_form_fill_data.cc
@@ -42,6 +42,8 @@ username_field.name = form_on_page.username_element; username_field.value = preferred_match->username_value; username_field.unique_renderer_id = form_on_page.username_element_renderer_id; + result->username_may_use_prefilled_placeholder = + form_on_page.username_may_use_prefilled_placeholder; FormFieldData password_field; password_field.name = form_on_page.password_element; password_field.value = preferred_match->password_value;
diff --git a/components/autofill/core/common/password_form_fill_data.h b/components/autofill/core/common/password_form_fill_data.h index cdc56d10..cc90089 100644 --- a/components/autofill/core/common/password_form_fill_data.h +++ b/components/autofill/core/common/password_form_fill_data.h
@@ -64,6 +64,10 @@ FormFieldData username_field; FormFieldData password_field; + // True if the server-side classification believes that the field may be + // pre-filled with a placeholder in the value attribute. + bool username_may_use_prefilled_placeholder = false; + // The signon realm of the preferred user/pass pair. std::string preferred_realm;
diff --git a/components/autofill/core/common/password_form_fill_data_unittest.cc b/components/autofill/core/common/password_form_fill_data_unittest.cc index 44aafe5..ffe37b1 100644 --- a/components/autofill/core/common/password_form_fill_data_unittest.cc +++ b/components/autofill/core/common/password_form_fill_data_unittest.cc
@@ -249,6 +249,7 @@ form_on_page.action = GURL("https://foo.com/login"); form_on_page.username_element = ASCIIToUTF16("username"); form_on_page.password_element = ASCIIToUTF16("password"); + form_on_page.username_may_use_prefilled_placeholder = true; // Create an exact match in the database. PasswordForm preferred_match = form_on_page; @@ -277,6 +278,7 @@ result.username_field.unique_renderer_id); EXPECT_EQ(form_on_page.password_element_renderer_id, result.password_field.unique_renderer_id); + EXPECT_TRUE(result.username_may_use_prefilled_placeholder); } } // namespace autofill
diff --git a/components/autofill/ios/browser/autofill_agent.h b/components/autofill/ios/browser/autofill_agent.h index 10cc33e..dba1b58 100644 --- a/components/autofill/ios/browser/autofill_agent.h +++ b/components/autofill/ios/browser/autofill_agent.h
@@ -39,7 +39,7 @@ - (instancetype)init NS_UNAVAILABLE; // Callback by AutofillController when suggestions are ready. -- (void)onSuggestionsReady:(NSArray*)suggestions +- (void)onSuggestionsReady:(NSArray<FormSuggestion*>*)suggestions popupDelegate: (const base::WeakPtr<autofill::AutofillPopupDelegate>&) delegate;
diff --git a/components/autofill/ios/browser/autofill_agent.mm b/components/autofill/ios/browser/autofill_agent.mm index dd281d0..1073e86 100644 --- a/components/autofill/ios/browser/autofill_agent.mm +++ b/components/autofill/ios/browser/autofill_agent.mm
@@ -137,7 +137,7 @@ // Rearranges and filters the suggestions to move profile or credit card // suggestions to the front if the user has selected one recently and remove // key/value suggestions if the user hasn't started typing. -- (NSArray*)processSuggestions:(NSArray*)suggestions; +- (NSArray*)processSuggestions:(NSArray<FormSuggestion*>*)suggestions; // Sends the the |formData| to the JavaScript manager of |webState_| to actually // fill the data. @@ -166,10 +166,6 @@ // focused form element in order to force filling of the currently selected // form element, even if it's non-empty. base::string16 pendingAutocompleteField_; - // The identifier of the most recent suggestion accepted by the user. Only - // used to reorder future suggestion lists, placing matching suggestions first - // in the list. - NSInteger mostRecentSelectedIdentifier_; // Suggestions state: // The most recent form suggestions. @@ -308,27 +304,11 @@ }]; } -- (NSArray*)processSuggestions:(NSArray*)suggestions { +- (NSArray*)processSuggestions:(NSArray<FormSuggestion*>*)suggestions { // The suggestion array is cloned (to claim ownership) and to slightly // reorder; a future improvement is to base order on text typed in other // fields by users as well as accepted suggestions (crbug.com/245261). - NSMutableArray* suggestionsCopy = [suggestions mutableCopy]; - - // If the most recently selected suggestion was a profile or credit card - // suggestion, move it to the front of the suggestions. - if (mostRecentSelectedIdentifier_ > 0) { - NSUInteger idx = [suggestionsCopy - indexOfObjectPassingTest:^BOOL(id obj, NSUInteger, BOOL*) { - FormSuggestion* suggestion = obj; - return suggestion.identifier == mostRecentSelectedIdentifier_; - }]; - - if (idx != NSNotFound) { - FormSuggestion* suggestion = suggestionsCopy[idx]; - [suggestionsCopy removeObjectAtIndex:idx]; - [suggestionsCopy insertObject:suggestion atIndex:0]; - } - } + NSMutableArray<FormSuggestion*>* suggestionsCopy = [suggestions mutableCopy]; // Filter out any key/value suggestions if the user hasn't typed yet. if ([typedValue_ length] == 0) { @@ -341,12 +321,17 @@ } // If "clear form" entry exists then move it to the front of the suggestions. + // If "GPay branding" icon is present, it remains as the first suggestion. for (NSInteger idx = [suggestionsCopy count] - 1; idx > 0; idx--) { FormSuggestion* suggestion = suggestionsCopy[idx]; if (suggestion.identifier == autofill::POPUP_ITEM_ID_CLEAR_FORM) { - FormSuggestion* suggestionToMove = suggestionsCopy[idx]; + BOOL hasGPayBranding = suggestionsCopy[0].identifier == + autofill::POPUP_ITEM_ID_GOOGLE_PAY_BRANDING; + + FormSuggestion* clearFormSuggestion = suggestionsCopy[idx]; [suggestionsCopy removeObjectAtIndex:idx]; - [suggestionsCopy insertObject:suggestionToMove atIndex:0]; + [suggestionsCopy insertObject:clearFormSuggestion + atIndex:hasGPayBranding ? 1 : 0]; break; } } @@ -354,7 +339,7 @@ return suggestionsCopy; } -- (void)onSuggestionsReady:(NSArray*)suggestions +- (void)onSuggestionsReady:(NSArray<FormSuggestion*>*)suggestions popupDelegate: (const base::WeakPtr<autofill::AutofillPopupDelegate>&) delegate { @@ -482,7 +467,6 @@ completionHandler:(SuggestionHandledCompletion)completion { [[UIDevice currentDevice] playInputClick]; suggestionHandledCompletion_ = [completion copy]; - mostRecentSelectedIdentifier_ = suggestion.identifier; if (suggestion.identifier > 0) { pendingAutocompleteField_ = base::SysNSStringToUTF16(fieldIdentifier);
diff --git a/components/autofill/ios/browser/autofill_agent_unittests.mm b/components/autofill/ios/browser/autofill_agent_unittests.mm index 5fdb4811..5f8c2b4 100644 --- a/components/autofill/ios/browser/autofill_agent_unittests.mm +++ b/components/autofill/ios/browser/autofill_agent_unittests.mm
@@ -7,6 +7,7 @@ #include "base/strings/utf_string_conversions.h" #import "base/test/ios/wait_util.h" #include "components/autofill/core/browser/autofill_test_utils.h" +#include "components/autofill/core/browser/popup_item_ids.h" #include "components/autofill/core/common/autofill_pref_names.h" #include "components/autofill/core/common/form_data.h" #import "components/autofill/ios/browser/js_autofill_manager.h" @@ -25,6 +26,8 @@ #error "This file requires ARC support." #endif +using autofill::POPUP_ITEM_ID_CLEAR_FORM; +using autofill::POPUP_ITEM_ID_GOOGLE_PAY_BRANDING; using base::test::ios::WaitUntilCondition; // Test fixture for AutofillAgent testing. @@ -155,7 +158,7 @@ // Tests that when a user initiated form activity is registered the script to // extract forms is executed. -TEST_F(AutofillAgentTests, SuggestionsAvailable_UserInitiatedActivity) { +TEST_F(AutofillAgentTests, CheckIfSuggestionsAvailable_UserInitiatedActivity) { [[mock_js_injection_receiver_ expect] executeJavaScript:@"__gCrWeb.autofill.extractForms(1, true);" completionHandler:[OCMArg any]]; @@ -177,7 +180,8 @@ // Tests that when a non user initiated form activity is registered the // completion callback passed to the call to check if suggestions are available // is invoked with no suggestions. -TEST_F(AutofillAgentTests, SuggestionsAvailable_NonUserInitiatedActivity) { +TEST_F(AutofillAgentTests, + CheckIfSuggestionsAvailable_NonUserInitiatedActivity) { __block BOOL completion_handler_success = NO; __block BOOL completion_handler_called = NO; @@ -202,3 +206,120 @@ }); EXPECT_FALSE(completion_handler_success); } + +// Tests that when Autofill suggestions are made available to AutofillManager +// "Clear Form" is moved to the start of the list and the order of other +// suggestions remains unchanged. +TEST_F(AutofillAgentTests, onSuggestionsReady_ClearForm) { + __block NSArray<FormSuggestion*>* completion_handler_suggestions = nil; + __block BOOL completion_handler_called = NO; + + // Make the suggestions available to AutofillManager. + NSArray* suggestions = @[ + [FormSuggestion suggestionWithValue:@"" + displayDescription:nil + icon:@"" + identifier:123], + [FormSuggestion suggestionWithValue:@"" + displayDescription:nil + icon:@"" + identifier:321], + [FormSuggestion suggestionWithValue:@"" + displayDescription:nil + icon:@"" + identifier:autofill::POPUP_ITEM_ID_CLEAR_FORM] + ]; + [autofill_agent_ + onSuggestionsReady:suggestions + popupDelegate:base::WeakPtr<autofill::AutofillPopupDelegate>()]; + + // Retrieves the suggestions. + auto completionHandler = ^(NSArray<FormSuggestion*>* suggestions, + id<FormSuggestionProvider> delegate) { + completion_handler_suggestions = [suggestions copy]; + completion_handler_called = YES; + }; + [autofill_agent_ retrieveSuggestionsForForm:@"form" + fieldName:@"address" + fieldIdentifier:@"address" + fieldType:@"text" + type:@"focus" + typedValue:@"" + webState:&test_web_state_ + completionHandler:completionHandler]; + test_web_state_.WasShown(); + + // Wait until the expected handler is called. + WaitUntilCondition(^bool() { + return completion_handler_called; + }); + + // "Clear Form" should appear as the first suggestion. Otherwise, the order of + // suggestions should not change. + EXPECT_EQ(3U, completion_handler_suggestions.count); + EXPECT_EQ(POPUP_ITEM_ID_CLEAR_FORM, + completion_handler_suggestions[0].identifier); + EXPECT_EQ(123, completion_handler_suggestions[1].identifier); + EXPECT_EQ(321, completion_handler_suggestions[2].identifier); +} + +// Tests that when Autofill suggestions are made available to AutofillManager +// GPay icon remains as the first suggestion. +TEST_F(AutofillAgentTests, onSuggestionsReady_ClearFormWithGPay) { + __block NSArray<FormSuggestion*>* completion_handler_suggestions = nil; + __block BOOL completion_handler_called = NO; + + // Make the suggestions available to AutofillManager. + NSArray* suggestions = @[ + [FormSuggestion suggestionWithValue:@"" + displayDescription:nil + icon:@"" + identifier:POPUP_ITEM_ID_GOOGLE_PAY_BRANDING], + [FormSuggestion suggestionWithValue:@"" + displayDescription:nil + icon:@"" + identifier:123], + [FormSuggestion suggestionWithValue:@"" + displayDescription:nil + icon:@"" + identifier:321], + [FormSuggestion suggestionWithValue:@"" + displayDescription:nil + icon:@"" + identifier:POPUP_ITEM_ID_CLEAR_FORM] + ]; + [autofill_agent_ + onSuggestionsReady:suggestions + popupDelegate:base::WeakPtr<autofill::AutofillPopupDelegate>()]; + + // Retrieves the suggestions. + auto completionHandler = ^(NSArray<FormSuggestion*>* suggestions, + id<FormSuggestionProvider> delegate) { + completion_handler_suggestions = [suggestions copy]; + completion_handler_called = YES; + }; + [autofill_agent_ retrieveSuggestionsForForm:@"form" + fieldName:@"address" + fieldIdentifier:@"address" + fieldType:@"text" + type:@"focus" + typedValue:@"" + webState:&test_web_state_ + completionHandler:completionHandler]; + test_web_state_.WasShown(); + + // Wait until the expected handler is called. + WaitUntilCondition(^bool() { + return completion_handler_called; + }); + + // GPay icon should appear as the first suggestion followed by "Clear Form". + // Otherwise, the order of suggestions should not change. + EXPECT_EQ(4U, completion_handler_suggestions.count); + EXPECT_EQ(POPUP_ITEM_ID_GOOGLE_PAY_BRANDING, + completion_handler_suggestions[0].identifier); + EXPECT_EQ(POPUP_ITEM_ID_CLEAR_FORM, + completion_handler_suggestions[1].identifier); + EXPECT_EQ(123, completion_handler_suggestions[2].identifier); + EXPECT_EQ(321, completion_handler_suggestions[3].identifier); +}
diff --git a/components/autofill/ios/browser/form_suggestion_provider.h b/components/autofill/ios/browser/form_suggestion_provider.h index 606d8f5..e06720de 100644 --- a/components/autofill/ios/browser/form_suggestion_provider.h +++ b/components/autofill/ios/browser/form_suggestion_provider.h
@@ -14,8 +14,9 @@ } // namespace web typedef void (^SuggestionsAvailableCompletion)(BOOL suggestionsAvailable); -typedef void (^SuggestionsReadyCompletion)(NSArray* suggestions, - id<FormSuggestionProvider> delegate); +typedef void (^SuggestionsReadyCompletion)( + NSArray<FormSuggestion*>* suggestions, + id<FormSuggestionProvider> delegate); typedef void (^SuggestionHandledCompletion)(void); // Provides user-selectable suggestions for an input field of a web form
diff --git a/components/browser_sync/profile_sync_service.cc b/components/browser_sync/profile_sync_service.cc index 87729c1..a15e288 100644 --- a/components/browser_sync/profile_sync_service.cc +++ b/components/browser_sync/profile_sync_service.cc
@@ -784,14 +784,6 @@ // Since there is no disable reason, Sync can start in principle. DCHECK(CanSyncStart()); - // The presence of an auth error overrides any non-disabled state. - // Note: We typically shouldn't have auth errors without an initialized - // engine, but it can happen e.g. if a refresh token gets revoked while Sync - // is off. - if (GetAuthError().state() != GoogleServiceAuthError::NONE) { - return State::AUTH_ERROR; - } - // Typically, Sync won't start until the initial setup is at least in // progress. StartupController::TryStartImmediately bypasses the first setup // check though, so we first have to check whether the engine is initialized. @@ -813,27 +805,26 @@ // The DataTypeManager gets created once the engine is initialized. DCHECK(data_type_manager_); - if (!IsFirstSetupComplete()) { - DCHECK(data_type_manager_->state() != DataTypeManager::CONFIGURED); - return State::WAITING_FOR_CONSENT; + // At this point we should usually be able to configure our data types (and + // once the data types can be configured, they must actually get configured). + // However, if the initial setup hasn't been completed, then we can't + // configure the data types. Also if a later (non-initial) setup happens to be + // in progress, we won't configure them right now. + if (data_type_manager_->state() == DataTypeManager::STOPPED) { + DCHECK(!CanConfigureDataTypes()); + return State::PENDING_DESIRED_CONFIGURATION; } -#if DCHECK_IS_ON() - // At this point we should generally be able to configure our data types, - // except if a setup is currently in progress (i.e. the Sync settings UI is - // being shown). Note that if a setup is started after the data types have - // been configured, then they'll stay configured even though - // CanConfigureDataTypes will be false. - if (!IsSetupInProgress()) { - DCHECK(CanConfigureDataTypes()); - // After data types *can* be configured, they must actually get configured, - // so the DataTypeManager should have gotten out of the initial STOPPED - // state. It can only go back to STOPPED in case of unrecoverable errors, - // for which we already checked above. - DCHECK_NE(data_type_manager_->state(), DataTypeManager::STOPPED); - DCHECK(IsSyncActive()); - } -#endif // DCHECK_IS_ON() + // The DataTypeManager shouldn't get configured (i.e. leave the STOPPED state) + // before the initial setup is complete. + DCHECK(IsFirstSetupComplete()); + + // Note that if a setup is started after the data types have been configured, + // then they'll stay configured even though CanConfigureDataTypes will be + // false. + DCHECK(CanConfigureDataTypes() || IsSetupInProgress()); + + DCHECK(IsSyncActive()); if (data_type_manager_->state() != DataTypeManager::CONFIGURED) { return State::CONFIGURING;
diff --git a/components/browser_sync/profile_sync_service_autofill_unittest.cc b/components/browser_sync/profile_sync_service_autofill_unittest.cc index 87737a9b..254144d 100644 --- a/components/browser_sync/profile_sync_service_autofill_unittest.cc +++ b/components/browser_sync/profile_sync_service_autofill_unittest.cc
@@ -369,6 +369,7 @@ EXPECT_CALL(personal_data_manager(), LoadCreditCards()); personal_data_manager_->Init(web_data_service_, + /*account_database=*/nullptr, profile_sync_service_bundle()->pref_service(), /*identity_manager=*/nullptr, /*is_off_the_record=*/false);
diff --git a/components/browser_sync/profile_sync_service_startup_unittest.cc b/components/browser_sync/profile_sync_service_startup_unittest.cc index a145766..1ea8d3b 100644 --- a/components/browser_sync/profile_sync_service_startup_unittest.cc +++ b/components/browser_sync/profile_sync_service_startup_unittest.cc
@@ -214,7 +214,7 @@ EXPECT_EQ(syncer::SyncService::DISABLE_REASON_NONE, sync_service()->GetDisableReasons()); EXPECT_FALSE(sync_service()->IsSyncActive()); - EXPECT_EQ(syncer::SyncService::State::WAITING_FOR_CONSENT, + EXPECT_EQ(syncer::SyncService::State::PENDING_DESIRED_CONFIGURATION, sync_service()->GetState()); // Setup is already in progress, so confirmation still isn't needed. @@ -229,7 +229,7 @@ ASSERT_FALSE(sync_service()->IsSetupInProgress()); EXPECT_TRUE(sync_service()->IsSyncConfirmationNeeded()); EXPECT_FALSE(sync_service()->IsSyncActive()); - EXPECT_EQ(syncer::SyncService::State::WAITING_FOR_CONSENT, + EXPECT_EQ(syncer::SyncService::State::PENDING_DESIRED_CONFIGURATION, sync_service()->GetState()); // Marking first setup complete will let ProfileSyncService configure the @@ -605,15 +605,16 @@ syncer::ModelTypeSet(), syncer::WeakHandle<syncer::JsBackend>(), syncer::WeakHandle<syncer::DataTypeDebugInfoListener>(), "test-guid", /*success=*/true); - EXPECT_EQ(syncer::SyncService::State::WAITING_FOR_CONSENT, + ASSERT_TRUE(sync_service()->IsEngineInitialized()); + EXPECT_EQ(syncer::SyncService::State::PENDING_DESIRED_CONFIGURATION, sync_service()->GetState()); // Once the user finishes the initial setup, the service can actually start - // configuring the data types. It won't actually call the DataTypeManager yet - // though, because setup is still considered in progress (we haven't released - // the setup-in-progress handle). + // configuring the data types. Just marking the initial setup as complete + // isn't enough though, because setup is still considered in progress (we + // haven't released the setup-in-progress handle). sync_service()->SetFirstSetupComplete(); - EXPECT_EQ(syncer::SyncService::State::CONFIGURING, + EXPECT_EQ(syncer::SyncService::State::PENDING_DESIRED_CONFIGURATION, sync_service()->GetState()); // Releasing the setup in progress handle lets the service actually configure
diff --git a/components/browser_sync/profile_sync_service_unittest.cc b/components/browser_sync/profile_sync_service_unittest.cc index f4561e8..ab764fb2 100644 --- a/components/browser_sync/profile_sync_service_unittest.cc +++ b/components/browser_sync/profile_sync_service_unittest.cc
@@ -385,14 +385,14 @@ // Note: At this point the engine *can* start, but nothing has kicked it off // (usually that happens via getting and then releasing a // SyncSetupInProgressHandle), so the state is still WAITING_FOR_START_REQUEST - // and not WAITING_FOR_CONSENT. + // and not PENDING_DESIRED_CONFIGURATION. EXPECT_EQ(syncer::SyncService::State::WAITING_FOR_START_REQUEST, service()->GetState()); // Once we kick off initialization by getting and releasing a setup handle, - // the state goes to WAITING_FOR_CONSENT. + // the state goes to PENDING_DESIRED_CONFIGURATION. service()->GetSetupInProgressHandle(); - EXPECT_EQ(syncer::SyncService::State::WAITING_FOR_CONSENT, + EXPECT_EQ(syncer::SyncService::State::PENDING_DESIRED_CONFIGURATION, service()->GetState()); // The last sync time shouldn't be cleared. @@ -680,8 +680,8 @@ // The observer should have been notified of the auth error state. EXPECT_EQ(rejected_by_client, observer.auth_error()); - - EXPECT_EQ(syncer::SyncService::State::AUTH_ERROR, service()->GetState()); + // The overall state should remain ACTIVE. + EXPECT_EQ(syncer::SyncService::State::ACTIVE, service()->GetState()); service()->RemoveObserver(&observer); } @@ -798,7 +798,8 @@ service()->GetAuthError().state()); EXPECT_EQ(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS, observer.auth_error().state()); - EXPECT_EQ(syncer::SyncService::State::AUTH_ERROR, service()->GetState()); + // The overall state should remain ACTIVE. + EXPECT_EQ(syncer::SyncService::State::ACTIVE, service()->GetState()); service()->RemoveObserver(&observer); } @@ -858,7 +859,8 @@ // Check that the invalid token is returned from sync. ASSERT_EQ(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS, service()->GetAuthError().state()); - ASSERT_EQ(syncer::SyncService::State::AUTH_ERROR, service()->GetState()); + // The overall state should remain ACTIVE. + ASSERT_EQ(syncer::SyncService::State::ACTIVE, service()->GetState()); // Now emulate Chrome receiving a new, valid LST. auth_service()->UpdateCredentials(primary_account_id, "totally valid token"); @@ -870,7 +872,7 @@ // Check that sync auth error state cleared. EXPECT_EQ(GoogleServiceAuthError::NONE, service()->GetAuthError().state()); EXPECT_EQ(GoogleServiceAuthError::NONE, observer.auth_error().state()); - EXPECT_NE(syncer::SyncService::State::AUTH_ERROR, service()->GetState()); + EXPECT_EQ(syncer::SyncService::State::ACTIVE, service()->GetState()); service()->RemoveObserver(&observer); }
diff --git a/components/browser_sync/profile_sync_test_util.cc b/components/browser_sync/profile_sync_test_util.cc index 427723ce..de26d9c 100644 --- a/components/browser_sync/profile_sync_test_util.cc +++ b/components/browser_sync/profile_sync_test_util.cc
@@ -250,7 +250,13 @@ &account_tracker_, nullptr), #endif - identity_manager_(&signin_manager_, &auth_service_, &account_tracker_), + gaia_cookie_manager_service_(&auth_service_, + "profile_sync_service_bundle", + &signin_client_), + identity_manager_(&signin_manager_, + &auth_service_, + &account_tracker_, + &gaia_cookie_manager_service_), url_request_context_(new net::TestURLRequestContextGetter( base::ThreadTaskRunnerHandle::Get())) { RegisterPrefsForProfileSyncService(pref_service_.registry());
diff --git a/components/browser_sync/profile_sync_test_util.h b/components/browser_sync/profile_sync_test_util.h index c2091d2f..935d922 100644 --- a/components/browser_sync/profile_sync_test_util.h +++ b/components/browser_sync/profile_sync_test_util.h
@@ -14,6 +14,7 @@ #include "components/browser_sync/profile_sync_service.h" #include "components/invalidation/impl/fake_invalidation_service.h" #include "components/signin/core/browser/account_tracker_service.h" +#include "components/signin/core/browser/fake_gaia_cookie_manager_service.h" #include "components/signin/core/browser/fake_profile_oauth2_token_service.h" #include "components/signin/core/browser/fake_signin_manager.h" #include "components/signin/core/browser/test_signin_client.h" @@ -172,6 +173,7 @@ AccountTrackerService account_tracker_; FakeSigninManagerType signin_manager_; FakeProfileOAuth2TokenService auth_service_; + FakeGaiaCookieManagerService gaia_cookie_manager_service_; identity::IdentityManager identity_manager_; testing::NiceMock<syncer::SyncApiComponentFactoryMock> component_factory_; std::unique_ptr<sync_sessions::LocalSessionEventRouter>
diff --git a/components/consent_auditor/consent_auditor_impl_unittest.cc b/components/consent_auditor/consent_auditor_impl_unittest.cc index ddb10716..b0d9ec655 100644 --- a/components/consent_auditor/consent_auditor_impl_unittest.cc +++ b/components/consent_auditor/consent_auditor_impl_unittest.cc
@@ -15,7 +15,6 @@ #include "components/sync/driver/sync_driver_switches.h" #include "components/sync/model/fake_model_type_controller_delegate.h" #include "components/sync/user_events/fake_user_event_service.h" -#include "components/variations/variations_params_manager.h" #include "testing/gtest/include/gtest/gtest.h" using sync_pb::UserConsentSpecifics; @@ -108,7 +107,11 @@ public: void SetUp() override { pref_service_ = std::make_unique<TestingPrefServiceSimple>(); - user_event_service_ = std::make_unique<syncer::FakeUserEventService>(); + if (base::FeatureList::IsEnabled(switches::kSyncUserConsentSeparateType)) { + consent_sync_bridge_ = std::make_unique<FakeConsentSyncBridge>(); + } else { + user_event_service_ = std::make_unique<syncer::FakeUserEventService>(); + } ConsentAuditorImpl::RegisterProfilePrefs(pref_service_->registry()); app_version_ = kCurrentAppVersion; app_locale_ = kCurrentAppLocale; @@ -138,16 +141,8 @@ } void SetIsSeparateConsentTypeEnabledFeature(bool new_value) { - // VariationParamsManager supports only one - // |SetVariationParamsWithFeatureAssociations| at a time, so we clear - // previous settings first to make this explicit. - params_manager_.ClearAllVariationParams(); - if (new_value) { - params_manager_.SetVariationParamsWithFeatureAssociations( - /*trial_name=*/switches::kSyncUserConsentSeparateType.name, - /*param_values=*/std::map<std::string, std::string>(), - {switches::kSyncUserConsentSeparateType.name}); - } + feature_list_.InitWithFeatureState(switches::kSyncUserConsentSeparateType, + new_value); } ConsentAuditorImpl* consent_auditor() { return consent_auditor_.get(); } @@ -165,12 +160,15 @@ std::string app_locale_; std::unique_ptr<syncer::ConsentSyncBridge> consent_sync_bridge_; - variations::testing::VariationParamsManager params_manager_; + base::test::ScopedFeatureList feature_list_; }; TEST_F(ConsentAuditorImplTest, LocalConsentPrefRepresentation) { + SetIsSeparateConsentTypeEnabledFeature(true); SetAppVersion(kCurrentAppVersion); SetAppLocale(kCurrentAppLocale); + SetConsentSyncBridge(std::make_unique<FakeConsentSyncBridge>()); + SetUserEventService(nullptr); BuildConsentAuditorImpl(); // No consents are written at first. @@ -221,6 +219,8 @@ const std::string kFeature2NewAppLocale = "de"; SetAppVersion(kFeature2NewAppVersion); SetAppLocale(kFeature2NewAppLocale); + SetConsentSyncBridge(std::make_unique<FakeConsentSyncBridge>()); + SetUserEventService(nullptr); // We rebuild consent auditor to emulate restarting Chrome. This is the only // way to change app version or app locale. BuildConsentAuditorImpl(); @@ -240,6 +240,9 @@ TEST_F(ConsentAuditorImplTest, RecordingEnabled) { SetIsSeparateConsentTypeEnabledFeature(false); + SetConsentSyncBridge(nullptr); + SetUserEventService(std::make_unique<syncer::FakeUserEventService>()); + BuildConsentAuditorImpl(); consent_auditor()->RecordGaiaConsent(kAccountId, Feature::CHROME_SYNC, {}, 0, ConsentStatus::GIVEN); @@ -249,6 +252,9 @@ TEST_F(ConsentAuditorImplTest, RecordingDisabled) { SetIsSeparateConsentTypeEnabledFeature(false); + SetConsentSyncBridge(nullptr); + SetUserEventService(std::make_unique<syncer::FakeUserEventService>()); + BuildConsentAuditorImpl(); base::test::ScopedFeatureList scoped_feature_list; scoped_feature_list.InitAndDisableFeature(switches::kSyncUserConsentEvents); @@ -261,6 +267,7 @@ TEST_F(ConsentAuditorImplTest, RecordGaiaConsentAsUserEvent) { SetIsSeparateConsentTypeEnabledFeature(false); SetConsentSyncBridge(nullptr); + SetUserEventService(std::make_unique<syncer::FakeUserEventService>()); SetAppVersion(kCurrentAppVersion); SetAppLocale(kCurrentAppLocale); BuildConsentAuditorImpl(); @@ -295,7 +302,6 @@ auto wrapped_fake_bridge = std::make_unique<FakeConsentSyncBridge>(); FakeConsentSyncBridge* fake_bridge = wrapped_fake_bridge.get(); - SetIsSeparateConsentTypeEnabledFeature(true); SetConsentSyncBridge(std::move(wrapped_fake_bridge)); SetUserEventService(nullptr); SetAppVersion(kCurrentAppVersion); @@ -333,6 +339,7 @@ TEST_F(ConsentAuditorImplTest, ShouldReturnNoSyncDelegateWhenNoBridge) { SetIsSeparateConsentTypeEnabledFeature(false); SetConsentSyncBridge(nullptr); + SetUserEventService(std::make_unique<syncer::FakeUserEventService>()); BuildConsentAuditorImpl(); // There is no bridge (i.e. separate sync type for consents is disabled),
diff --git a/components/dom_distiller/DEPS b/components/dom_distiller/DEPS index 35ae9ad..d34ee2cf 100644 --- a/components/dom_distiller/DEPS +++ b/components/dom_distiller/DEPS
@@ -17,6 +17,8 @@ "+net/http", "+net/traffic_annotation", "+net/url_request", + "+services/network/public/cpp", + "+services/network/test", "+ui/base/l10n", "+ui/base/resource", "+ui/gfx",
diff --git a/components/dom_distiller/core/BUILD.gn b/components/dom_distiller/core/BUILD.gn index 63fe88e..ecd5d50 100644 --- a/components/dom_distiller/core/BUILD.gn +++ b/components/dom_distiller/core/BUILD.gn
@@ -71,6 +71,7 @@ "//components/strings", "//components/sync", "//components/variations", + "//services/network/public/cpp:cpp", "//skia", "//third_party/re2", "//ui/base", @@ -152,6 +153,8 @@ "//components/sync", "//components/sync_preferences:test_support", "//net:test_support", + "//services/network:test_support", + "//services/network/public/cpp:cpp", "//testing/gmock", "//testing/gtest", "//ui/base",
diff --git a/components/dom_distiller/core/distiller_unittest.cc b/components/dom_distiller/core/distiller_unittest.cc index 3420e54..b27dd92 100644 --- a/components/dom_distiller/core/distiller_unittest.cc +++ b/components/dom_distiller/core/distiller_unittest.cc
@@ -30,6 +30,7 @@ #include "components/dom_distiller/core/proto/distilled_article.pb.h" #include "components/dom_distiller/core/proto/distilled_page.pb.h" #include "net/url_request/url_request_context_getter.h" +#include "services/network/public/cpp/shared_url_loader_factory.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/dom_distiller_js/dom_distiller.pb.h"
diff --git a/components/dom_distiller/core/distiller_url_fetcher.cc b/components/dom_distiller/core/distiller_url_fetcher.cc index 2cf48ec..035d40c 100644 --- a/components/dom_distiller/core/distiller_url_fetcher.cc +++ b/components/dom_distiller/core/distiller_url_fetcher.cc
@@ -4,34 +4,27 @@ #include "components/dom_distiller/core/distiller_url_fetcher.h" -#include "components/data_use_measurement/core/data_use_user_data.h" -#include "net/http/http_status_code.h" #include "net/traffic_annotation/network_traffic_annotation.h" -#include "net/url_request/url_fetcher.h" -#include "net/url_request/url_fetcher_delegate.h" -#include "net/url_request/url_request_context_getter.h" -#include "net/url_request/url_request_status.h" +#include "services/network/public/cpp/shared_url_loader_factory.h" +#include "services/network/public/cpp/simple_url_loader.h" #include "url/gurl.h" -using net::URLFetcher; - namespace dom_distiller { DistillerURLFetcherFactory::DistillerURLFetcherFactory( - net::URLRequestContextGetter* context_getter) - : context_getter_(context_getter) { -} + scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory) + : url_loader_factory_(url_loader_factory) {} + +DistillerURLFetcherFactory::~DistillerURLFetcherFactory() {} DistillerURLFetcher* DistillerURLFetcherFactory::CreateDistillerURLFetcher() const { - return new DistillerURLFetcher(context_getter_); + return new DistillerURLFetcher(url_loader_factory_); } - DistillerURLFetcher::DistillerURLFetcher( - net::URLRequestContextGetter* context_getter) - : context_getter_(context_getter) { -} + scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory) + : url_loader_factory_(url_loader_factory) {} DistillerURLFetcher::~DistillerURLFetcher() { } @@ -39,14 +32,16 @@ void DistillerURLFetcher::FetchURL(const std::string& url, const URLFetcherCallback& callback) { // Don't allow a fetch if one is pending. - DCHECK(!url_fetcher_ || !url_fetcher_->GetStatus().is_io_pending()); + DCHECK(!url_loader_); callback_ = callback; - url_fetcher_ = CreateURLFetcher(context_getter_, url); - url_fetcher_->Start(); + url_loader_ = CreateURLFetcher(url); + url_loader_->DownloadToStringOfUnboundedSizeUntilCrashAndDie( + url_loader_factory_.get(), + base::BindOnce(&DistillerURLFetcher::OnURLLoadComplete, + base::Unretained(this))); } -std::unique_ptr<URLFetcher> DistillerURLFetcher::CreateURLFetcher( - net::URLRequestContextGetter* context_getter, +std::unique_ptr<network::SimpleURLLoader> DistillerURLFetcher::CreateURLFetcher( const std::string& url) { net::NetworkTrafficAnnotationTag traffic_annotation = net::DefineNetworkTrafficAnnotation("dom_distiller", R"( @@ -79,24 +74,30 @@ "Not implemented, considered not useful as no content is being " "uploaded; this request merely downloads the resources on the web." })"); - std::unique_ptr<net::URLFetcher> fetcher = - URLFetcher::Create(GURL(url), URLFetcher::GET, this, traffic_annotation); - data_use_measurement::DataUseUserData::AttachToFetcher( - fetcher.get(), data_use_measurement::DataUseUserData::DOM_DISTILLER); - fetcher->SetRequestContext(context_getter); + auto resource_request = std::make_unique<network::ResourceRequest>(); + resource_request->url = GURL(url); + resource_request->method = "GET"; + + // TODO(crbug.com/808498): Restore the data use measurement when bug is fixed. + auto url_loader = network::SimpleURLLoader::Create( + std::move(resource_request), traffic_annotation); static const int kMaxRetries = 5; - fetcher->SetMaxRetriesOn5xx(kMaxRetries); - return fetcher; + url_loader->SetRetryOptions(kMaxRetries, + network::SimpleURLLoader::RETRY_ON_5XX); + + return url_loader; } -void DistillerURLFetcher::OnURLFetchComplete( - const URLFetcher* source) { +void DistillerURLFetcher::OnURLLoadComplete( + std::unique_ptr<std::string> response_body) { + // The loader is not needed at this point anymore. + url_loader_.reset(); + std::string response; - if (source && source->GetStatus().is_success() && - source->GetResponseCode() == net::HTTP_OK) { + if (response_body) { // Only copy over the data if the request was successful. Insert // an empty string into the proto otherwise. - source->GetResponseAsString(&response); + response = std::move(*response_body); } callback_.Run(response); }
diff --git a/components/dom_distiller/core/distiller_url_fetcher.h b/components/dom_distiller/core/distiller_url_fetcher.h index d0178e0..c883dca2 100644 --- a/components/dom_distiller/core/distiller_url_fetcher.h +++ b/components/dom_distiller/core/distiller_url_fetcher.h
@@ -9,9 +9,11 @@ #include "base/callback.h" #include "base/macros.h" -#include "net/url_request/url_fetcher.h" -#include "net/url_request/url_fetcher_delegate.h" -#include "net/url_request/url_request_context_getter.h" + +namespace network { +class SharedURLLoaderFactory; +class SimpleURLLoader; +} // namespace network namespace dom_distiller { @@ -20,21 +22,23 @@ // Class for creating a DistillerURLFetcher. class DistillerURLFetcherFactory { public: - explicit DistillerURLFetcherFactory( - net::URLRequestContextGetter* context_getter); - virtual ~DistillerURLFetcherFactory() {} + DistillerURLFetcherFactory( + scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory); + virtual ~DistillerURLFetcherFactory(); virtual DistillerURLFetcher* CreateDistillerURLFetcher() const; private: - net::URLRequestContextGetter* context_getter_; + friend class TestDistillerURLFetcherFactory; + scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_; }; -// This class fetches a URL, and notifies the caller when the operation +// This class loads a URL, and notifies the caller when the operation // completes or fails. If the request fails, an empty string will be returned. -class DistillerURLFetcher : public net::URLFetcherDelegate { +class DistillerURLFetcher { public: - explicit DistillerURLFetcher(net::URLRequestContextGetter* context_getter); - ~DistillerURLFetcher() override; + explicit DistillerURLFetcher( + scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory); + virtual ~DistillerURLFetcher(); // Indicates when a fetch is done. typedef base::Callback<void(const std::string& data)> URLFetcherCallback; @@ -44,17 +48,15 @@ const URLFetcherCallback& callback); protected: - virtual std::unique_ptr<net::URLFetcher> CreateURLFetcher( - net::URLRequestContextGetter* context_getter, + virtual std::unique_ptr<network::SimpleURLLoader> CreateURLFetcher( const std::string& url); private: - // net::URLFetcherDelegate: - void OnURLFetchComplete(const net::URLFetcher* source) override; + void OnURLLoadComplete(std::unique_ptr<std::string> response_body); - std::unique_ptr<net::URLFetcher> url_fetcher_; + std::unique_ptr<network::SimpleURLLoader> url_loader_; URLFetcherCallback callback_; - net::URLRequestContextGetter* context_getter_; + scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_; DISALLOW_COPY_AND_ASSIGN(DistillerURLFetcher); };
diff --git a/components/dom_distiller/core/distiller_url_fetcher_unittest.cc b/components/dom_distiller/core/distiller_url_fetcher_unittest.cc index 7454119..a19e6a0 100644 --- a/components/dom_distiller/core/distiller_url_fetcher_unittest.cc +++ b/components/dom_distiller/core/distiller_url_fetcher_unittest.cc
@@ -2,16 +2,14 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "components/dom_distiller/core/distiller_url_fetcher.h" #include "base/bind.h" #include "base/bind_helpers.h" #include "base/message_loop/message_loop.h" #include "base/run_loop.h" -#include "components/dom_distiller/core/distiller_url_fetcher.h" -#include "net/http/http_status_code.h" -#include "net/url_request/test_url_fetcher_factory.h" -#include "net/url_request/url_fetcher.h" -#include "net/url_request/url_request_context_getter.h" -#include "net/url_request/url_request_status.h" +#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h" +#include "services/network/test/test_url_loader_factory.h" +#include "services/network/test/test_utils.h" #include "testing/gtest/include/gtest/gtest.h" #include "url/gurl.h" @@ -23,6 +21,11 @@ class DistillerURLFetcherTest : public testing::Test { public: + DistillerURLFetcherTest() + : test_shared_url_loader_factory_( + base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>( + &test_url_loader_factory_)) {} + void FetcherCallback(const std::string& response) { response_ = response; } @@ -30,18 +33,16 @@ protected: // testing::Test implementation: void SetUp() override { - url_fetcher_.reset(new dom_distiller::DistillerURLFetcher(nullptr)); - factory_.reset(new net::FakeURLFetcherFactory(nullptr)); - factory_->SetFakeResponse( - GURL(kTestPageA), - std::string(kTestPageAResponse, sizeof(kTestPageAResponse)), - net::HTTP_OK, - net::URLRequestStatus::SUCCESS); - factory_->SetFakeResponse( + url_fetcher_.reset(new dom_distiller::DistillerURLFetcher( + test_shared_url_loader_factory_)); + test_url_loader_factory_.AddResponse( + kTestPageA, + std::string(kTestPageAResponse, sizeof(kTestPageAResponse))); + test_url_loader_factory_.AddResponse( GURL(kTestPageB), + network::CreateResourceResponseHead(net::HTTP_INTERNAL_SERVER_ERROR), std::string(kTestPageBResponse, sizeof(kTestPageBResponse)), - net::HTTP_INTERNAL_SERVER_ERROR, - net::URLRequestStatus::SUCCESS); + network::URLLoaderCompletionStatus(net::OK)); } void Fetch(const std::string& url, @@ -56,7 +57,9 @@ } std::unique_ptr<dom_distiller::DistillerURLFetcher> url_fetcher_; - std::unique_ptr<net::FakeURLFetcherFactory> factory_; + network::TestURLLoaderFactory test_url_loader_factory_; + scoped_refptr<network::SharedURLLoaderFactory> + test_shared_url_loader_factory_; std::string response_; };
diff --git a/components/dom_distiller/standalone/content_extractor_browsertest.cc b/components/dom_distiller/standalone/content_extractor_browsertest.cc index 5acc5f3..1286ea0 100644 --- a/components/dom_distiller/standalone/content_extractor_browsertest.cc +++ b/components/dom_distiller/standalone/content_extractor_browsertest.cc
@@ -142,7 +142,7 @@ std::unique_ptr<DistillerURLFetcherFactory> distiller_url_fetcher_factory( new DistillerURLFetcherFactory( content::BrowserContext::GetDefaultStoragePartition(context) - ->GetURLRequestContext())); + ->GetURLLoaderFactoryForBrowserProcess())); dom_distiller::proto::DomDistillerOptions options; if (base::CommandLine::ForCurrentProcess()->HasSwitch(kExtractTextOnly)) {
diff --git a/components/download/internal/common/BUILD.gn b/components/download/internal/common/BUILD.gn index 5121397..7e0ffb3 100644 --- a/components/download/internal/common/BUILD.gn +++ b/components/download/internal/common/BUILD.gn
@@ -48,7 +48,6 @@ "save_package_download_job.h", "stream_handle_input_stream.cc", "url_download_handler_factory.cc", - "url_download_request_handle.cc", ] public_deps = [
diff --git a/components/download/internal/common/download_response_handler.cc b/components/download/internal/common/download_response_handler.cc index c151ec4..c57935b 100644 --- a/components/download/internal/common/download_response_handler.cc +++ b/components/download/internal/common/download_response_handler.cc
@@ -202,10 +202,8 @@ ConvertInterruptReasonToMojoNetworkRequestStatus(reason)); } - if (started_) { - delegate_->OnResponseCompleted(); + if (started_) return; - } // OnComplete() called without OnReceiveResponse(). This should only // happen when the request was aborted. @@ -213,7 +211,6 @@ create_info_->result = reason; OnResponseStarted(mojom::DownloadStreamHandlePtr()); - delegate_->OnResponseCompleted(); } void DownloadResponseHandler::OnResponseStarted(
diff --git a/components/download/internal/common/resource_downloader.cc b/components/download/internal/common/resource_downloader.cc index 5d958e9..6cc8757 100644 --- a/components/download/internal/common/resource_downloader.cc +++ b/components/download/internal/common/resource_downloader.cc
@@ -8,7 +8,6 @@ #include "components/download/public/common/download_url_loader_factory_getter.h" #include "components/download/public/common/stream_handle_input_stream.h" -#include "components/download/public/common/url_download_request_handle.h" #include "services/network/public/cpp/shared_url_loader_factory.h" namespace network { @@ -199,8 +198,6 @@ void ResourceDownloader::OnResponseStarted( std::unique_ptr<DownloadCreateInfo> download_create_info, mojom::DownloadStreamHandlePtr stream_handle) { - download_create_info->request_handle.reset(new UrlDownloadRequestHandle( - weak_ptr_factory_.GetWeakPtr(), base::SequencedTaskRunnerHandle::Get())); download_create_info->is_new_download = is_new_download_; download_create_info->guid = guid_; download_create_info->site_url = site_url_; @@ -222,19 +219,4 @@ url_loader_->FollowRedirect(base::nullopt, base::nullopt); } -void ResourceDownloader::OnResponseCompleted() { - Destroy(); -} - -void ResourceDownloader::CancelRequest() { - Destroy(); -} - -void ResourceDownloader::Destroy() { - delegate_task_runner_->PostTask( - FROM_HERE, - base::BindOnce(&UrlDownloadHandler::Delegate::OnUrlDownloadStopped, - delegate_, this)); -} - } // namespace download
diff --git a/components/download/internal/common/resource_downloader.h b/components/download/internal/common/resource_downloader.h index a3557f8..a3b144b 100644 --- a/components/download/internal/common/resource_downloader.h +++ b/components/download/internal/common/resource_downloader.h
@@ -73,7 +73,6 @@ std::unique_ptr<download::DownloadCreateInfo> download_create_info, download::mojom::DownloadStreamHandlePtr stream_handle) override; void OnReceiveRedirect() override; - void OnResponseCompleted() override; private: // Helper method to start the network request. @@ -88,12 +87,6 @@ net::CertStatus cert_status, network::mojom::URLLoaderClientEndpointsPtr url_loader_client_endpoints); - // UrlDownloadHandler implementations. - void CancelRequest() override; - - // Ask the |delegate_| to destroy this object. - void Destroy(); - base::WeakPtr<download::UrlDownloadHandler::Delegate> delegate_; // The ResourceRequest for this object.
diff --git a/components/download/internal/common/url_download_request_handle.cc b/components/download/internal/common/url_download_request_handle.cc deleted file mode 100644 index 7fa6bd8..0000000 --- a/components/download/internal/common/url_download_request_handle.cc +++ /dev/null
@@ -1,47 +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. - -#include "components/download/public/common/url_download_request_handle.h" - -namespace download { - -UrlDownloadRequestHandle::UrlDownloadRequestHandle( - base::WeakPtr<UrlDownloadHandler> downloader, - scoped_refptr<base::SequencedTaskRunner> downloader_task_runner) - : downloader_(downloader), - downloader_task_runner_(downloader_task_runner) {} - -UrlDownloadRequestHandle::UrlDownloadRequestHandle( - UrlDownloadRequestHandle&& other) - : downloader_(std::move(other.downloader_)), - downloader_task_runner_(std::move(other.downloader_task_runner_)) {} - -UrlDownloadRequestHandle& UrlDownloadRequestHandle::operator=( - UrlDownloadRequestHandle&& other) { - downloader_ = std::move(other.downloader_); - downloader_task_runner_ = std::move(other.downloader_task_runner_); - return *this; -} - -UrlDownloadRequestHandle::~UrlDownloadRequestHandle() = default; - -void UrlDownloadRequestHandle::PauseRequest() { - downloader_task_runner_->PostTask( - FROM_HERE, - base::BindOnce(&UrlDownloadHandler::PauseRequest, downloader_)); -} - -void UrlDownloadRequestHandle::ResumeRequest() { - downloader_task_runner_->PostTask( - FROM_HERE, - base::BindOnce(&UrlDownloadHandler::ResumeRequest, downloader_)); -} - -void UrlDownloadRequestHandle::CancelRequest(bool user_cancel) { - downloader_task_runner_->PostTask( - FROM_HERE, - base::BindOnce(&UrlDownloadHandler::CancelRequest, downloader_)); -} - -} // namespace download
diff --git a/components/download/public/common/BUILD.gn b/components/download/public/common/BUILD.gn index 5c8c8717..00453df 100644 --- a/components/download/public/common/BUILD.gn +++ b/components/download/public/common/BUILD.gn
@@ -57,7 +57,6 @@ "resume_mode.h", "stream_handle_input_stream.h", "url_download_handler_factory.h", - "url_download_request_handle.h", ] configs += [ ":components_download_implementation" ]
diff --git a/components/download/public/common/download_response_handler.h b/components/download/public/common/download_response_handler.h index b50702c1..db38793 100644 --- a/components/download/public/common/download_response_handler.h +++ b/components/download/public/common/download_response_handler.h
@@ -26,14 +26,13 @@ class COMPONENTS_DOWNLOAD_EXPORT DownloadResponseHandler : public network::mojom::URLLoaderClient { public: - // Class for handling the stream response. + // Class for handling the stream once response starts. class Delegate { public: virtual void OnResponseStarted( std::unique_ptr<DownloadCreateInfo> download_create_info, mojom::DownloadStreamHandlePtr stream_handle) = 0; virtual void OnReceiveRedirect() = 0; - virtual void OnResponseCompleted() = 0; }; DownloadResponseHandler(
diff --git a/components/download/public/common/url_download_handler.h b/components/download/public/common/url_download_handler.h index 7972520..ce059d923 100644 --- a/components/download/public/common/url_download_handler.h +++ b/components/download/public/common/url_download_handler.h
@@ -40,15 +40,6 @@ UrlDownloadHandler() = default; virtual ~UrlDownloadHandler() = default; - // Called on the io thread to pause the url request. - virtual void PauseRequest() {} - - // Called on the io thread to resume the url request. - virtual void ResumeRequest() {} - - // Called on the io thread to cancel the url request. - virtual void CancelRequest() {} - DISALLOW_COPY_AND_ASSIGN(UrlDownloadHandler); };
diff --git a/components/download/public/common/url_download_request_handle.h b/components/download/public/common/url_download_request_handle.h deleted file mode 100644 index 46c7756..0000000 --- a/components/download/public/common/url_download_request_handle.h +++ /dev/null
@@ -1,40 +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. - -#ifndef COMPONENTS_DOWNLOAD_PUBLIC_COMMON_URL_DOWNLOAD_REQUEST_HANDLE_H_ -#define COMPONENTS_DOWNLOAD_PUBLIC_COMMON_URL_DOWNLOAD_REQUEST_HANDLE_H_ - -#include "base/memory/weak_ptr.h" -#include "components/download/public/common/download_export.h" -#include "components/download/public/common/download_request_handle_interface.h" -#include "components/download/public/common/url_download_handler.h" - -namespace download { - -// Implementation of the DownloadRequestHandleInterface to handle url download. -class COMPONENTS_DOWNLOAD_EXPORT UrlDownloadRequestHandle - : public DownloadRequestHandleInterface { - public: - UrlDownloadRequestHandle( - base::WeakPtr<UrlDownloadHandler> downloader, - scoped_refptr<base::SequencedTaskRunner> downloader_task_runner); - UrlDownloadRequestHandle(UrlDownloadRequestHandle&& other); - UrlDownloadRequestHandle& operator=(UrlDownloadRequestHandle&& other); - ~UrlDownloadRequestHandle() override; - - // DownloadRequestHandleInterface - void PauseRequest() override; - void ResumeRequest() override; - void CancelRequest(bool user_cancel) override; - - private: - base::WeakPtr<UrlDownloadHandler> downloader_; - scoped_refptr<base::SequencedTaskRunner> downloader_task_runner_; - - DISALLOW_COPY_AND_ASSIGN(UrlDownloadRequestHandle); -}; - -} // namespace download - -#endif // COMPONENTS_DOWNLOAD_PUBLIC_COMMON_URL_DOWNLOAD_REQUEST_HANDLE_H_
diff --git a/components/feed/DEPS b/components/feed/DEPS index c08d0fa..8bbbd796 100644 --- a/components/feed/DEPS +++ b/components/feed/DEPS
@@ -6,6 +6,7 @@ "+components/prefs", "+components/variations", "+components/version_info", + "+components/web_resource", "+net/base", "+net/http", "+net/traffic_annotation",
diff --git a/components/feed/core/BUILD.gn b/components/feed/core/BUILD.gn index 9cfbe730..f7b47dd 100644 --- a/components/feed/core/BUILD.gn +++ b/components/feed/core/BUILD.gn
@@ -44,6 +44,7 @@ "//components/variations", "//components/variations/net", "//components/variations/service", + "//components/web_resource", "//google_apis", "//services/identity/public/cpp", "//services/network/public/cpp", @@ -78,6 +79,7 @@ "//components/leveldb_proto:test_support", "//components/prefs:test_support", "//components/variations:test_support", + "//components/web_resource", "//net:test_support", "//services/identity/public/cpp", "//services/identity/public/cpp:test_support",
diff --git a/components/feed/core/feed_scheduler_host.cc b/components/feed/core/feed_scheduler_host.cc index daee2e3..0712d1c 100644 --- a/components/feed/core/feed_scheduler_host.cc +++ b/components/feed/core/feed_scheduler_host.cc
@@ -7,9 +7,11 @@ #include <map> #include <string> #include <utility> +#include <vector> #include "base/metrics/field_trial_params.h" #include "base/stl_util.h" +#include "base/strings/string_split.h" #include "base/time/clock.h" #include "base/time/time.h" #include "components/feed/core/pref_names.h" @@ -17,6 +19,8 @@ #include "components/feed/feed_feature_list.h" #include "components/prefs/pref_registry_simple.h" #include "components/prefs/pref_service.h" +#include "components/web_resource/web_resource_pref_names.h" +#include "net/base/network_change_notifier.h" namespace feed { @@ -30,6 +34,11 @@ double default_value; }; +const base::FeatureParam<int> kSuppressRefreshDurationMinutes{ + &kInterestFeedContentSuggestions, "suppress_refresh_duration_minutes", 30}; +const base::FeatureParam<std::string> kDisableTriggerTypes{ + &kInterestFeedContentSuggestions, "disable_trigger_types", ""}; + // The Cartesian product of TriggerType and UserClass each need a different // param name in case we decide to change it via a config change. This nested // switch lookup ensures that all combinations are defined, along with a @@ -44,6 +53,9 @@ return {"foregrounded_hours_rare_ntp_user", 24.0}; case TriggerType::FIXED_TIMER: return {"fixed_timer_hours_rare_ntp_user", 96.0}; + case TriggerType::COUNT: + NOTREACHED(); + return {"", 0.0}; } case UserClass::ACTIVE_NTP_USER: switch (trigger) { @@ -53,6 +65,9 @@ return {"foregrounded_hours_active_ntp_user", 24.0}; case TriggerType::FIXED_TIMER: return {"fixed_timer_hours_active_ntp_user", 48.0}; + case TriggerType::COUNT: + NOTREACHED(); + return {"", 0.0}; } case UserClass::ACTIVE_SUGGESTIONS_CONSUMER: switch (trigger) { @@ -62,10 +77,44 @@ return {"foregrounded_hours_active_suggestions_consumer", 12.0}; case TriggerType::FIXED_TIMER: return {"fixed_timer_hours_active_suggestions_consumer", 24.0}; + case TriggerType::COUNT: + NOTREACHED(); + return {"", 0.0}; } } } +// Coverts from base::StringPiece to TriggerType and adds it to the set if the +// trigger is recognized. Otherwise it is ignored. +void TryAddTriggerType(base::StringPiece trigger_as_string_piece, + std::set<TriggerType>* set) { + static_assert(static_cast<unsigned int>(TriggerType::COUNT) == 3, + "New TriggerTypes must be handled below."); + if (trigger_as_string_piece == "ntp_shown") { + set->insert(TriggerType::NTP_SHOWN); + } else if (trigger_as_string_piece == "foregrounded") { + set->insert(TriggerType::FOREGROUNDED); + } else if (trigger_as_string_piece == "fixed_timer") { + set->insert(TriggerType::FIXED_TIMER); + } +} + +// Generates a set of disabled triggers. +std::set<TriggerType> GetDisabledTriggerTypes() { + std::set<TriggerType> disabled_triggers; + + // Do not in-line FeatureParam::Get(), |param_value| must stay alive while + // StringPieces reference segments. + std::string param_value = kDisableTriggerTypes.Get(); + + for (auto token : + base::SplitStringPiece(param_value, ",", base::TRIM_WHITESPACE, + base::SPLIT_WANT_NONEMPTY)) { + TryAddTriggerType(token, &disabled_triggers); + } + return disabled_triggers; +} + // Run the given closure if it is valid. void TryRun(base::OnceClosure closure) { if (closure) { @@ -75,11 +124,19 @@ } // namespace -FeedSchedulerHost::FeedSchedulerHost(PrefService* pref_service, +FeedSchedulerHost::FeedSchedulerHost(PrefService* profile_prefs, + PrefService* local_state, base::Clock* clock) - : pref_service_(pref_service), + : profile_prefs_(profile_prefs), clock_(clock), - user_classifier_(pref_service, clock) {} + user_classifier_(profile_prefs, clock), + disabled_triggers_(GetDisabledTriggerTypes()), + eula_accepted_notifier_( + web_resource::EulaAcceptedNotifier::Create(local_state)) { + if (eula_accepted_notifier_) { + eula_accepted_notifier_->Init(this); + } +} FeedSchedulerHost::~FeedSchedulerHost() = default; @@ -104,7 +161,7 @@ std::move(schedule_background_task_callback); base::TimeDelta old_period = - pref_service_->GetTimeDelta(prefs::kBackgroundRefreshPeriod); + profile_prefs_->GetTimeDelta(prefs::kBackgroundRefreshPeriod); base::TimeDelta new_period = GetTriggerThreshold(TriggerType::FIXED_TIMER); if (old_period != new_period) { ScheduleFixedTimerWakeUp(new_period); @@ -115,8 +172,18 @@ bool has_content, base::Time content_creation_date_time, bool has_outstanding_request) { + // The scheduler may not always know of outstanding requests, but the Feed + // should know about them all, and the scheduler should be notified upon + // completion of all requests. We should never encounter a scenario where only + // the scheduler thinks there is an outstanding request. + + // TODO(skym): Resolve ambiguity around this expectation. + // DCHECK(has_outstanding_request || !tracking_oustanding_request_); + + tracking_oustanding_request_ |= has_outstanding_request; + NativeRequestBehavior behavior; - if (!has_outstanding_request && ShouldRefresh(TriggerType::NTP_SHOWN)) { + if (ShouldRefresh(TriggerType::NTP_SHOWN)) { if (!has_content) { behavior = REQUEST_WITH_WAIT; } else if (IsContentStale(content_creation_date_time)) { @@ -143,20 +210,24 @@ // TODO(skym): Record requested behavior into histogram. user_classifier_.OnEvent(UserClassifier::Event::NTP_OPENED); + DVLOG(2) << "Specifying NativeRequestBehavior of " + << static_cast<int>(behavior); return behavior; } void FeedSchedulerHost::OnReceiveNewContent( base::Time content_creation_date_time) { - pref_service_->SetTime(prefs::kLastFetchAttemptTime, - content_creation_date_time); + profile_prefs_->SetTime(prefs::kLastFetchAttemptTime, + content_creation_date_time); TryRun(std::move(fixed_timer_completion_)); ScheduleFixedTimerWakeUp(GetTriggerThreshold(TriggerType::FIXED_TIMER)); + tracking_oustanding_request_ = false; } void FeedSchedulerHost::OnRequestError(int network_response_code) { - pref_service_->SetTime(prefs::kLastFetchAttemptTime, clock_->Now()); + profile_prefs_->SetTime(prefs::kLastFetchAttemptTime, clock_->Now()); TryRun(std::move(fixed_timer_completion_)); + tracking_oustanding_request_ = false; } void FeedSchedulerHost::OnForegrounded() { @@ -190,11 +261,65 @@ user_classifier_.OnEvent(UserClassifier::Event::SUGGESTIONS_USED); } +void FeedSchedulerHost::OnHistoryCleared() { + // Due to privacy, we should not fetch for a while (unless the user explicitly + // asks for new suggestions) to give sync the time to propagate the changes in + // history to the server. + suppress_refreshes_until_ = + clock_->Now() + + base::TimeDelta::FromMinutes(kSuppressRefreshDurationMinutes.Get()); + // After that time elapses, we should fetch as soon as possible. + profile_prefs_->ClearPref(prefs::kLastFetchAttemptTime); +} + +void FeedSchedulerHost::OnEulaAccepted() { + OnForegrounded(); +} + bool FeedSchedulerHost::ShouldRefresh(TriggerType trigger) { - // TODO(skym): Check various other criteria are met, record metrics. - return (clock_->Now() - - pref_service_->GetTime(prefs::kLastFetchAttemptTime)) > - GetTriggerThreshold(trigger); + // TODO(skym): Record metrics throughout this method per design. + + if (tracking_oustanding_request_) { + DVLOG(2) << "Outstanding request stopped refresh from trigger " + << static_cast<int>(trigger); + return false; + } + + if (base::ContainsKey(disabled_triggers_, trigger)) { + DVLOG(2) << "Disabled trigger stopped refresh from trigger " + << static_cast<int>(trigger); + return false; + } + + if (net::NetworkChangeNotifier::IsOffline()) { + DVLOG(2) << "Network is offline stopped refresh from trigger " + << static_cast<int>(trigger); + return false; + } + + if (eula_accepted_notifier_ && !eula_accepted_notifier_->IsEulaAccepted()) { + DVLOG(2) << "EULA not being accepted stopped refresh from trigger " + << static_cast<int>(trigger); + return false; + } + + if (clock_->Now() < suppress_refreshes_until_) { + DVLOG(2) << "Refresh suppression until " << suppress_refreshes_until_ + << " stopped refresh from trigger " << static_cast<int>(trigger); + return false; + } + + base::TimeDelta attempt_age = + clock_->Now() - profile_prefs_->GetTime(prefs::kLastFetchAttemptTime); + if (attempt_age < GetTriggerThreshold(trigger)) { + DVLOG(2) << "Last attempt age of " << attempt_age + << " stopped refresh from trigger " << static_cast<int>(trigger); + return false; + } + + DVLOG(2) << "Requesting refresh from trigger " << static_cast<int>(trigger); + tracking_oustanding_request_ = true; + return true; } bool FeedSchedulerHost::IsContentStale(base::Time content_creation_date_time) { @@ -213,7 +338,7 @@ } void FeedSchedulerHost::ScheduleFixedTimerWakeUp(base::TimeDelta period) { - pref_service_->SetTimeDelta(prefs::kBackgroundRefreshPeriod, period); + profile_prefs_->SetTimeDelta(prefs::kBackgroundRefreshPeriod, period); schedule_background_task_callback_.Run(period); }
diff --git a/components/feed/core/feed_scheduler_host.h b/components/feed/core/feed_scheduler_host.h index 11270e5b..781277f6 100644 --- a/components/feed/core/feed_scheduler_host.h +++ b/components/feed/core/feed_scheduler_host.h
@@ -5,11 +5,15 @@ #ifndef COMPONENTS_FEED_CORE_FEED_SCHEDULER_HOST_H_ #define COMPONENTS_FEED_CORE_FEED_SCHEDULER_HOST_H_ +#include <memory> +#include <set> + #include "base/callback.h" #include "base/gtest_prod_util.h" #include "base/macros.h" #include "base/memory/weak_ptr.h" #include "components/feed/core/user_classifier.h" +#include "components/web_resource/eula_accepted_notifier.h" class PrefRegistrySimple; class PrefService; @@ -37,19 +41,19 @@ NO_REQUEST_WITH_TIMEOUT }; -enum class TriggerType; - // Implementation of the Feed Scheduler Host API. The scheduler host decides // what content is allowed to be shown, based on its age, and when to fetch new // content. -class FeedSchedulerHost { +class FeedSchedulerHost : web_resource::EulaAcceptedNotifier::Observer { public: // The TriggerType enum specifies values for the events that can trigger // refreshing articles. - enum class TriggerType { NTP_SHOWN = 0, FOREGROUNDED = 1, FIXED_TIMER = 2 }; + enum class TriggerType { NTP_SHOWN, FOREGROUNDED, FIXED_TIMER, COUNT }; - FeedSchedulerHost(PrefService* pref_service, base::Clock* clock); - ~FeedSchedulerHost(); + FeedSchedulerHost(PrefService* profile_prefs, + PrefService* local_state, + base::Clock* clock); + ~FeedSchedulerHost() override; using ScheduleBackgroundTaskCallback = base::RepeatingCallback<void(base::TimeDelta)>; @@ -94,12 +98,20 @@ // scheduler should be optimizing for. void OnSuggestionConsumed(); + // When the user clears history, the scheduler will clear out some stored data + // and stop requesting refreshes for a period of time. + void OnHistoryCleared(); + private: FRIEND_TEST_ALL_PREFIXES(FeedSchedulerHostTest, GetTriggerThreshold); + // web_resource::EulaAcceptedNotifier::Observer: + void OnEulaAccepted() override; + // Determines whether a refresh should be performed for the given |trigger|. // If this method is called and returns true we presume the refresh will - // happen, therefore we report metrics respectively. + // happen, therefore we report metrics respectively and update + // |tracking_oustanding_request_|. bool ShouldRefresh(TriggerType trigger); // Decides if content whose age is the difference between now and @@ -115,7 +127,7 @@ void ScheduleFixedTimerWakeUp(base::TimeDelta period); // Non-owning reference to pref service providing durable storage. - PrefService* pref_service_; + PrefService* profile_prefs_; // Non-owning reference to clock to get current time. base::Clock* clock_; @@ -135,6 +147,27 @@ // the refresh has completed. Is called on refresh success or failure. base::OnceClosure fixed_timer_completion_; + // Set of triggers that should be ignored. By default this is empty. + std::set<TriggerType> disabled_triggers_; + + // In some circumstances, such as when history is cleared, the scheduler will + // stop requesting refreshes for a given period. During this time, only direct + // user interaction with the NTP (and outside of the scheduler's control) + // should cause a refresh to occur. + base::Time suppress_refreshes_until_; + + // Whether the scheduler is aware of an outstanding refresh or not. There are + // cases where a refresh may be occurring without the scheduler knowing about + // it, such as user interaction with UI on the NTP. If this field holds a + // value of true, it is expected that either OnReceiveNewContent or + // OnRequestError will be called eventually, somewhere on the order of seconds + // from now, assuming the browser does not shut down. + bool tracking_oustanding_request_ = false; + + // May hold a nullptr if the platform does not show the user a EULA. Will only + // notify if IsEulaAccepted() is called and it returns false. + std::unique_ptr<web_resource::EulaAcceptedNotifier> eula_accepted_notifier_; + DISALLOW_COPY_AND_ASSIGN(FeedSchedulerHost); };
diff --git a/components/feed/core/feed_scheduler_host_unittest.cc b/components/feed/core/feed_scheduler_host_unittest.cc index 356d816..b66ec46 100644 --- a/components/feed/core/feed_scheduler_host_unittest.cc +++ b/components/feed/core/feed_scheduler_host_unittest.cc
@@ -4,7 +4,6 @@ #include "components/feed/core/feed_scheduler_host.h" -#include <memory> #include <vector> #include "base/bind.h" @@ -14,8 +13,11 @@ #include "components/feed/core/time_serialization.h" #include "components/feed/core/user_classifier.h" #include "components/feed/feed_feature_list.h" +#include "components/prefs/pref_registry_simple.h" #include "components/prefs/testing_pref_service.h" #include "components/variations/variations_params_manager.h" +#include "components/web_resource/web_resource_pref_names.h" +#include "net/base/network_change_notifier.h" #include "testing/gtest/include/gtest/gtest.h" namespace feed { @@ -24,6 +26,14 @@ char kNowString[] = "2018-06-11 15:41"; using base::Time; +using base::TimeDelta; + +class ForceDeviceOffline : public net::NetworkChangeNotifier { + public: + ConnectionType GetCurrentConnectionType() const override { + return NetworkChangeNotifier::CONNECTION_NONE; + } +}; class FeedSchedulerHostTest : public ::testing::Test { public: @@ -31,16 +41,16 @@ protected: FeedSchedulerHostTest() : weak_factory_(this) { + FeedSchedulerHost::RegisterProfilePrefs(profile_prefs_.registry()); + UserClassifier::RegisterProfilePrefs(profile_prefs_.registry()); + local_state()->registry()->RegisterBooleanPref(::prefs::kEulaAccepted, + true); + Time now; EXPECT_TRUE(Time::FromUTCString(kNowString, &now)); test_clock_.SetNow(now); - FeedSchedulerHost::RegisterProfilePrefs(pref_service_.registry()); - UserClassifier::RegisterProfilePrefs(pref_service_.registry()); - - scheduler_ = - std::make_unique<FeedSchedulerHost>(&pref_service_, &test_clock_); - InitializeScheduler(scheduler()); + NewScheduler(); } void InitializeScheduler(FeedSchedulerHost* scheduler) { @@ -51,11 +61,20 @@ base::Unretained(this))); } + // Recreates a new copy of the scheduler. This is useful if a test case needs + // to change some global state like prefs or params before the scheduler's + // constructor runs. + void NewScheduler() { + scheduler_ = std::make_unique<FeedSchedulerHost>( + &profile_prefs_, &local_state_, &test_clock_); + InitializeScheduler(scheduler()); + } + // Note: Time will be advanced. void ClassifyAsRareNtpUser() { // By moving time forward from initial seed events, the user will be moved // into RARE_NTP_USER classification. - test_clock()->Advance(base::TimeDelta::FromDays(7)); + test_clock()->Advance(TimeDelta::FromDays(7)); } // Note: Time will be advanced. @@ -63,28 +82,48 @@ // Click on some articles to move the user into ACTIVE_SUGGESTIONS_CONSUMER // classification. Separate by at least 30 minutes for different sessions. scheduler()->OnSuggestionConsumed(); - test_clock()->Advance(base::TimeDelta::FromMinutes(31)); + test_clock()->Advance(TimeDelta::FromMinutes(31)); scheduler()->OnSuggestionConsumed(); } + // Many test cases want to ask the scheduler multiple times in a row to see + // which of the different triggers or under which conditions the scheduler + // will request a refresh. However the scheduler updates internal state when + // it decides a refresh must be made, most importantly, it sets + // |tracking_oustanding_request_| to true. Any subsequent trigger would then + // not start a refresh. To get around this, this method clears out + // |tracking_oustanding_request_|. + void ResetRefreshState(Time last_attempt) { + // OnRequestError() has the side effect of setting kLastFetchAttemptTime to + // the scheduler's clock's now. This typically is not helpful to most test + // cases, so override it. + scheduler()->OnRequestError(0); + profile_prefs()->SetTime(prefs::kLastFetchAttemptTime, last_attempt); + } + + bool PlatformSupportsEula() { + return web_resource::EulaAcceptedNotifier::Create(local_state()) != nullptr; + } + // This helper method sets prefs::kLastFetchAttemptTime to the same value // that's about to be passed into ShouldSessionRequestData(). This is what the - // scheduler will typically experience when refreshes are successful. + // scheduler will typically experience when refreshes are successful. Also + // clears out |tracking_oustanding_request_| through OnRequestError(). NativeRequestBehavior ShouldSessionRequestData( bool has_content, - base::Time content_creation_date_time, + Time content_creation_date_time, bool has_outstanding_request) { - pref_service()->SetTime(prefs::kLastFetchAttemptTime, - content_creation_date_time); + ResetRefreshState(content_creation_date_time); return scheduler()->ShouldSessionRequestData( has_content, content_creation_date_time, has_outstanding_request); } - PrefService* pref_service() { return &pref_service_; } + TestingPrefServiceSimple* profile_prefs() { return &profile_prefs_; } + TestingPrefServiceSimple* local_state() { return &local_state_; } base::SimpleTestClock* test_clock() { return &test_clock_; } FeedSchedulerHost* scheduler() { return scheduler_.get(); } int refresh_call_count() { return refresh_call_count_; } - const std::vector<base::TimeDelta>& schedule_wake_up_times() { + const std::vector<TimeDelta>& schedule_wake_up_times() { return schedule_wake_up_times_; } int cancel_wake_up_call_count() { return cancel_wake_up_call_count_; } @@ -93,24 +132,25 @@ private: void TriggerRefresh() { refresh_call_count_++; } - void ScheduleWakeUp(base::TimeDelta threshold_ms) { + void ScheduleWakeUp(TimeDelta threshold_ms) { schedule_wake_up_times_.push_back(threshold_ms); } void CancelWakeUp() { cancel_wake_up_call_count_++; } - TestingPrefServiceSimple pref_service_; + TestingPrefServiceSimple profile_prefs_; + TestingPrefServiceSimple local_state_; base::SimpleTestClock test_clock_; std::unique_ptr<FeedSchedulerHost> scheduler_; int refresh_call_count_ = 0; - std::vector<base::TimeDelta> schedule_wake_up_times_; + std::vector<TimeDelta> schedule_wake_up_times_; int cancel_wake_up_call_count_ = 0; int fixed_timer_completion_count_ = 0; base::WeakPtrFactory<FeedSchedulerHostTest> weak_factory_; }; TEST_F(FeedSchedulerHostTest, GetTriggerThreshold) { - // Make sure that there is no missing configuration in the cartesian product + // Make sure that there is no missing configuration in the Cartesian product // of states between TriggerType and UserClass. std::vector<FeedSchedulerHost::TriggerType> triggers = { FeedSchedulerHost::TriggerType::NTP_SHOWN, @@ -137,13 +177,10 @@ // For an ACTIVE_NTP_USER, refreshes on NTP_OPEN should be triggered after 4 // hours, and staleness should be at 24 hours. Each case tests a range of // values. - base::Time no_refresh_large = - test_clock()->Now() - base::TimeDelta::FromHours(3); - base::Time refresh_only_small = - test_clock()->Now() - base::TimeDelta::FromHours(5); - base::Time refresh_only_large = - test_clock()->Now() - base::TimeDelta::FromHours(23); - base::Time stale_small = test_clock()->Now() - base::TimeDelta::FromHours(25); + Time no_refresh_large = test_clock()->Now() - TimeDelta::FromHours(3); + Time refresh_only_small = test_clock()->Now() - TimeDelta::FromHours(5); + Time refresh_only_large = test_clock()->Now() - TimeDelta::FromHours(23); + Time stale_small = test_clock()->Now() - TimeDelta::FromHours(25); EXPECT_EQ(REQUEST_WITH_WAIT, ShouldSessionRequestData( @@ -166,11 +203,10 @@ ShouldSessionRequestData( /*has_content*/ true, /*content_creation_date_time*/ stale_small, /*has_outstanding_request*/ false)); - EXPECT_EQ( - REQUEST_WITH_TIMEOUT, - ShouldSessionRequestData( - /*has_content*/ true, /*content_creation_date_time*/ base::Time(), - /*has_outstanding_request*/ false)); + EXPECT_EQ(REQUEST_WITH_TIMEOUT, + ShouldSessionRequestData( + /*has_content*/ true, /*content_creation_date_time*/ Time(), + /*has_outstanding_request*/ false)); // |content_creation_date_time| should be ignored when |has_content| is false. EXPECT_EQ(NO_REQUEST_WITH_WAIT, @@ -209,11 +245,10 @@ ShouldSessionRequestData( /*has_content*/ true, /*content_creation_date_time*/ stale_small, /*has_outstanding_request*/ true)); - EXPECT_EQ( - NO_REQUEST_WITH_TIMEOUT, - ShouldSessionRequestData( - /*has_content*/ true, /*content_creation_date_time*/ base::Time(), - /*has_outstanding_request*/ true)); + EXPECT_EQ(NO_REQUEST_WITH_TIMEOUT, + ShouldSessionRequestData( + /*has_content*/ true, /*content_creation_date_time*/ Time(), + /*has_outstanding_request*/ true)); } TEST_F(FeedSchedulerHostTest, ShouldSessionRequestDataDivergentTimes) { @@ -225,25 +260,24 @@ // Like above case, the user is an ACTIVE_NTP_USER, staleness at 24 hours and // refresh at 4. - base::Time refresh = test_clock()->Now() - base::TimeDelta::FromHours(5); - base::Time no_refresh = test_clock()->Now() - base::TimeDelta::FromHours(3); - base::Time stale = test_clock()->Now() - base::TimeDelta::FromHours(25); - base::Time not_stale = test_clock()->Now() - base::TimeDelta::FromHours(23); + Time refresh = test_clock()->Now() - TimeDelta::FromHours(5); + Time no_refresh = test_clock()->Now() - TimeDelta::FromHours(3); + Time stale = test_clock()->Now() - TimeDelta::FromHours(25); + Time not_stale = test_clock()->Now() - TimeDelta::FromHours(23); - pref_service()->SetTime(prefs::kLastFetchAttemptTime, no_refresh); + ResetRefreshState(no_refresh); EXPECT_EQ(NO_REQUEST_WITH_CONTENT, scheduler()->ShouldSessionRequestData( /*has_content*/ true, /*content_creation_date_time*/ stale, /*has_outstanding_request*/ false)); - pref_service()->SetTime(prefs::kLastFetchAttemptTime, refresh); - + ResetRefreshState(refresh); EXPECT_EQ(REQUEST_WITH_CONTENT, scheduler()->ShouldSessionRequestData( /*has_content*/ true, /*content_creation_date_time*/ not_stale, /*has_outstanding_request*/ false)); - pref_service()->SetTime(prefs::kLastFetchAttemptTime, refresh); + ResetRefreshState(refresh); EXPECT_EQ(REQUEST_WITH_TIMEOUT, scheduler()->ShouldSessionRequestData( /*has_content*/ true, /*content_creation_date_time*/ stale, @@ -252,7 +286,7 @@ // This shouldn't be possible, since last attempt is farther back than // |content_creation_date_time| which updates on success, but verify scheduler // handles it reasonably. - pref_service()->SetTime(prefs::kLastFetchAttemptTime, base::Time()); + ResetRefreshState(Time()); EXPECT_EQ(REQUEST_WITH_CONTENT, scheduler()->ShouldSessionRequestData( /*has_content*/ true, @@ -265,17 +299,20 @@ {{"foregrounded_hours_active_ntp_user", "7.5"}}, {kInterestFeedContentSuggestions.name}); + ResetRefreshState(Time()); EXPECT_EQ(REQUEST_WITH_CONTENT, scheduler()->ShouldSessionRequestData( /*has_content*/ true, /*content_creation_date_time*/ test_clock()->Now() - - base::TimeDelta::FromHours(7), + TimeDelta::FromHours(7), /*has_outstanding_request*/ false)); + + ResetRefreshState(Time()); EXPECT_EQ(REQUEST_WITH_TIMEOUT, scheduler()->ShouldSessionRequestData( /*has_content*/ true, /*content_creation_date_time*/ test_clock()->Now() - - base::TimeDelta::FromHours(8), + TimeDelta::FromHours(8), /*has_outstanding_request*/ false)); } @@ -289,14 +326,14 @@ ShouldSessionRequestData( /*has_content*/ true, /*content_creation_date_time*/ test_clock()->Now() - - base::TimeDelta::FromHours(2), + TimeDelta::FromHours(2), /*has_outstanding_request*/ false)); EXPECT_EQ(REQUEST_WITH_CONTENT, ShouldSessionRequestData( /*has_content*/ true, /*content_creation_date_time*/ test_clock()->Now() - - base::TimeDelta::FromHours(3), + TimeDelta::FromHours(3), /*has_outstanding_request*/ false)); } @@ -312,7 +349,7 @@ ShouldSessionRequestData( /*has_content*/ true, /*content_creation_date_time*/ test_clock()->Now() - - base::TimeDelta::FromHours(1), + TimeDelta::FromHours(1), /*has_outstanding_request*/ false)); // ShouldSessionRequestData() has the side effect of adding NTP_SHOWN event to @@ -323,7 +360,7 @@ ShouldSessionRequestData( /*has_content*/ true, /*content_creation_date_time*/ test_clock()->Now() - - base::TimeDelta::FromHours(2), + TimeDelta::FromHours(2), /*has_outstanding_request*/ false)); } @@ -334,14 +371,14 @@ ShouldSessionRequestData( /*has_content*/ true, /*content_creation_date_time*/ test_clock()->Now() - - base::TimeDelta::FromMinutes(59), + TimeDelta::FromMinutes(59), /*has_outstanding_request*/ false)); EXPECT_EQ(REQUEST_WITH_CONTENT, ShouldSessionRequestData( /*has_content*/ true, /*content_creation_date_time*/ test_clock()->Now() - - base::TimeDelta::FromMinutes(61), + TimeDelta::FromMinutes(61), /*has_outstanding_request*/ false)); variations::testing::VariationParamsManager variation_params( @@ -353,31 +390,31 @@ ShouldSessionRequestData( /*has_content*/ true, /*content_creation_date_time*/ test_clock()->Now() - - base::TimeDelta::FromHours(7), + TimeDelta::FromHours(7), /*has_outstanding_request*/ false)); EXPECT_EQ(REQUEST_WITH_CONTENT, ShouldSessionRequestData( /*has_content*/ true, /*content_creation_date_time*/ test_clock()->Now() - - base::TimeDelta::FromHours(8), + TimeDelta::FromHours(8), /*has_outstanding_request*/ false)); } TEST_F(FeedSchedulerHostTest, OnReceiveNewContentVerifyPref) { - EXPECT_EQ(Time(), pref_service()->GetTime(prefs::kLastFetchAttemptTime)); + EXPECT_EQ(Time(), profile_prefs()->GetTime(prefs::kLastFetchAttemptTime)); scheduler()->OnReceiveNewContent(Time()); - EXPECT_EQ(Time(), pref_service()->GetTime(prefs::kLastFetchAttemptTime)); + EXPECT_EQ(Time(), profile_prefs()->GetTime(prefs::kLastFetchAttemptTime)); // Scheduler should prefer to use specified time over clock time. EXPECT_NE(test_clock()->Now(), - pref_service()->GetTime(prefs::kLastFetchAttemptTime)); + profile_prefs()->GetTime(prefs::kLastFetchAttemptTime)); } TEST_F(FeedSchedulerHostTest, OnRequestErrorVerifyPref) { - EXPECT_EQ(Time(), pref_service()->GetTime(prefs::kLastFetchAttemptTime)); + EXPECT_EQ(Time(), profile_prefs()->GetTime(prefs::kLastFetchAttemptTime)); scheduler()->OnRequestError(0); EXPECT_EQ(test_clock()->Now(), - pref_service()->GetTime(prefs::kLastFetchAttemptTime)); + profile_prefs()->GetTime(prefs::kLastFetchAttemptTime)); } TEST_F(FeedSchedulerHostTest, OnForegroundedActiveNtpUser) { @@ -386,11 +423,11 @@ scheduler()->OnReceiveNewContent(test_clock()->Now()); // Default is 24 hours. - test_clock()->Advance(base::TimeDelta::FromHours(23)); + test_clock()->Advance(TimeDelta::FromHours(23)); scheduler()->OnForegrounded(); EXPECT_EQ(1, refresh_call_count()); - test_clock()->Advance(base::TimeDelta::FromHours(2)); + test_clock()->Advance(TimeDelta::FromHours(2)); scheduler()->OnForegrounded(); EXPECT_EQ(2, refresh_call_count()); scheduler()->OnReceiveNewContent(test_clock()->Now()); @@ -400,11 +437,11 @@ {{"foregrounded_hours_active_ntp_user", "7.5"}}, {kInterestFeedContentSuggestions.name}); - test_clock()->Advance(base::TimeDelta::FromHours(7)); + test_clock()->Advance(TimeDelta::FromHours(7)); scheduler()->OnForegrounded(); EXPECT_EQ(2, refresh_call_count()); - test_clock()->Advance(base::TimeDelta::FromHours(1)); + test_clock()->Advance(TimeDelta::FromHours(1)); scheduler()->OnForegrounded(); EXPECT_EQ(3, refresh_call_count()); } @@ -417,11 +454,11 @@ scheduler()->OnReceiveNewContent(test_clock()->Now()); // Default is 24 hours. - test_clock()->Advance(base::TimeDelta::FromHours(23)); + test_clock()->Advance(TimeDelta::FromHours(23)); scheduler()->OnForegrounded(); EXPECT_EQ(1, refresh_call_count()); - test_clock()->Advance(base::TimeDelta::FromHours(2)); + test_clock()->Advance(TimeDelta::FromHours(2)); scheduler()->OnForegrounded(); EXPECT_EQ(2, refresh_call_count()); scheduler()->OnReceiveNewContent(test_clock()->Now()); @@ -431,11 +468,11 @@ {{"foregrounded_hours_rare_ntp_user", "7.5"}}, {kInterestFeedContentSuggestions.name}); - test_clock()->Advance(base::TimeDelta::FromHours(7)); + test_clock()->Advance(TimeDelta::FromHours(7)); scheduler()->OnForegrounded(); EXPECT_EQ(2, refresh_call_count()); - test_clock()->Advance(base::TimeDelta::FromHours(1)); + test_clock()->Advance(TimeDelta::FromHours(1)); scheduler()->OnForegrounded(); EXPECT_EQ(3, refresh_call_count()); } @@ -448,11 +485,11 @@ scheduler()->OnReceiveNewContent(test_clock()->Now()); // Default is 12 hours. - test_clock()->Advance(base::TimeDelta::FromHours(11)); + test_clock()->Advance(TimeDelta::FromHours(11)); scheduler()->OnForegrounded(); EXPECT_EQ(1, refresh_call_count()); - test_clock()->Advance(base::TimeDelta::FromHours(2)); + test_clock()->Advance(TimeDelta::FromHours(2)); scheduler()->OnForegrounded(); EXPECT_EQ(2, refresh_call_count()); scheduler()->OnReceiveNewContent(test_clock()->Now()); @@ -462,11 +499,11 @@ {{"foregrounded_hours_active_suggestions_consumer", "7.5"}}, {kInterestFeedContentSuggestions.name}); - test_clock()->Advance(base::TimeDelta::FromHours(7)); + test_clock()->Advance(TimeDelta::FromHours(7)); scheduler()->OnForegrounded(); EXPECT_EQ(2, refresh_call_count()); - test_clock()->Advance(base::TimeDelta::FromHours(1)); + test_clock()->Advance(TimeDelta::FromHours(1)); scheduler()->OnForegrounded(); EXPECT_EQ(3, refresh_call_count()); } @@ -500,11 +537,11 @@ scheduler()->OnReceiveNewContent(test_clock()->Now()); // Default is 48 hours. - test_clock()->Advance(base::TimeDelta::FromHours(47)); + test_clock()->Advance(TimeDelta::FromHours(47)); scheduler()->OnFixedTimer(base::OnceClosure()); EXPECT_EQ(1, refresh_call_count()); - test_clock()->Advance(base::TimeDelta::FromHours(2)); + test_clock()->Advance(TimeDelta::FromHours(2)); scheduler()->OnFixedTimer(base::OnceClosure()); EXPECT_EQ(2, refresh_call_count()); scheduler()->OnReceiveNewContent(test_clock()->Now()); @@ -514,11 +551,11 @@ {{"fixed_timer_hours_active_ntp_user", "7.5"}}, {kInterestFeedContentSuggestions.name}); - test_clock()->Advance(base::TimeDelta::FromHours(7)); + test_clock()->Advance(TimeDelta::FromHours(7)); scheduler()->OnFixedTimer(base::OnceClosure()); EXPECT_EQ(2, refresh_call_count()); - test_clock()->Advance(base::TimeDelta::FromHours(1)); + test_clock()->Advance(TimeDelta::FromHours(1)); scheduler()->OnFixedTimer(base::OnceClosure()); EXPECT_EQ(3, refresh_call_count()); } @@ -531,11 +568,11 @@ scheduler()->OnReceiveNewContent(test_clock()->Now()); // Default is 96 hours. - test_clock()->Advance(base::TimeDelta::FromHours(95)); + test_clock()->Advance(TimeDelta::FromHours(95)); scheduler()->OnFixedTimer(base::OnceClosure()); EXPECT_EQ(1, refresh_call_count()); - test_clock()->Advance(base::TimeDelta::FromHours(2)); + test_clock()->Advance(TimeDelta::FromHours(2)); scheduler()->OnFixedTimer(base::OnceClosure()); EXPECT_EQ(2, refresh_call_count()); scheduler()->OnReceiveNewContent(test_clock()->Now()); @@ -545,11 +582,11 @@ {{"fixed_timer_hours_rare_ntp_user", "7.5"}}, {kInterestFeedContentSuggestions.name}); - test_clock()->Advance(base::TimeDelta::FromHours(7)); + test_clock()->Advance(TimeDelta::FromHours(7)); scheduler()->OnFixedTimer(base::OnceClosure()); EXPECT_EQ(2, refresh_call_count()); - test_clock()->Advance(base::TimeDelta::FromHours(1)); + test_clock()->Advance(TimeDelta::FromHours(1)); scheduler()->OnFixedTimer(base::OnceClosure()); EXPECT_EQ(3, refresh_call_count()); } @@ -562,11 +599,11 @@ scheduler()->OnReceiveNewContent(test_clock()->Now()); // Default is 24 hours. - test_clock()->Advance(base::TimeDelta::FromHours(23)); + test_clock()->Advance(TimeDelta::FromHours(23)); scheduler()->OnFixedTimer(base::OnceClosure()); EXPECT_EQ(1, refresh_call_count()); - test_clock()->Advance(base::TimeDelta::FromHours(2)); + test_clock()->Advance(TimeDelta::FromHours(2)); scheduler()->OnFixedTimer(base::OnceClosure()); EXPECT_EQ(2, refresh_call_count()); scheduler()->OnReceiveNewContent(test_clock()->Now()); @@ -576,11 +613,11 @@ {{"fixed_timer_hours_active_suggestions_consumer", "7.5"}}, {kInterestFeedContentSuggestions.name}); - test_clock()->Advance(base::TimeDelta::FromHours(7)); + test_clock()->Advance(TimeDelta::FromHours(7)); scheduler()->OnFixedTimer(base::OnceClosure()); EXPECT_EQ(2, refresh_call_count()); - test_clock()->Advance(base::TimeDelta::FromHours(1)); + test_clock()->Advance(TimeDelta::FromHours(1)); scheduler()->OnFixedTimer(base::OnceClosure()); EXPECT_EQ(3, refresh_call_count()); } @@ -593,7 +630,8 @@ // Make another scheduler to initialize, make sure it doesn't schedule a // wake up. - FeedSchedulerHost second_scheduler(pref_service(), test_clock()); + FeedSchedulerHost second_scheduler(profile_prefs(), local_state(), + test_clock()); InitializeScheduler(&second_scheduler); EXPECT_EQ(2U, schedule_wake_up_times().size()); } @@ -604,4 +642,197 @@ EXPECT_EQ(2U, schedule_wake_up_times().size()); } +TEST_F(FeedSchedulerHostTest, ShouldRefreshOffline) { + { + ForceDeviceOffline forceDeviceOffline; + scheduler()->OnForegrounded(); + EXPECT_EQ(0, refresh_call_count()); + } + + scheduler()->OnForegrounded(); + EXPECT_EQ(1, refresh_call_count()); +} + +TEST_F(FeedSchedulerHostTest, EulaNotAccepted) { + if (PlatformSupportsEula()) { + local_state()->SetBoolean(::prefs::kEulaAccepted, false); + scheduler()->OnForegrounded(); + EXPECT_EQ(0, refresh_call_count()); + + // The transition should kick off a refresh. + local_state()->SetBoolean(::prefs::kEulaAccepted, true); + EXPECT_EQ(1, refresh_call_count()); + + // And now it doesn't block normal triggers either. + ResetRefreshState(Time()); + scheduler()->OnForegrounded(); + EXPECT_EQ(2, refresh_call_count()); + } +} + +TEST_F(FeedSchedulerHostTest, DisableOneTrigger) { + variations::testing::VariationParamsManager variation_params( + kInterestFeedContentSuggestions.name, + {{"disable_trigger_types", "foregrounded"}}, + {kInterestFeedContentSuggestions.name}); + NewScheduler(); + + scheduler()->OnForegrounded(); + EXPECT_EQ(0, refresh_call_count()); + + scheduler()->OnFixedTimer(base::OnceClosure()); + EXPECT_EQ(1, refresh_call_count()); + + EXPECT_EQ(REQUEST_WITH_WAIT, + ShouldSessionRequestData( + /*has_content*/ false, /*content_creation_date_time*/ Time(), + /*has_outstanding_request*/ false)); +} + +TEST_F(FeedSchedulerHostTest, DisableAllTriggers) { + variations::testing::VariationParamsManager variation_params( + kInterestFeedContentSuggestions.name, + {{"disable_trigger_types", "ntp_shown,foregrounded,fixed_timer"}}, + {kInterestFeedContentSuggestions.name}); + NewScheduler(); + + scheduler()->OnForegrounded(); + EXPECT_EQ(0, refresh_call_count()); + + scheduler()->OnFixedTimer(base::OnceClosure()); + EXPECT_EQ(0, refresh_call_count()); + + EXPECT_EQ(NO_REQUEST_WITH_WAIT, + ShouldSessionRequestData( + /*has_content*/ false, /*content_creation_date_time*/ Time(), + /*has_outstanding_request*/ false)); +} + +TEST_F(FeedSchedulerHostTest, DisableBogusTriggers) { + variations::testing::VariationParamsManager variation_params( + kInterestFeedContentSuggestions.name, + {{"disable_trigger_types", "foo,123,#$*,,"}}, + {kInterestFeedContentSuggestions.name}); + + NewScheduler(); + + scheduler()->OnForegrounded(); + EXPECT_EQ(1, refresh_call_count()); + + ResetRefreshState(Time()); + scheduler()->OnFixedTimer(base::OnceClosure()); + EXPECT_EQ(2, refresh_call_count()); + + EXPECT_EQ(REQUEST_WITH_WAIT, + ShouldSessionRequestData( + /*has_content*/ false, /*content_creation_date_time*/ Time(), + /*has_outstanding_request*/ false)); +} + +TEST_F(FeedSchedulerHostTest, OnHistoryCleared) { + // OnForegrounded() does nothing because content is fresher than threshold. + scheduler()->OnReceiveNewContent(test_clock()->Now()); + scheduler()->OnForegrounded(); + EXPECT_EQ(0, refresh_call_count()); + + scheduler()->OnHistoryCleared(); + + scheduler()->OnForegrounded(); + EXPECT_EQ(0, refresh_call_count()); + + scheduler()->OnFixedTimer(base::OnceClosure()); + EXPECT_EQ(0, refresh_call_count()); + + EXPECT_EQ(NO_REQUEST_WITH_WAIT, + ShouldSessionRequestData( + /*has_content*/ false, /*content_creation_date_time*/ Time(), + /*has_outstanding_request*/ false)); + + test_clock()->Advance(TimeDelta::FromMinutes(29)); + + scheduler()->OnForegrounded(); + EXPECT_EQ(0, refresh_call_count()); + + test_clock()->Advance(TimeDelta::FromMinutes(1)); + + // Normally this would still be within foreground threshold, but the + // OnHistoryCleared() cleared the last attempt time. + scheduler()->OnForegrounded(); + EXPECT_EQ(1, refresh_call_count()); +} + +TEST_F(FeedSchedulerHostTest, SuppressRefreshDuration) { + variations::testing::VariationParamsManager variation_params( + kInterestFeedContentSuggestions.name, + {{"suppress_refresh_duration_minutes", "100"}}, + {kInterestFeedContentSuggestions.name}); + scheduler()->OnHistoryCleared(); + + test_clock()->Advance(TimeDelta::FromMinutes(99)); + scheduler()->OnForegrounded(); + EXPECT_EQ(0, refresh_call_count()); + + test_clock()->Advance(TimeDelta::FromMinutes(1)); + scheduler()->OnForegrounded(); + EXPECT_EQ(1, refresh_call_count()); +} + +TEST_F(FeedSchedulerHostTest, OustandingRequest) { + scheduler()->OnForegrounded(); + EXPECT_EQ(1, refresh_call_count()); + + scheduler()->OnForegrounded(); + scheduler()->OnFixedTimer(base::OnceClosure()); + EXPECT_EQ(1, refresh_call_count()); + EXPECT_EQ(NO_REQUEST_WITH_WAIT, + scheduler()->ShouldSessionRequestData( + /*has_content*/ false, /*content_creation_date_time*/ Time(), + /*has_outstanding_request*/ true)); + + test_clock()->Advance(TimeDelta::FromDays(7)); + scheduler()->OnForegrounded(); + EXPECT_EQ(1, refresh_call_count()); + + // Although this clears outstanding, it also updates last attempted time, so + // still expect no refresh. + scheduler()->OnRequestError(0); + scheduler()->OnForegrounded(); + EXPECT_EQ(1, refresh_call_count()); + + test_clock()->Advance(TimeDelta::FromDays(7)); + scheduler()->OnForegrounded(); + EXPECT_EQ(2, refresh_call_count()); + + // OnReceiveNewContent() should also clear tracked outstanding request, but + // similar to above, last attempted time is also set. + scheduler()->OnReceiveNewContent(test_clock()->Now()); + scheduler()->OnForegrounded(); + EXPECT_EQ(2, refresh_call_count()); + + test_clock()->Advance(TimeDelta::FromDays(7)); + scheduler()->OnForegrounded(); + EXPECT_EQ(3, refresh_call_count()); + + // Although this shouldn't typically happen, OnReceiveNewContent() takes a + // time that could be wildly divergent from the scheduler's clock's now. + scheduler()->OnReceiveNewContent(test_clock()->Now() - + TimeDelta::FromDays(7)); + scheduler()->OnForegrounded(); + EXPECT_EQ(4, refresh_call_count()); +} + +TEST_F(FeedSchedulerHostTest, IncorporatesExternalOustandingRequest) { + EXPECT_EQ(NO_REQUEST_WITH_WAIT, + scheduler()->ShouldSessionRequestData( + /*has_content*/ false, /*content_creation_date_time*/ Time(), + /*has_outstanding_request*/ true)); + + // Normally this would trigger a refresh. In this case the scheduler will + // notice the ShouldSessionRequestData() call carries information that there + // is an outstanding request, which the scheduler should remember and then + // prevent the OnForegrounded() from requesting a refresh. + scheduler()->OnForegrounded(); + EXPECT_EQ(0, refresh_call_count()); +} + } // namespace feed
diff --git a/components/gcm_driver/gcm_account_tracker_unittest.cc b/components/gcm_driver/gcm_account_tracker_unittest.cc index 9314885..f17b7a6 100644 --- a/components/gcm_driver/gcm_account_tracker_unittest.cc +++ b/components/gcm_driver/gcm_account_tracker_unittest.cc
@@ -13,6 +13,7 @@ #include "base/message_loop/message_loop.h" #include "components/gcm_driver/fake_gcm_driver.h" #include "components/signin/core/browser/account_tracker_service.h" +#include "components/signin/core/browser/fake_gaia_cookie_manager_service.h" #include "components/signin/core/browser/fake_profile_oauth2_token_service.h" #include "components/signin/core/browser/fake_signin_manager.h" #include "components/signin/core/browser/test_signin_client.h" @@ -217,6 +218,8 @@ std::unique_ptr<TestSigninClient> test_signin_client_; std::unique_ptr<SigninManagerForTest> fake_signin_manager_; std::unique_ptr<FakeProfileOAuth2TokenService> fake_token_service_; + std::unique_ptr<FakeGaiaCookieManagerService> + fake_gaia_cookie_manager_service_; std::unique_ptr<identity::IdentityManager> identity_manager_; std::unique_ptr<GCMAccountTracker> tracker_; }; @@ -234,6 +237,9 @@ &account_tracker_service_, nullptr)); #endif + fake_gaia_cookie_manager_service_.reset(new FakeGaiaCookieManagerService( + fake_token_service_.get(), "gcm_account_tracker_unittest", + test_signin_client_.get())); AccountTrackerService::RegisterPrefs(pref_service_.registry()); SigninManagerBase::RegisterProfilePrefs(pref_service_.registry()); SigninManagerBase::RegisterPrefs(pref_service_.registry()); @@ -241,7 +247,7 @@ identity_manager_ = std::make_unique<identity::IdentityManager>( fake_signin_manager_.get(), fake_token_service_.get(), - &account_tracker_service_); + &account_tracker_service_, fake_gaia_cookie_manager_service_.get()); std::unique_ptr<AccountTracker> gaia_account_tracker(new AccountTracker( fake_signin_manager_.get(), fake_token_service_.get(),
diff --git a/components/ntp_snippets/remote/remote_suggestions_scheduler_impl.cc b/components/ntp_snippets/remote/remote_suggestions_scheduler_impl.cc index f3f0eea2..4f0777a 100644 --- a/components/ntp_snippets/remote/remote_suggestions_scheduler_impl.cc +++ b/components/ntp_snippets/remote/remote_suggestions_scheduler_impl.cc
@@ -358,7 +358,7 @@ eula_notifier_->Init(this); } - ~EulaState() = default; + ~EulaState() override = default; bool IsEulaAccepted() { if (!eula_notifier_) {
diff --git a/components/password_manager/core/browser/form_parsing/form_parser.cc b/components/password_manager/core/browser/form_parsing/form_parser.cc index ce07a0c..f03a0d1 100644 --- a/components/password_manager/core/browser/form_parsing/form_parser.cc +++ b/components/password_manager/core/browser/form_parsing/form_parser.cc
@@ -12,9 +12,14 @@ #include <utility> #include <vector> +#include "base/no_destructor.h" #include "base/stl_util.h" +#include "base/strings/string16.h" #include "base/strings/string_piece.h" #include "base/strings/string_split.h" +#include "base/strings/utf_string_conversions.h" +#include "components/autofill/core/common/autofill_regex_constants.h" +#include "components/autofill/core/common/autofill_regexes.h" #include "components/autofill/core/common/form_data.h" #include "components/autofill/core/common/password_form.h" #include "components/password_manager/core/common/password_manager_features.h" @@ -101,6 +106,21 @@ Interactability interactability = Interactability::kUnlikely; }; +// Returns true if the |str| contains words related to CVC fields. +bool StringMatchesCVC(const base::string16& str) { + static const base::NoDestructor<base::string16> kCardCvcReCached( + base::UTF8ToUTF16(autofill::kCardCvcRe)); + + return autofill::MatchesPattern(str, *kCardCvcReCached); +} + +// TODO(crbug.com/860700): Remove once server-side provides hints for CVC +// fields. +// Returns true if the |field|'s name or id hint at the field being a CVC field. +bool IsFieldCVC(const FormFieldData& field) { + return StringMatchesCVC(field.name) || StringMatchesCVC(field.id); +} + // Returns true iff |processed_field| matches the |interactability_bar|. That is // when either: // (1) |processed_field.interactability| is not less than |interactability_bar|, @@ -244,7 +264,8 @@ // Returns only relevant password fields from |processed_fields|. Namely, if // |mode| == SAVING return only non-empty fields (for saving empty fields are // useless). This ignores all passwords with Interactability below -// |best_interactability|. Stores the iterator to the first relevant password in +// |best_interactability| and also fields with names which sound like CVC +// fields. Stores the iterator to the first relevant password in // |first_relevant_password|. std::vector<const FormFieldData*> GetRelevantPasswords( const std::vector<ProcessedField>& processed_fields, @@ -277,6 +298,8 @@ FieldPropertiesFlags::AUTOFILLED))) { continue; } + if (IsFieldCVC(*processed_field.field)) + continue; if (*first_relevant_password == processed_fields.end()) *first_relevant_password = it; result.push_back(processed_field.field); @@ -379,6 +402,8 @@ continue; if (consider_only_non_empty && it->field->value.empty()) continue; + if (IsFieldCVC(*it->field)) + continue; if (!username) username = it->field; if (it->field->is_focusable) { @@ -396,16 +421,20 @@ return field ? field->unique_renderer_id : FormFieldData::kNotSetFormControlRendererId; } -// Tries to find the username and password fields in |processed_fields| based on -// the structure (how the fields are ordered). If |mode| is SAVING, only +// Tries to find the username and password fields in |processed_fields| based +// on the structure (how the fields are ordered). If |mode| is SAVING, only // considers non-empty fields. The |found_fields| is both an input and output // argument: if some password field and the username are already present, the // the function exits early. If something is missing, the function tries to -// complete it. The result is stored back in |found_fields|. +// complete it. The result is stored back in |found_fields|. The best +// interactability for usernames, which depends on position of the found +// passwords as well, is returned through |username_max| to be used in other +// kinds of analysis. void ParseUsingBaseHeuristics( const std::vector<ProcessedField>& processed_fields, FormParsingMode mode, - SignificantFields* found_fields) { + SignificantFields* found_fields, + Interactability* username_max) { // If there is both the username and the minimal set of fields to build a // PasswordForm, return early -- no more work to do. if (found_fields->HasPasswords() && found_fields->username) @@ -456,15 +485,15 @@ return; // What is the best interactability among text fields preceding the passwords? - Interactability username_max = Interactability::kUnlikely; + *username_max = Interactability::kUnlikely; for (auto it = processed_fields.begin(); it != first_relevant_password; ++it) { if (!it->is_password) - username_max = std::max(username_max, it->interactability); + *username_max = std::max(*username_max, it->interactability); } found_fields->username = FindUsernameFieldBaseHeuristics( - processed_fields, first_relevant_password, mode, username_max); + processed_fields, first_relevant_password, mode, *username_max); return; } @@ -559,15 +588,18 @@ } // Find the first element in |username_predictions| (i.e. the most reliable -// prediction) that occurs in |processed_fields|. +// prediction) that occurs in |processed_fields| and has interactability level +// at least |username_max|. const FormFieldData* FindUsernameInPredictions( const std::vector<uint32_t>& username_predictions, - const std::vector<ProcessedField>& processed_fields) { + const std::vector<ProcessedField>& processed_fields, + Interactability username_max) { for (uint32_t predicted_id : username_predictions) { auto iter = std::find_if( processed_fields.begin(), processed_fields.end(), - [predicted_id](const ProcessedField& processed_field) { - return processed_field.field->unique_renderer_id == predicted_id; + [predicted_id, username_max](const ProcessedField& processed_field) { + return processed_field.field->unique_renderer_id == predicted_id && + MatchesInteractability(processed_field, username_max); }); if (iter != processed_fields.end()) { return iter->field; @@ -576,15 +608,33 @@ return nullptr; } +// Return true if |significant_fields| has an username field and +// |form_predictions| has |may_use_prefilled_placeholder| == true for the +// username field. +bool GetMayUsePrefilledPlaceholder( + const FormPredictions* form_predictions, + const SignificantFields& significant_fields) { + if (!form_predictions || !significant_fields.username) + return false; + + uint32_t username_id = significant_fields.username->unique_renderer_id; + auto it = form_predictions->find(username_id); + if (it == form_predictions->end()) + return false; + return it->second.may_use_prefilled_placeholder; +} + // Puts together a PasswordForm, the result of the parsing, based on the // |form_data| description of the form metadata (e.g., action), the already // parsed information about what are the |significant_fields|, and the list // |all_possible_passwords| of all non-empty password values and associated -// element names which occurred in the form. +// element names which occurred in the form. |form_predictions| is used to find +// fields that may have preffilled placeholders. std::unique_ptr<PasswordForm> AssemblePasswordForm( const autofill::FormData& form_data, const SignificantFields* significant_fields, - autofill::ValueElementVector all_possible_passwords) { + autofill::ValueElementVector all_possible_passwords, + const FormPredictions* form_predictions) { if (!significant_fields || !significant_fields->HasPasswords()) return nullptr; @@ -599,6 +649,8 @@ result->preferred = false; result->blacklisted_by_user = false; result->type = PasswordForm::TYPE_MANUAL; + result->username_may_use_prefilled_placeholder = + GetMayUsePrefilledPlaceholder(form_predictions, *significant_fields); // Set data related to specific fields. SetFields(*significant_fields, result.get()); @@ -633,12 +685,22 @@ if (!significant_fields) significant_fields = std::make_unique<SignificantFields>(); - // Try to find the username based on the context of the fields. - if (!significant_fields->username && + const bool username_found_before_heuristic = significant_fields->username; + + // Try to parse with base heuristic. + Interactability username_max = Interactability::kUnlikely; + ParseUsingBaseHeuristics(processed_fields, mode, significant_fields.get(), + &username_max); + + // Additionally, and based on the best interactability computed by base + // heuristics, try to improve the username based on the context of the + // fields, unless the username already came from more reliable types of + // analysis. + if (!username_found_before_heuristic && base::FeatureList::IsEnabled( password_manager::features::kHtmlBasedUsernameDetector)) { const FormFieldData* username_field_by_context = FindUsernameInPredictions( - form_data.username_predictions, processed_fields); + form_data.username_predictions, processed_fields, username_max); if (username_field_by_context && !(mode == FormParsingMode::SAVING && username_field_by_context->value.empty())) { @@ -646,11 +708,9 @@ } } - // Try to parse with base heuristic. - ParseUsingBaseHeuristics(processed_fields, mode, significant_fields.get()); - return AssemblePasswordForm(form_data, significant_fields.get(), - std::move(all_possible_passwords)); + std::move(all_possible_passwords), + form_predictions); } } // namespace password_manager
diff --git a/components/password_manager/core/browser/form_parsing/form_parser_unittest.cc b/components/password_manager/core/browser/form_parsing/form_parser_unittest.cc index b380b80..14051456 100644 --- a/components/password_manager/core/browser/form_parsing/form_parser_unittest.cc +++ b/components/password_manager/core/browser/form_parsing/form_parser_unittest.cc
@@ -79,6 +79,7 @@ int number_of_all_possible_passwords = -1; // null means no checking const autofill::ValueElementVector* all_possible_passwords = nullptr; + bool username_may_use_prefilled_placeholder = false; }; // Returns numbers which are distinct from each other within the scope of one @@ -311,6 +312,8 @@ EXPECT_FALSE(parsed_form->blacklisted_by_user); EXPECT_EQ(PasswordForm::TYPE_MANUAL, parsed_form->type); EXPECT_TRUE(parsed_form->has_renderer_ids); + EXPECT_EQ(test_case.username_may_use_prefilled_placeholder, + parsed_form->username_may_use_prefilled_placeholder); CheckPasswordFormFields(*parsed_form, form_data, expected_ids); CheckAllValuesUnique(parsed_form->all_possible_passwords); if (test_case.number_of_all_possible_passwords >= 0) { @@ -918,7 +921,8 @@ "Username-only predictions are ignored", { {.form_control_type = "text", - .prediction = {.type = autofill::USERNAME}}, + .prediction = {.type = autofill::USERNAME, + .may_use_prefilled_placeholder = true}}, {.role = ElementRole::USERNAME, .form_control_type = "text"}, {.role = ElementRole::CURRENT_PASSWORD, .form_control_type = "password"}, @@ -929,13 +933,16 @@ { {.role = ElementRole::USERNAME, .form_control_type = "text", - .prediction = {.type = autofill::USERNAME_AND_EMAIL_ADDRESS}}, + .prediction = {.type = autofill::USERNAME_AND_EMAIL_ADDRESS, + .may_use_prefilled_placeholder = true}}, {.form_control_type = "text"}, {.form_control_type = "password"}, {.role = ElementRole::CURRENT_PASSWORD, - .prediction = {.type = autofill::PASSWORD}, + .prediction = {.type = autofill::PASSWORD, + .may_use_prefilled_placeholder = true}, .form_control_type = "password"}, }, + .username_may_use_prefilled_placeholder = true, }, { .description_for_logging = "Longer predictions work", @@ -1035,6 +1042,20 @@ {.form_control_type = "text", .is_focusable = true, .value = ""}, }, }, + { + "Interactability also matters for HTML classifier.", + { + {.form_control_type = "text", + .is_focusable = false, + .predicted_username = 0}, + {.role = ElementRole::USERNAME, + .form_control_type = "text", + .is_focusable = true}, + {.role = ElementRole::CURRENT_PASSWORD, + .form_control_type = "password", + .is_focusable = true}, + }, + }, }); } @@ -1233,6 +1254,22 @@ }); } +// Until autofill server learns to provide CVC-related hints, the parser should +// try to get the hint from the field names. +TEST(FormParserTest, CVC) { + CheckTestData({ + { + "Name of 'verification_type' matches the CVC pattern.", + { + {.role = ElementRole::USERNAME, .form_control_type = "text"}, + {.form_control_type = "text", .name = "verification_type"}, + {.role = ElementRole::CURRENT_PASSWORD, + .form_control_type = "password"}, + }, + }, + }); +} + } // namespace } // namespace password_manager
diff --git a/components/password_manager/core/browser/form_parsing/password_field_prediction.cc b/components/password_manager/core/browser/form_parsing/password_field_prediction.cc index b47fb057..4f49355 100644 --- a/components/password_manager/core/browser/form_parsing/password_field_prediction.cc +++ b/components/password_manager/core/browser/form_parsing/password_field_prediction.cc
@@ -42,12 +42,19 @@ FormPredictions ConvertToFormPredictions(const FormStructure& form_structure) { FormPredictions result; - for (const auto& field : form_structure) { ServerFieldType server_type = field->server_type(); - if (IsCredentialRelatedPrediction(server_type)) - result[field->unique_renderer_id] = - PasswordFieldPrediction{.type = server_type}; + if (IsCredentialRelatedPrediction(server_type)) { + bool may_use_prefilled_placeholder = false; + for (const auto& predictions : field->server_predictions()) { + may_use_prefilled_placeholder |= + predictions.may_use_prefilled_placeholder(); + } + + result[field->unique_renderer_id] = PasswordFieldPrediction{ + .type = server_type, + .may_use_prefilled_placeholder = may_use_prefilled_placeholder}; + } } return result;
diff --git a/components/password_manager/core/browser/form_parsing/password_field_prediction.h b/components/password_manager/core/browser/form_parsing/password_field_prediction.h index 9a9339a..c4bb30a 100644 --- a/components/password_manager/core/browser/form_parsing/password_field_prediction.h +++ b/components/password_manager/core/browser/form_parsing/password_field_prediction.h
@@ -29,12 +29,9 @@ CredentialFieldType DeriveFromServerFieldType(autofill::ServerFieldType type); // Contains server predictions for a field. -// This is the struct rather than using because it will be expanded soon with -// additional information. -// TODO(https://crbug.com/831123): Remove comment about struct usage purposes as -// soon as additional fields added. struct PasswordFieldPrediction { autofill::ServerFieldType type; + bool may_use_prefilled_placeholder = false; }; // Contains server predictions for a form. Keys are unique renderer ids of
diff --git a/components/password_manager/core/browser/form_parsing/password_field_prediction_unittest.cc b/components/password_manager/core/browser/form_parsing/password_field_prediction_unittest.cc index ec2b283..2904367 100644 --- a/components/password_manager/core/browser/form_parsing/password_field_prediction_unittest.cc +++ b/components/password_manager/core/browser/form_parsing/password_field_prediction_unittest.cc
@@ -27,6 +27,9 @@ using autofill::USERNAME_AND_EMAIL_ADDRESS; using base::ASCIIToUTF16; +using FieldPrediction = + autofill::AutofillQueryResponseContents::Field::FieldPrediction; + namespace password_manager { namespace { @@ -37,14 +40,15 @@ std::string form_control_type; ServerFieldType input_type; ServerFieldType expected_type; + bool may_use_prefilled_placeholder; } test_fields[] = { - {"full_name", "text", UNKNOWN_TYPE, UNKNOWN_TYPE}, + {"full_name", "text", UNKNOWN_TYPE, UNKNOWN_TYPE, false}, // Password Manager is interested only in credential related types. - {"Email", "email", EMAIL_ADDRESS, UNKNOWN_TYPE}, - {"username", "text", USERNAME, USERNAME}, - {"Password", "password", PASSWORD, PASSWORD}, + {"Email", "email", EMAIL_ADDRESS, UNKNOWN_TYPE, false}, + {"username", "text", USERNAME, USERNAME, true}, + {"Password", "password", PASSWORD, PASSWORD, false}, {"confirm_password", "password", CONFIRMATION_PASSWORD, - CONFIRMATION_PASSWORD}}; + CONFIRMATION_PASSWORD, true}}; FormData form_data; for (size_t i = 0; i < base::size(test_fields); ++i) { @@ -57,11 +61,17 @@ FormStructure form_structure(form_data); size_t expected_predictions = 0; - // Set server predictions and create expectected votes. + // Set server predictions and create expected votes. for (size_t i = 0; i < base::size(test_fields); ++i) { AutofillField* field = form_structure.field(i); field->set_server_type(test_fields[i].input_type); ServerFieldType expected_type = test_fields[i].expected_type; + + FieldPrediction prediction; + prediction.set_may_use_prefilled_placeholder( + test_fields[i].may_use_prefilled_placeholder); + field->set_server_predictions({prediction}); + if (expected_type != UNKNOWN_TYPE) ++expected_predictions; } @@ -79,6 +89,8 @@ } else { ASSERT_NE(actual_predictions.end(), it); EXPECT_EQ(test_fields[i].expected_type, it->second.type); + EXPECT_EQ(test_fields[i].may_use_prefilled_placeholder, + it->second.may_use_prefilled_placeholder); } } }
diff --git a/components/password_manager/core/browser/password_manager.cc b/components/password_manager/core/browser/password_manager.cc index 7ad176eb..689ecb9 100644 --- a/components/password_manager/core/browser/password_manager.cc +++ b/components/password_manager/core/browser/password_manager.cc
@@ -251,6 +251,27 @@ outcome); } +// Returns true if the user needs to be prompted before a password can be +// saved (instead of automatically saving the password), based on inspecting +// the state of |manager|. +bool ShouldPromptUserToSavePassword(const PasswordFormManager& manager) { + if (IsPasswordUpdate(manager)) { + // Updating a credential might erase a useful stored value by accident. + // Always ask the user to confirm. + return true; + } + + // User successfully signed-in with PSL match credentials. These credentials + // should be automatically saved in order to be autofilled on next login. + if (manager.IsPendingCredentialsPublicSuffixMatch()) + return false; + + if (manager.has_generated_password()) + return false; + + return manager.IsNewLogin(); +} + } // namespace // static @@ -550,15 +571,17 @@ FormFetcher::State::WAITING) { return; } - ProvisionallySaveManager(password_form, matched_manager, nullptr); + + std::unique_ptr<PasswordFormManager> manager = matched_manager->Clone(); + PasswordForm form(password_form); + form.preferred = true; + manager->ProvisionallySave(form); // Show the fallback if a prompt or a confirmation bubble should be available. - bool has_generated_password = - provisional_save_manager_->has_generated_password(); - if (ShouldPromptUserToSavePassword() || has_generated_password) { - DCHECK(provisional_save_manager_); - bool is_update = IsPasswordUpdate(*provisional_save_manager_); - client_->ShowManualFallbackForSaving(std::move(provisional_save_manager_), + bool has_generated_password = manager->has_generated_password(); + if (ShouldPromptUserToSavePassword(*manager) || has_generated_password) { + bool is_update = IsPasswordUpdate(*manager); + client_->ShowManualFallbackForSaving(std::move(manager), has_generated_password, is_update); matched_manager->GetMetricsRecorder()->RecordShowManualFallbackForSaving( has_generated_password, is_update); @@ -808,24 +831,6 @@ !new_origin.SchemeIsCryptographic(); } -bool PasswordManager::ShouldPromptUserToSavePassword() const { - if (IsPasswordUpdate(*provisional_save_manager_)) { - // Updating a credential might erase a useful stored value by accident. - // Always ask the user to confirm. - return true; - } - - // User successfully signed-in with PSL match credentials. These credentials - // should be automatically saved in order to be autofilled on next login. - if (provisional_save_manager_->IsPendingCredentialsPublicSuffixMatch()) - return false; - - if (provisional_save_manager_->has_generated_password()) - return false; - - return provisional_save_manager_->IsNewLogin(); -} - bool PasswordManager::ShouldPromptUserToSavePasswordOld() const { return (provisional_save_manager_->IsNewLogin() || provisional_save_manager_ @@ -957,9 +962,10 @@ DCHECK(!provisional_save_manager_->GetPendingCredentials() .only_for_fallback_saving); - LogShouldShouldPromptComparison(ShouldPromptUserToSavePasswordOld(), - ShouldPromptUserToSavePassword()); - if (ShouldPromptUserToSavePassword()) { + LogShouldShouldPromptComparison( + ShouldPromptUserToSavePasswordOld(), + ShouldPromptUserToSavePassword(*provisional_save_manager_)); + if (ShouldPromptUserToSavePassword(*provisional_save_manager_)) { bool empty_password = provisional_save_manager_->GetPendingCredentials() .username_value.empty(); UMA_HISTOGRAM_BOOLEAN("PasswordManager.EmptyUsernames.OfferedToSave",
diff --git a/components/password_manager/core/browser/password_manager.h b/components/password_manager/core/browser/password_manager.h index 3dd07a78..0998e368 100644 --- a/components/password_manager/core/browser/password_manager.h +++ b/components/password_manager/core/browser/password_manager.h
@@ -172,6 +172,10 @@ const std::vector<std::unique_ptr<NewPasswordFormManager>>& form_managers() { return form_managers_; } + + const PasswordFormManager* provisional_save_manager() { + return provisional_save_manager_.get(); + } #endif NavigationEntryToCheck entry_to_check() const { return entry_to_check_; } @@ -208,12 +212,6 @@ bool ShouldBlockPasswordForSameOriginButDifferentScheme( const autofill::PasswordForm& form) const; - // Returns true if the user needs to be prompted before a password can be - // saved (instead of automatically saving - // the password), based on inspecting the state of - // |provisional_save_manager_|. - bool ShouldPromptUserToSavePassword() const; - // The old version of ShouldPromptUserToSavePassword, it is left for // comparison and metric sending. // TODO(crbug.com/856543): Remove it after M-70.
diff --git a/components/password_manager/core/browser/password_manager_unittest.cc b/components/password_manager/core/browser/password_manager_unittest.cc index d4ff65c7..11b6eec 100644 --- a/components/password_manager/core/browser/password_manager_unittest.cc +++ b/components/password_manager/core/browser/password_manager_unittest.cc
@@ -44,6 +44,8 @@ using base::TestMockTimeTaskRunner; using testing::_; using testing::AnyNumber; +using testing::IsNull; +using testing::NotNull; using testing::Return; using testing::ReturnRef; using testing::SaveArg; @@ -2552,4 +2554,39 @@ EXPECT_EQ(1u, manager()->form_managers().size()); } +TEST_F(PasswordManagerTest, + ShowManualFallbacksDontChangeProvisionalSaveManager) { + EXPECT_CALL(client_, IsSavingAndFillingEnabledForCurrentPage()) + .WillRepeatedly(Return(true)); + + std::vector<PasswordForm> observed; + PasswordForm form(MakeSimpleForm()); + observed.push_back(form); + EXPECT_CALL(*store_, GetLogins(_, _)) + .WillRepeatedly(WithArg<1>(InvokeConsumer(form))); + EXPECT_CALL(driver_, FillPasswordForm(_)).Times(2); + manager()->OnPasswordFormsParsed(&driver_, observed); + manager()->OnPasswordFormsRendered(&driver_, observed, true); + + EXPECT_THAT(manager()->provisional_save_manager(), IsNull()); + manager()->ShowManualFallbackForSaving(&driver_, form); + EXPECT_THAT(manager()->provisional_save_manager(), IsNull()); + + // The user submits the form and a provisional save manager is set. + OnPasswordFormSubmitted(form); + + EXPECT_THAT(manager()->provisional_save_manager(), NotNull()); + const PasswordFormManager* last_provisional_save_manager = + manager()->provisional_save_manager(); + + EXPECT_CALL(client_, HideManualFallbackForSaving()); + // The call to manual fallback with |form| equal to already saved should close + // the fallback. + manager()->ShowManualFallbackForSaving(&driver_, form); + + EXPECT_THAT(manager()->provisional_save_manager(), NotNull()); + EXPECT_EQ(last_provisional_save_manager, + manager()->provisional_save_manager()); +} + } // namespace password_manager
diff --git a/components/payments/core/payment_manifest_downloader.cc b/components/payments/core/payment_manifest_downloader.cc index 78155cf..3ba5bec 100644 --- a/components/payments/core/payment_manifest_downloader.cc +++ b/components/payments/core/payment_manifest_downloader.cc
@@ -14,6 +14,7 @@ #include "components/data_use_measurement/core/data_use_user_data.h" #include "components/link_header_util/link_header_util.h" #include "net/base/load_flags.h" +#include "net/base/registry_controlled_domains/registry_controlled_domain.h" #include "net/base/url_util.h" #include "net/http/http_response_headers.h" #include "net/http/http_status_code.h" @@ -154,7 +155,11 @@ if (download->allowed_number_of_redirects > 0) { DCHECK(download->method == "HEAD"); GURL redirect_url = ParseRedirectUrl(redirect_info); - if (!redirect_url.is_empty()) { + if (!redirect_url.is_empty() && + // Do not allow cross site redirects. + net::registry_controlled_domains::SameDomainOrHost( + download->original_url, redirect_url, + net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES)) { InitiateDownload(redirect_url, "HEAD", --download->allowed_number_of_redirects, std::move(download->callback));
diff --git a/components/payments/core/payment_manifest_downloader_unittest.cc b/components/payments/core/payment_manifest_downloader_unittest.cc index c1cd95b..695c34f 100644 --- a/components/payments/core/payment_manifest_downloader_unittest.cc +++ b/components/payments/core/payment_manifest_downloader_unittest.cc
@@ -178,17 +178,17 @@ TEST_F(PaymentMethodManifestDownloaderTest, 300IsUnsupportedRedirect) { EXPECT_CALL(*this, OnManifestDownload(std::string())); - CallRedirect(300, GURL("https://alicepay.com")); + CallRedirect(300, GURL("https://pay.bobpay.com")); } TEST_F(PaymentMethodManifestDownloaderTest, 301And302AreSupportedRedirects) { - CallRedirect(301, GURL("https://alicepay.com")); + CallRedirect(301, GURL("https://pay.bobpay.com")); - EXPECT_EQ(GetOriginalURL(), GURL("https://alicepay.com")); + EXPECT_EQ(GetOriginalURL(), GURL("https://pay.bobpay.com")); - CallRedirect(302, GURL("https://charliepay.com")); + CallRedirect(302, GURL("https://newpay.bobpay.com")); - EXPECT_EQ(GetOriginalURL(), GURL("https://charliepay.com")); + EXPECT_EQ(GetOriginalURL(), GURL("https://newpay.bobpay.com")); CallComplete(200, "Link: <manifest.json>; rel=payment-method-manifest"); @@ -198,13 +198,13 @@ } TEST_F(PaymentMethodManifestDownloaderTest, 302And303AreSupportedRedirects) { - CallRedirect(302, GURL("https://alicepay.com")); + CallRedirect(302, GURL("https://pay.bobpay.com")); - EXPECT_EQ(GetOriginalURL(), GURL("https://alicepay.com")); + EXPECT_EQ(GetOriginalURL(), GURL("https://pay.bobpay.com")); - CallRedirect(303, GURL("https://charliepay.com")); + CallRedirect(303, GURL("https://newpay.bobpay.com")); - EXPECT_EQ(GetOriginalURL(), GURL("https://charliepay.com")); + EXPECT_EQ(GetOriginalURL(), GURL("https://newpay.bobpay.com")); CallComplete(200, "Link: <manifest.json>; rel=payment-method-manifest"); @@ -216,23 +216,23 @@ TEST_F(PaymentMethodManifestDownloaderTest, 304IsUnsupportedRedirect) { EXPECT_CALL(*this, OnManifestDownload(std::string())); - CallRedirect(304, GURL("https://alicepay.com")); + CallRedirect(304, GURL("https://pay.bobpay.com")); } TEST_F(PaymentMethodManifestDownloaderTest, 305IsUnsupportedRedirect) { EXPECT_CALL(*this, OnManifestDownload(std::string())); - CallRedirect(305, GURL("https://alicepay.com")); + CallRedirect(305, GURL("https://pay.bobpay.com")); } TEST_F(PaymentMethodManifestDownloaderTest, 307And308AreSupportedRedirects) { - CallRedirect(307, GURL("https://alicepay.com")); + CallRedirect(307, GURL("https://pay.bobpay.com")); - EXPECT_EQ(GetOriginalURL(), GURL("https://alicepay.com")); + EXPECT_EQ(GetOriginalURL(), GURL("https://pay.bobpay.com")); - CallRedirect(308, GURL("https://charliepay.com")); + CallRedirect(308, GURL("https://newpay.bobpay.com")); - EXPECT_EQ(GetOriginalURL(), GURL("https://charliepay.com")); + EXPECT_EQ(GetOriginalURL(), GURL("https://newpay.bobpay.com")); CallComplete(200, "Link: <manifest.json>; rel=payment-method-manifest"); @@ -242,27 +242,33 @@ } TEST_F(PaymentMethodManifestDownloaderTest, NoMoreThanThreeRedirects) { - CallRedirect(301, GURL("https://alicepay.com")); + CallRedirect(301, GURL("https://pay.bobpay.com")); - EXPECT_EQ(GetOriginalURL(), GURL("https://alicepay.com")); + EXPECT_EQ(GetOriginalURL(), GURL("https://pay.bobpay.com")); - CallRedirect(302, GURL("https://charliepay.com")); + CallRedirect(302, GURL("https://oldpay.bobpay.com")); - EXPECT_EQ(GetOriginalURL(), GURL("https://charliepay.com")); + EXPECT_EQ(GetOriginalURL(), GURL("https://oldpay.bobpay.com")); - CallRedirect(308, GURL("https://davepay.com")); + CallRedirect(308, GURL("https://newpay.bobpay.com")); - EXPECT_EQ(GetOriginalURL(), GURL("https://davepay.com")); + EXPECT_EQ(GetOriginalURL(), GURL("https://newpay.bobpay.com")); EXPECT_CALL(*this, OnManifestDownload(std::string())); - CallRedirect(308, GURL("https://davepay.com")); + CallRedirect(308, GURL("https://newpay.bobpay.com")); } TEST_F(PaymentMethodManifestDownloaderTest, InvalidRedirectUrlIsFailure) { EXPECT_CALL(*this, OnManifestDownload(std::string())); - CallRedirect(308, GURL("alicepay.com")); + CallRedirect(308, GURL("pay.bobpay.com")); +} + +TEST_F(PaymentMethodManifestDownloaderTest, NotAllowCrossSiteRedirects) { + EXPECT_CALL(*this, OnManifestDownload(std::string())); + + CallRedirect(301, GURL("https://alicepay.com")); } class WebAppManifestDownloaderTest : public testing::Test {
diff --git a/components/policy/resources/policy_templates.json b/components/policy/resources/policy_templates.json index 70b9ba5..c147691 100644 --- a/components/policy/resources/policy_templates.json +++ b/components/policy/resources/policy_templates.json
@@ -12516,7 +12516,30 @@ If the policy is set to false then tab lifecycles are disabled, and all tabs will be left running normally. If the policy is set to true or left unspecified then tab lifecycles are enabled.''', - } + }, + { + 'name': 'UrlKeyedAnonymizedDataCollectionEnabled', + 'type': 'main', + 'schema': { 'type': 'boolean' }, + 'supported_on': ['chrome.*:69-', 'chrome_os:69-', 'android:70-'], + 'features': { + 'dynamic_refresh': True, + 'per_profile': True, + }, + 'example_value': True, + 'id': 461, + 'caption': '''Enable URL-keyed anonymized data collection''', + 'tags': ['google-sharing'], + 'desc': '''Enable URL-keyed anonymized data collection in <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> and prevents users from changing this setting. + + URL-keyed anonymized data collection sends URLs of pages the user visits to Google to make searches and browsing better. + + If you enable this policy, URL-keyed anonymized data collection is always active. + + If you disable this policy, URL-keyed anonymized data collection is never active. + + If this policy is left not set, URL-keyed anonymized data collection will be enabled but the user will be able to change it.''', + }, ], 'messages': { @@ -12658,4 +12681,5 @@ }, 'placeholders': [], 'deleted_policy_ids': [412], - 'highest_id_currently_used': 460} + 'highest_id_currently_used': 461 +}
diff --git a/components/sync/driver/about_sync_util.cc b/components/sync/driver/about_sync_util.cc index c7f78fc..ad69928 100644 --- a/components/sync/driver/about_sync_util.cc +++ b/components/sync/driver/about_sync_util.cc
@@ -201,10 +201,8 @@ return "Start deferred"; case syncer::SyncService::State::INITIALIZING: return "Initializing"; - case syncer::SyncService::State::AUTH_ERROR: - return "Auth error"; - case syncer::SyncService::State::WAITING_FOR_CONSENT: - return "Waiting for initial setup"; + case syncer::SyncService::State::PENDING_DESIRED_CONFIGURATION: + return "Pending desired configuration"; case syncer::SyncService::State::CONFIGURING: return "Configuring data types"; case syncer::SyncService::State::ACTIVE:
diff --git a/components/sync/driver/fake_sync_service.cc b/components/sync/driver/fake_sync_service.cc index 36adf859..0710e54 100644 --- a/components/sync/driver/fake_sync_service.cc +++ b/components/sync/driver/fake_sync_service.cc
@@ -49,17 +49,13 @@ } // From this point on, Sync can start in principle. DCHECK(CanSyncStart()); - // The presence of an auth error overrides any non-disabled state. - if (GetAuthError() != GoogleServiceAuthError::AuthErrorNone()) { - return State::AUTH_ERROR; - } // Note: We don't distinguish here if the engine doesn't exist at all, or // exists but hasn't finished initializing. if (!IsEngineInitialized()) { return State::INITIALIZING; } if (!IsFirstSetupComplete()) { - return State::WAITING_FOR_CONSENT; + return State::PENDING_DESIRED_CONFIGURATION; } DCHECK(IsSyncActive()); if (!configuration_done_) {
diff --git a/components/sync/driver/sync_driver_switches.cc b/components/sync/driver/sync_driver_switches.cc index 8ebc3e3..e409510 100644 --- a/components/sync/driver/sync_driver_switches.cc +++ b/components/sync/driver/sync_driver_switches.cc
@@ -52,7 +52,7 @@ // Emit user consents through a separate sync type USER_CONSENTS instead of // USER_EVENTS. This feature does not override kSyncUserConsentEvents. const base::Feature kSyncUserConsentSeparateType{ - "SyncUserConsentSeparateType", base::FEATURE_DISABLED_BY_DEFAULT}; + "SyncUserConsentSeparateType", base::FEATURE_ENABLED_BY_DEFAULT}; // Gates registration for user language detection events. const base::Feature kSyncUserLanguageDetectionEvents{
diff --git a/components/sync/driver/sync_service.h b/components/sync/driver/sync_service.h index 5ec33f8..9ebd74c 100644 --- a/components/sync/driver/sync_service.h +++ b/components/sync/driver/sync_service.h
@@ -107,11 +107,6 @@ // disabled it, or simply because there is no authenticated user. Call // GetDisableReasons to figure out which of these it is. DISABLED, - // Sync has encountered an authentication error. Note that Sync may have - // been in any of the below states before, and might go straight back to it - // if the auth error gets resolved. Call GetAuthError for more details on - // the error. - AUTH_ERROR, // Sync can start in principle, but nothing has prodded it to actually do it // yet. Note that during subsequent browser startups, Sync starts // automatically, i.e. no prod is necessary, but during the first start Sync @@ -127,9 +122,12 @@ START_DEFERRED, // The Sync engine is in the process of initializing. INITIALIZING, - // The Sync engine is initialized, but the user hasn't completed the initial - // Sync setup yet, so we won't actually configure the data types. - WAITING_FOR_CONSENT, + // The Sync engine is initialized, but the process of configuring the data + // types hasn't been started yet. This usually occurs if the user hasn't + // completed the initial Sync setup yet (i.e. IsFirstSetupComplete() is + // false), but it can also occur if a (non-initial) Sync setup happens to be + // ongoing while the Sync service is starting up. + PENDING_DESIRED_CONFIGURATION, // The Sync engine itself is up and running, but the individual data types // are being (re)configured. GetActiveDataTypes() will still be empty. CONFIGURING,
diff --git a/components/sync/driver/sync_service_utils.cc b/components/sync/driver/sync_service_utils.cc index 94a91cf8..4c088772 100644 --- a/components/sync/driver/sync_service_utils.cc +++ b/components/sync/driver/sync_service_utils.cc
@@ -32,22 +32,20 @@ return UploadState::NOT_ACTIVE; } + // Persistent auth errors always map to NOT_ACTIVE. For transient errors, we + // give the benefit of the doubt and may still say we're INITIALIZING. + if (sync_service->GetAuthError().IsPersistentError()) { + return UploadState::NOT_ACTIVE; + } + switch (sync_service->GetState()) { case SyncService::State::DISABLED: return UploadState::NOT_ACTIVE; - case SyncService::State::AUTH_ERROR: - // For transient errors, give the benefit of the doubt and say we're - // INITIALIZING. - if (sync_service->GetAuthError().IsTransientError()) { - return UploadState::INITIALIZING; - } - return UploadState::NOT_ACTIVE; - case SyncService::State::WAITING_FOR_START_REQUEST: case SyncService::State::START_DEFERRED: case SyncService::State::INITIALIZING: - case SyncService::State::WAITING_FOR_CONSENT: + case SyncService::State::PENDING_DESIRED_CONFIGURATION: case SyncService::State::CONFIGURING: return UploadState::INITIALIZING; @@ -57,6 +55,9 @@ if (!sync_service->GetActiveDataTypes().Has(type)) { return UploadState::NOT_ACTIVE; } + if (sync_service->GetAuthError().IsTransientError()) { + return UploadState::INITIALIZING; + } // TODO(crbug.com/831579): We currently need to wait for // GetLastCycleSnapshot to return an initialized snapshot because we don't // actually know if the token is valid until sync has tried it. This is
diff --git a/components/sync/engine_impl/syncer_proto_util.cc b/components/sync/engine_impl/syncer_proto_util.cc index f564d37e..29fa3037 100644 --- a/components/sync/engine_impl/syncer_proto_util.cc +++ b/components/sync/engine_impl/syncer_proto_util.cc
@@ -364,7 +364,6 @@ ModelTypeSet* partial_failure_data_types) { DCHECK(response); DCHECK(!msg->get_updates().has_from_timestamp()); // Deprecated. - DCHECK(!msg->get_updates().has_requested_types()); // Deprecated. // Add must-have fields. SetProtocolVersion(msg);
diff --git a/components/sync/protocol/proto_value_conversions_unittest.cc b/components/sync/protocol/proto_value_conversions_unittest.cc index 1cbb49c2..40f990c 100644 --- a/components/sync/protocol/proto_value_conversions_unittest.cc +++ b/components/sync/protocol/proto_value_conversions_unittest.cc
@@ -43,59 +43,76 @@ namespace { // Keep this file in sync with the .proto files in this directory. -class ProtoValueConversionsTest : public testing::Test { - protected: - template <class T> - void TestSpecificsToValue( - std::unique_ptr<base::DictionaryValue> (*specifics_to_value)(const T&)) { - const T& specifics(T::default_instance()); - std::unique_ptr<base::DictionaryValue> value = - specifics_to_value(specifics); - // We can't do much but make sure that this doesn't crash. - } -}; -static_assert(42 == syncer::MODEL_TYPE_COUNT, - "When adding a new type, add a unit test for " - "{NewType}SpecificsToValue below."); +#define DEFINE_SPECIFICS_TO_VALUE_TEST(Key) \ + TEST(ProtoValueConversionsTest, Proto_##Key##_SpecificsToValue) { \ + sync_pb::EntitySpecifics specifics; \ + specifics.mutable_##Key(); \ + std::unique_ptr<base::DictionaryValue> value( \ + EntitySpecificsToValue(specifics)); \ + EXPECT_EQ(1, static_cast<int>(value->size())); \ + } // We'd also like to check if we changed any field in our messages. However, // that's hard to do: sizeof could work, but it's platform-dependent. // default_instance().ByteSize() won't change for most changes, since most of // our fields are optional. So we just settle for comments in the proto files. -TEST_F(ProtoValueConversionsTest, EncryptedDataToValue) { - TestSpecificsToValue(EncryptedDataToValue); -} +DEFINE_SPECIFICS_TO_VALUE_TEST(encrypted); -TEST_F(ProtoValueConversionsTest, SessionHeaderToValue) { - TestSpecificsToValue(SessionHeaderToValue); -} +static_assert(42 == syncer::MODEL_TYPE_COUNT, + "When adding a new field, add a DEFINE_SPECIFICS_TO_VALUE_TEST " + "for your field below, and optionally a test for the specific " + "conversions."); -TEST_F(ProtoValueConversionsTest, SessionTabToValue) { - TestSpecificsToValue(SessionTabToValue); -} +DEFINE_SPECIFICS_TO_VALUE_TEST(app); +DEFINE_SPECIFICS_TO_VALUE_TEST(app_list); +DEFINE_SPECIFICS_TO_VALUE_TEST(app_notification); +DEFINE_SPECIFICS_TO_VALUE_TEST(app_setting); +DEFINE_SPECIFICS_TO_VALUE_TEST(arc_package); +DEFINE_SPECIFICS_TO_VALUE_TEST(article); +DEFINE_SPECIFICS_TO_VALUE_TEST(autofill); +DEFINE_SPECIFICS_TO_VALUE_TEST(autofill_profile); +DEFINE_SPECIFICS_TO_VALUE_TEST(autofill_wallet); +DEFINE_SPECIFICS_TO_VALUE_TEST(bookmark); +DEFINE_SPECIFICS_TO_VALUE_TEST(device_info); +DEFINE_SPECIFICS_TO_VALUE_TEST(dictionary); +DEFINE_SPECIFICS_TO_VALUE_TEST(experiments); +DEFINE_SPECIFICS_TO_VALUE_TEST(extension); +DEFINE_SPECIFICS_TO_VALUE_TEST(extension_setting); +DEFINE_SPECIFICS_TO_VALUE_TEST(favicon_image); +DEFINE_SPECIFICS_TO_VALUE_TEST(favicon_tracking); +DEFINE_SPECIFICS_TO_VALUE_TEST(history_delete_directive); +DEFINE_SPECIFICS_TO_VALUE_TEST(managed_user); +DEFINE_SPECIFICS_TO_VALUE_TEST(managed_user_setting); +DEFINE_SPECIFICS_TO_VALUE_TEST(managed_user_shared_setting); +DEFINE_SPECIFICS_TO_VALUE_TEST(managed_user_whitelist); +DEFINE_SPECIFICS_TO_VALUE_TEST(mountain_share); +DEFINE_SPECIFICS_TO_VALUE_TEST(nigori); +DEFINE_SPECIFICS_TO_VALUE_TEST(password); +DEFINE_SPECIFICS_TO_VALUE_TEST(preference); +DEFINE_SPECIFICS_TO_VALUE_TEST(printer); +DEFINE_SPECIFICS_TO_VALUE_TEST(priority_preference); +DEFINE_SPECIFICS_TO_VALUE_TEST(reading_list); +DEFINE_SPECIFICS_TO_VALUE_TEST(search_engine); +DEFINE_SPECIFICS_TO_VALUE_TEST(session); +DEFINE_SPECIFICS_TO_VALUE_TEST(synced_notification); +DEFINE_SPECIFICS_TO_VALUE_TEST(synced_notification_app_info); +DEFINE_SPECIFICS_TO_VALUE_TEST(theme); +DEFINE_SPECIFICS_TO_VALUE_TEST(typed_url); +DEFINE_SPECIFICS_TO_VALUE_TEST(user_consent); +DEFINE_SPECIFICS_TO_VALUE_TEST(user_event); +DEFINE_SPECIFICS_TO_VALUE_TEST(wallet_metadata); +DEFINE_SPECIFICS_TO_VALUE_TEST(wifi_credential); -TEST_F(ProtoValueConversionsTest, SessionWindowToValue) { - TestSpecificsToValue(SessionWindowToValue); -} - -TEST_F(ProtoValueConversionsTest, TabNavigationToValue) { - TestSpecificsToValue(TabNavigationToValue); -} - -TEST_F(ProtoValueConversionsTest, NavigationRedirectToValue) { - TestSpecificsToValue(NavigationRedirectToValue); -} - -TEST_F(ProtoValueConversionsTest, PasswordSpecifics) { +TEST(ProtoValueConversionsTest, PasswordSpecifics) { sync_pb::PasswordSpecifics specifics; specifics.mutable_client_only_encrypted_data(); auto value = PasswordSpecificsToValue(specifics); EXPECT_FALSE(value->Get("client_only_encrypted_data", nullptr)); } -TEST_F(ProtoValueConversionsTest, PasswordSpecificsData) { +TEST(ProtoValueConversionsTest, PasswordSpecificsData) { sync_pb::PasswordSpecificsData specifics; specifics.set_password_value("secret"); std::unique_ptr<base::DictionaryValue> value( @@ -106,15 +123,7 @@ EXPECT_EQ("<redacted>", password_value); } -TEST_F(ProtoValueConversionsTest, AppListSpecificsToValue) { - TestSpecificsToValue(AppListSpecificsToValue); -} - -TEST_F(ProtoValueConversionsTest, AppNotificationToValue) { - TestSpecificsToValue(AppNotificationToValue); -} - -TEST_F(ProtoValueConversionsTest, AppSettingSpecificsToValue) { +TEST(ProtoValueConversionsTest, AppSettingSpecificsToValue) { sync_pb::AppNotificationSettings specifics; specifics.set_disabled(true); specifics.set_oauth_client_id("some_id_value"); @@ -129,27 +138,7 @@ EXPECT_EQ("some_id_value", oauth_client_id_value); } -TEST_F(ProtoValueConversionsTest, AppSpecificsToValue) { - TestSpecificsToValue(AppSpecificsToValue); -} - -TEST_F(ProtoValueConversionsTest, ArcPackageSpecificsToValue) { - TestSpecificsToValue(ArcPackageSpecificsToValue); -} - -TEST_F(ProtoValueConversionsTest, ArticleSpecificsToValue) { - TestSpecificsToValue(ArticleSpecificsToValue); -} - -TEST_F(ProtoValueConversionsTest, AutofillSpecificsToValue) { - TestSpecificsToValue(AutofillSpecificsToValue); -} - -TEST_F(ProtoValueConversionsTest, AutofillProfileSpecificsToValue) { - TestSpecificsToValue(AutofillProfileSpecificsToValue); -} - -TEST_F(ProtoValueConversionsTest, AutofillWalletSpecificsToValue) { +TEST(ProtoValueConversionsTest, AutofillWalletSpecificsToValue) { sync_pb::AutofillWalletSpecifics specifics; specifics.mutable_masked_card()->set_name_on_card("Igloo"); specifics.mutable_address()->set_recipient_name("John"); @@ -170,15 +159,7 @@ EXPECT_TRUE(value->Get("address", nullptr)); } -TEST_F(ProtoValueConversionsTest, WalletMetadataSpecificsToValue) { - TestSpecificsToValue(WalletMetadataSpecificsToValue); -} - -TEST_F(ProtoValueConversionsTest, BookmarkSpecificsToValue) { - TestSpecificsToValue(BookmarkSpecificsToValue); -} - -TEST_F(ProtoValueConversionsTest, BookmarkSpecificsData) { +TEST(ProtoValueConversionsTest, BookmarkSpecificsData) { const base::Time creation_time(base::Time::Now()); const std::string icon_url = "http://www.google.com/favicon.ico"; sync_pb::BookmarkSpecifics specifics; @@ -218,23 +199,7 @@ EXPECT_EQ("value2", meta_value); } -TEST_F(ProtoValueConversionsTest, LinkedAppIconInfoToValue) { - TestSpecificsToValue(LinkedAppIconInfoToValue); -} - -TEST_F(ProtoValueConversionsTest, PriorityPreferenceSpecificsToValue) { - TestSpecificsToValue(PriorityPreferenceSpecificsToValue); -} - -TEST_F(ProtoValueConversionsTest, DeviceInfoSpecificsToValue) { - TestSpecificsToValue(DeviceInfoSpecificsToValue); -} - -TEST_F(ProtoValueConversionsTest, DictionarySpecificsToValue) { - TestSpecificsToValue(DictionarySpecificsToValue); -} - -TEST_F(ProtoValueConversionsTest, ExperimentsSpecificsToValue) { +TEST(ProtoValueConversionsTest, ExperimentsSpecificsToValue) { #define TEST_EXPERIMENT_ENABLED_FIELD(field) \ { \ sync_pb::ExperimentsSpecifics specifics; \ @@ -252,173 +217,17 @@ EXPECT_FALSE(field_enabled); \ } - TEST_EXPERIMENT_ENABLED_FIELD(keystore_encryption) - TEST_EXPERIMENT_ENABLED_FIELD(history_delete_directives) - TEST_EXPERIMENT_ENABLED_FIELD(autofill_culling) - TEST_EXPERIMENT_ENABLED_FIELD(pre_commit_update_avoidance) - TEST_EXPERIMENT_ENABLED_FIELD(gcm_channel) - TEST_EXPERIMENT_ENABLED_FIELD(gcm_invalidations) + TEST_EXPERIMENT_ENABLED_FIELD(keystore_encryption); + TEST_EXPERIMENT_ENABLED_FIELD(history_delete_directives); + TEST_EXPERIMENT_ENABLED_FIELD(autofill_culling); + TEST_EXPERIMENT_ENABLED_FIELD(pre_commit_update_avoidance); + TEST_EXPERIMENT_ENABLED_FIELD(gcm_channel); + TEST_EXPERIMENT_ENABLED_FIELD(gcm_invalidations); #undef TEST_EXPERIMENT_ENABLED_FIELD } -TEST_F(ProtoValueConversionsTest, ExtensionSettingSpecificsToValue) { - TestSpecificsToValue(ExtensionSettingSpecificsToValue); -} - -TEST_F(ProtoValueConversionsTest, ExtensionSpecificsToValue) { - TestSpecificsToValue(ExtensionSpecificsToValue); -} - -TEST_F(ProtoValueConversionsTest, FaviconImageSpecificsToValue) { - TestSpecificsToValue(FaviconImageSpecificsToValue); -} - -TEST_F(ProtoValueConversionsTest, FaviconTrackingSpecificsToValue) { - TestSpecificsToValue(FaviconTrackingSpecificsToValue); -} - -TEST_F(ProtoValueConversionsTest, HistoryDeleteDirectiveSpecificsToValue) { - TestSpecificsToValue(HistoryDeleteDirectiveSpecificsToValue); -} - -TEST_F(ProtoValueConversionsTest, ManagedUserSettingSpecificsToValue) { - TestSpecificsToValue(ManagedUserSettingSpecificsToValue); -} - -TEST_F(ProtoValueConversionsTest, ManagedUserSpecificsToValue) { - TestSpecificsToValue(ManagedUserSpecificsToValue); -} - -TEST_F(ProtoValueConversionsTest, ManagedUserSharedSettingSpecificsToValue) { - TestSpecificsToValue(ManagedUserSharedSettingSpecificsToValue); -} - -TEST_F(ProtoValueConversionsTest, ManagedUserWhitelistSpecificsToValue) { - TestSpecificsToValue(ManagedUserWhitelistSpecificsToValue); -} - -TEST_F(ProtoValueConversionsTest, MountainShareSpecificsToValue) { - TestSpecificsToValue(MountainShareSpecificsToValue); -} - -TEST_F(ProtoValueConversionsTest, NigoriSpecificsToValue) { - TestSpecificsToValue(NigoriSpecificsToValue); -} - -TEST_F(ProtoValueConversionsTest, PasswordSpecificsToValue) { - TestSpecificsToValue(PasswordSpecificsToValue); -} - -TEST_F(ProtoValueConversionsTest, PreferenceSpecificsToValue) { - TestSpecificsToValue(PreferenceSpecificsToValue); -} - -TEST_F(ProtoValueConversionsTest, PrinterSpecificsToValue) { - TestSpecificsToValue(PrinterSpecificsToValue); -} - -TEST_F(ProtoValueConversionsTest, ReadingListSpecificsToValue) { - TestSpecificsToValue(ReadingListSpecificsToValue); -} - -TEST_F(ProtoValueConversionsTest, SearchEngineSpecificsToValue) { - TestSpecificsToValue(SearchEngineSpecificsToValue); -} - -TEST_F(ProtoValueConversionsTest, SessionSpecificsToValue) { - TestSpecificsToValue(SessionSpecificsToValue); -} - -TEST_F(ProtoValueConversionsTest, SyncedNotificationAppInfoSpecificsToValue) { - TestSpecificsToValue(SyncedNotificationAppInfoSpecificsToValue); -} - -TEST_F(ProtoValueConversionsTest, SyncedNotificationSpecificsToValue) { - TestSpecificsToValue(SyncedNotificationSpecificsToValue); -} - -TEST_F(ProtoValueConversionsTest, ThemeSpecificsToValue) { - TestSpecificsToValue(ThemeSpecificsToValue); -} - -TEST_F(ProtoValueConversionsTest, TypedUrlSpecificsToValue) { - TestSpecificsToValue(TypedUrlSpecificsToValue); -} - -TEST_F(ProtoValueConversionsTest, UserConsentSpecificsToValue) { - TestSpecificsToValue(UserConsentSpecificsToValue); -} - -TEST_F(ProtoValueConversionsTest, UserEventSpecificsToValue) { - TestSpecificsToValue(UserEventSpecificsToValue); -} - -TEST_F(ProtoValueConversionsTest, WifiCredentialSpecificsToValue) { - TestSpecificsToValue(WifiCredentialSpecificsToValue); -} - -// TODO(akalin): Figure out how to better test EntitySpecificsToValue. - -TEST_F(ProtoValueConversionsTest, EntitySpecificsToValue) { - static_assert(42 == syncer::MODEL_TYPE_COUNT, - "When adding a new type, add its field below."); - - sync_pb::EntitySpecifics specifics; -// Touch the extensions to make sure it shows up in the generated -// value. -#define SET_FIELD(key) (void)specifics.mutable_##key() - - SET_FIELD(app); - SET_FIELD(app_list); - SET_FIELD(app_notification); - SET_FIELD(app_setting); - SET_FIELD(arc_package); - SET_FIELD(article); - SET_FIELD(autofill); - SET_FIELD(autofill_profile); - SET_FIELD(autofill_wallet); - SET_FIELD(bookmark); - SET_FIELD(device_info); - SET_FIELD(dictionary); - SET_FIELD(experiments); - SET_FIELD(extension); - SET_FIELD(extension_setting); - SET_FIELD(favicon_image); - SET_FIELD(favicon_tracking); - SET_FIELD(history_delete_directive); - SET_FIELD(managed_user); - SET_FIELD(managed_user_setting); - SET_FIELD(managed_user_shared_setting); - SET_FIELD(managed_user_whitelist); - SET_FIELD(mountain_share); - SET_FIELD(nigori); - SET_FIELD(password); - SET_FIELD(preference); - SET_FIELD(printer); - SET_FIELD(priority_preference); - SET_FIELD(reading_list); - SET_FIELD(search_engine); - SET_FIELD(session); - SET_FIELD(synced_notification); - SET_FIELD(synced_notification_app_info); - SET_FIELD(theme); - SET_FIELD(typed_url); - SET_FIELD(user_consent); - SET_FIELD(user_event); - SET_FIELD(wallet_metadata); - SET_FIELD(wifi_credential); - -#undef SET_FIELD - - std::unique_ptr<base::DictionaryValue> value( - EntitySpecificsToValue(specifics)); - EXPECT_EQ(MODEL_TYPE_COUNT - FIRST_REAL_MODEL_TYPE - - (LAST_PROXY_TYPE - FIRST_PROXY_TYPE + 1), - static_cast<int>(value->size())); -} - -TEST_F(ProtoValueConversionsTest, UniquePositionToValue) { +TEST(ProtoValueConversionsTest, UniquePositionToValue) { sync_pb::SyncEntity entity; entity.mutable_unique_position()->set_custom_compressed_v1("test"); @@ -431,7 +240,7 @@ EXPECT_EQ(expected_unique_position, unique_position); } -TEST_F(ProtoValueConversionsTest, SyncEntityToValueIncludeSpecifics) { +TEST(ProtoValueConversionsTest, SyncEntityToValueIncludeSpecifics) { sync_pb::SyncEntity entity; entity.mutable_specifics(); @@ -463,7 +272,7 @@ // Create a ClientToServerMessage with an EntitySpecifics. Converting it to // a value should respect the |include_specifics| flag. -TEST_F(ProtoValueConversionsTest, ClientToServerMessageToValue) { +TEST(ProtoValueConversionsTest, ClientToServerMessageToValue) { sync_pb::ClientToServerMessage message; sync_pb::CommitMessage* commit_message = message.mutable_commit(); sync_pb::SyncEntity* entity = commit_message->add_entries(); @@ -484,7 +293,7 @@ // Create a ClientToServerResponse with an EntitySpecifics. Converting it to // a value should respect the |include_specifics| flag. -TEST_F(ProtoValueConversionsTest, ClientToServerResponseToValue) { +TEST(ProtoValueConversionsTest, ClientToServerResponseToValue) { sync_pb::ClientToServerResponse message; sync_pb::GetUpdatesResponse* response = message.mutable_get_updates(); sync_pb::SyncEntity* entity = response->add_entries();
diff --git a/components/sync/protocol/sync.proto b/components/sync/protocol/sync.proto index 5aec1ab..fa6c4d1 100644 --- a/components/sync/protocol/sync.proto +++ b/components/sync/protocol/sync.proto
@@ -104,47 +104,47 @@ // noise in logs and debugging contexts, and creating a divergent subset of // tags would only make things a bit more confusing. - optional AutofillSpecifics autofill = 31729; - optional BookmarkSpecifics bookmark = 32904; - optional PreferenceSpecifics preference = 37702; - optional TypedUrlSpecifics typed_url = 40781; - optional ThemeSpecifics theme = 41210; - optional AppNotification app_notification = 45184; - optional PasswordSpecifics password = 45873; - optional NigoriSpecifics nigori = 47745; - optional ExtensionSpecifics extension = 48119; - optional AppSpecifics app = 48364; - optional SessionSpecifics session = 50119; - optional AutofillProfileSpecifics autofill_profile = 63951; - optional SearchEngineSpecifics search_engine = 88610; - optional ExtensionSettingSpecifics extension_setting = 96159; - optional AppSettingSpecifics app_setting = 103656; - optional HistoryDeleteDirectiveSpecifics history_delete_directive = 150251; - optional SyncedNotificationSpecifics synced_notification = 153108; - optional SyncedNotificationAppInfoSpecifics synced_notification_app_info = - 235816; - optional DeviceInfoSpecifics device_info = 154522; - optional ExperimentsSpecifics experiments = 161496; - optional PriorityPreferenceSpecifics priority_preference = 163425; - optional DictionarySpecifics dictionary = 170540; - optional FaviconTrackingSpecifics favicon_tracking = 181534; - optional FaviconImageSpecifics favicon_image = 182019; - optional ManagedUserSettingSpecifics managed_user_setting = 186662; - optional ManagedUserSpecifics managed_user = 194582; - optional ManagedUserSharedSettingSpecifics managed_user_shared_setting = - 202026; - optional ManagedUserWhitelistSpecifics managed_user_whitelist = 306060; - optional ArticleSpecifics article = 223759; - optional AppListSpecifics app_list = 229170; - optional WifiCredentialSpecifics wifi_credential = 218175; - optional AutofillWalletSpecifics autofill_wallet = 306270; - optional WalletMetadataSpecifics wallet_metadata = 330441; - optional ArcPackageSpecifics arc_package = 340906; - optional PrinterSpecifics printer = 410745; - optional ReadingListSpecifics reading_list = 411028; - optional UserEventSpecifics user_event = 455206; - optional UserConsentSpecifics user_consent = 556014; - optional MountainShareSpecifics mountain_share = 545005; + oneof specifics_variant { + AutofillSpecifics autofill = 31729; + BookmarkSpecifics bookmark = 32904; + PreferenceSpecifics preference = 37702; + TypedUrlSpecifics typed_url = 40781; + ThemeSpecifics theme = 41210; + AppNotification app_notification = 45184; + PasswordSpecifics password = 45873; + NigoriSpecifics nigori = 47745; + ExtensionSpecifics extension = 48119; + AppSpecifics app = 48364; + SessionSpecifics session = 50119; + AutofillProfileSpecifics autofill_profile = 63951; + SearchEngineSpecifics search_engine = 88610; + ExtensionSettingSpecifics extension_setting = 96159; + AppSettingSpecifics app_setting = 103656; + HistoryDeleteDirectiveSpecifics history_delete_directive = 150251; + SyncedNotificationSpecifics synced_notification = 153108; + SyncedNotificationAppInfoSpecifics synced_notification_app_info = 235816; + DeviceInfoSpecifics device_info = 154522; + ExperimentsSpecifics experiments = 161496; + PriorityPreferenceSpecifics priority_preference = 163425; + DictionarySpecifics dictionary = 170540; + FaviconTrackingSpecifics favicon_tracking = 181534; + FaviconImageSpecifics favicon_image = 182019; + ManagedUserSettingSpecifics managed_user_setting = 186662; + ManagedUserSpecifics managed_user = 194582; + ManagedUserSharedSettingSpecifics managed_user_shared_setting = 202026; + ManagedUserWhitelistSpecifics managed_user_whitelist = 306060; + ArticleSpecifics article = 223759; + AppListSpecifics app_list = 229170; + WifiCredentialSpecifics wifi_credential = 218175; + AutofillWalletSpecifics autofill_wallet = 306270; + WalletMetadataSpecifics wallet_metadata = 330441; + ArcPackageSpecifics arc_package = 340906; + PrinterSpecifics printer = 410745; + ReadingListSpecifics reading_list = 411028; + UserEventSpecifics user_event = 455206; + UserConsentSpecifics user_consent = 556014; + MountainShareSpecifics mountain_share = 545005; + } } message SyncEntity { @@ -632,20 +632,6 @@ // Indicates whether related folders should be fetched. optional bool fetch_folders = 3 [default = true]; - // The presence of an individual EntitySpecifics field indicates that the - // client requests sync object types associated with that field. This - // determination depends only on the presence of the field, not its - // contents -- thus clients should send empty messages as the field value. - // For backwards compatibility only bookmark objects will be sent to the - // client should requested_types not be present. - // - // requested_types may contain multiple EntitySpecifics fields -- in this - // event, the server will return items of all the indicated types. - // - // requested_types has been deprecated; clients should use - // |from_progress_marker| instead, which allows more flexibility. - optional EntitySpecifics requested_types = 4; - // Client-requested limit on the maximum number of updates to return at once. // The server may opt to return fewer updates than this amount, but it should // not return more. @@ -690,6 +676,11 @@ // Set of optional per-client datatype contexts. repeated DataTypeContext client_contexts = 11; + + // requested_types is a deprecated predecessor to using progress markers. + // See https://crbug.com/687426. + reserved 4; + reserved "requested_types"; }; message AuthenticateMessage {
diff --git a/components/sync/test/engine/mock_connection_manager.cc b/components/sync/test/engine/mock_connection_manager.cc index 8c5ae4b..bacd915c 100644 --- a/components/sync/test/engine/mock_connection_manager.cc +++ b/components/sync/test/engine/mock_connection_manager.cc
@@ -534,7 +534,6 @@ const GetUpdatesMessage& gu = csm->get_updates(); num_get_updates_requests_++; EXPECT_FALSE(gu.has_from_timestamp()); - EXPECT_FALSE(gu.has_requested_types()); if (fail_non_periodic_get_updates_) { EXPECT_EQ(sync_pb::SyncEnums::PERIODIC, gu.get_updates_origin()); @@ -544,7 +543,6 @@ // the types requested by the client. If this fails, it probably indicates // a test bug. EXPECT_TRUE(gu.fetch_folders()); - EXPECT_FALSE(gu.has_requested_types()); if (update_queue_.empty()) { GetUpdateResponse(); }
diff --git a/components/sync_bookmarks/bookmark_model_observer_impl.cc b/components/sync_bookmarks/bookmark_model_observer_impl.cc index 6bb932aa..91d40c7b 100644 --- a/components/sync_bookmarks/bookmark_model_observer_impl.cc +++ b/components/sync_bookmarks/bookmark_model_observer_impl.cc
@@ -137,13 +137,25 @@ nudge_for_commit_closure_.Run(); } +void BookmarkModelObserverImpl::OnWillRemoveBookmarks( + bookmarks::BookmarkModel* model, + const bookmarks::BookmarkNode* parent, + int old_index, + const bookmarks::BookmarkNode* node) { + // TODO(crbug.com/516866): continue only if + // model->client()->CanSyncNode(node). + ProcessDelete(parent, node); + nudge_for_commit_closure_.Run(); +} + void BookmarkModelObserverImpl::BookmarkNodeRemoved( bookmarks::BookmarkModel* model, const bookmarks::BookmarkNode* parent, int old_index, const bookmarks::BookmarkNode* node, const std::set<GURL>& removed_urls) { - NOTIMPLEMENTED(); + // All the work should have already been done in OnWillRemoveBookmarks. + DCHECK(bookmark_tracker_->GetEntityForBookmarkNode(node) == nullptr); } void BookmarkModelObserverImpl::BookmarkAllUserNodesRemoved( @@ -290,4 +302,23 @@ suffix); } +void BookmarkModelObserverImpl::ProcessDelete( + const bookmarks::BookmarkNode* parent, + const bookmarks::BookmarkNode* node) { + // If not a leaf node, process all children first. + for (int i = 0; i < node->child_count(); ++i) { + const bookmarks::BookmarkNode* child = node->GetChild(i); + ProcessDelete(node, child); + } + // Process the current node. + const SyncedBookmarkTracker::Entity* entity = + bookmark_tracker_->GetEntityForBookmarkNode(node); + // Shouldn't try to delete untracked entities. + DCHECK(entity); + const std::string& sync_id = entity->metadata()->server_id(); + bookmark_tracker_->MarkDeleted(sync_id); + // Mark the entity that it needs to be committed. + bookmark_tracker_->IncrementSequenceNumber(sync_id); +} + } // namespace sync_bookmarks
diff --git a/components/sync_bookmarks/bookmark_model_observer_impl.h b/components/sync_bookmarks/bookmark_model_observer_impl.h index 6a020b1..ef5ecc7 100644 --- a/components/sync_bookmarks/bookmark_model_observer_impl.h +++ b/components/sync_bookmarks/bookmark_model_observer_impl.h
@@ -44,6 +44,10 @@ void BookmarkNodeAdded(bookmarks::BookmarkModel* model, const bookmarks::BookmarkNode* parent, int index) override; + void OnWillRemoveBookmarks(bookmarks::BookmarkModel* model, + const bookmarks::BookmarkNode* parent, + int old_index, + const bookmarks::BookmarkNode* node) override; void BookmarkNodeRemoved(bookmarks::BookmarkModel* model, const bookmarks::BookmarkNode* parent, int old_index, @@ -66,6 +70,13 @@ int index, const std::string& sync_id); + // Processes the deletion of a bookmake node and updates the + // |bookmark_tracker_| accordingly. If |node| is a bookmark, it gets marked + // as deleted and that it requires a commit. If it's a folder, it recurses + // over all children before processing the folder itself. + void ProcessDelete(const bookmarks::BookmarkNode* parent, + const bookmarks::BookmarkNode* node); + // Points to the tracker owned by the processor. It keeps the mapping between // bookmark nodes and corresponding sync server entities. SyncedBookmarkTracker* const bookmark_tracker_;
diff --git a/components/sync_bookmarks/bookmark_model_observer_impl_unittest.cc b/components/sync_bookmarks/bookmark_model_observer_impl_unittest.cc index 7b905cf8..4a83412 100644 --- a/components/sync_bookmarks/bookmark_model_observer_impl_unittest.cc +++ b/components/sync_bookmarks/bookmark_model_observer_impl_unittest.cc
@@ -4,6 +4,7 @@ #include "components/sync_bookmarks/bookmark_model_observer_impl.h" +#include <algorithm> #include <memory> #include <vector> @@ -25,9 +26,11 @@ using testing::Ne; using testing::NiceMock; using testing::NotNull; +using testing::ElementsAre; const char kBookmarkBarId[] = "bookmark_bar_id"; const char kBookmarkBarTag[] = "bookmark_bar"; +const size_t kMaxEntries = 1000; class BookmarkModelObserverImplTest : public testing::Test { public: @@ -49,16 +52,15 @@ specifics); } - void SimulateCommitResponseForBookmarkNode( - const bookmarks::BookmarkNode* bookmark_node, - int64_t acked_sequence_number, - int64_t server_version) { - const std::string id = bookmark_tracker() - ->GetEntityForBookmarkNode(bookmark_node) - ->metadata() - ->server_id(); - bookmark_tracker()->UpdateUponCommitResponse(id, id, acked_sequence_number, - server_version); + void SimulateCommitResponseForAllLocalChanges() { + for (const SyncedBookmarkTracker::Entity* entity : + bookmark_tracker()->GetEntitiesWithLocalChanges(kMaxEntries)) { + const std::string id = entity->metadata()->server_id(); + // Don't simulate change in id for simplicity. + bookmark_tracker()->UpdateUponCommitResponse(id, id, + /*acked_sequence_number=*/1, + /*server_version=*/1); + } } syncer::UniquePosition PositionOf( @@ -88,7 +90,6 @@ BookmarkAddedShouldPutInTheTrackerAndNudgeForCommit) { const std::string kTitle = "title"; const std::string kUrl = "http://www.url.com"; - const size_t kMaxEntries = 10; EXPECT_CALL(*nudge_for_commit_closure(), Run()); const bookmarks::BookmarkNode* bookmark_bar_node = @@ -113,7 +114,6 @@ const std::string kTitle2 = "title2"; const std::string kUrl2 = "http://www.url2.com"; const std::string kNewTitle2 = "new_title2"; - const size_t kMaxEntries = 10; const bookmarks::BookmarkNode* bookmark_bar_node = bookmark_model()->bookmark_bar_node(); @@ -129,12 +129,7 @@ ASSERT_THAT( bookmark_tracker()->GetEntitiesWithLocalChanges(kMaxEntries).size(), 2U); - // Record commits responses for both nodes (without change in ids for - // simplcitiy of this test). - SimulateCommitResponseForBookmarkNode( - bookmark_node1, /*acked_sequence_number=*/1, /*server_version=*/1); - SimulateCommitResponseForBookmarkNode( - bookmark_node2, /*acked_sequence_number=*/1, /*server_version=*/1); + SimulateCommitResponseForAllLocalChanges(); // There should be no local changes now. ASSERT_TRUE( @@ -180,7 +175,6 @@ // |- folder1 // |- bookmark1 const GURL kUrl("http://www.url1.com"); - const size_t kMaxEntries = 10; const bookmarks::BookmarkNode* bookmark_bar_node = bookmark_model()->bookmark_bar_node(); @@ -197,12 +191,8 @@ // All bookmarks should be tracked now. ASSERT_THAT(bookmark_tracker()->TrackedEntitiesCountForTest(), 3U); - // Record commits responses for all nodes (without change in ids for - // simplcitiy of this test). - SimulateCommitResponseForBookmarkNode( - folder1_node, /*acked_sequence_number=*/1, /*server_version=*/1); - SimulateCommitResponseForBookmarkNode( - bookmark1_node, /*acked_sequence_number=*/1, /*server_version=*/1); + + SimulateCommitResponseForAllLocalChanges(); // There should be no local changes now. ASSERT_TRUE( @@ -223,7 +213,6 @@ ReorderChildrenShouldUpdateTheTrackerAndNudgeForCommit) { const std::string kTitle = "title"; const std::string kUrl = "http://www.url.com"; - const size_t kMaxEntries = 10; // Build this structure: // bookmark_bar @@ -248,12 +237,7 @@ // All bookmarks should be tracked now. ASSERT_THAT(bookmark_tracker()->TrackedEntitiesCountForTest(), 5U); - // Record commits responses for all nodes (without change in ids for - // simplcitiy of this test). - for (const bookmarks::BookmarkNode* node : nodes) { - SimulateCommitResponseForBookmarkNode(node, /*acked_sequence_number=*/1, - /*server_version=*/1); - } + SimulateCommitResponseForAllLocalChanges(); // Reorder it to be: // bookmark_bar @@ -284,6 +268,138 @@ } } +TEST_F(BookmarkModelObserverImplTest, + BookmarkRemovalShouldUpdateTheTrackerAndNudgeForCommit) { + // Build this structure: + // bookmark_bar + // |- folder1 + // |- bookmark1 + // |- folder2 + // |- bookmark2 + // |- bookmark3 + + // and then delete folder2. + const GURL kUrl("http://www.url1.com"); + + const bookmarks::BookmarkNode* bookmark_bar_node = + bookmark_model()->bookmark_bar_node(); + const bookmarks::BookmarkNode* folder1_node = bookmark_model()->AddFolder( + /*parent=*/bookmark_bar_node, /*index=*/0, base::UTF8ToUTF16("folder1")); + const bookmarks::BookmarkNode* bookmark1_node = bookmark_model()->AddURL( + /*parent=*/folder1_node, /*index=*/0, base::UTF8ToUTF16("bookmark1"), + kUrl); + const bookmarks::BookmarkNode* folder2_node = bookmark_model()->AddFolder( + /*parent=*/folder1_node, /*index=*/1, base::UTF8ToUTF16("folder2")); + const bookmarks::BookmarkNode* bookmark2_node = bookmark_model()->AddURL( + /*parent=*/folder2_node, /*index=*/0, base::UTF8ToUTF16("bookmark2"), + kUrl); + const bookmarks::BookmarkNode* bookmark3_node = bookmark_model()->AddURL( + /*parent=*/folder2_node, /*index=*/1, base::UTF8ToUTF16("bookmark3"), + kUrl); + + // All bookmarks should be tracked now. + ASSERT_THAT(bookmark_tracker()->TrackedEntitiesCountForTest(), 6U); + + SimulateCommitResponseForAllLocalChanges(); + + // There should be no local changes now. + ASSERT_TRUE( + bookmark_tracker()->GetEntitiesWithLocalChanges(kMaxEntries).empty()); + + const SyncedBookmarkTracker::Entity* folder2_entity = + bookmark_tracker()->GetEntityForBookmarkNode(folder2_node); + const SyncedBookmarkTracker::Entity* bookmark2_entity = + bookmark_tracker()->GetEntityForBookmarkNode(bookmark2_node); + const SyncedBookmarkTracker::Entity* bookmark3_entity = + bookmark_tracker()->GetEntityForBookmarkNode(bookmark3_node); + + ASSERT_FALSE(folder2_entity->metadata()->is_deleted()); + ASSERT_FALSE(bookmark2_entity->metadata()->is_deleted()); + ASSERT_FALSE(bookmark3_entity->metadata()->is_deleted()); + + const std::string& folder2_entity_id = + folder2_entity->metadata()->server_id(); + const std::string& bookmark2_entity_id = + bookmark2_entity->metadata()->server_id(); + const std::string& bookmark3_entity_id = + bookmark3_entity->metadata()->server_id(); + // Delete folder2. + EXPECT_CALL(*nudge_for_commit_closure(), Run()); + bookmark_model()->Remove(folder2_node); + + // folder2, bookmark2, and bookmark3 should be marked deleted. + EXPECT_TRUE(bookmark_tracker() + ->GetEntityForSyncId(folder2_entity_id) + ->metadata() + ->is_deleted()); + EXPECT_TRUE(bookmark_tracker() + ->GetEntityForSyncId(bookmark2_entity_id) + ->metadata() + ->is_deleted()); + EXPECT_TRUE(bookmark_tracker() + ->GetEntityForSyncId(bookmark3_entity_id) + ->metadata() + ->is_deleted()); + + // folder2, bookmark2, and bookmark3 should be in the local changes list. + std::vector<const SyncedBookmarkTracker::Entity*> local_changes = + bookmark_tracker()->GetEntitiesWithLocalChanges(kMaxEntries); + ASSERT_THAT(local_changes.size(), 3U); + + // All deleted nodes entities should exist in the set of local changes to be + // committed and folder2 deletion should be the last one (after all children + // deletions). + EXPECT_THAT( + local_changes, + ElementsAre(bookmark_tracker()->GetEntityForSyncId(bookmark2_entity_id), + bookmark_tracker()->GetEntityForSyncId(bookmark3_entity_id), + bookmark_tracker()->GetEntityForSyncId(folder2_entity_id))); + + // folder1 and bookmark1 are still tracked. + EXPECT_TRUE(bookmark_tracker()->GetEntityForBookmarkNode(folder1_node)); + EXPECT_TRUE(bookmark_tracker()->GetEntityForBookmarkNode(bookmark1_node)); +} + +TEST_F(BookmarkModelObserverImplTest, + BookmarkCreationAndRemovalShouldRequireTwoCommitResponsesBeforeRemoval) { + const bookmarks::BookmarkNode* bookmark_bar_node = + bookmark_model()->bookmark_bar_node(); + const bookmarks::BookmarkNode* folder_node = bookmark_model()->AddFolder( + /*parent=*/bookmark_bar_node, /*index=*/0, base::UTF8ToUTF16("folder")); + + // Node should be tracked now. + ASSERT_THAT(bookmark_tracker()->TrackedEntitiesCountForTest(), 2U); + const std::string id = bookmark_tracker() + ->GetEntityForBookmarkNode(folder_node) + ->metadata() + ->server_id(); + ASSERT_THAT( + bookmark_tracker()->GetEntitiesWithLocalChanges(kMaxEntries).size(), 1U); + + // Remove the folder. + bookmark_model()->Remove(folder_node); + + // Simulate a commit response for the first commit request (the creation). + // Don't simulate change in id for simplcity. + bookmark_tracker()->UpdateUponCommitResponse(id, id, + /*acked_sequence_number=*/1, + /*server_version=*/1); + + // There should still be one local change (the deletion). + EXPECT_THAT( + bookmark_tracker()->GetEntitiesWithLocalChanges(kMaxEntries).size(), 1U); + + // Entity is still tracked. + EXPECT_THAT(bookmark_tracker()->TrackedEntitiesCountForTest(), 2U); + + // Commit the deletion. + bookmark_tracker()->UpdateUponCommitResponse(id, id, + /*acked_sequence_number=*/2, + /*server_version=*/2); + // Entity should have been dropped. + EXPECT_THAT(bookmark_tracker()->TrackedEntitiesCountForTest(), 1U); +} + TEST_F(BookmarkModelObserverImplTest, ShouldPositionSiblings) { const std::string kTitle = "title"; const std::string kUrl = "http://www.url.com";
diff --git a/components/sync_bookmarks/synced_bookmark_tracker.cc b/components/sync_bookmarks/synced_bookmark_tracker.cc index 7e1cbd0..7efd7c6 100644 --- a/components/sync_bookmarks/synced_bookmark_tracker.cc +++ b/components/sync_bookmarks/synced_bookmark_tracker.cc
@@ -71,7 +71,15 @@ const std::string& sync_id = node_metadata.second->server_id(); auto entity = std::make_unique<Entity>(node_metadata.first, std::move(node_metadata.second)); - bookmark_node_to_entities_map_[node_metadata.first] = entity.get(); + if (node_metadata.first) { + // Non-null node means it's not a tombstone. + bookmark_node_to_entities_map_[node_metadata.first] = entity.get(); + } else { + // Otherwise, it must be a deletion so we must remember to deletion + // ordering. + DCHECK(entity->metadata()->is_deleted()); + ordered_local_tombstones_.push_back(entity.get()); + } sync_id_to_entities_map_[sync_id] = std::move(entity); } } @@ -129,12 +137,29 @@ syncer::TimeToProtoTime(modification_time)); *entity->metadata()->mutable_unique_position() = unique_position; HashSpecifics(specifics, entity->metadata()->mutable_specifics_hash()); + // TODO(crbug.com/516866): in case of conflict, the entity might exist in + // |ordered_local_tombstones_| as well if it has been locally deleted. +} + +void SyncedBookmarkTracker::MarkDeleted(const std::string& sync_id) { + auto it = sync_id_to_entities_map_.find(sync_id); + Entity* entity = it->second.get(); + DCHECK(entity); + entity->metadata()->set_is_deleted(true); + // Clear all references to the deleted bookmark node. + bookmark_node_to_entities_map_.erase(entity->bookmark_node()); + entity->clear_bookmark_node(); + ordered_local_tombstones_.push_back(entity); } void SyncedBookmarkTracker::Remove(const std::string& sync_id) { const Entity* entity = GetEntityForSyncId(sync_id); DCHECK(entity); bookmark_node_to_entities_map_.erase(entity->bookmark_node()); + ordered_local_tombstones_.erase( + std::remove(ordered_local_tombstones_.begin(), + ordered_local_tombstones_.end(), entity), + ordered_local_tombstones_.end()); sync_id_to_entities_map_.erase(sync_id); } @@ -142,7 +167,6 @@ const std::string& sync_id) { Entity* entity = sync_id_to_entities_map_.find(sync_id)->second.get(); DCHECK(entity); - DCHECK(!entity->metadata()->is_deleted()); // TODO(crbug.com/516866): Update base hash specifics here if the entity is // not already out of sync. entity->metadata()->set_sequence_number( @@ -154,13 +178,24 @@ sync_pb::BookmarkModelMetadata model_metadata; for (const std::pair<const std::string, std::unique_ptr<Entity>>& pair : sync_id_to_entities_map_) { + if (pair.second->metadata()->is_deleted()) { + // Deletions will be added later because they need to maintain the same + // order as in |ordered_local_tombstones_|. + continue; + } + DCHECK(pair.second->bookmark_node()); sync_pb::BookmarkMetadata* bookmark_metadata = model_metadata.add_bookmarks_metadata(); - if (pair.second->bookmark_node() != nullptr) { - bookmark_metadata->set_id(pair.second->bookmark_node()->id()); - } + bookmark_metadata->set_id(pair.second->bookmark_node()->id()); *bookmark_metadata->mutable_metadata() = *pair.second->metadata(); } + // Add pending deletions. + for (const Entity* tombstone_entity : ordered_local_tombstones_) { + DCHECK(tombstone_entity->metadata()->is_deleted()); + sync_pb::BookmarkMetadata* bookmark_metadata = + model_metadata.add_bookmarks_metadata(); + *bookmark_metadata->mutable_metadata() = *tombstone_entity->metadata(); + } *model_metadata.mutable_model_type_state() = *model_type_state_; return model_metadata; } @@ -180,14 +215,23 @@ SyncedBookmarkTracker::GetEntitiesWithLocalChanges(size_t max_entries) const { // TODO(crbug.com/516866): Reorder local changes to e.g. parent creation // before child creation and the otherway around for deletions. + // TODO(crbug.com/516866): Return no more than |max_entries|. std::vector<const SyncedBookmarkTracker::Entity*> entities_with_local_changes; for (const std::pair<const std::string, std::unique_ptr<Entity>>& pair : sync_id_to_entities_map_) { Entity* entity = pair.second.get(); + if (entity->metadata()->is_deleted()) { + // Deletion are stored sorted in |ordered_local_tombstones_| and will be + // added later. + continue; + } if (entity->IsUnsynced()) { entities_with_local_changes.push_back(entity); } } + for (const Entity* tombstone_entity : ordered_local_tombstones_) { + entities_with_local_changes.push_back(tombstone_entity); + } return entities_with_local_changes; } @@ -200,14 +244,18 @@ auto it = sync_id_to_entities_map_.find(old_id); Entity* entity = it != sync_id_to_entities_map_.end() ? it->second.get() : nullptr; - if (it == sync_id_to_entities_map_.end()) { + if (!entity) { DLOG(WARNING) << "Trying to update a non existing entity."; return; } - const bookmarks::BookmarkNode* node = entity->bookmark_node(); - // TODO(crbug.com/516866): For tombstones, node would be null and the DCHECK - // below would be invalid. Handle deletions here may be or in the processor. - DCHECK(node); + + entity->metadata()->set_acked_sequence_number(acked_sequence_number); + entity->metadata()->set_server_version(server_version); + // If there are no pending commits, remove tombstones. + if (!entity->IsUnsynced() && entity->metadata()->is_deleted()) { + Remove(old_id); + return; + } if (old_id != new_id) { auto it = sync_id_to_entities_map_.find(old_id); @@ -215,8 +263,6 @@ sync_id_to_entities_map_[new_id] = std::move(it->second); sync_id_to_entities_map_.erase(old_id); } - entity->metadata()->set_acked_sequence_number(acked_sequence_number); - entity->metadata()->set_server_version(server_version); } std::size_t SyncedBookmarkTracker::TrackedEntitiesCountForTest() const {
diff --git a/components/sync_bookmarks/synced_bookmark_tracker.h b/components/sync_bookmarks/synced_bookmark_tracker.h index f986430..415c5e3 100644 --- a/components/sync_bookmarks/synced_bookmark_tracker.h +++ b/components/sync_bookmarks/synced_bookmark_tracker.h
@@ -55,6 +55,9 @@ return bookmark_node_; } + // Used in local deletions to mark and entity as a tommstone. + void clear_bookmark_node() { bookmark_node_ = nullptr; } + const sync_pb::EntityMetadata* metadata() const { return metadata_.get(); } sync_pb::EntityMetadata* metadata() { return metadata_.get(); } @@ -62,7 +65,8 @@ // Check whether |specifics| matches the stored specifics_hash. bool MatchesSpecificsHash(const sync_pb::EntitySpecifics& specifics) const; - const bookmarks::BookmarkNode* const bookmark_node_; + // Null for tombstones. + const bookmarks::BookmarkNode* bookmark_node_; // Serializable Sync metadata. std::unique_ptr<sync_pb::EntityMetadata> metadata_; @@ -70,7 +74,8 @@ DISALLOW_COPY_AND_ASSIGN(Entity); }; - // |model_type_state| must not be null. + // |model_type_state| must not be null. null nodes in |nodes_metadata| can be + // used to represent local tombstones. SyncedBookmarkTracker( std::vector<NodeMetadataPair> nodes_metadata, std::unique_ptr<sync_pb::ModelTypeState> model_type_state); @@ -100,6 +105,11 @@ const sync_pb::UniquePosition& unique_position, const sync_pb::EntitySpecifics& specifics); + // This class maintains the order of calls to this method and the same order + // is gauaranteed when returning local changes in + // GetEntitiesWithLocalChanges() as well as in BuildBookmarkModelMetadata(). + void MarkDeleted(const std::string& sync_id); + // Removes the entry coressponding to the |sync_id| from // |sync_id_to_entities_map_|. void Remove(const std::string& sync_id); @@ -149,6 +159,12 @@ std::map<const bookmarks::BookmarkNode*, Entity*> bookmark_node_to_entities_map_; + // A list of pending local bookmark deletions. They should be sent to the + // server in the same order as stored in the list. The same order should also + // be maintained across browser restarts (i.e. across calls to the ctor() and + // BuildBookmarkModelMetadata(). + std::vector<Entity*> ordered_local_tombstones_; + // The model metadata (progress marker, initial sync done, etc). std::unique_ptr<sync_pb::ModelTypeState> model_type_state_;
diff --git a/components/sync_bookmarks/synced_bookmark_tracker_unittest.cc b/components/sync_bookmarks/synced_bookmark_tracker_unittest.cc index 5483bcd..5fafc83 100644 --- a/components/sync_bookmarks/synced_bookmark_tracker_unittest.cc +++ b/components/sync_bookmarks/synced_bookmark_tracker_unittest.cc
@@ -139,6 +139,130 @@ EXPECT_THAT(entity->metadata()->server_version(), Eq(kNewServerVersion)); } +TEST(SyncedBookmarkTrackerTest, + ShouldMaintainTombstoneOrderBetweenCtorAndBuildBookmarkModelMetadata) { + // Feed a metadata batch of 5 entries to the constructor of the tracker. + // First 2 are for node, and the last 4 are for tombstones. + + // Server ids. + const std::string kId0 = "id0"; + const std::string kId1 = "id1"; + const std::string kId2 = "id2"; + const std::string kId3 = "id3"; + const std::string kId4 = "id4"; + + const GURL kUrl("http://www.foo.com"); + bookmarks::BookmarkNode node0(/*id=*/0, kUrl); + bookmarks::BookmarkNode node1(/*id=*/1, kUrl); + + auto metadata0 = std::make_unique<sync_pb::EntityMetadata>(); + metadata0->set_server_id(kId0); + + auto metadata1 = std::make_unique<sync_pb::EntityMetadata>(); + metadata1->set_server_id(kId1); + + auto metadata2 = std::make_unique<sync_pb::EntityMetadata>(); + metadata2->set_server_id(kId2); + metadata2->set_is_deleted(true); + + auto metadata3 = std::make_unique<sync_pb::EntityMetadata>(); + metadata3->set_server_id(kId3); + metadata3->set_is_deleted(true); + + auto metadata4 = std::make_unique<sync_pb::EntityMetadata>(); + metadata4->set_server_id(kId4); + metadata4->set_is_deleted(true); + + std::vector<NodeMetadataPair> node_metadata_pairs; + node_metadata_pairs.emplace_back(&node0, std::move(metadata0)); + node_metadata_pairs.emplace_back(&node1, std::move(metadata1)); + node_metadata_pairs.emplace_back(nullptr, std::move(metadata2)); + node_metadata_pairs.emplace_back(nullptr, std::move(metadata3)); + node_metadata_pairs.emplace_back(nullptr, std::move(metadata4)); + + SyncedBookmarkTracker tracker(std::move(node_metadata_pairs), + std::make_unique<sync_pb::ModelTypeState>()); + + sync_pb::BookmarkModelMetadata bookmark_model_metadata = + tracker.BuildBookmarkModelMetadata(); + + // Tombstones should be the last 3 entries in the metadata and in the same + // order as given to the constructor. + ASSERT_THAT(bookmark_model_metadata.bookmarks_metadata().size(), Eq(5)); + EXPECT_THAT( + bookmark_model_metadata.bookmarks_metadata(2).metadata().server_id(), + Eq(kId2)); + EXPECT_THAT( + bookmark_model_metadata.bookmarks_metadata(3).metadata().server_id(), + Eq(kId3)); + EXPECT_THAT( + bookmark_model_metadata.bookmarks_metadata(4).metadata().server_id(), + Eq(kId4)); +} + +TEST(SyncedBookmarkTrackerTest, + ShouldMaintainOrderOfMarkDeletedCallsWhenBuildBookmarkModelMetadata) { + // Server ids. + const std::string kId0 = "id0"; + const std::string kId1 = "id1"; + const std::string kId2 = "id2"; + const std::string kId3 = "id3"; + const std::string kId4 = "id4"; + + const GURL kUrl("http://www.foo.com"); + bookmarks::BookmarkNode node0(/*id=*/0, kUrl); + bookmarks::BookmarkNode node1(/*id=*/1, kUrl); + bookmarks::BookmarkNode node2(/*id=*/2, kUrl); + bookmarks::BookmarkNode node3(/*id=*/3, kUrl); + bookmarks::BookmarkNode node4(/*id=*/4, kUrl); + + auto metadata0 = std::make_unique<sync_pb::EntityMetadata>(); + metadata0->set_server_id(kId0); + + auto metadata1 = std::make_unique<sync_pb::EntityMetadata>(); + metadata1->set_server_id(kId1); + + auto metadata2 = std::make_unique<sync_pb::EntityMetadata>(); + metadata2->set_server_id(kId2); + + auto metadata3 = std::make_unique<sync_pb::EntityMetadata>(); + metadata3->set_server_id(kId3); + + auto metadata4 = std::make_unique<sync_pb::EntityMetadata>(); + metadata4->set_server_id(kId4); + + std::vector<NodeMetadataPair> node_metadata_pairs; + node_metadata_pairs.emplace_back(&node0, std::move(metadata0)); + node_metadata_pairs.emplace_back(&node1, std::move(metadata1)); + node_metadata_pairs.emplace_back(&node2, std::move(metadata2)); + node_metadata_pairs.emplace_back(&node3, std::move(metadata3)); + node_metadata_pairs.emplace_back(&node4, std::move(metadata4)); + + SyncedBookmarkTracker tracker(std::move(node_metadata_pairs), + std::make_unique<sync_pb::ModelTypeState>()); + + // Mark entities deleted in that order kId2, kId4, kId1 + tracker.MarkDeleted(kId2); + tracker.MarkDeleted(kId4); + tracker.MarkDeleted(kId1); + + sync_pb::BookmarkModelMetadata bookmark_model_metadata = + tracker.BuildBookmarkModelMetadata(); + + // Tombstones should be the last 3 entries in the metadata and in the same as + // calling MarkDeleted(). + ASSERT_THAT(bookmark_model_metadata.bookmarks_metadata().size(), Eq(5)); + EXPECT_THAT( + bookmark_model_metadata.bookmarks_metadata(2).metadata().server_id(), + Eq(kId2)); + EXPECT_THAT( + bookmark_model_metadata.bookmarks_metadata(3).metadata().server_id(), + Eq(kId4)); + EXPECT_THAT( + bookmark_model_metadata.bookmarks_metadata(4).metadata().server_id(), + Eq(kId1)); +} + } // namespace } // namespace sync_bookmarks
diff --git a/components/viz/host/server_gpu_memory_buffer_manager_unittest.cc b/components/viz/host/server_gpu_memory_buffer_manager_unittest.cc index 9557bcb..54f117a 100644 --- a/components/viz/host/server_gpu_memory_buffer_manager_unittest.cc +++ b/components/viz/host/server_gpu_memory_buffer_manager_unittest.cc
@@ -58,6 +58,7 @@ void EstablishGpuChannel(int32_t client_id, uint64_t client_tracing_id, bool is_gpu_host, + bool cache_shaders_on_disk, EstablishGpuChannelCallback callback) override {} void CloseChannel(int32_t client_id) override {} @@ -111,7 +112,9 @@ void RequestHDRStatus(RequestHDRStatusCallback callback) override {} - void LoadedShader(const std::string& key, const std::string& data) override {} + void LoadedShader(int32_t client_id, + const std::string& key, + const std::string& data) override {} void WakeUpGpu() override {}
diff --git a/components/viz/service/display_embedder/in_process_gpu_memory_buffer_manager.cc b/components/viz/service/display_embedder/in_process_gpu_memory_buffer_manager.cc index 37fa4f2c..84d25eb 100644 --- a/components/viz/service/display_embedder/in_process_gpu_memory_buffer_manager.cc +++ b/components/viz/service/display_embedder/in_process_gpu_memory_buffer_manager.cc
@@ -5,6 +5,7 @@ #include "components/viz/service/display_embedder/in_process_gpu_memory_buffer_manager.h" #include "base/bind.h" +#include "gpu/ipc/common/gpu_client_ids.h" #include "gpu/ipc/common/gpu_memory_buffer_impl.h" #include "gpu/ipc/common/gpu_memory_buffer_support.h" #include "gpu/ipc/in_process_command_buffer.h" @@ -16,7 +17,7 @@ InProcessGpuMemoryBufferManager::InProcessGpuMemoryBufferManager( gpu::GpuChannelManager* channel_manager) : gpu_memory_buffer_support_(new gpu::GpuMemoryBufferSupport()), - client_id_(gpu::InProcessCommandBuffer::kGpuClientId), + client_id_(gpu::kInProcessCommandBufferClientId), channel_manager_(channel_manager), weak_factory_(this) { weak_ptr_ = weak_factory_.GetWeakPtr();
diff --git a/components/viz/service/gl/gpu_service_impl.cc b/components/viz/service/gl/gpu_service_impl.cc index a9e578db..e4a80f8 100644 --- a/components/viz/service/gl/gpu_service_impl.cc +++ b/components/viz/service/gl/gpu_service_impl.cc
@@ -28,6 +28,7 @@ #include "gpu/config/gpu_info_collector.h" #include "gpu/config/gpu_switches.h" #include "gpu/config/gpu_util.h" +#include "gpu/ipc/common/gpu_client_ids.h" #include "gpu/ipc/common/gpu_memory_buffer_support.h" #include "gpu/ipc/common/memory_stats.h" #include "gpu/ipc/in_process_command_buffer.h" @@ -702,8 +703,9 @@ void GpuServiceImpl::EstablishGpuChannel(int32_t client_id, uint64_t client_tracing_id, bool is_gpu_host, + bool cache_shaders_on_disk, EstablishGpuChannelCallback callback) { - if (oopd_enabled_ && client_id == gpu::InProcessCommandBuffer::kGpuClientId) { + if (gpu::IsReservedClientId(client_id)) { std::move(callback).Run(mojo::ScopedMessagePipeHandle()); return; } @@ -717,14 +719,15 @@ }, io_runner_, std::move(callback)); main_runner_->PostTask( - FROM_HERE, base::BindOnce(&GpuServiceImpl::EstablishGpuChannel, - weak_ptr_, client_id, client_tracing_id, - is_gpu_host, std::move(wrap_callback))); + FROM_HERE, + base::BindOnce(&GpuServiceImpl::EstablishGpuChannel, weak_ptr_, + client_id, client_tracing_id, is_gpu_host, + cache_shaders_on_disk, std::move(wrap_callback))); return; } gpu::GpuChannel* gpu_channel = gpu_channel_manager_->EstablishChannel( - client_id, client_tracing_id, is_gpu_host); + client_id, client_tracing_id, is_gpu_host, cache_shaders_on_disk); mojo::MessagePipe pipe; gpu_channel->Init(std::make_unique<gpu::SyncChannelFilteredSender>( @@ -744,14 +747,16 @@ gpu_channel_manager_->RemoveChannel(client_id); } -void GpuServiceImpl::LoadedShader(const std::string& key, +void GpuServiceImpl::LoadedShader(int32_t client_id, + const std::string& key, const std::string& data) { if (io_runner_->BelongsToCurrentThread()) { - main_runner_->PostTask(FROM_HERE, base::Bind(&GpuServiceImpl::LoadedShader, - weak_ptr_, key, data)); + main_runner_->PostTask(FROM_HERE, + base::Bind(&GpuServiceImpl::LoadedShader, weak_ptr_, + client_id, key, data)); return; } - gpu_channel_manager_->PopulateShaderCache(key, data); + gpu_channel_manager_->PopulateShaderCache(client_id, key, data); } void GpuServiceImpl::WakeUpGpu() {
diff --git a/components/viz/service/gl/gpu_service_impl.h b/components/viz/service/gl/gpu_service_impl.h index 31e1b51..13657335 100644 --- a/components/viz/service/gl/gpu_service_impl.h +++ b/components/viz/service/gl/gpu_service_impl.h
@@ -182,6 +182,7 @@ void EstablishGpuChannel(int32_t client_id, uint64_t client_tracing_id, bool is_gpu_host, + bool cache_shaders_on_disk, EstablishGpuChannelCallback callback) override; void CloseChannel(int32_t client_id) override; #if defined(OS_CHROMEOS) @@ -217,7 +218,9 @@ void GetGpuSupportedRuntimeVersion( GetGpuSupportedRuntimeVersionCallback callback) override; void RequestHDRStatus(RequestHDRStatusCallback callback) override; - void LoadedShader(const std::string& key, const std::string& data) override; + void LoadedShader(int32_t client_id, + const std::string& key, + const std::string& data) override; void WakeUpGpu() override; void GpuSwitched() override; void DestroyAllChannels() override;
diff --git a/components/web_resource/eula_accepted_notifier.h b/components/web_resource/eula_accepted_notifier.h index b386e1f..3dcdf1e 100644 --- a/components/web_resource/eula_accepted_notifier.h +++ b/components/web_resource/eula_accepted_notifier.h
@@ -19,6 +19,7 @@ // Observes EULA accepted state changes. class Observer { public: + virtual ~Observer() {} virtual void OnEulaAccepted() = 0; };
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn index 7185462..97c38fe1 100644 --- a/content/browser/BUILD.gn +++ b/content/browser/BUILD.gn
@@ -1050,8 +1050,6 @@ "loader/upload_data_stream_builder.h", "loader/url_loader_factory_impl.cc", "loader/url_loader_factory_impl.h", - "loader/wake_lock_resource_throttle.cc", - "loader/wake_lock_resource_throttle.h", "loader_delegate_impl.cc", "loader_delegate_impl.h", "locks/lock_manager.cc",
diff --git a/content/browser/browser_main_loop.cc b/content/browser/browser_main_loop.cc index adc3aa8..611c1d6 100644 --- a/content/browser/browser_main_loop.cc +++ b/content/browser/browser_main_loop.cc
@@ -638,6 +638,17 @@ command_line->GetSwitchValueASCII(switches::kDisableFeatures)); } +#if defined(OS_ANDROID) || defined(OS_CHROMEOS) + // Up the priority of the UI thread unless it was already high (since recent + // versions of Android (O+) do this automatically). + if (base::PlatformThread::GetCurrentThreadPriority() < + base::ThreadPriority::DISPLAY || + base::FeatureList::IsEnabled(features::kOverrideUIThreadPriority)) { + base::PlatformThread::SetCurrentThreadPriority( + base::ThreadPriority::DISPLAY); + } +#endif // defined(OS_ANDROID) || defined(OS_CHROMEOS) + #if defined(OS_MACOSX) || defined(OS_LINUX) || defined(OS_CHROMEOS) || \ defined(OS_ANDROID) // We use quite a few file descriptors for our IPC as well as disk the disk @@ -1238,10 +1249,6 @@ #endif HistogramSynchronizer::GetInstance(); -#if defined(OS_ANDROID) || defined(OS_CHROMEOS) - // Up the priority of the UI thread. - base::PlatformThread::SetCurrentThreadPriority(base::ThreadPriority::DISPLAY); -#endif // cc assumes a single client name for metrics in a process, which is // is inconsistent with single process mode where both the renderer and
diff --git a/content/browser/download/url_downloader.cc b/content/browser/download/url_downloader.cc index e44234a3..034fadd 100644 --- a/content/browser/download/url_downloader.cc +++ b/content/browser/download/url_downloader.cc
@@ -13,7 +13,6 @@ #include "components/download/public/common/download_interrupt_reasons.h" #include "components/download/public/common/download_request_handle_interface.h" #include "components/download/public/common/download_url_parameters.h" -#include "components/download/public/common/url_download_request_handle.h" #include "content/browser/byte_stream.h" #include "content/browser/download/byte_stream_input_stream.h" #include "content/public/browser/browser_thread.h" @@ -26,6 +25,43 @@ namespace content { +class UrlDownloader::RequestHandle + : public download::DownloadRequestHandleInterface { + public: + RequestHandle(base::WeakPtr<UrlDownloader> downloader, + scoped_refptr<base::SequencedTaskRunner> downloader_task_runner) + : downloader_(downloader), + downloader_task_runner_(downloader_task_runner) {} + RequestHandle(RequestHandle&& other) + : downloader_(std::move(other.downloader_)), + downloader_task_runner_(std::move(other.downloader_task_runner_)) {} + RequestHandle& operator=(RequestHandle&& other) { + downloader_ = std::move(other.downloader_); + downloader_task_runner_ = std::move(other.downloader_task_runner_); + return *this; + } + + // DownloadRequestHandleInterface + void PauseRequest() override { + downloader_task_runner_->PostTask( + FROM_HERE, base::BindOnce(&UrlDownloader::PauseRequest, downloader_)); + } + void ResumeRequest() override { + downloader_task_runner_->PostTask( + FROM_HERE, base::BindOnce(&UrlDownloader::ResumeRequest, downloader_)); + } + void CancelRequest(bool user_cancel) override { + downloader_task_runner_->PostTask( + FROM_HERE, base::BindOnce(&UrlDownloader::CancelRequest, downloader_)); + } + + private: + base::WeakPtr<UrlDownloader> downloader_; + scoped_refptr<base::SequencedTaskRunner> downloader_task_runner_; + + DISALLOW_COPY_AND_ASSIGN(RequestHandle); +}; + // static std::unique_ptr<UrlDownloader> UrlDownloader::BeginDownload( base::WeakPtr<download::UrlDownloadHandler::Delegate> delegate, @@ -185,7 +221,7 @@ std::unique_ptr<download::DownloadCreateInfo> create_info, std::unique_ptr<ByteStreamReader> stream_reader, const download::DownloadUrlParameters::OnStartedCallback& callback) { - create_info->request_handle.reset(new download::UrlDownloadRequestHandle( + create_info->request_handle.reset(new RequestHandle( weak_ptr_factory_.GetWeakPtr(), base::SequencedTaskRunnerHandle::Get())); BrowserThread::PostTask(
diff --git a/content/browser/download/url_downloader.h b/content/browser/download/url_downloader.h index e5b7289..0ee10fe 100644 --- a/content/browser/download/url_downloader.h +++ b/content/browser/download/url_downloader.h
@@ -42,6 +42,8 @@ bool is_parallel_request); private: + class RequestHandle; + void Start(); // URLRequest::Delegate: @@ -62,10 +64,9 @@ override; void OnReadyToRead() override; - // UrlDownloadHandler implementations. - void PauseRequest() override; - void ResumeRequest() override; - void CancelRequest() override; + void PauseRequest(); + void ResumeRequest(); + void CancelRequest(); // Called when the UrlDownloader is done with the request. Posts a task to // remove itself from its download manager, which in turn would cause the
diff --git a/content/browser/gpu/browser_gpu_channel_host_factory.cc b/content/browser/gpu/browser_gpu_channel_host_factory.cc index ca32413b..4b14d65a 100644 --- a/content/browser/gpu/browser_gpu_channel_host_factory.cc +++ b/content/browser/gpu/browser_gpu_channel_host_factory.cc
@@ -28,6 +28,8 @@ #include "content/public/common/content_client.h" #include "content/public/common/content_switches.h" #include "gpu/command_buffer/service/gpu_switches.h" +#include "gpu/config/gpu_finch_features.h" +#include "gpu/ipc/common/gpu_client_ids.h" #include "gpu/ipc/in_process_command_buffer.h" #include "services/resource_coordinator/public/mojom/memory_instrumentation/constants.mojom.h" #include "services/service_manager/runner/common/client_util.h" @@ -270,6 +272,19 @@ &BrowserGpuChannelHostFactory::InitializeShaderDiskCacheOnIO, gpu_client_id_, cache_dir)); } + + if (base::FeatureList::IsEnabled( + features::kDefaultEnableOopRasterization)) { + base::FilePath gr_cache_dir = + GetContentClient()->browser()->GetGrShaderDiskCacheDirectory(); + if (!gr_cache_dir.empty()) { + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + base::BindOnce( + &BrowserGpuChannelHostFactory::InitializeGrShaderDiskCacheOnIO, + gr_cache_dir)); + } + } } } @@ -386,8 +401,15 @@ GetShaderCacheFactorySingleton()->SetCacheInfo(gpu_client_id, cache_dir); if (base::FeatureList::IsEnabled(features::kVizDisplayCompositor)) { GetShaderCacheFactorySingleton()->SetCacheInfo( - gpu::InProcessCommandBuffer::kGpuClientId, cache_dir); + gpu::kInProcessCommandBufferClientId, cache_dir); } } +// static +void BrowserGpuChannelHostFactory::InitializeGrShaderDiskCacheOnIO( + const base::FilePath& cache_dir) { + GetShaderCacheFactorySingleton()->SetCacheInfo(gpu::kGrShaderCacheClientId, + cache_dir); +} + } // namespace content
diff --git a/content/browser/gpu/browser_gpu_channel_host_factory.h b/content/browser/gpu/browser_gpu_channel_host_factory.h index 12aa00c..0ef66fb 100644 --- a/content/browser/gpu/browser_gpu_channel_host_factory.h +++ b/content/browser/gpu/browser_gpu_channel_host_factory.h
@@ -57,6 +57,7 @@ static void InitializeShaderDiskCacheOnIO(int gpu_client_id, const base::FilePath& cache_dir); + static void InitializeGrShaderDiskCacheOnIO(const base::FilePath& cache_dir); const int gpu_client_id_; const uint64_t gpu_client_tracing_id_;
diff --git a/content/browser/gpu/gpu_process_host.cc b/content/browser/gpu/gpu_process_host.cc index 8f0c3ee..9771a22 100644 --- a/content/browser/gpu/gpu_process_host.cc +++ b/content/browser/gpu/gpu_process_host.cc
@@ -63,7 +63,9 @@ #include "gpu/command_buffer/service/gpu_switches.h" #include "gpu/config/gpu_driver_bug_list.h" #include "gpu/config/gpu_driver_bug_workaround_type.h" +#include "gpu/config/gpu_finch_features.h" #include "gpu/config/gpu_preferences.h" +#include "gpu/ipc/common/gpu_client_ids.h" #include "gpu/ipc/host/shader_disk_cache.h" #include "gpu/ipc/in_process_command_buffer.h" #include "media/base/media_switches.h" @@ -994,10 +996,9 @@ return; } - bool oopd_enabled = - base::FeatureList::IsEnabled(features::kVizDisplayCompositor); - if (oopd_enabled && client_id == gpu::InProcessCommandBuffer::kGpuClientId) { - // The display-compositor in the gpu process uses this special client id. + if (gpu::IsReservedClientId(client_id)) { + // The display-compositor/GrShaderCache in the gpu process uses these + // special client ids. callback.Run(mojo::ScopedMessagePipeHandle(), gpu::GPUInfo(), gpu::GpuFeatureInfo(), EstablishChannelStatus::GPU_ACCESS_DENIED); @@ -1007,18 +1008,28 @@ DCHECK_EQ(preempts, allow_view_command_buffers); DCHECK_EQ(preempts, allow_real_time_streams); bool is_gpu_host = preempts; + bool cache_shaders_on_disk = + GetShaderCacheFactorySingleton()->Get(client_id) != nullptr; channel_requests_.push(callback); gpu_service_ptr_->EstablishGpuChannel( - client_id, client_tracing_id, is_gpu_host, + client_id, client_tracing_id, is_gpu_host, cache_shaders_on_disk, base::BindOnce(&GpuProcessHost::OnChannelEstablished, weak_ptr_factory_.GetWeakPtr(), client_id, callback)); if (!base::CommandLine::ForCurrentProcess()->HasSwitch( switches::kDisableGpuShaderDiskCache)) { CreateChannelCache(client_id); + + bool oopd_enabled = + base::FeatureList::IsEnabled(features::kVizDisplayCompositor); if (oopd_enabled) - CreateChannelCache(gpu::InProcessCommandBuffer::kGpuClientId); + CreateChannelCache(gpu::kInProcessCommandBufferClientId); + + bool oopr_enabled = + base::FeatureList::IsEnabled(features::kDefaultEnableOopRasterization); + if (oopr_enabled) + CreateChannelCache(gpu::kGrShaderCacheClientId); } } @@ -1588,7 +1599,8 @@ return shader_prefix_key_; } -void GpuProcessHost::LoadedShader(const std::string& key, +void GpuProcessHost::LoadedShader(int32_t client_id, + const std::string& key, const std::string& data) { std::string prefix = GetShaderPrefixKey(); bool prefix_ok = !key.compare(0, prefix.length(), prefix); @@ -1596,7 +1608,7 @@ if (prefix_ok) { // Remove the prefix from the key before load. std::string key_no_prefix = key.substr(prefix.length() + 1); - gpu_service_ptr_->LoadedShader(key_no_prefix, data); + gpu_service_ptr_->LoadedShader(client_id, key_no_prefix, data); } } @@ -1614,7 +1626,8 @@ return; cache->set_shader_loaded_callback(base::Bind(&GpuProcessHost::LoadedShader, - weak_ptr_factory_.GetWeakPtr())); + weak_ptr_factory_.GetWeakPtr(), + client_id)); client_id_to_shader_cache_[client_id] = cache; }
diff --git a/content/browser/gpu/gpu_process_host.h b/content/browser/gpu/gpu_process_host.h index 3ca9cf5..5b3188180 100644 --- a/content/browser/gpu/gpu_process_host.h +++ b/content/browser/gpu/gpu_process_host.h
@@ -172,7 +172,9 @@ // Forcefully terminates the GPU process. void ForceShutdown(); - void LoadedShader(const std::string& key, const std::string& data); + void LoadedShader(int32_t client_id, + const std::string& key, + const std::string& data); CONTENT_EXPORT viz::mojom::GpuService* gpu_service();
diff --git a/content/browser/loader/resource_dispatcher_host_impl.cc b/content/browser/loader/resource_dispatcher_host_impl.cc index 73c656ee..061ad18 100644 --- a/content/browser/loader/resource_dispatcher_host_impl.cc +++ b/content/browser/loader/resource_dispatcher_host_impl.cc
@@ -54,7 +54,6 @@ #include "content/browser/loader/stream_resource_handler.h" #include "content/browser/loader/throttling_resource_handler.h" #include "content/browser/loader/upload_data_stream_builder.h" -#include "content/browser/loader/wake_lock_resource_throttle.h" #include "content/browser/resource_context_impl.h" #include "content/browser/service_worker/service_worker_context_wrapper.h" #include "content/browser/service_worker/service_worker_navigation_handle_core.h" @@ -1144,12 +1143,6 @@ &throttles); } - if (request->has_upload()) { - // Request wake lock while uploading data. - throttles.push_back( - std::make_unique<WakeLockResourceThrottle>(request->url().host())); - } - // The Clear-Site-Data throttle. std::unique_ptr<ResourceThrottle> clear_site_data_throttle = ClearSiteDataThrottle::MaybeCreateThrottleForRequest(request);
diff --git a/content/browser/loader/wake_lock_resource_throttle.cc b/content/browser/loader/wake_lock_resource_throttle.cc deleted file mode 100644 index 9ba1bd0d..0000000 --- a/content/browser/loader/wake_lock_resource_throttle.cc +++ /dev/null
@@ -1,70 +0,0 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "content/browser/loader/wake_lock_resource_throttle.h" - -#include "content/browser/service_manager/service_manager_context.h" -#include "content/public/browser/browser_thread.h" -#include "mojo/public/cpp/bindings/interface_request.h" -#include "services/device/public/mojom/constants.mojom.h" -#include "services/device/public/mojom/wake_lock_provider.mojom.h" -#include "services/service_manager/public/cpp/connector.h" - -namespace content { - -namespace { - -const int kWakeLockDelaySeconds = 30; - -} // namespace - -WakeLockResourceThrottle::WakeLockResourceThrottle(const std::string& host) - : host_(host) {} - -WakeLockResourceThrottle::~WakeLockResourceThrottle() {} - -void WakeLockResourceThrottle::WillStartRequest(bool* defer) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - - // Delay wake lock request to dismiss small requests. - timer_.Start(FROM_HERE, base::TimeDelta::FromSeconds(kWakeLockDelaySeconds), - this, &WakeLockResourceThrottle::RequestWakeLock); -} - -void WakeLockResourceThrottle::WillProcessResponse(bool* defer) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - - // Cancel wake lock after request finishes. - if (wake_lock_) - wake_lock_->CancelWakeLock(); - - timer_.Stop(); -} - -const char* WakeLockResourceThrottle::GetNameForLogging() const { - return "WakeLockResourceThrottle"; -} - -void WakeLockResourceThrottle::RequestWakeLock() { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - DCHECK(!wake_lock_); - - service_manager::Connector* connector = - ServiceManagerContext::GetConnectorForIOThread(); - // |connector| might be nullptr in some testing contexts, in which the - // service manager connection isn't initialized. - if (connector) { - device::mojom::WakeLockProviderPtr wake_lock_provider; - connector->BindInterface(device::mojom::kServiceName, - mojo::MakeRequest(&wake_lock_provider)); - wake_lock_provider->GetWakeLockWithoutContext( - device::mojom::WakeLockType::kPreventAppSuspension, - device::mojom::WakeLockReason::kOther, "Uploading data to " + host_, - mojo::MakeRequest(&wake_lock_)); - - wake_lock_->RequestWakeLock(); - } -} - -} // namespace content
diff --git a/content/browser/loader/wake_lock_resource_throttle.h b/content/browser/loader/wake_lock_resource_throttle.h deleted file mode 100644 index 986b566..0000000 --- a/content/browser/loader/wake_lock_resource_throttle.h +++ /dev/null
@@ -1,46 +0,0 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CONTENT_BROWSER_LOADER_WAKE_LOCK_RESOURCE_THROTTLE_H_ -#define CONTENT_BROWSER_LOADER_WAKE_LOCK_RESOURCE_THROTTLE_H_ - -#include <memory> -#include <string> - -#include "base/compiler_specific.h" -#include "base/macros.h" -#include "base/timer/timer.h" -#include "content/public/browser/resource_throttle.h" -#include "services/device/public/mojom/wake_lock.mojom.h" - -namespace content { - -// This ResourceThrottle holds wake lock until large upload request finishes. -class WakeLockResourceThrottle : public ResourceThrottle { - public: - WakeLockResourceThrottle(const std::string& host); - ~WakeLockResourceThrottle() override; - - // ResourceThrottle overrides: - void WillStartRequest(bool* defer) override; - void WillProcessResponse(bool* defer) override; - const char* GetNameForLogging() const override; - - private: - void RequestWakeLock(); - - const std::string host_; - base::OneShotTimer timer_; - - // Destruction of wake_lock_ will trigger - // WakeLock::OnConnectionError on the service side, so there is no - // need to call CancelWakeLock() in the destructor. - device::mojom::WakeLockPtr wake_lock_; - - DISALLOW_COPY_AND_ASSIGN(WakeLockResourceThrottle); -}; - -} // namespace content - -#endif // CONTENT_BROWSER_LOADER_WAKE_LOCK_RESOURCE_THROTTLE_H_
diff --git a/content/public/browser/content_browser_client.cc b/content/public/browser/content_browser_client.cc index 7f2f608..a37e39d 100644 --- a/content/public/browser/content_browser_client.cc +++ b/content/public/browser/content_browser_client.cc
@@ -463,6 +463,10 @@ return base::FilePath(); } +base::FilePath ContentBrowserClient::GetGrShaderDiskCacheDirectory() { + return base::FilePath(); +} + BrowserPpapiHost* ContentBrowserClient::GetExternalBrowserPpapiHost(int plugin_process_id) { return nullptr;
diff --git a/content/public/browser/content_browser_client.h b/content/public/browser/content_browser_client.h index fd5d62fe..2cd82bf 100644 --- a/content/public/browser/content_browser_client.h +++ b/content/public/browser/content_browser_client.h
@@ -743,6 +743,10 @@ // Returns the path to the browser shader disk cache root. virtual base::FilePath GetShaderDiskCacheDirectory(); + // Returns the path to the shader disk cache root for shaders generated by + // skia. + virtual base::FilePath GetGrShaderDiskCacheDirectory(); + // Notification that a pepper plugin has just been spawned. This allows the // embedder to add filters onto the host to implement interfaces. // This is called on the IO thread.
diff --git a/content/public/common/content_features.cc b/content/public/common/content_features.cc index de59bd1..0a5fdbc 100644 --- a/content/public/common/content_features.cc +++ b/content/public/common/content_features.cc
@@ -646,9 +646,15 @@ // The V2 sandbox on MacOS removes the unsandboed warmup phase and sandboxes the // entire life of the process. const base::Feature kMacV2Sandbox{"MacV2Sandbox", - base::FEATURE_DISABLED_BY_DEFAULT}; + base::FEATURE_ENABLED_BY_DEFAULT}; #endif // defined(OS_MACOSX) +#if defined(OS_ANDROID) || defined(OS_CHROMEOS) +// Always set the UI thread's priority to DISPLAY. +const base::Feature kOverrideUIThreadPriority{ + "OverrideUIThreadPriority", base::FEATURE_DISABLED_BY_DEFAULT}; +#endif // defined(OS_ANDROID) || defined(OS_CHROMEOS) + bool IsVideoCaptureServiceEnabledForOutOfProcess() { #if defined(OS_ANDROID) return false;
diff --git a/content/public/common/content_features.h b/content/public/common/content_features.h index 249b7b21..6fed8c3e 100644 --- a/content/public/common/content_features.h +++ b/content/public/common/content_features.h
@@ -153,6 +153,10 @@ CONTENT_EXPORT extern const base::Feature kWebUIPolymer2; #endif // !defined(OS_ANDROID) +#if defined(OS_ANDROID) || defined(OS_CHROMEOS) +CONTENT_EXPORT extern const base::Feature kOverrideUIThreadPriority; +#endif // defined(OS_ANDROID) || defined(OS_CHROMEOS) + #if defined(OS_MACOSX) CONTENT_EXPORT extern const base::Feature kDeviceMonitorMac; CONTENT_EXPORT extern const base::Feature kIOSurfaceCapturer;
diff --git a/content/renderer/media/webrtc/rtc_peer_connection_handler.cc b/content/renderer/media/webrtc/rtc_peer_connection_handler.cc index 9a81f81..1223b87f 100644 --- a/content/renderer/media/webrtc/rtc_peer_connection_handler.cc +++ b/content/renderer/media/webrtc/rtc_peer_connection_handler.cc
@@ -1247,7 +1247,8 @@ scoped_refptr<webrtc::SetSessionDescriptionObserver> webrtc_observer( WebRtcSetLocalDescriptionObserverHandler::Create( task_runner_, signaling_thread(), native_peer_connection_, - track_adapter_map_, content_observer) + track_adapter_map_, content_observer, + true /* surface_receivers_only*/) .get()); signaling_thread()->PostTask( @@ -1314,7 +1315,7 @@ webrtc_observer(WebRtcSetRemoteDescriptionObserverHandler::Create( task_runner_, signaling_thread(), native_peer_connection_, track_adapter_map_, - content_observer) + content_observer, true /* surface_receivers_only*/) .get()); signaling_thread()->PostTask(
diff --git a/content/renderer/media/webrtc/rtc_rtp_receiver.cc b/content/renderer/media/webrtc/rtc_rtp_receiver.cc index 2271721..aecfc98 100644 --- a/content/renderer/media/webrtc/rtc_rtp_receiver.cc +++ b/content/renderer/media/webrtc/rtc_rtp_receiver.cc
@@ -301,14 +301,21 @@ return webrtc::RtpTransceiverDirection::kSendOnly; } +void RTCRtpReceiverOnlyTransceiver::SetDirection( + webrtc::RtpTransceiverDirection direction) { + NOTREACHED(); +} + base::Optional<webrtc::RtpTransceiverDirection> RTCRtpReceiverOnlyTransceiver::CurrentDirection() const { NOTREACHED(); return webrtc::RtpTransceiverDirection::kSendOnly; } -void RTCRtpReceiverOnlyTransceiver::Stop() { +base::Optional<webrtc::RtpTransceiverDirection> +RTCRtpReceiverOnlyTransceiver::FiredDirection() const { NOTREACHED(); + return webrtc::RtpTransceiverDirection::kSendOnly; } } // namespace content
diff --git a/content/renderer/media/webrtc/rtc_rtp_receiver.h b/content/renderer/media/webrtc/rtc_rtp_receiver.h index 107933a6..ef9b92c 100644 --- a/content/renderer/media/webrtc/rtc_rtp_receiver.h +++ b/content/renderer/media/webrtc/rtc_rtp_receiver.h
@@ -142,9 +142,11 @@ std::unique_ptr<blink::WebRTCRtpReceiver> Receiver() const override; bool Stopped() const override; webrtc::RtpTransceiverDirection Direction() const override; + void SetDirection(webrtc::RtpTransceiverDirection direction) override; base::Optional<webrtc::RtpTransceiverDirection> CurrentDirection() const override; - void Stop() override; + base::Optional<webrtc::RtpTransceiverDirection> FiredDirection() + const override; private: std::unique_ptr<RTCRtpReceiver> receiver_;
diff --git a/content/renderer/media/webrtc/rtc_rtp_sender.cc b/content/renderer/media/webrtc/rtc_rtp_sender.cc index 9355493..7057550 100644 --- a/content/renderer/media/webrtc/rtc_rtp_sender.cc +++ b/content/renderer/media/webrtc/rtc_rtp_sender.cc
@@ -475,14 +475,21 @@ return webrtc::RtpTransceiverDirection::kSendOnly; } +void RTCRtpSenderOnlyTransceiver::SetDirection( + webrtc::RtpTransceiverDirection direction) { + NOTREACHED(); +} + base::Optional<webrtc::RtpTransceiverDirection> RTCRtpSenderOnlyTransceiver::CurrentDirection() const { NOTREACHED(); return webrtc::RtpTransceiverDirection::kSendOnly; } -void RTCRtpSenderOnlyTransceiver::Stop() { +base::Optional<webrtc::RtpTransceiverDirection> +RTCRtpSenderOnlyTransceiver::FiredDirection() const { NOTREACHED(); + return webrtc::RtpTransceiverDirection::kSendOnly; } } // namespace content
diff --git a/content/renderer/media/webrtc/rtc_rtp_sender.h b/content/renderer/media/webrtc/rtc_rtp_sender.h index 1fb9171..8ba0c4f 100644 --- a/content/renderer/media/webrtc/rtc_rtp_sender.h +++ b/content/renderer/media/webrtc/rtc_rtp_sender.h
@@ -168,9 +168,11 @@ std::unique_ptr<blink::WebRTCRtpReceiver> Receiver() const override; bool Stopped() const override; webrtc::RtpTransceiverDirection Direction() const override; + void SetDirection(webrtc::RtpTransceiverDirection direction) override; base::Optional<webrtc::RtpTransceiverDirection> CurrentDirection() const override; - void Stop() override; + base::Optional<webrtc::RtpTransceiverDirection> FiredDirection() + const override; private: std::unique_ptr<RTCRtpSender> sender_;
diff --git a/content/renderer/media/webrtc/rtc_rtp_transceiver.cc b/content/renderer/media/webrtc/rtc_rtp_transceiver.cc index 30d3d48..7bdda93 100644 --- a/content/renderer/media/webrtc/rtc_rtp_transceiver.cc +++ b/content/renderer/media/webrtc/rtc_rtp_transceiver.cc
@@ -18,7 +18,8 @@ base::Optional<std::string> mid, bool stopped, webrtc::RtpTransceiverDirection direction, - base::Optional<webrtc::RtpTransceiverDirection> current_direction) + base::Optional<webrtc::RtpTransceiverDirection> current_direction, + base::Optional<webrtc::RtpTransceiverDirection> fired_direction) : main_task_runner_(std::move(main_task_runner)), signaling_task_runner_(std::move(signaling_task_runner)), webrtc_transceiver_(std::move(webrtc_transceiver)), @@ -28,7 +29,8 @@ mid_(std::move(mid)), stopped_(std::move(stopped)), direction_(std::move(direction)), - current_direction_(std::move(current_direction)) { + current_direction_(std::move(current_direction)), + fired_direction_(std::move(fired_direction)) { DCHECK(main_task_runner_); DCHECK(signaling_task_runner_); DCHECK(webrtc_transceiver_); @@ -44,7 +46,8 @@ mid_(std::move(other.mid_)), stopped_(std::move(other.stopped_)), direction_(std::move(other.direction_)), - current_direction_(std::move(other.current_direction_)) { + current_direction_(std::move(other.current_direction_)), + fired_direction_(std::move(other.fired_direction_)) { // Explicitly null |other|'s task runners for use in destructor. other.main_task_runner_ = nullptr; other.signaling_task_runner_ = nullptr; @@ -74,6 +77,7 @@ stopped_ = std::move(other.stopped_); direction_ = std::move(other.direction_); current_direction_ = std::move(other.current_direction_); + fired_direction_ = std::move(other.fired_direction_); return *this; } @@ -150,12 +154,24 @@ return direction_; } +void RtpTransceiverState::set_direction( + webrtc::RtpTransceiverDirection direction) { + DCHECK(main_task_runner_->BelongsToCurrentThread()); + direction_ = direction; +} + base::Optional<webrtc::RtpTransceiverDirection> RtpTransceiverState::current_direction() const { DCHECK(main_task_runner_->BelongsToCurrentThread()); return current_direction_; } +base::Optional<webrtc::RtpTransceiverDirection> +RtpTransceiverState::fired_direction() const { + DCHECK(main_task_runner_->BelongsToCurrentThread()); + return fired_direction_; +} + class RTCRtpTransceiver::RTCRtpTransceiverInternal : public base::RefCountedThreadSafe< RTCRtpTransceiver::RTCRtpTransceiverInternal, @@ -201,40 +217,14 @@ return receiver_.get(); } - blink::WebString Mid() const { + void SetDirection(webrtc::RtpTransceiverDirection direction) { DCHECK(main_task_runner_->BelongsToCurrentThread()); - return state_.mid() ? blink::WebString::FromUTF8(*state_.mid()) - : blink::WebString(); // IsNull() + // This implicitly performs a blocking invoke on the webrtc signaling thread + // due to use of PROXY references for |webrtc_transceiver_|. + webrtc_transceiver_->SetDirection(direction); + state_.set_direction(webrtc_transceiver_->direction()); } - std::unique_ptr<blink::WebRTCRtpSender> Sender() const { - DCHECK(main_task_runner_->BelongsToCurrentThread()); - return sender_->ShallowCopy(); - } - - std::unique_ptr<blink::WebRTCRtpReceiver> Receiver() const { - DCHECK(main_task_runner_->BelongsToCurrentThread()); - return receiver_->ShallowCopy(); - } - - bool Stopped() const { - DCHECK(main_task_runner_->BelongsToCurrentThread()); - return state_.stopped(); - } - - webrtc::RtpTransceiverDirection Direction() const { - DCHECK(main_task_runner_->BelongsToCurrentThread()); - return state_.direction(); - } - - base::Optional<webrtc::RtpTransceiverDirection> CurrentDirection() const { - DCHECK(main_task_runner_->BelongsToCurrentThread()); - return state_.current_direction(); - } - - // TODO(hbos): Implement. https://crbug.com/777617 - void Stop() { NOTREACHED() << "Not implemented."; } - private: friend struct RTCRtpTransceiver::RTCRtpTransceiverInternalTraits; @@ -328,32 +318,40 @@ } blink::WebString RTCRtpTransceiver::Mid() const { - return internal_->Mid(); + const auto& mid = internal_->state().mid(); + return mid ? blink::WebString::FromUTF8(*mid) + : blink::WebString(); // IsNull() } std::unique_ptr<blink::WebRTCRtpSender> RTCRtpTransceiver::Sender() const { - return internal_->Sender(); + return internal_->content_sender()->ShallowCopy(); } std::unique_ptr<blink::WebRTCRtpReceiver> RTCRtpTransceiver::Receiver() const { - return internal_->Receiver(); + return internal_->content_receiver()->ShallowCopy(); } bool RTCRtpTransceiver::Stopped() const { - return internal_->Stopped(); + return internal_->state().stopped(); } webrtc::RtpTransceiverDirection RTCRtpTransceiver::Direction() const { - return internal_->Direction(); + return internal_->state().direction(); +} + +void RTCRtpTransceiver::SetDirection( + webrtc::RtpTransceiverDirection direction) { + internal_->SetDirection(direction); } base::Optional<webrtc::RtpTransceiverDirection> RTCRtpTransceiver::CurrentDirection() const { - return internal_->CurrentDirection(); + return internal_->state().current_direction(); } -void RTCRtpTransceiver::Stop() { - internal_->Stop(); +base::Optional<webrtc::RtpTransceiverDirection> +RTCRtpTransceiver::FiredDirection() const { + return internal_->state().fired_direction(); } } // namespace content
diff --git a/content/renderer/media/webrtc/rtc_rtp_transceiver.h b/content/renderer/media/webrtc/rtc_rtp_transceiver.h index bb6ba52..5eadedf 100644 --- a/content/renderer/media/webrtc/rtc_rtp_transceiver.h +++ b/content/renderer/media/webrtc/rtc_rtp_transceiver.h
@@ -63,7 +63,8 @@ base::Optional<std::string> mid, bool stopped, webrtc::RtpTransceiverDirection direction, - base::Optional<webrtc::RtpTransceiverDirection> current_direction); + base::Optional<webrtc::RtpTransceiverDirection> current_direction, + base::Optional<webrtc::RtpTransceiverDirection> fired_direction); RtpTransceiverState(RtpTransceiverState&&); RtpTransceiverState(const RtpTransceiverState&) = delete; ~RtpTransceiverState(); @@ -87,7 +88,9 @@ base::Optional<std::string> mid() const; bool stopped() const; webrtc::RtpTransceiverDirection direction() const; + void set_direction(webrtc::RtpTransceiverDirection); base::Optional<webrtc::RtpTransceiverDirection> current_direction() const; + base::Optional<webrtc::RtpTransceiverDirection> fired_direction() const; private: scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_; @@ -100,6 +103,7 @@ bool stopped_; webrtc::RtpTransceiverDirection direction_; base::Optional<webrtc::RtpTransceiverDirection> current_direction_; + base::Optional<webrtc::RtpTransceiverDirection> fired_direction_; }; // Used to surface |webrtc::RtpTransceiverInterface| to blink. Multiple @@ -138,9 +142,11 @@ std::unique_ptr<blink::WebRTCRtpReceiver> Receiver() const override; bool Stopped() const override; webrtc::RtpTransceiverDirection Direction() const override; + void SetDirection(webrtc::RtpTransceiverDirection direction) override; base::Optional<webrtc::RtpTransceiverDirection> CurrentDirection() const override; - void Stop() override; + base::Optional<webrtc::RtpTransceiverDirection> FiredDirection() + const override; private: class RTCRtpTransceiverInternal;
diff --git a/content/renderer/media/webrtc/rtc_rtp_transceiver_unittest.cc b/content/renderer/media/webrtc/rtc_rtp_transceiver_unittest.cc index 571f286..3690936b 100644 --- a/content/renderer/media/webrtc/rtc_rtp_transceiver_unittest.cc +++ b/content/renderer/media/webrtc/rtc_rtp_transceiver_unittest.cc
@@ -143,7 +143,8 @@ std::move(receiver_stream_ids)), ToBaseOptional(webrtc_transceiver->mid()), webrtc_transceiver->stopped(), webrtc_transceiver->direction(), - ToBaseOptional(webrtc_transceiver->current_direction())); + ToBaseOptional(webrtc_transceiver->current_direction()), + ToBaseOptional(webrtc_transceiver->fired_direction())); } protected: @@ -227,6 +228,8 @@ EXPECT_TRUE(transceiver_state.direction() == webrtc_transceiver->direction()); EXPECT_TRUE(OptionalEquals(transceiver_state.current_direction(), webrtc_transceiver->current_direction())); + EXPECT_TRUE(OptionalEquals(transceiver_state.fired_direction(), + webrtc_transceiver->fired_direction())); } TEST_F(RTCRtpTransceiverTest, CreateTranceiver) { @@ -253,6 +256,7 @@ EXPECT_EQ(transceiver.Direction(), webrtc::RtpTransceiverDirection::kSendRecv); EXPECT_FALSE(transceiver.CurrentDirection()); + EXPECT_FALSE(transceiver.FiredDirection()); } TEST_F(RTCRtpTransceiverTest, ModifyTransceiver) { @@ -296,6 +300,7 @@ EXPECT_EQ(transceiver.Direction(), webrtc::RtpTransceiverDirection::kSendRecv); EXPECT_FALSE(transceiver.CurrentDirection()); + EXPECT_FALSE(transceiver.FiredDirection()); // Setting the state should make the transceiver state up-to-date. transceiver.set_state(std::move(modified_transceiver_state)); @@ -307,6 +312,7 @@ webrtc::RtpTransceiverDirection::kInactive); EXPECT_TRUE(transceiver.CurrentDirection() == webrtc::RtpTransceiverDirection::kSendRecv); + EXPECT_FALSE(transceiver.FiredDirection()); } TEST_F(RTCRtpTransceiverTest, ShallowCopy) {
diff --git a/content/renderer/media/webrtc/transceiver_state_surfacer.cc b/content/renderer/media/webrtc/transceiver_state_surfacer.cc index f9603767..4792cef 100644 --- a/content/renderer/media/webrtc/transceiver_state_surfacer.cc +++ b/content/renderer/media/webrtc/transceiver_state_surfacer.cc
@@ -101,7 +101,8 @@ std::move(sender_state), std::move(receiver_state), ToBaseOptional(webrtc_transceiver->mid()), webrtc_transceiver->stopped(), webrtc_transceiver->direction(), - ToBaseOptional(webrtc_transceiver->current_direction()))); + ToBaseOptional(webrtc_transceiver->current_direction()), + ToBaseOptional(webrtc_transceiver->fired_direction()))); } is_initialized_ = true; }
diff --git a/content/renderer/media/webrtc/webrtc_set_description_observer.cc b/content/renderer/media/webrtc/webrtc_set_description_observer.cc index 0f6c04f6..f36ef07 100644 --- a/content/renderer/media/webrtc/webrtc_set_description_observer.cc +++ b/content/renderer/media/webrtc/webrtc_set_description_observer.cc
@@ -35,12 +35,14 @@ scoped_refptr<base::SingleThreadTaskRunner> signaling_task_runner, scoped_refptr<webrtc::PeerConnectionInterface> pc, scoped_refptr<WebRtcMediaStreamTrackAdapterMap> track_adapter_map, - scoped_refptr<WebRtcSetDescriptionObserver> observer) + scoped_refptr<WebRtcSetDescriptionObserver> observer, + bool surface_receivers_only) : main_task_runner_(std::move(main_task_runner)), signaling_task_runner_(std::move(signaling_task_runner)), pc_(std::move(pc)), track_adapter_map_(std::move(track_adapter_map)), - observer_(std::move(observer)) {} + observer_(std::move(observer)), + surface_receivers_only_(surface_receivers_only) {} WebRtcSetDescriptionObserverHandlerImpl:: ~WebRtcSetDescriptionObserverHandlerImpl() = default; @@ -50,14 +52,18 @@ CHECK(signaling_task_runner_->BelongsToCurrentThread()); std::vector<rtc::scoped_refptr<webrtc::RtpTransceiverInterface>> receiver_only_transceivers; - for (const auto& receiver : pc_->GetReceivers()) { - receiver_only_transceivers.push_back( - new SurfaceReceiverStateOnly(receiver)); + std::vector<rtc::scoped_refptr<webrtc::RtpTransceiverInterface>> transceivers; + if (surface_receivers_only_) { + for (const auto& receiver : pc_->GetReceivers()) { + transceivers.push_back(new SurfaceReceiverStateOnly(receiver)); + } + } else { + transceivers = pc_->GetTransceivers(); } TransceiverStateSurfacer transceiver_state_surfacer(main_task_runner_, signaling_task_runner_); transceiver_state_surfacer.Initialize(track_adapter_map_, - std::move(receiver_only_transceivers)); + std::move(transceivers)); main_task_runner_->PostTask( FROM_HERE, base::BindOnce(&WebRtcSetDescriptionObserverHandlerImpl:: OnSetDescriptionCompleteOnMainThread, @@ -83,10 +89,12 @@ scoped_refptr<base::SingleThreadTaskRunner> signaling_task_runner, scoped_refptr<webrtc::PeerConnectionInterface> pc, scoped_refptr<WebRtcMediaStreamTrackAdapterMap> track_adapter_map, - scoped_refptr<WebRtcSetDescriptionObserver> observer) { + scoped_refptr<WebRtcSetDescriptionObserver> observer, + bool surface_receivers_only) { return new rtc::RefCountedObject<WebRtcSetLocalDescriptionObserverHandler>( std::move(main_task_runner), std::move(signaling_task_runner), - std::move(pc), std::move(track_adapter_map), std::move(observer)); + std::move(pc), std::move(track_adapter_map), std::move(observer), + surface_receivers_only); } WebRtcSetLocalDescriptionObserverHandler:: @@ -95,13 +103,15 @@ scoped_refptr<base::SingleThreadTaskRunner> signaling_task_runner, scoped_refptr<webrtc::PeerConnectionInterface> pc, scoped_refptr<WebRtcMediaStreamTrackAdapterMap> track_adapter_map, - scoped_refptr<WebRtcSetDescriptionObserver> observer) + scoped_refptr<WebRtcSetDescriptionObserver> observer, + bool surface_receivers_only) : handler_impl_(new WebRtcSetDescriptionObserverHandlerImpl( std::move(main_task_runner), std::move(signaling_task_runner), std::move(pc), std::move(track_adapter_map), - std::move(observer))) {} + std::move(observer), + surface_receivers_only)) {} WebRtcSetLocalDescriptionObserverHandler:: ~WebRtcSetLocalDescriptionObserverHandler() = default; @@ -121,10 +131,12 @@ scoped_refptr<base::SingleThreadTaskRunner> signaling_task_runner, scoped_refptr<webrtc::PeerConnectionInterface> pc, scoped_refptr<WebRtcMediaStreamTrackAdapterMap> track_adapter_map, - scoped_refptr<WebRtcSetDescriptionObserver> observer) { + scoped_refptr<WebRtcSetDescriptionObserver> observer, + bool surface_receivers_only) { return new rtc::RefCountedObject<WebRtcSetRemoteDescriptionObserverHandler>( std::move(main_task_runner), std::move(signaling_task_runner), - std::move(pc), std::move(track_adapter_map), std::move(observer)); + std::move(pc), std::move(track_adapter_map), std::move(observer), + surface_receivers_only); } WebRtcSetRemoteDescriptionObserverHandler:: @@ -133,13 +145,15 @@ scoped_refptr<base::SingleThreadTaskRunner> signaling_task_runner, scoped_refptr<webrtc::PeerConnectionInterface> pc, scoped_refptr<WebRtcMediaStreamTrackAdapterMap> track_adapter_map, - scoped_refptr<WebRtcSetDescriptionObserver> observer) + scoped_refptr<WebRtcSetDescriptionObserver> observer, + bool surface_receivers_only) : handler_impl_(new WebRtcSetDescriptionObserverHandlerImpl( std::move(main_task_runner), std::move(signaling_task_runner), std::move(pc), std::move(track_adapter_map), - std::move(observer))) {} + std::move(observer), + surface_receivers_only)) {} WebRtcSetRemoteDescriptionObserverHandler:: ~WebRtcSetRemoteDescriptionObserverHandler() = default;
diff --git a/content/renderer/media/webrtc/webrtc_set_description_observer.h b/content/renderer/media/webrtc/webrtc_set_description_observer.h index 7828929..41e84950 100644 --- a/content/renderer/media/webrtc/webrtc_set_description_observer.h +++ b/content/renderer/media/webrtc/webrtc_set_description_observer.h
@@ -88,7 +88,8 @@ scoped_refptr<base::SingleThreadTaskRunner> signaling_task_runner, scoped_refptr<webrtc::PeerConnectionInterface> pc, scoped_refptr<WebRtcMediaStreamTrackAdapterMap> track_adapter_map, - scoped_refptr<WebRtcSetDescriptionObserver> observer); + scoped_refptr<WebRtcSetDescriptionObserver> observer, + bool surface_receivers_only); // Must be called on the webrtc signaling thread internally by the handler // when the Set[Local/Remote]Description() operation finishes. @@ -109,6 +110,7 @@ scoped_refptr<webrtc::PeerConnectionInterface> pc_; scoped_refptr<WebRtcMediaStreamTrackAdapterMap> track_adapter_map_; scoped_refptr<WebRtcSetDescriptionObserver> observer_; + bool surface_receivers_only_; DISALLOW_COPY_AND_ASSIGN(WebRtcSetDescriptionObserverHandlerImpl); }; @@ -123,7 +125,8 @@ scoped_refptr<base::SingleThreadTaskRunner> signaling_task_runner, scoped_refptr<webrtc::PeerConnectionInterface> pc, scoped_refptr<WebRtcMediaStreamTrackAdapterMap> track_adapter_map, - scoped_refptr<WebRtcSetDescriptionObserver> observer); + scoped_refptr<WebRtcSetDescriptionObserver> observer, + bool surface_receivers_only); // webrtc::SetSessionDescriptionObserver implementation. Implementation calls // WebRtcSetDescriptionObserverHandlerImpl::OnSetDescriptionComplete(). @@ -136,7 +139,8 @@ scoped_refptr<base::SingleThreadTaskRunner> signaling_task_runner, scoped_refptr<webrtc::PeerConnectionInterface> pc, scoped_refptr<WebRtcMediaStreamTrackAdapterMap> track_adapter_map, - scoped_refptr<WebRtcSetDescriptionObserver> observer); + scoped_refptr<WebRtcSetDescriptionObserver> observer, + bool surface_receivers_only); ~WebRtcSetLocalDescriptionObserverHandler() override; scoped_refptr<WebRtcSetDescriptionObserverHandlerImpl> handler_impl_; @@ -154,7 +158,8 @@ scoped_refptr<base::SingleThreadTaskRunner> signaling_task_runner, scoped_refptr<webrtc::PeerConnectionInterface> pc, scoped_refptr<WebRtcMediaStreamTrackAdapterMap> track_adapter_map, - scoped_refptr<WebRtcSetDescriptionObserver> observer); + scoped_refptr<WebRtcSetDescriptionObserver> observer, + bool surface_receivers_only); // webrtc::SetRemoteDescriptionObserverInterface implementation. // Implementation calls @@ -167,7 +172,8 @@ scoped_refptr<base::SingleThreadTaskRunner> signaling_task_runner, scoped_refptr<webrtc::PeerConnectionInterface> pc, scoped_refptr<WebRtcMediaStreamTrackAdapterMap> track_adapter_map, - scoped_refptr<WebRtcSetDescriptionObserver> observer); + scoped_refptr<WebRtcSetDescriptionObserver> observer, + bool surface_receivers_only); ~WebRtcSetRemoteDescriptionObserverHandler() override; scoped_refptr<WebRtcSetDescriptionObserverHandlerImpl> handler_impl_;
diff --git a/content/renderer/media/webrtc/webrtc_set_description_observer_unittest.cc b/content/renderer/media/webrtc/webrtc_set_description_observer_unittest.cc index 34b2b25..c2f7dc78 100644 --- a/content/renderer/media/webrtc/webrtc_set_description_observer_unittest.cc +++ b/content/renderer/media/webrtc/webrtc_set_description_observer_unittest.cc
@@ -61,7 +61,8 @@ // TODO(hbos): This only tests WebRtcSetRemoteDescriptionObserverHandler, // parameterize the test to make it also test -// WebRtcSetLocalDescriptionObserverHandler. +// WebRtcSetLocalDescriptionObserverHandler and with "surface_receivers_only" as +// both true and false. https://crbug.com/865006 class WebRtcSetRemoteDescriptionObserverHandlerTest : public ::testing::Test { public: void SetUp() override { @@ -76,7 +77,7 @@ observer_ = new WebRtcSetDescriptionObserverForTest(); observer_handler_ = WebRtcSetRemoteDescriptionObserverHandler::Create( main_thread_, dependency_factory_->GetWebRtcSignalingThread(), pc_, map, - observer_); + observer_, true /* surface_receivers_only*/); } void TearDown() override { blink::WebHeap::CollectAllGarbageForTesting(); }
diff --git a/content/shell/test_runner/web_frame_test_proxy.h b/content/shell/test_runner/web_frame_test_proxy.h index 43df3b5..fe1f701 100644 --- a/content/shell/test_runner/web_frame_test_proxy.h +++ b/content/shell/test_runner/web_frame_test_proxy.h
@@ -52,10 +52,12 @@ // WebFrameTestProxy is used during LayoutTests and always instantiated, at time // of writing with Base=RenderFrameImpl. It does not directly inherit from it // for layering purposes. -template <class Base, typename P> +template <class Base> class WebFrameTestProxy : public Base, public WebFrameTestProxyBase { public: - explicit WebFrameTestProxy(P p) : Base(std::move(p)) {} + template <typename... Args> + explicit WebFrameTestProxy(Args&&... args) + : Base(std::forward<Args>(args)...) {} virtual ~WebFrameTestProxy() {}
diff --git a/content/shell/test_runner/web_view_test_proxy.h b/content/shell/test_runner/web_view_test_proxy.h index e77faf5..52aca28b 100644 --- a/content/shell/test_runner/web_view_test_proxy.h +++ b/content/shell/test_runner/web_view_test_proxy.h
@@ -212,11 +212,13 @@ // override RenderViewImpl's getter and call a getter from // WebViewTestProxyBase instead. In addition, WebViewTestProxyBase will have // a public setter that could be called from the TestRunner. -template <class Base, typename... Args> +template <class Base> class WebViewTestProxy : public Base, public WebViewTestProxyBase { public: - explicit WebViewTestProxy(Args... args) - : Base(args...), WebViewTestProxyBase(Base::WidgetClient()) {} + template <typename... Args> + explicit WebViewTestProxy(Args&&... args) + : Base(std::forward<Args>(args)...), + WebViewTestProxyBase(Base::WidgetClient()) {} // WebViewClient implementation. blink::WebView* CreateView(blink::WebLocalFrame* creator,
diff --git a/content/shell/test_runner/web_widget_test_proxy.h b/content/shell/test_runner/web_widget_test_proxy.h index 1675543..7976c5e 100644 --- a/content/shell/test_runner/web_widget_test_proxy.h +++ b/content/shell/test_runner/web_widget_test_proxy.h
@@ -89,10 +89,12 @@ // override RenderViewImpl's getter and call a getter from // WebWidgetTestProxyBase instead. In addition, WebWidgetTestProxyBase will // have a public setter that could be called from the TestRunner. -template <class Base, typename... Args> +template <class Base> class WebWidgetTestProxy : public Base, public WebWidgetTestProxyBase { public: - explicit WebWidgetTestProxy(Args... args) : Base(args...) {} + template <typename... Args> + explicit WebWidgetTestProxy(Args&&... args) + : Base(std::forward<Args>(args)...) {} // WebWidgetClient implementation. blink::WebLayerTreeView* InitializeLayerTreeView() override {
diff --git a/content/test/layouttest_support.cc b/content/test/layouttest_support.cc index 267ca99..4702a006 100644 --- a/content/test/layouttest_support.cc +++ b/content/test/layouttest_support.cc
@@ -92,24 +92,9 @@ base::LazyInstance<FrameProxyCreationCallback>::Leaky g_frame_test_proxy_callback = LAZY_INSTANCE_INITIALIZER; -using WebViewTestProxyType = - test_runner::WebViewTestProxy<RenderViewImpl, - CompositorDependencies*, - const mojom::CreateViewParams&, - scoped_refptr<base::SingleThreadTaskRunner>>; -using WebWidgetTestProxyType = test_runner::WebWidgetTestProxy< - RenderWidget, - int32_t, - CompositorDependencies*, - blink::WebPopupType, - const ScreenInfo&, - bool, - bool, - bool, - scoped_refptr<base::SingleThreadTaskRunner>>; -using WebFrameTestProxyType = - test_runner::WebFrameTestProxy<RenderFrameImpl, - RenderFrameImpl::CreateParams>; +using WebViewTestProxyType = test_runner::WebViewTestProxy<RenderViewImpl>; +using WebWidgetTestProxyType = test_runner::WebWidgetTestProxy<RenderWidget>; +using WebFrameTestProxyType = test_runner::WebFrameTestProxy<RenderFrameImpl>; RenderViewImpl* CreateWebViewTestProxy(CompositorDependencies* compositor_deps, const mojom::CreateViewParams& params) {
diff --git a/docs/mac_build_instructions.md b/docs/mac_build_instructions.md index 782319b5..f4dd826b 100644 --- a/docs/mac_build_instructions.md +++ b/docs/mac_build_instructions.md
@@ -193,6 +193,16 @@ command line, that is possible without building in Xcode (see [Debugging in Xcode](https://www.chromium.org/developers/how-tos/debugging-on-os-x/building-with-ninja-debugging-with-xcode)). +Tips for printing variables from `lldb` prompt (both in Xcode or in terminal): +* If `uptr` is a `std::unique_ptr`, the address it wraps is accessible as + `uptr.__ptr_.__value_`. +* To pretty-print `base::string16`, ensure you have a `~/.lldbinit` file and + add the following line into it (substitute {SRC} for your actual path to the + root of Chromium's sources): +``` +command script import {SRC}/tools/lldb/lldb_chrome.py +``` + ## Update your checkout To update an existing checkout, you can run
diff --git a/extensions/browser/api/messaging/message_service.cc b/extensions/browser/api/messaging/message_service.cc index 3d39acb..3e609a7 100644 --- a/extensions/browser/api/messaging/message_service.cc +++ b/extensions/browser/api/messaging/message_service.cc
@@ -56,6 +56,8 @@ namespace extensions { +namespace { + const char kReceivingEndDoesntExistError[] = "Could not establish connection. Receiving end does not exist."; #if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX) @@ -66,6 +68,30 @@ "administrator."; #endif +enum class IncludeTlsChannelIdBehavior { + // The TLS channel ID was not requested. + kNotRequested = 0, + + // The TLS channel ID was requested, but was not included because the target + // extension did not allow it. + kRequestedButDenied = 1, + + // The TLS channel ID was requested, but was not found. + kRequestedButNotFound = 2, + + // The TLS channel ID was requested, allowed, and included in the response. + kRequestedAndIncluded = 3, + + kMaxValue = kRequestedAndIncluded, +}; + +void RecordIncludeTlsChannelIdBehavior(IncludeTlsChannelIdBehavior behavior) { + UMA_HISTOGRAM_ENUMERATION("Extensions.Messaging.IncludeChannelIdBehavior", + behavior); +} + +} // namespace + struct MessageService::MessageChannel { std::unique_ptr<MessagePort> opener; std::unique_ptr<MessagePort> receiver; @@ -83,6 +109,7 @@ GURL source_url; std::string channel_name; bool include_tls_channel_id; + bool requested_tls_channel_id; std::string tls_channel_id; bool include_guest_process_info; @@ -98,6 +125,7 @@ const GURL& source_url, const std::string& channel_name, bool include_tls_channel_id, + bool requested_tls_channel_id, bool include_guest_process_info) : source_process_id(source_process_id), source_routing_id(source_routing_id), @@ -109,6 +137,7 @@ source_url(source_url), channel_name(channel_name), include_tls_channel_id(include_tls_channel_id), + requested_tls_channel_id(requested_tls_channel_id), include_guest_process_info(include_guest_process_info) { if (source_tab) this->source_tab = std::move(source_tab); @@ -172,6 +201,10 @@ DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK(source_port_id.is_opener); + // Record if the channel requested the channel id. We may not respect the + // request if the target extension is not externally connectable. + const bool requested_include_tls_channel_id = include_tls_channel_id; + content::RenderFrameHost* source = content::RenderFrameHost::FromID(source_process_id, source_routing_id); if (!source) @@ -267,7 +300,7 @@ source_process_id, source_routing_id, std::move(source_tab), source_frame_id, nullptr, receiver_port_id, source_extension_id, target_extension_id, source_url, channel_name, include_tls_channel_id, - include_guest_process_info)); + requested_include_tls_channel_id, include_guest_process_info)); pending_incognito_channels_[params->receiver_port_id.GetChannelId()] = PendingMessagesQueue(); @@ -454,7 +487,7 @@ -1, // If there is no tab, then there is no frame either. receiver.release(), receiver_port_id, extension_id, extension_id, GURL(), // Source URL doesn't make sense for opening to tabs. - channel_name, + channel_name, false, false, // Connections to tabs don't get TLS channel IDs. false)); // Connections to tabs aren't webview guests. OpenChannelImpl(receiver_contents->GetBrowserContext(), std::move(params), @@ -826,9 +859,21 @@ source_process->GetStoragePartition(), source_url, base::Bind(&MessageService::GotChannelID, weak_factory_.GetWeakPtr(), base::Passed(¶ms))); + // Flow continues in MessageService::GotChannelID(), which will also record + // tls channel ID behavior. return; } + { + // The connection is not including TLS channel ID information. Log the + // result. + const auto tls_channel_id_behavior = + params->requested_tls_channel_id + ? IncludeTlsChannelIdBehavior::kRequestedButDenied + : IncludeTlsChannelIdBehavior::kNotRequested; + RecordIncludeTlsChannelIdBehavior(tls_channel_id_behavior); + } + ExtensionRegistry* registry = ExtensionRegistry::Get(context); const Extension* target_extension = registry->enabled_extensions().GetByID(params->target_extension_id); @@ -852,6 +897,13 @@ void MessageService::GotChannelID(std::unique_ptr<OpenChannelParams> params, const std::string& tls_channel_id) { DCHECK_CURRENTLY_ON(BrowserThread::UI); + { + const auto tls_channel_id_behavior = + tls_channel_id.empty() + ? IncludeTlsChannelIdBehavior::kRequestedButNotFound + : IncludeTlsChannelIdBehavior::kRequestedAndIncluded; + RecordIncludeTlsChannelIdBehavior(tls_channel_id_behavior); + } params->tls_channel_id.assign(tls_channel_id); ChannelId channel_id = params->receiver_port_id.GetChannelId();
diff --git a/gpu/BUILD.gn b/gpu/BUILD.gn index 0e9fa82..c5ecd79f 100644 --- a/gpu/BUILD.gn +++ b/gpu/BUILD.gn
@@ -350,6 +350,7 @@ "command_buffer/service/gpu_service_test.h", "command_buffer/service/gpu_tracer_unittest.cc", "command_buffer/service/gr_cache_controller_unittest.cc", + "command_buffer/service/gr_shader_cache_unittest.cc", "command_buffer/service/id_manager_unittest.cc", "command_buffer/service/indexed_buffer_binding_host_unittest.cc", "command_buffer/service/mailbox_manager_unittest.cc",
diff --git a/gpu/command_buffer/client/gles2_implementation.cc b/gpu/command_buffer/client/gles2_implementation.cc index 09c5ef3..33cd9dc 100644 --- a/gpu/command_buffer/client/gles2_implementation.cc +++ b/gpu/command_buffer/client/gles2_implementation.cc
@@ -5969,6 +5969,9 @@ GPU_CLIENT_SINGLE_THREAD_CHECK(); GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glTraceBeginCHROMIUM(" << category_name << ", " << trace_name << ")"); + static constexpr size_t kMaxStrLen = 256; + DCHECK_LE(strlen(category_name), kMaxStrLen); + DCHECK_LE(strlen(trace_name), kMaxStrLen); SetBucketAsCString(kResultBucketId, category_name); SetBucketAsCString(kResultBucketId + 1, trace_name); helper_->TraceBeginCHROMIUM(kResultBucketId, kResultBucketId + 1);
diff --git a/gpu/command_buffer/client/raster_implementation.cc b/gpu/command_buffer/client/raster_implementation.cc index 74204e29..4bb8e20 100644 --- a/gpu/command_buffer/client/raster_implementation.cc +++ b/gpu/command_buffer/client/raster_implementation.cc
@@ -1238,6 +1238,9 @@ GPU_CLIENT_SINGLE_THREAD_CHECK(); GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glTraceBeginCHROMIUM(" << category_name << ", " << trace_name << ")"); + static constexpr size_t kMaxStrLen = 256; + DCHECK_LE(strlen(category_name), kMaxStrLen); + DCHECK_LE(strlen(trace_name), kMaxStrLen); SetBucketAsCString(kResultBucketId, category_name); SetBucketAsCString(kResultBucketId + 1, trace_name); helper_->TraceBeginCHROMIUM(kResultBucketId, kResultBucketId + 1);
diff --git a/gpu/command_buffer/service/BUILD.gn b/gpu/command_buffer/service/BUILD.gn index a8a16dcc..e445d70 100644 --- a/gpu/command_buffer/service/BUILD.gn +++ b/gpu/command_buffer/service/BUILD.gn
@@ -161,6 +161,8 @@ "gpu_tracer.h", "gr_cache_controller.cc", "gr_cache_controller.h", + "gr_shader_cache.cc", + "gr_shader_cache.h", "id_manager.cc", "id_manager.h", "indexed_buffer_binding_host.cc",
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.cc b/gpu/command_buffer/service/gles2_cmd_decoder.cc index 8b8d0d2..f10b0b2d 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder.cc +++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc
@@ -18255,8 +18255,10 @@ *static_cast<const volatile gles2::cmds::TraceBeginCHROMIUM*>(cmd_data); Bucket* category_bucket = GetBucket(c.category_bucket_id); Bucket* name_bucket = GetBucket(c.name_bucket_id); + static constexpr size_t kMaxStrLen = 256; if (!category_bucket || category_bucket->size() == 0 || - !name_bucket || name_bucket->size() == 0) { + category_bucket->size() > kMaxStrLen || !name_bucket || + name_bucket->size() == 0 || name_bucket->size() > kMaxStrLen) { return error::kInvalidArguments; }
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 cb7c052..01b355a 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_handlers.cc +++ b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_handlers.cc
@@ -1651,8 +1651,10 @@ Bucket* category_bucket = GetBucket(category_bucket_id); Bucket* name_bucket = GetBucket(name_bucket_id); - if (!category_bucket || category_bucket->size() == 0 || !name_bucket || - name_bucket->size() == 0) { + static constexpr size_t kMaxStrLen = 256; + if (!category_bucket || category_bucket->size() == 0 || + category_bucket->size() > kMaxStrLen || !name_bucket || + name_bucket->size() == 0 || name_bucket->size() > kMaxStrLen) { return error::kInvalidArguments; }
diff --git a/gpu/command_buffer/service/gr_cache_controller_unittest.cc b/gpu/command_buffer/service/gr_cache_controller_unittest.cc index eb69950..467ffe9 100644 --- a/gpu/command_buffer/service/gr_cache_controller_unittest.cc +++ b/gpu/command_buffer/service/gr_cache_controller_unittest.cc
@@ -36,7 +36,7 @@ context_state_ = new raster::RasterDecoderContextState( std::move(share_group), std::move(surface), std::move(context), false /* use_virtualized_gl_contexts */); - context_state_->InitializeGrContext(workarounds); + context_state_->InitializeGrContext(workarounds, nullptr); controller_ = std::make_unique<GrCacheController>(context_state_.get(), task_runner_);
diff --git a/gpu/command_buffer/service/gr_shader_cache.cc b/gpu/command_buffer/service/gr_shader_cache.cc new file mode 100644 index 0000000..9fcff56 --- /dev/null +++ b/gpu/command_buffer/service/gr_shader_cache.cc
@@ -0,0 +1,164 @@ +// Copyright (c) 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "gpu/command_buffer/service/gr_shader_cache.h" + +#include "base/trace_event/trace_event.h" + +namespace gpu { +namespace raster { +namespace { + +std::string MakeString(const SkData* data) { + return std::string(static_cast<const char*>(data->data()), data->size()); +} + +sk_sp<SkData> MakeData(const std::string& str) { + return SkData::MakeWithCopy(str.c_str(), str.length()); +} + +} // namespace + +GrShaderCache::GrShaderCache(size_t max_cache_size_bytes, Client* client) + : cache_size_limit_(max_cache_size_bytes), + store_(Store::NO_AUTO_EVICT), + client_(client) {} + +GrShaderCache::~GrShaderCache() = default; + +sk_sp<SkData> GrShaderCache::load(const SkData& key) { + TRACE_EVENT0("gpu", "GrShaderCache::load"); + DCHECK_NE(current_client_id_, kInvalidClientId); + + CacheKey cache_key(SkData::MakeWithoutCopy(key.data(), key.size())); + auto it = store_.Get(cache_key); + if (it == store_.end()) + return nullptr; + + WriteToDisk(it->first, &it->second); + return it->second.data; +} + +void GrShaderCache::store(const SkData& key, const SkData& data) { + TRACE_EVENT0("gpu", "GrShaderCache::store"); + DCHECK_NE(current_client_id_, kInvalidClientId); + + if (data.size() > cache_size_limit_) + return; + EnforceLimits(data.size()); + + CacheKey cache_key(SkData::MakeWithCopy(key.data(), key.size())); + CacheData cache_data(SkData::MakeWithCopy(data.data(), data.size())); + auto it = store_.Put(std::move(cache_key), std::move(cache_data)); + curr_size_bytes_ += it->second.data->size(); + + WriteToDisk(it->first, &it->second); +} + +void GrShaderCache::PopulateCache(const std::string& key, + const std::string& data) { + if (data.length() > cache_size_limit_) + return; + + EnforceLimits(data.size()); + + CacheKey cache_key(MakeData(key)); + CacheData cache_data(MakeData(data)); + auto it = store_.Put(std::move(cache_key), std::move(cache_data)); + curr_size_bytes_ += it->second.data->size(); + + // This was loaded off the disk cache, no need to push this back for disk + // write. + it->second.pending_disk_write = false; +} + +void GrShaderCache::CacheClientIdOnDisk(int32_t client_id) { + client_ids_to_cache_on_disk_.insert(client_id); +} + +void GrShaderCache::PurgeMemory( + base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level) { + size_t original_limit = cache_size_limit_; + + switch (memory_pressure_level) { + case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE: + // This function is only called with moderate or critical pressure. + NOTREACHED(); + return; + case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE: + cache_size_limit_ = cache_size_limit_ / 4; + break; + case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL: + cache_size_limit_ = 0; + break; + } + + EnforceLimits(0u); + cache_size_limit_ = original_limit; +} + +void GrShaderCache::WriteToDisk(const CacheKey& key, CacheData* data) { + DCHECK_NE(current_client_id_, kInvalidClientId); + + if (!data->pending_disk_write) + return; + + // Only cache the shader on disk if this client id is permitted. + if (client_ids_to_cache_on_disk_.count(current_client_id_) == 0) + return; + + data->pending_disk_write = false; + client_->StoreShader(MakeString(key.data.get()), + MakeString(data->data.get())); +} + +void GrShaderCache::EnforceLimits(size_t size_needed) { + DCHECK_LE(size_needed, cache_size_limit_); + + while (size_needed + curr_size_bytes_ > cache_size_limit_) { + auto it = store_.rbegin(); + DCHECK_GE(curr_size_bytes_, it->second.data->size()); + curr_size_bytes_ -= it->second.data->size(); + store_.Erase(it); + } +} + +GrShaderCache::ScopedCacheUse::ScopedCacheUse(GrShaderCache* cache, + int32_t client_id) + : cache_(cache) { + cache_->current_client_id_ = client_id; +} + +GrShaderCache::ScopedCacheUse::~ScopedCacheUse() { + cache_->current_client_id_ = kInvalidClientId; +} + +GrShaderCache::CacheKey::CacheKey(sk_sp<SkData> data) : data(std::move(data)) { + hash = base::Hash(this->data->data(), this->data->size()); +} +GrShaderCache::CacheKey::CacheKey(const CacheKey& other) = default; +GrShaderCache::CacheKey::CacheKey(CacheKey&& other) = default; +GrShaderCache::CacheKey& GrShaderCache::CacheKey::operator=( + const CacheKey& other) = default; +GrShaderCache::CacheKey& GrShaderCache::CacheKey::operator=(CacheKey&& other) = + default; +GrShaderCache::CacheKey::~CacheKey() = default; + +bool GrShaderCache::CacheKey::operator==(const CacheKey& other) const { + return data->equals(other.data.get()); +} + +GrShaderCache::CacheData::CacheData(sk_sp<SkData> data) + : data(std::move(data)) {} +GrShaderCache::CacheData::CacheData(CacheData&& other) = default; +GrShaderCache::CacheData& GrShaderCache::CacheData::operator=( + CacheData&& other) = default; +GrShaderCache::CacheData::~CacheData() = default; + +bool GrShaderCache::CacheData::operator==(const CacheData& other) const { + return data->equals(other.data.get()); +} + +} // namespace raster +} // namespace gpu
diff --git a/gpu/command_buffer/service/gr_shader_cache.h b/gpu/command_buffer/service/gr_shader_cache.h new file mode 100644 index 0000000..bb1ae2e8 --- /dev/null +++ b/gpu/command_buffer/service/gr_shader_cache.h
@@ -0,0 +1,105 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef GPU_COMMAND_BUFFER_SERVICE_GR_SHADER_CACHE_H_ +#define GPU_COMMAND_BUFFER_SERVICE_GR_SHADER_CACHE_H_ + +#include "base/containers/flat_set.h" +#include "base/containers/mru_cache.h" +#include "base/hash.h" +#include "base/memory/memory_pressure_listener.h" +#include "gpu/gpu_gles2_export.h" +#include "third_party/skia/include/gpu/GrContextOptions.h" + +namespace gpu { +namespace raster { + +class GPU_GLES2_EXPORT GrShaderCache + : public GrContextOptions::PersistentCache { + public: + class GPU_GLES2_EXPORT Client { + public: + virtual ~Client() {} + + virtual void StoreShader(const std::string& key, + const std::string& shader) = 0; + }; + + class GPU_GLES2_EXPORT ScopedCacheUse { + public: + ScopedCacheUse(GrShaderCache* cache, int32_t client_id); + ~ScopedCacheUse(); + + private: + GrShaderCache* cache_; + }; + + GrShaderCache(size_t max_cache_size_bytes, Client* client); + ~GrShaderCache() override; + + // GrContextOptions::PersistentCache implementation. + sk_sp<SkData> load(const SkData& key) override; + void store(const SkData& key, const SkData& data) override; + + void PopulateCache(const std::string& key, const std::string& data); + void CacheClientIdOnDisk(int32_t client_id); + void PurgeMemory( + base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level); + + size_t num_cache_entries() const { return store_.size(); } + + private: + static constexpr int32_t kInvalidClientId = 0; + + struct CacheKey { + explicit CacheKey(sk_sp<SkData> data); + CacheKey(CacheKey&& other); + CacheKey(const CacheKey& other); + CacheKey& operator=(const CacheKey& other); + CacheKey& operator=(CacheKey&& other); + ~CacheKey(); + + bool operator==(const CacheKey& other) const; + + sk_sp<SkData> data; + size_t hash; + }; + + struct CacheData { + public: + explicit CacheData(sk_sp<SkData> data); + CacheData(CacheData&& other); + CacheData& operator=(CacheData&& other); + ~CacheData(); + + bool operator==(const CacheData& other) const; + + sk_sp<SkData> data; + bool pending_disk_write = true; + }; + + struct CacheKeyHash { + size_t operator()(const CacheKey& key) const { return key.hash; } + }; + + void EnforceLimits(size_t size_needed); + void WriteToDisk(const CacheKey& key, CacheData* data); + + size_t cache_size_limit_; + size_t curr_size_bytes_ = 0u; + using Store = base::HashingMRUCache<CacheKey, CacheData, CacheKeyHash>; + Store store_; + + Client* const client_; + base::flat_set<int32_t> client_ids_to_cache_on_disk_; + + int32_t current_client_id_ = kInvalidClientId; + + DISALLOW_COPY_AND_ASSIGN(GrShaderCache); +}; + +} // namespace raster +} // namespace gpu + +#endif // GPU_COMMAND_BUFFER_SERVICE_GR_SHADER_CACHE_H_
diff --git a/gpu/command_buffer/service/gr_shader_cache_unittest.cc b/gpu/command_buffer/service/gr_shader_cache_unittest.cc new file mode 100644 index 0000000..ce334c8a2 --- /dev/null +++ b/gpu/command_buffer/service/gr_shader_cache_unittest.cc
@@ -0,0 +1,133 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "gpu/command_buffer/service/gr_shader_cache.h" + +#include "testing/gtest/include/gtest/gtest.h" + +namespace gpu { +namespace raster { +namespace { +constexpr char kShaderKey[] = "key"; +constexpr char kShader[] = "shader"; +constexpr size_t kCacheLimit = 1024u; + +} // namespace + +class GrShaderCacheTest : public GrShaderCache::Client, public testing::Test { + public: + GrShaderCacheTest() : cache_(kCacheLimit, this) {} + + void StoreShader(const std::string& key, const std::string& shader) override { + CHECK_EQ(disk_cache_.count(key), 0u); + disk_cache_[key] = shader; + } + + GrShaderCache cache_; + std::unordered_map<std::string, std::string> disk_cache_; +}; + +TEST_F(GrShaderCacheTest, DoesNotCacheForIncognito) { + int32_t incognito_client_id = 2; + auto key = SkData::MakeWithCString(kShaderKey); + auto shader = SkData::MakeWithCString(kShader); + { + GrShaderCache::ScopedCacheUse cache_use(&cache_, incognito_client_id); + EXPECT_EQ(cache_.load(*key), nullptr); + cache_.store(*key, *shader); + } + EXPECT_EQ(disk_cache_.size(), 0u); + + int32_t regular_client_id = 3; + cache_.CacheClientIdOnDisk(regular_client_id); + { + GrShaderCache::ScopedCacheUse cache_use(&cache_, regular_client_id); + auto cached_shader = cache_.load(*key); + ASSERT_TRUE(cached_shader); + EXPECT_TRUE(cached_shader->equals(shader.get())); + } + EXPECT_EQ(disk_cache_.size(), 1u); + + { + GrShaderCache::ScopedCacheUse cache_use(&cache_, regular_client_id); + auto second_key = SkData::MakeWithCString("key2"); + EXPECT_EQ(cache_.load(*second_key), nullptr); + cache_.store(*second_key, *shader); + } + EXPECT_EQ(disk_cache_.size(), 2u); +} + +TEST_F(GrShaderCacheTest, LoadedFromDisk) { + int32_t regular_client_id = 3; + cache_.CacheClientIdOnDisk(regular_client_id); + auto key = SkData::MakeWithCopy(kShaderKey, strlen(kShaderKey)); + auto shader = SkData::MakeWithCString(kShader); + + std::string key_str(static_cast<const char*>(key->data()), key->size()); + std::string shader_str(static_cast<const char*>(shader->data()), + shader->size()); + cache_.PopulateCache(key_str, shader_str); + { + GrShaderCache::ScopedCacheUse cache_use(&cache_, regular_client_id); + auto cached_shader = cache_.load(*key); + ASSERT_TRUE(cached_shader); + EXPECT_TRUE(cached_shader->equals(shader.get())); + } + EXPECT_EQ(disk_cache_.size(), 0u); +} + +TEST_F(GrShaderCacheTest, EnforcesLimits) { + int32_t regular_client_id = 3; + cache_.CacheClientIdOnDisk(regular_client_id); + + auto key = SkData::MakeWithCopy(kShaderKey, strlen(kShaderKey)); + auto shader = SkData::MakeUninitialized(kCacheLimit); + { + GrShaderCache::ScopedCacheUse cache_use(&cache_, regular_client_id); + EXPECT_EQ(cache_.load(*key), nullptr); + cache_.store(*key, *shader); + } + EXPECT_EQ(cache_.num_cache_entries(), 1u); + + { + auto second_key = SkData::MakeWithCString("key2"); + GrShaderCache::ScopedCacheUse cache_use(&cache_, regular_client_id); + EXPECT_EQ(cache_.load(*second_key), nullptr); + cache_.store(*second_key, *shader); + } + EXPECT_EQ(cache_.num_cache_entries(), 1u); + + { + auto third_key = SkData::MakeWithCString("key3"); + GrShaderCache::ScopedCacheUse cache_use(&cache_, regular_client_id); + EXPECT_EQ(cache_.load(*third_key), nullptr); + std::string key_str(static_cast<const char*>(third_key->data()), + third_key->size()); + std::string shader_str(static_cast<const char*>(shader->data()), + shader->size()); + cache_.PopulateCache(key_str, shader_str); + } + EXPECT_EQ(cache_.num_cache_entries(), 1u); +} + +TEST_F(GrShaderCacheTest, MemoryPressure) { + int32_t regular_client_id = 3; + cache_.CacheClientIdOnDisk(regular_client_id); + + auto key = SkData::MakeWithCopy(kShaderKey, strlen(kShaderKey)); + auto shader = SkData::MakeUninitialized(kCacheLimit); + { + GrShaderCache::ScopedCacheUse cache_use(&cache_, regular_client_id); + EXPECT_EQ(cache_.load(*key), nullptr); + cache_.store(*key, *shader); + } + EXPECT_EQ(cache_.num_cache_entries(), 1u); + + cache_.PurgeMemory( + base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL); + EXPECT_EQ(cache_.num_cache_entries(), 0u); +} + +} // namespace raster +} // namespace gpu
diff --git a/gpu/command_buffer/service/raster_decoder.cc b/gpu/command_buffer/service/raster_decoder.cc index a2c1bc07..afe7a50 100644 --- a/gpu/command_buffer/service/raster_decoder.cc +++ b/gpu/command_buffer/service/raster_decoder.cc
@@ -2284,8 +2284,10 @@ *static_cast<const volatile gles2::cmds::TraceBeginCHROMIUM*>(cmd_data); Bucket* category_bucket = GetBucket(c.category_bucket_id); Bucket* name_bucket = GetBucket(c.name_bucket_id); - if (!category_bucket || category_bucket->size() == 0 || !name_bucket || - name_bucket->size() == 0) { + static constexpr size_t kMaxStrLen = 256; + if (!category_bucket || category_bucket->size() == 0 || + category_bucket->size() > kMaxStrLen || !name_bucket || + name_bucket->size() == 0 || name_bucket->size() > kMaxStrLen) { return error::kInvalidArguments; }
diff --git a/gpu/command_buffer/service/raster_decoder_context_state.cc b/gpu/command_buffer/service/raster_decoder_context_state.cc index 9eab0cc9..ff5f5a0 100644 --- a/gpu/command_buffer/service/raster_decoder_context_state.cc +++ b/gpu/command_buffer/service/raster_decoder_context_state.cc
@@ -39,7 +39,8 @@ } void RasterDecoderContextState::InitializeGrContext( - const GpuDriverBugWorkarounds& workarounds) { + const GpuDriverBugWorkarounds& workarounds, + GrContextOptions::PersistentCache* cache) { DCHECK(context->IsCurrent(surface.get())); sk_sp<const GrGLInterface> interface( @@ -61,6 +62,7 @@ raster::DetermineGrCacheLimitsFromAvailableMemory( &max_resource_cache_bytes, &glyph_cache_max_texture_bytes); options.fGlyphCacheTextureMaximumBytes = glyph_cache_max_texture_bytes; + options.fPersistentCache = cache; gr_context = GrContext::MakeGL(std::move(interface), options); if (!gr_context) { LOG(ERROR) << "OOP raster support disabled: GrContext creation "
diff --git a/gpu/command_buffer/service/raster_decoder_context_state.h b/gpu/command_buffer/service/raster_decoder_context_state.h index 45bb2f6..daaf1640 100644 --- a/gpu/command_buffer/service/raster_decoder_context_state.h +++ b/gpu/command_buffer/service/raster_decoder_context_state.h
@@ -32,7 +32,8 @@ scoped_refptr<gl::GLSurface> surface, scoped_refptr<gl::GLContext> context, bool use_virtualized_gl_contexts); - void InitializeGrContext(const GpuDriverBugWorkarounds& workarounds); + void InitializeGrContext(const GpuDriverBugWorkarounds& workarounds, + GrContextOptions::PersistentCache* cache); void PurgeMemory( base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level);
diff --git a/gpu/command_buffer/service/raster_decoder_unittest.cc b/gpu/command_buffer/service/raster_decoder_unittest.cc index 73ef774..4eee55e 100644 --- a/gpu/command_buffer/service/raster_decoder_unittest.cc +++ b/gpu/command_buffer/service/raster_decoder_unittest.cc
@@ -548,7 +548,7 @@ context_state_ = new raster::RasterDecoderContextState( std::move(share_group), std::move(surface), std::move(context), false /* use_virtualized_gl_contexts */); - context_state_->InitializeGrContext(workarounds); + context_state_->InitializeGrContext(workarounds, nullptr); GpuFeatureInfo gpu_feature_info; gpu_feature_info.status_values[GPU_FEATURE_TYPE_OOP_RASTERIZATION] =
diff --git a/gpu/command_buffer/tests/fuzzer_main.cc b/gpu/command_buffer/tests/fuzzer_main.cc index ab4cc15b..28ae5bc 100644 --- a/gpu/command_buffer/tests/fuzzer_main.cc +++ b/gpu/command_buffer/tests/fuzzer_main.cc
@@ -359,7 +359,7 @@ new raster::RasterDecoderContextState( share_group_, surface_, context_, config_.workarounds.use_virtualized_gl_contexts); - context_state->InitializeGrContext(config_.workarounds); + context_state->InitializeGrContext(config_.workarounds, nullptr); decoder_.reset(raster::RasterDecoder::Create( command_buffer_.get(), command_buffer_->service(), &outputter_, context_group.get(), std::move(context_state)));
diff --git a/gpu/command_buffer/tests/gl_manager.cc b/gpu/command_buffer/tests/gl_manager.cc index c861fb2..e4d4117e 100644 --- a/gpu/command_buffer/tests/gl_manager.cc +++ b/gpu/command_buffer/tests/gl_manager.cc
@@ -36,6 +36,7 @@ #include "gpu/command_buffer/service/memory_tracking.h" #include "gpu/command_buffer/service/service_utils.h" #include "gpu/command_buffer/service/transfer_buffer_manager.h" +#include "gpu/ipc/common/gpu_client_ids.h" #include "gpu/ipc/in_process_command_buffer.h" #include "gpu/ipc/service/gpu_memory_buffer_factory.h" #include "testing/gtest/include/gtest/gtest.h" @@ -515,7 +516,7 @@ gl_image = gpu_memory_buffer_factory_->AsImageFactory() ->CreateImageForGpuMemoryBuffer( handle, size, format, internalformat, - gpu::InProcessCommandBuffer::kGpuClientId, + gpu::kInProcessCommandBufferClientId, gpu::kNullSurfaceHandle); if (!gl_image) return -1;
diff --git a/gpu/ipc/BUILD.gn b/gpu/ipc/BUILD.gn index 5d99902..4605852 100644 --- a/gpu/ipc/BUILD.gn +++ b/gpu/ipc/BUILD.gn
@@ -37,6 +37,7 @@ "//gpu/command_buffer/service:gles2", "//gpu/config", "//gpu/ipc/client", + "//gpu/ipc/common", "//gpu/ipc/common:surface_handle_type", "//gpu/ipc/host", "//gpu/ipc/service",
diff --git a/gpu/ipc/common/BUILD.gn b/gpu/ipc/common/BUILD.gn index f11ae7d..1f50805 100644 --- a/gpu/ipc/common/BUILD.gn +++ b/gpu/ipc/common/BUILD.gn
@@ -67,6 +67,7 @@ sources = [ "flush_params.cc", "flush_params.h", + "gpu_client_ids.h", "gpu_memory_buffer_impl.cc", "gpu_memory_buffer_impl.h", "gpu_memory_buffer_impl_shared_memory.cc",
diff --git a/gpu/ipc/common/gpu_client_ids.h b/gpu/ipc/common/gpu_client_ids.h new file mode 100644 index 0000000..4189674c --- /dev/null +++ b/gpu/ipc/common/gpu_client_ids.h
@@ -0,0 +1,31 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef GPU_IPC_COMMON_GPU_CLIENT_IDS_H_ +#define GPU_IPC_COMMON_GPU_CLIENT_IDS_H_ + +namespace gpu { + +// The list of client id constants used to identify unique GPU clients. In the +// general case, GPU clients are assigned unique IDs in the browser. But these +// special constants are used for particular clients that should always be +// assigned the same ID. + +// The ID used by the InProcessCommandBuffer created in the GPU process for viz +// display compositor. +constexpr int32_t kInProcessCommandBufferClientId = -1; + +// The ID used for storing shaders created by skia in the GPU process. Note that +// this ID doesn't correspond to a real Gpu client/channel, but is required so +// we can use the same disk caching system for shaders and use a unique +// namespace for these shaders. +constexpr int32_t kGrShaderCacheClientId = -2; + +inline bool IsReservedClientId(int32_t client_id) { + return client_id < 0; +} + +} // namespace gpu + +#endif // GPU_IPC_COMMON_GPU_CLIENT_IDS_H_
diff --git a/gpu/ipc/in_process_command_buffer.cc b/gpu/ipc/in_process_command_buffer.cc index fff9280..20aeea5 100644 --- a/gpu/ipc/in_process_command_buffer.cc +++ b/gpu/ipc/in_process_command_buffer.cc
@@ -54,6 +54,7 @@ #include "gpu/config/gpu_preferences.h" #include "gpu/config/gpu_switches.h" #include "gpu/ipc/command_buffer_task_executor.h" +#include "gpu/ipc/common/gpu_client_ids.h" #include "gpu/ipc/gpu_in_process_thread_service.h" #include "gpu/ipc/host/gpu_memory_buffer_support.h" #include "gpu/ipc/service/gpu_channel_manager_delegate.h" @@ -160,9 +161,6 @@ } // anonyous namespace -const int InProcessCommandBuffer::kGpuClientId = - std::numeric_limits<int>::max(); - InProcessCommandBuffer::InProcessCommandBuffer( scoped_refptr<CommandBufferTaskExecutor> task_executer) : command_buffer_id_(CommandBufferId::FromUnsafeValue( @@ -434,7 +432,7 @@ new raster::RasterDecoderContextState(gl_share_group_, surface_, real_context, use_virtualized_gl_context_); - context_state->InitializeGrContext(workarounds); + context_state->InitializeGrContext(workarounds, nullptr); if (base::ThreadTaskRunnerHandle::IsSet()) { gr_cache_controller_.emplace(context_state.get(), @@ -924,8 +922,8 @@ scoped_refptr<gl::GLImage> image = image_factory_->CreateImageForGpuMemoryBuffer( - std::move(handle), size, format, internalformat, kGpuClientId, - kNullSurfaceHandle); + std::move(handle), size, format, internalformat, + kInProcessCommandBufferClientId, kNullSurfaceHandle); if (!image.get()) { LOG(ERROR) << "Failed to create image for buffer."; return; @@ -967,7 +965,8 @@ void InProcessCommandBuffer::CacheShader(const std::string& key, const std::string& shader) { if (gpu_channel_manager_delegate_) - gpu_channel_manager_delegate_->StoreShaderToDisk(kGpuClientId, key, shader); + gpu_channel_manager_delegate_->StoreShaderToDisk( + kInProcessCommandBufferClientId, key, shader); } void InProcessCommandBuffer::OnFenceSyncRelease(uint64_t release) {
diff --git a/gpu/ipc/in_process_command_buffer.h b/gpu/ipc/in_process_command_buffer.h index fcc35e4..7e2864a 100644 --- a/gpu/ipc/in_process_command_buffer.h +++ b/gpu/ipc/in_process_command_buffer.h
@@ -187,8 +187,6 @@ gpu::ServiceTransferCache* GetTransferCacheForTest() const; int GetRasterDecoderIdForTest() const; - static const int kGpuClientId; - CommandBufferTaskExecutor* service_for_testing() const { return task_executor_.get(); }
diff --git a/gpu/ipc/service/command_buffer_stub.cc b/gpu/ipc/service/command_buffer_stub.cc index a7bc3518..e6e816c 100644 --- a/gpu/ipc/service/command_buffer_stub.cc +++ b/gpu/ipc/service/command_buffer_stub.cc
@@ -611,7 +611,15 @@ last_flush_id_ = flush_id; CommandBuffer::State pre_state = command_buffer_->GetState(); FastSetActiveURL(active_url_, active_url_hash_, channel_); - command_buffer_->Flush(put_offset, decoder_context_.get()); + + { + auto* gr_shader_cache = channel_->gpu_channel_manager()->gr_shader_cache(); + base::Optional<raster::GrShaderCache::ScopedCacheUse> cache_use; + if (gr_shader_cache) + cache_use.emplace(gr_shader_cache, channel_->client_id()); + command_buffer_->Flush(put_offset, decoder_context_.get()); + } + CommandBuffer::State post_state = command_buffer_->GetState(); if (pre_state.get_offset != post_state.get_offset)
diff --git a/gpu/ipc/service/gpu_channel_manager.cc b/gpu/ipc/service/gpu_channel_manager.cc index eb2da596..be6239fb 100644 --- a/gpu/ipc/service/gpu_channel_manager.cc +++ b/gpu/ipc/service/gpu_channel_manager.cc
@@ -25,6 +25,7 @@ #include "gpu/command_buffer/service/scheduler.h" #include "gpu/command_buffer/service/service_utils.h" #include "gpu/command_buffer/service/sync_point_manager.h" +#include "gpu/ipc/common/gpu_client_ids.h" #include "gpu/ipc/common/gpu_messages.h" #include "gpu/ipc/common/memory_stats.h" #include "gpu/ipc/service/gpu_channel.h" @@ -82,6 +83,15 @@ DCHECK(task_runner->BelongsToCurrentThread()); DCHECK(io_task_runner); DCHECK(scheduler); + + const bool enable_raster_transport = + gpu_feature_info_.status_values[GPU_FEATURE_TYPE_OOP_RASTERIZATION] == + gpu::kGpuFeatureStatusEnabled; + bool disable_disk_cache = + gpu_preferences_.disable_gpu_shader_disk_cache || + gpu_driver_bug_workarounds_.disable_program_disk_cache; + if (enable_raster_transport && !disable_disk_cache) + gr_shader_cache_.emplace(gpu_preferences.gpu_program_cache_size, this); } GpuChannelManager::~GpuChannelManager() { @@ -133,7 +143,11 @@ GpuChannel* GpuChannelManager::EstablishChannel(int client_id, uint64_t client_tracing_id, - bool is_gpu_host) { + bool is_gpu_host, + bool cache_shaders_on_disk) { + if (gr_shader_cache_ && cache_shaders_on_disk) + gr_shader_cache_->CacheClientIdOnDisk(client_id); + std::unique_ptr<GpuChannel> gpu_channel = std::make_unique<GpuChannel>( this, scheduler_, sync_point_manager_, share_group_, task_runner_, io_task_runner_, client_id, client_tracing_id, is_gpu_host); @@ -170,8 +184,15 @@ } } -void GpuChannelManager::PopulateShaderCache(const std::string& key, +void GpuChannelManager::PopulateShaderCache(int32_t client_id, + const std::string& key, const std::string& program) { + if (client_id == kGrShaderCacheClientId) { + if (gr_shader_cache_) + gr_shader_cache_->PopulateCache(key, program); + return; + } + if (program_cache()) program_cache()->LoadProgram(key, program); } @@ -322,6 +343,8 @@ discardable_manager_.HandleMemoryPressure(memory_pressure_level); if (raster_decoder_context_state_) raster_decoder_context_state_->PurgeMemory(memory_pressure_level); + if (gr_shader_cache_) + gr_shader_cache_->PurgeMemory(memory_pressure_level); } scoped_refptr<raster::RasterDecoderContextState> @@ -411,7 +434,7 @@ gpu::kGpuFeatureStatusEnabled; if (enable_raster_transport) { raster_decoder_context_state_->InitializeGrContext( - gpu_driver_bug_workarounds_); + gpu_driver_bug_workarounds_, gr_shader_cache()); } gr_cache_controller_.emplace(raster_decoder_context_state_.get(), @@ -426,4 +449,9 @@ gr_cache_controller_->ScheduleGrContextCleanup(); } +void GpuChannelManager::StoreShader(const std::string& key, + const std::string& shader) { + delegate_->StoreShaderToDisk(kGrShaderCacheClientId, key, shader); +} + } // namespace gpu
diff --git a/gpu/ipc/service/gpu_channel_manager.h b/gpu/ipc/service/gpu_channel_manager.h index d446407..255986d1 100644 --- a/gpu/ipc/service/gpu_channel_manager.h +++ b/gpu/ipc/service/gpu_channel_manager.h
@@ -22,6 +22,7 @@ #include "gpu/command_buffer/common/activity_flags.h" #include "gpu/command_buffer/common/constants.h" #include "gpu/command_buffer/service/gr_cache_controller.h" +#include "gpu/command_buffer/service/gr_shader_cache.h" #include "gpu/command_buffer/service/raster_decoder_context_state.h" #include "gpu/command_buffer/service/service_discardable_manager.h" #include "gpu/command_buffer/service/shader_translator_cache.h" @@ -59,7 +60,8 @@ // A GpuChannelManager is a thread responsible for issuing rendering commands // managing the lifetimes of GPU channels and forwarding IPC requests from the // browser process to them based on the corresponding renderer ID. -class GPU_IPC_SERVICE_EXPORT GpuChannelManager { +class GPU_IPC_SERVICE_EXPORT GpuChannelManager + : public raster::GrShaderCache::Client { public: GpuChannelManager(const GpuPreferences& gpu_preferences, GpuChannelManagerDelegate* delegate, @@ -71,16 +73,19 @@ GpuMemoryBufferFactory* gpu_memory_buffer_factory, const GpuFeatureInfo& gpu_feature_info, GpuProcessActivityFlags activity_flags); - ~GpuChannelManager(); + ~GpuChannelManager() override; GpuChannelManagerDelegate* delegate() const { return delegate_; } GpuWatchdogThread* watchdog() const { return watchdog_; } GpuChannel* EstablishChannel(int client_id, uint64_t client_tracing_id, - bool is_gpu_host); + bool is_gpu_host, + bool cache_shaders_on_disk); - void PopulateShaderCache(const std::string& key, const std::string& program); + void PopulateShaderCache(int32_t client_id, + const std::string& key, + const std::string& program); void DestroyGpuMemoryBuffer(gfx::GpuMemoryBufferId id, int client_id, const SyncToken& sync_token); @@ -142,6 +147,12 @@ scoped_refptr<raster::RasterDecoderContextState> GetRasterDecoderContextState( ContextResult* result); void ScheduleGrContextCleanup(); + raster::GrShaderCache* gr_shader_cache() { + return gr_shader_cache_ ? &*gr_shader_cache_ : nullptr; + } + + // raster::GrShaderCache::Client implementation. + void StoreShader(const std::string& key, const std::string& shader) override; private: void InternalDestroyGpuMemoryBuffer(gfx::GpuMemoryBufferId id, int client_id); @@ -210,6 +221,7 @@ // order to avoid having the GpuChannelManager keep the lost context state // alive until all clients have recovered, we use a ref-counted object and // allow the decoders to manage its lifetime. + base::Optional<raster::GrShaderCache> gr_shader_cache_; base::Optional<raster::GrCacheController> gr_cache_controller_; scoped_refptr<raster::RasterDecoderContextState> raster_decoder_context_state_;
diff --git a/gpu/ipc/service/gpu_channel_manager_unittest.cc b/gpu/ipc/service/gpu_channel_manager_unittest.cc index c62e49af..d9f5b351 100644 --- a/gpu/ipc/service/gpu_channel_manager_unittest.cc +++ b/gpu/ipc/service/gpu_channel_manager_unittest.cc
@@ -73,8 +73,8 @@ uint64_t kClientTracingId = 1; ASSERT_TRUE(channel_manager()); - GpuChannel* channel = - channel_manager()->EstablishChannel(kClientId, kClientTracingId, false); + GpuChannel* channel = channel_manager()->EstablishChannel( + kClientId, kClientTracingId, false, true); EXPECT_TRUE(channel); EXPECT_EQ(channel_manager()->LookupChannel(kClientId), channel); }
diff --git a/gpu/ipc/service/gpu_channel_test_common.cc b/gpu/ipc/service/gpu_channel_test_common.cc index eddef8f..0705f075 100644 --- a/gpu/ipc/service/gpu_channel_test_common.cc +++ b/gpu/ipc/service/gpu_channel_test_common.cc
@@ -107,7 +107,7 @@ bool is_gpu_host) { uint64_t kClientTracingId = 1; GpuChannel* channel = channel_manager()->EstablishChannel( - client_id, kClientTracingId, is_gpu_host); + client_id, kClientTracingId, is_gpu_host, true); channel->Init(std::make_unique<TestSinkFilteredSender>()); base::ProcessId kProcessId = 1; channel->OnChannelConnected(kProcessId);
diff --git a/ios/chrome/browser/DEPS b/ios/chrome/browser/DEPS index 298ad23..cbabe33 100644 --- a/ios/chrome/browser/DEPS +++ b/ios/chrome/browser/DEPS
@@ -81,6 +81,7 @@ "+components/ui_metrics", "+components/ukm", "+components/undo", + "+components/unified_consent", "+components/update_client", "+components/upload_list", "+components/url_formatter",
diff --git a/ios/chrome/browser/autofill/form_suggestion_controller.mm b/ios/chrome/browser/autofill/form_suggestion_controller.mm index 68849685..240c94f 100644 --- a/ios/chrome/browser/autofill/form_suggestion_controller.mm +++ b/ios/chrome/browser/autofill/form_suggestion_controller.mm
@@ -232,7 +232,8 @@ // Once the suggestions are retrieved, update the suggestions UI. SuggestionsReadyCompletion readyCompletion = - ^(NSArray* suggestions, id<FormSuggestionProvider> provider) { + ^(NSArray<FormSuggestion*>* suggestions, + id<FormSuggestionProvider> provider) { [weakSelf onSuggestionsReady:suggestions provider:provider]; }; @@ -263,7 +264,7 @@ passwords::RunSearchPipeline(findProviderBlocks, completion); } -- (void)onSuggestionsReady:(NSArray*)suggestions +- (void)onSuggestionsReady:(NSArray<FormSuggestion*>*)suggestions provider:(id<FormSuggestionProvider>)provider { // TODO(ios): crbug.com/249916. If we can also pass in the form/field for // which |suggestions| are, we should check here if |suggestions| are for
diff --git a/ios/chrome/browser/autofill/personal_data_manager_factory.cc b/ios/chrome/browser/autofill/personal_data_manager_factory.cc index 09618fb..14eb2867 100644 --- a/ios/chrome/browser/autofill/personal_data_manager_factory.cc +++ b/ios/chrome/browser/autofill/personal_data_manager_factory.cc
@@ -49,10 +49,11 @@ ios::ChromeBrowserState::FromBrowserState(context); std::unique_ptr<PersonalDataManager> service( new PersonalDataManager(GetApplicationContext()->GetApplicationLocale())); - service->Init( + auto autofill_db = ios::WebDataServiceFactory::GetAutofillWebDataForBrowserState( - chrome_browser_state, ServiceAccessType::EXPLICIT_ACCESS), - chrome_browser_state->GetPrefs(), + chrome_browser_state, ServiceAccessType::EXPLICIT_ACCESS); + service->Init( + autofill_db, nullptr, chrome_browser_state->GetPrefs(), IdentityManagerFactory::GetForBrowserState(chrome_browser_state), chrome_browser_state->IsOffTheRecord()); return service;
diff --git a/ios/chrome/browser/browser_state/BUILD.gn b/ios/chrome/browser/browser_state/BUILD.gn index fc360af..2cb9cac 100644 --- a/ios/chrome/browser/browser_state/BUILD.gn +++ b/ios/chrome/browser/browser_state/BUILD.gn
@@ -84,7 +84,6 @@ "//ios/chrome/browser/bookmarks", "//ios/chrome/browser/browser_state_metrics", "//ios/chrome/browser/browsing_data", - "//ios/chrome/browser/consent_auditor", "//ios/chrome/browser/content_settings", "//ios/chrome/browser/desktop_promotion", "//ios/chrome/browser/dom_distiller",
diff --git a/ios/chrome/browser/browser_state/browser_state_keyed_service_factories.mm b/ios/chrome/browser/browser_state/browser_state_keyed_service_factories.mm index 35157751..656c144 100644 --- a/ios/chrome/browser/browser_state/browser_state_keyed_service_factories.mm +++ b/ios/chrome/browser/browser_state/browser_state_keyed_service_factories.mm
@@ -12,7 +12,6 @@ #include "ios/chrome/browser/bookmarks/bookmark_model_factory.h" #include "ios/chrome/browser/bookmarks/startup_task_runner_service_factory.h" #include "ios/chrome/browser/browsing_data/browsing_data_remover_factory.h" -#include "ios/chrome/browser/consent_auditor/consent_auditor_factory.h" #include "ios/chrome/browser/content_settings/cookie_settings_factory.h" #include "ios/chrome/browser/desktop_promotion/desktop_promotion_sync_service_factory.h" #include "ios/chrome/browser/dom_distiller/dom_distiller_service_factory.h" @@ -49,6 +48,7 @@ #include "ios/chrome/browser/signin/signin_manager_factory.h" #import "ios/chrome/browser/snapshots/snapshot_cache_factory.h" #include "ios/chrome/browser/suggestions/suggestions_service_factory.h" +#include "ios/chrome/browser/sync/consent_auditor_factory.h" #include "ios/chrome/browser/sync/ios_user_event_service_factory.h" #include "ios/chrome/browser/sync/profile_sync_service_factory.h" #include "ios/chrome/browser/sync/sync_setup_service_factory.h"
diff --git a/ios/chrome/browser/consent_auditor/BUILD.gn b/ios/chrome/browser/consent_auditor/BUILD.gn deleted file mode 100644 index fbde3627..0000000 --- a/ios/chrome/browser/consent_auditor/BUILD.gn +++ /dev/null
@@ -1,24 +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. - -source_set("consent_auditor") { - configs += [ "//build/config/compiler:enable_arc" ] - sources = [ - "consent_auditor_factory.cc", - "consent_auditor_factory.h", - ] - deps = [ - "//base", - "//components/browser_sync", - "//components/consent_auditor", - "//components/keyed_service/ios", - "//components/pref_registry", - "//components/version_info", - "//ios/chrome/browser", - "//ios/chrome/browser/browser_state", - "//ios/chrome/browser/sync", - "//ios/chrome/common", - "//ios/web/public", - ] -}
diff --git a/ios/chrome/browser/consent_auditor/OWNERS b/ios/chrome/browser/consent_auditor/OWNERS deleted file mode 100644 index 9cd99df3..0000000 --- a/ios/chrome/browser/consent_auditor/OWNERS +++ /dev/null
@@ -1,6 +0,0 @@ -jlebel@chromium.org -markusheintz@chromium.org -msarda@chromium.org -msramek@chromium.org - -# COMPONENT: Privacy
diff --git a/ios/chrome/browser/dom_distiller/dom_distiller_service_factory.cc b/ios/chrome/browser/dom_distiller/dom_distiller_service_factory.cc index 8d94469..65492d2 100644 --- a/ios/chrome/browser/dom_distiller/dom_distiller_service_factory.cc +++ b/ios/chrome/browser/dom_distiller/dom_distiller_service_factory.cc
@@ -22,6 +22,7 @@ #include "ios/chrome/browser/browser_state/chrome_browser_state.h" #include "ios/web/public/browser_state.h" #include "ios/web/public/web_thread.h" +#include "services/network/public/cpp/shared_url_loader_factory.h" namespace { // A simple wrapper for DomDistillerService to expose it as a @@ -77,7 +78,7 @@ std::unique_ptr<DistillerURLFetcherFactory> distiller_url_fetcher_factory = std::make_unique<DistillerURLFetcherFactory>( - context->GetRequestContext()); + context->GetSharedURLLoaderFactory()); dom_distiller::proto::DomDistillerOptions options; std::unique_ptr<DistillerFactory> distiller_factory =
diff --git a/ios/chrome/browser/prefs/BUILD.gn b/ios/chrome/browser/prefs/BUILD.gn index 138a3d5..6f6075f3 100644 --- a/ios/chrome/browser/prefs/BUILD.gn +++ b/ios/chrome/browser/prefs/BUILD.gn
@@ -60,7 +60,6 @@ "//components/web_resource", "//ios/chrome/browser", "//ios/chrome/browser/browser_state", - "//ios/chrome/browser/consent_auditor", "//ios/chrome/browser/desktop_promotion", "//ios/chrome/browser/first_run", "//ios/chrome/browser/geolocation",
diff --git a/ios/chrome/browser/reading_list/reading_list_download_service_factory.cc b/ios/chrome/browser/reading_list/reading_list_download_service_factory.cc index fd70a2fe..d7f2c3ad 100644 --- a/ios/chrome/browser/reading_list/reading_list_download_service_factory.cc +++ b/ios/chrome/browser/reading_list/reading_list_download_service_factory.cc
@@ -16,6 +16,7 @@ #include "ios/chrome/browser/reading_list/reading_list_distiller_page_factory.h" #include "ios/chrome/browser/reading_list/reading_list_download_service.h" #include "ios/chrome/browser/reading_list/reading_list_model_factory.h" +#include "services/network/public/cpp/shared_url_loader_factory.h" // static ReadingListDownloadService* @@ -55,7 +56,7 @@ auto distiller_url_fetcher_factory = std::make_unique<dom_distiller::DistillerURLFetcherFactory>( - context->GetRequestContext()); + context->GetSharedURLLoaderFactory()); dom_distiller::proto::DomDistillerOptions options; auto distiller_factory =
diff --git a/ios/chrome/browser/reading_list/url_downloader.cc b/ios/chrome/browser/reading_list/url_downloader.cc index 3602c8af..0fec280 100644 --- a/ios/chrome/browser/reading_list/url_downloader.cc +++ b/ios/chrome/browser/reading_list/url_downloader.cc
@@ -23,7 +23,6 @@ #include "net/base/load_flags.h" #include "net/http/http_response_headers.h" #include "net/url_request/url_fetcher.h" -#include "net/url_request/url_fetcher_delegate.h" #include "net/url_request/url_request_context_getter.h" #include "net/url_request/url_request_status.h" #include "url/gurl.h"
diff --git a/ios/chrome/browser/reading_list/url_downloader.h b/ios/chrome/browser/reading_list/url_downloader.h index 28d7cbc..ed283ad 100644 --- a/ios/chrome/browser/reading_list/url_downloader.h +++ b/ios/chrome/browser/reading_list/url_downloader.h
@@ -7,9 +7,11 @@ #include "base/callback.h" #include "base/containers/circular_deque.h" +#include "base/files/file_path.h" #include "base/task/cancelable_task_tracker.h" #include "ios/chrome/browser/dom_distiller/distiller_viewer.h" #include "ios/chrome/browser/reading_list/reading_list_distiller_page.h" +#include "net/url_request/url_fetcher_delegate.h" class PrefService; class GURL;
diff --git a/ios/chrome/browser/signin/identity_manager_factory.cc b/ios/chrome/browser/signin/identity_manager_factory.cc index c6de09f..8a25a41 100644 --- a/ios/chrome/browser/signin/identity_manager_factory.cc +++ b/ios/chrome/browser/signin/identity_manager_factory.cc
@@ -11,6 +11,7 @@ #include "components/signin/core/browser/signin_manager.h" #include "ios/chrome/browser/browser_state/chrome_browser_state.h" #include "ios/chrome/browser/signin/account_tracker_service_factory.h" +#include "ios/chrome/browser/signin/gaia_cookie_manager_service_factory.h" #include "ios/chrome/browser/signin/profile_oauth2_token_service_factory.h" #include "ios/chrome/browser/signin/signin_manager_factory.h" #include "services/identity/public/cpp/identity_manager.h" @@ -30,6 +31,8 @@ ios::SigninManagerFactory::GetForBrowserState(browser_state), ProfileOAuth2TokenServiceFactory::GetForBrowserState(browser_state), ios::AccountTrackerServiceFactory::GetForBrowserState( + browser_state), + ios::GaiaCookieManagerServiceFactory::GetForBrowserState( browser_state)) {} }; @@ -38,6 +41,7 @@ "IdentityManager", BrowserStateDependencyManager::GetInstance()) { DependsOn(ios::AccountTrackerServiceFactory::GetInstance()); + DependsOn(ios::GaiaCookieManagerServiceFactory::GetInstance()); DependsOn(ProfileOAuth2TokenServiceFactory::GetInstance()); DependsOn(ios::SigninManagerFactory::GetInstance()); }
diff --git a/ios/chrome/browser/sync/BUILD.gn b/ios/chrome/browser/sync/BUILD.gn index 07ff3c2..3f46b6fb 100644 --- a/ios/chrome/browser/sync/BUILD.gn +++ b/ios/chrome/browser/sync/BUILD.gn
@@ -5,6 +5,12 @@ source_set("sync") { configs += [ "//build/config/compiler:enable_arc" ] sources = [ + # TODO(crbug.com/850428): Move consent_auditor_factory.* back to + # ios/chrome/browser/consent_auditor, when it does not depend on + # UserEventService anymore. Currently this is not possible due to a + # BUILD.gn depedency. + "consent_auditor_factory.cc", + "consent_auditor_factory.h", "ios_chrome_sync_client.h", "ios_chrome_sync_client.mm", "ios_chrome_synced_tab_delegate.h", @@ -26,6 +32,7 @@ "//base", "//components/autofill/core/browser", "//components/browser_sync", + "//components/consent_auditor", "//components/dom_distiller/core", "//components/history/core/browser", "//components/invalidation/impl", @@ -34,6 +41,7 @@ "//components/network_time", "//components/password_manager/core/browser", "//components/password_manager/sync/browser", + "//components/pref_registry", "//components/prefs", "//components/reading_list/core", "//components/search_engines", @@ -42,6 +50,7 @@ "//components/sync", "//components/sync_preferences", "//components/sync_sessions", + "//components/version_info", "//google_apis", "//ios/chrome/browser", "//ios/chrome/browser/autofill",
diff --git a/ios/chrome/browser/consent_auditor/DEPS b/ios/chrome/browser/sync/DEPS similarity index 100% rename from ios/chrome/browser/consent_auditor/DEPS rename to ios/chrome/browser/sync/DEPS
diff --git a/ios/chrome/browser/sync/OWNERS b/ios/chrome/browser/sync/OWNERS index c1575f5..5dde3db56 100644 --- a/ios/chrome/browser/sync/OWNERS +++ b/ios/chrome/browser/sync/OWNERS
@@ -1,4 +1,9 @@ file://components/sync/OWNERS +per-file consent_auditor_factory.*=jlebel@chromium.org +per-file consent_auditor_factory.*=markusheintz@chromium.org +per-file consent_auditor_factory.*=msarda@chromium.org +per-file consent_auditor_factory.*=msramek@chromium.org + # TEAM: ios-directory-owners@chromium.org # OS: iOS
diff --git a/ios/chrome/browser/consent_auditor/consent_auditor_factory.cc b/ios/chrome/browser/sync/consent_auditor_factory.cc similarity index 93% rename from ios/chrome/browser/consent_auditor/consent_auditor_factory.cc rename to ios/chrome/browser/sync/consent_auditor_factory.cc index ff64ee1..e023a0a 100644 --- a/ios/chrome/browser/consent_auditor/consent_auditor_factory.cc +++ b/ios/chrome/browser/sync/consent_auditor_factory.cc
@@ -2,7 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "ios/chrome/browser/consent_auditor/consent_auditor_factory.h" +// TODO(crbug.com/850428): Move this and .h back to +// ios/chrome/browser/consent_auditor, when it does not depend on +// UserEventService anymore. Currently this is not possible due to a BUILD.gn +// depedency. + +#include "ios/chrome/browser/sync/consent_auditor_factory.h" #include "base/bind_helpers.h" #include "base/feature_list.h"
diff --git a/ios/chrome/browser/consent_auditor/consent_auditor_factory.h b/ios/chrome/browser/sync/consent_auditor_factory.h similarity index 77% rename from ios/chrome/browser/consent_auditor/consent_auditor_factory.h rename to ios/chrome/browser/sync/consent_auditor_factory.h index df19cf1..f79c2c2 100644 --- a/ios/chrome/browser/consent_auditor/consent_auditor_factory.h +++ b/ios/chrome/browser/sync/consent_auditor_factory.h
@@ -2,8 +2,13 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef IOS_CHROME_BROWSER_CONSENT_AUDITOR_CONSENT_AUDITOR_FACTORY_H_ -#define IOS_CHROME_BROWSER_CONSENT_AUDITOR_CONSENT_AUDITOR_FACTORY_H_ +#ifndef IOS_CHROME_BROWSER_SYNC_CONSENT_AUDITOR_FACTORY_H_ +#define IOS_CHROME_BROWSER_SYNC_CONSENT_AUDITOR_FACTORY_H_ + +// TODO(crbug.com/850428): Move this and .cc back to +// ios/chrome/browser/consent_auditor, when it does not depend on +// UserEventService anymore. Currently this is not possible due to a BUILD.gn +// depedency. #include "base/memory/singleton.h" #include "components/keyed_service/ios/browser_state_keyed_service_factory.h" @@ -42,4 +47,4 @@ DISALLOW_COPY_AND_ASSIGN(ConsentAuditorFactory); }; -#endif // IOS_CHROME_BROWSER_CONSENT_AUDITOR_CONSENT_AUDITOR_FACTORY_H_ +#endif // IOS_CHROME_BROWSER_SYNC_CONSENT_AUDITOR_FACTORY_H_
diff --git a/ios/chrome/browser/sync/ios_chrome_sync_client.mm b/ios/chrome/browser/sync/ios_chrome_sync_client.mm index 7810566..6d513071 100644 --- a/ios/chrome/browser/sync/ios_chrome_sync_client.mm +++ b/ios/chrome/browser/sync/ios_chrome_sync_client.mm
@@ -19,6 +19,7 @@ #include "components/browser_sync/browser_sync_switches.h" #include "components/browser_sync/profile_sync_components_factory_impl.h" #include "components/browser_sync/profile_sync_service.h" +#include "components/consent_auditor/consent_auditor.h" #include "components/dom_distiller/core/dom_distiller_service.h" #include "components/history/core/browser/history_model_worker.h" #include "components/history/core/browser/history_service.h" @@ -55,6 +56,7 @@ #include "ios/chrome/browser/passwords/ios_chrome_password_store_factory.h" #include "ios/chrome/browser/pref_names.h" #include "ios/chrome/browser/reading_list/reading_list_model_factory.h" +#include "ios/chrome/browser/sync/consent_auditor_factory.h" #include "ios/chrome/browser/sync/glue/sync_start_util.h" #include "ios/chrome/browser/sync/ios_user_event_service_factory.h" #include "ios/chrome/browser/sync/profile_sync_service_factory.h" @@ -352,9 +354,8 @@ : base::WeakPtr<syncer::ModelTypeControllerDelegate>(); } case syncer::USER_CONSENTS: - // TODO(crbug.com/840357): Return the real delegate once it is wired to - // the consent auditor. - return base::WeakPtr<syncer::ModelTypeControllerDelegate>(); + return ConsentAuditorFactory::GetForBrowserState(browser_state_) + ->GetControllerDelegateOnUIThread(); case syncer::USER_EVENTS: return IOSUserEventServiceFactory::GetForBrowserState(browser_state_) ->GetSyncBridge()
diff --git a/ios/chrome/browser/sync/profile_sync_service_factory.cc b/ios/chrome/browser/sync/profile_sync_service_factory.cc index 9cf4a37..d3a2a46 100644 --- a/ios/chrome/browser/sync/profile_sync_service_factory.cc +++ b/ios/chrome/browser/sync/profile_sync_service_factory.cc
@@ -32,6 +32,7 @@ #include "ios/chrome/browser/signin/identity_manager_factory.h" #include "ios/chrome/browser/signin/signin_client_factory.h" #include "ios/chrome/browser/signin/signin_manager_factory.h" +#include "ios/chrome/browser/sync/consent_auditor_factory.h" #include "ios/chrome/browser/sync/ios_chrome_sync_client.h" #include "ios/chrome/browser/undo/bookmark_undo_service_factory.h" #include "ios/chrome/browser/web_data_service_factory.h" @@ -97,11 +98,7 @@ // when it is shut down. Specify those dependencies here to build the proper // destruction order. DependsOn(autofill::PersonalDataManagerFactory::GetInstance()); - // TODO(crbug.com/850428): This should have - // DependsOn(ConsentAuditorFactory::GetInstance()), but it can't due to a - // BUILD.gn dependency cycle. Note that this can be added only after - // ConsentAuditorFactory does not depend on both UserEventService and - // SyncService. + DependsOn(ConsentAuditorFactory::GetInstance()); DependsOn(ios::AboutSigninInternalsFactory::GetInstance()); DependsOn(ios::BookmarkModelFactory::GetInstance()); DependsOn(ios::BookmarkUndoServiceFactory::GetInstance());
diff --git a/ios/chrome/browser/ui/activity_services/activity_service_controller_egtest.mm b/ios/chrome/browser/ui/activity_services/activity_service_controller_egtest.mm index 5f8dd2c7..35fcf61 100644 --- a/ios/chrome/browser/ui/activity_services/activity_service_controller_egtest.mm +++ b/ios/chrome/browser/ui/activity_services/activity_service_controller_egtest.mm
@@ -85,13 +85,14 @@ EARL_GREY_TEST_DISABLED(@"Test disabled on device."); #endif - // TODO(crbug.com/849932): re-enable this test on iOS 10 once the share button - // is implemented. + // TODO(crbug.com/864597): Reenable this test. + EARL_GREY_TEST_DISABLED(@"Test disabled due to changes in error pages."); + // TODO(crbug.com/747622): re-enable this test on iOS 11 once earl grey can // interact with the share menu. - EARL_GREY_TEST_DISABLED( - @"Disabled until share button is implemented and " - "EG interaction is fixed on iOS 11"); + if (base::ios::IsRunningOnIOS11OrLater()) { + EARL_GREY_TEST_DISABLED(@"Disabled on iOS 11."); + } std::map<GURL, std::string> responses; const GURL regularPageURL = web::test::HttpServer::MakeUrl("http://choux"); @@ -123,13 +124,14 @@ } - (void)testActivityServiceControllerCantPrintUnprintablePages { - // TODO(crbug.com/849932): re-enable this test on iOS 10 once the share button - // is implemented. // TODO(crbug.com/747622): re-enable this test on iOS 11 once earl grey can // interact with the share menu. - EARL_GREY_TEST_DISABLED( - @"Disabled until share button is implemented and " - "EG interaction is fixed on iOS 11"); + if (base::ios::IsRunningOnIOS11OrLater()) { + EARL_GREY_TEST_DISABLED(@"Disabled on iOS 11."); + } + + // TODO(crbug.com/864597): Reenable this test. + EARL_GREY_TEST_DISABLED(@"Test disabled due to changes in error pages."); std::unique_ptr<web::DataResponseProvider> provider( new ErrorPageResponseProvider()); @@ -158,19 +160,11 @@ } - (void)testActivityServiceControllerIsDisabled { - // TODO(crbug.com/849932): re-enable this test on iOS 10 once the share button - // is implemented. - // TODO(crbug.com/747622): re-enable this test on iOS 11 once earl grey can - // interact with the share menu. - EARL_GREY_TEST_DISABLED( - @"Disabled until share button is implemented and " - "EG interaction is fixed on iOS 11"); - // Open an un-shareable page. GURL kURL("chrome://version"); [ChromeEarlGrey loadURL:kURL]; // Verify that the share button is disabled. - if (IsCompactWidth()) { + if (IsCompactWidth() && !IsUIRefreshPhase1Enabled()) { [ChromeEarlGreyUI openToolsMenu]; } id<GREYMatcher> share_button = chrome_test_util::ShareButton(); @@ -180,13 +174,11 @@ } - (void)testOpenActivityServiceControllerAndCopy { - // TODO(crbug.com/849932): re-enable this test on iOS 10 once the share button - // is implemented. // TODO(crbug.com/747622): re-enable this test on iOS 11 once earl grey can // interact with the share menu. - EARL_GREY_TEST_DISABLED( - @"Disabled until share button is implemented and " - "EG interaction is fixed on iOS 11"); + if (base::ios::IsRunningOnIOS11OrLater()) { + EARL_GREY_TEST_DISABLED(@"Disabled on iOS 11."); + } // Set up mock http server. std::map<GURL, std::string> responses;
diff --git a/ios/chrome/browser/ui/authentication/BUILD.gn b/ios/chrome/browser/ui/authentication/BUILD.gn index 507abba..1919ee9 100644 --- a/ios/chrome/browser/ui/authentication/BUILD.gn +++ b/ios/chrome/browser/ui/authentication/BUILD.gn
@@ -46,7 +46,6 @@ "//ios/chrome/app/strings", "//ios/chrome/browser", "//ios/chrome/browser/browser_state", - "//ios/chrome/browser/consent_auditor", "//ios/chrome/browser/infobars", "//ios/chrome/browser/signin", "//ios/chrome/browser/sync", @@ -141,7 +140,6 @@ "//ios/chrome/app/strings:ios_strings_grit", "//ios/chrome/browser", "//ios/chrome/browser/browser_state:test_support", - "//ios/chrome/browser/consent_auditor", "//ios/chrome/browser/infobars", "//ios/chrome/browser/prefs:browser_prefs", "//ios/chrome/browser/signin",
diff --git a/ios/chrome/browser/ui/authentication/chrome_signin_view_controller.mm b/ios/chrome/browser/ui/authentication/chrome_signin_view_controller.mm index 0d5e50b..728a9606 100644 --- a/ios/chrome/browser/ui/authentication/chrome_signin_view_controller.mm +++ b/ios/chrome/browser/ui/authentication/chrome_signin_view_controller.mm
@@ -24,12 +24,12 @@ #include "components/strings/grit/components_strings.h" #import "ios/chrome/browser/browser_state/chrome_browser_state.h" #include "ios/chrome/browser/chrome_url_constants.h" -#include "ios/chrome/browser/consent_auditor/consent_auditor_factory.h" #include "ios/chrome/browser/signin/account_tracker_service_factory.h" #import "ios/chrome/browser/signin/authentication_service.h" #import "ios/chrome/browser/signin/authentication_service_factory.h" #import "ios/chrome/browser/signin/chrome_identity_service_observer_bridge.h" #include "ios/chrome/browser/signin/signin_util.h" +#include "ios/chrome/browser/sync/consent_auditor_factory.h" #import "ios/chrome/browser/sync/sync_setup_service.h" #import "ios/chrome/browser/sync/sync_setup_service_factory.h" #import "ios/chrome/browser/ui/alert_coordinator/alert_coordinator.h"
diff --git a/ios/chrome/browser/ui/authentication/chrome_signin_view_controller_unittest.mm b/ios/chrome/browser/ui/authentication/chrome_signin_view_controller_unittest.mm index 0bbaef6..6dcc70b4 100644 --- a/ios/chrome/browser/ui/authentication/chrome_signin_view_controller_unittest.mm +++ b/ios/chrome/browser/ui/authentication/chrome_signin_view_controller_unittest.mm
@@ -17,10 +17,10 @@ #include "components/version_info/version_info.h" #include "ios/chrome/browser/application_context.h" #include "ios/chrome/browser/browser_state/test_chrome_browser_state.h" -#include "ios/chrome/browser/consent_auditor/consent_auditor_factory.h" #include "ios/chrome/browser/signin/account_tracker_service_factory.h" #import "ios/chrome/browser/signin/authentication_service_factory.h" #import "ios/chrome/browser/signin/authentication_service_fake.h" +#include "ios/chrome/browser/sync/consent_auditor_factory.h" #include "ios/chrome/browser/sync/ios_user_event_service_factory.h" #include "ios/chrome/grit/ios_chromium_strings.h" #include "ios/chrome/grit/ios_strings.h"
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_updater.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_updater.mm index 6843e9bb..ced62780 100644 --- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_updater.mm +++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_updater.mm
@@ -340,6 +340,18 @@ collectionView:self.collectionViewController.collectionView willDeleteItemsAtIndexPaths:oldItems]; + // Reset collection model data for |sectionIdentifier| + [self.collectionViewController.collectionViewModel + setFooter:nil + forSectionWithIdentifier:sectionIdentifier]; + [self.collectionViewController.collectionViewModel + setHeader:nil + forSectionWithIdentifier:sectionIdentifier]; + [self.sectionIdentifiersFromContentSuggestions + removeObject:@(sectionIdentifier)]; + [self.collectionViewController.collectionViewModel + removeSectionWithIdentifier:sectionIdentifier]; + [self addSectionsForSectionInfoToModel:@[ sectionInfo ]]; [self addSuggestionsToModel:[self.dataSource itemsForSectionInfo:sectionInfo] withSectionInfo:sectionInfo];
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_mediator.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_mediator.mm index 199072d..40ca0cf 100644 --- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_mediator.mm +++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_mediator.mm
@@ -378,8 +378,11 @@ self.sectionInformationByCategory[wrapper]; sectionInfo.expanded = [self.contentArticlesExpanded value]; - // Reload the data model. - [self.dataSink reloadAllData]; + // Reloading the section with animations looks bad because the section + // border with the new collapsed height draws before the elements collapse. + [UIView setAnimationsEnabled:NO]; + [self.dataSink reloadSection:sectionInfo]; + [UIView setAnimationsEnabled:YES]; } #pragma mark - ContentSuggestionsServiceObserver
diff --git a/ios/chrome/browser/ui/dialogs/javascript_dialog_egtest.mm b/ios/chrome/browser/ui/dialogs/javascript_dialog_egtest.mm index 925e69cd..8a7a1f7 100644 --- a/ios/chrome/browser/ui/dialogs/javascript_dialog_egtest.mm +++ b/ios/chrome/browser/ui/dialogs/javascript_dialog_egtest.mm
@@ -484,13 +484,11 @@ // Tests that an alert is presented after displaying the share menu. - (void)testShowJavaScriptAfterShareMenu { - // TODO(crbug.com/849932): re-enable this test on iOS 10 once the share button - // is implemented. // TODO(crbug.com/747622): re-enable this test on iOS 11 once earl grey can // interact with the share menu. - EARL_GREY_TEST_DISABLED( - @"Disabled until share button is implemented and " - "EG interaction is fixed on iOS 11"); + if (base::ios::IsRunningOnIOS11OrLater()) { + EARL_GREY_TEST_DISABLED(@"Disabled on iOS 11."); + } // Load the blank test page. [self loadBlankTestPage];
diff --git a/ios/chrome/browser/ui/location_bar/location_bar_consumer.h b/ios/chrome/browser/ui/location_bar/location_bar_consumer.h index a5dacc16..a1ccc90 100644 --- a/ios/chrome/browser/ui/location_bar/location_bar_consumer.h +++ b/ios/chrome/browser/ui/location_bar/location_bar_consumer.h
@@ -14,6 +14,11 @@ - (void)updateLocationIcon:(UIImage*)icon securityStatusText:(NSString*)statusText; +// Notifies the consumer about shareability of the current web page. Some web +// pages are not considered shareable (e.g. chrome://flags), and the share +// button for such pages should not be enabled. +- (void)updateLocationShareable:(BOOL)shareable; + // Notifies consumer to defocus the omnibox (for example on tab change). - (void)defocusOmnibox;
diff --git a/ios/chrome/browser/ui/location_bar/location_bar_coordinator.mm b/ios/chrome/browser/ui/location_bar/location_bar_coordinator.mm index 92060a1..91076db 100644 --- a/ios/chrome/browser/ui/location_bar/location_bar_coordinator.mm +++ b/ios/chrome/browser/ui/location_bar/location_bar_coordinator.mm
@@ -297,6 +297,10 @@ [self.viewController updateForNTP:YES]; } +- (void)updateLocationShareable:(BOOL)shareable { + [self.viewController setShareButtonEnabled:shareable]; +} + #pragma mark - private // Returns a dictionary with variation headers for qualified URLs. Can be empty.
diff --git a/ios/chrome/browser/ui/location_bar/location_bar_mediator.mm b/ios/chrome/browser/ui/location_bar/location_bar_mediator.mm index 6a3da90fc..70bc4485 100644 --- a/ios/chrome/browser/ui/location_bar/location_bar_mediator.mm +++ b/ios/chrome/browser/ui/location_bar/location_bar_mediator.mm
@@ -188,6 +188,7 @@ if (isNTP) { [self.consumer updateAfterNavigatingToNTP]; } + [self.consumer updateLocationShareable:[self isCurrentPageShareable]]; } - (void)notifyConsumerOfChangedSecurityIcon { @@ -228,4 +229,11 @@ return [UIImage imageNamed:@"location_bar_offline"]; } +#pragma mark Shareability helpers + +- (BOOL)isCurrentPageShareable { + const GURL& URL = self.webState->GetLastCommittedURL(); + return URL.is_valid() && !web::GetWebClient()->IsAppSpecificURL(URL); +} + @end
diff --git a/ios/chrome/browser/ui/location_bar/location_bar_steady_view.mm b/ios/chrome/browser/ui/location_bar/location_bar_steady_view.mm index 8419a95..f0ed182 100644 --- a/ios/chrome/browser/ui/location_bar/location_bar_steady_view.mm +++ b/ios/chrome/browser/ui/location_bar/location_bar_steady_view.mm
@@ -213,8 +213,6 @@ // Setup accessibility. _trailingButton.isAccessibilityElement = YES; - _trailingButton.accessibilityLabel = - l10n_util::GetNSString(IDS_IOS_TOOLS_MENU_SHARE); _locationButton.isAccessibilityElement = YES; _locationButton.accessibilityLabel = l10n_util::GetNSString(IDS_ACCNAME_LOCATION);
diff --git a/ios/chrome/browser/ui/location_bar/location_bar_view_controller.h b/ios/chrome/browser/ui/location_bar/location_bar_view_controller.h index 440cbde0..db6e6f6 100644 --- a/ios/chrome/browser/ui/location_bar/location_bar_view_controller.h +++ b/ios/chrome/browser/ui/location_bar/location_bar_view_controller.h
@@ -69,6 +69,8 @@ // view and hides the trailing button if |isNTP|. Otherwise, shows the // location text and the button as normal. - (void)updateForNTP:(BOOL)isNTP; +// Sets |enabled| of the share button. +- (void)setShareButtonEnabled:(BOOL)enabled; // Displays the voice search button instead of the share button in steady state, // and adds the voice search button to the empty textfield.
diff --git a/ios/chrome/browser/ui/location_bar/location_bar_view_controller.mm b/ios/chrome/browser/ui/location_bar/location_bar_view_controller.mm index e0a17dc..5c752f7c 100644 --- a/ios/chrome/browser/ui/location_bar/location_bar_view_controller.mm +++ b/ios/chrome/browser/ui/location_bar/location_bar_view_controller.mm
@@ -45,6 +45,11 @@ // visibility. @property(nonatomic, assign) BOOL hideShareButtonWhileOnIncognitoNTP; +// Keeps the share button enabled status. This is necessary to preserve the +// state of the share button if it's temporarily replaced by the voice search +// icon (in iPad multitasking). +@property(nonatomic, assign) BOOL shareButtonEnabled; + // Starts voice search, updating the NamedGuide to be constrained to the // trailing button. - (void)startVoiceSearch; @@ -64,6 +69,7 @@ @synthesize trailingButtonState = _trailingButtonState; @synthesize hideShareButtonWhileOnIncognitoNTP = _hideShareButtonWhileOnIncognitoNTP; +@synthesize shareButtonEnabled = _shareButtonEnabled; @synthesize offsetProvider = _offsetProvider; #pragma mark - public @@ -233,6 +239,13 @@ self.hideShareButtonWhileOnIncognitoNTP = isNTP; } +- (void)setShareButtonEnabled:(BOOL)enabled { + _shareButtonEnabled = enabled; + if (self.trailingButtonState == kShareButton) { + self.locationBarSteadyView.trailingButton.enabled = enabled; + } +} + #pragma mark - LocationBarAnimatee - (void)offsetEditViewToMatchSteadyView { @@ -340,6 +353,10 @@ [[UIImage imageNamed:@"location_bar_share"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate] forState:UIControlStateNormal]; + self.locationBarSteadyView.trailingButton.accessibilityLabel = + l10n_util::GetNSString(IDS_IOS_TOOLS_MENU_SHARE); + self.locationBarSteadyView.trailingButton.enabled = + self.shareButtonEnabled; break; }; case kVoiceSearchButton: { @@ -356,6 +373,9 @@ [[UIImage imageNamed:@"location_bar_voice"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate] forState:UIControlStateNormal]; + self.locationBarSteadyView.trailingButton.accessibilityLabel = + l10n_util::GetNSString(IDS_IOS_TOOLS_MENU_VOICE_SEARCH); + self.locationBarSteadyView.trailingButton.enabled = YES; } } }
diff --git a/ios/chrome/browser/ui/print/print_controller_egtest.mm b/ios/chrome/browser/ui/print/print_controller_egtest.mm index ad3f3f88..f71b9154 100644 --- a/ios/chrome/browser/ui/print/print_controller_egtest.mm +++ b/ios/chrome/browser/ui/print/print_controller_egtest.mm
@@ -64,14 +64,6 @@ // loaded. // TODO(crbug.com/683280): Does this test serve any purpose on iOS11? - (void)testPrintNormalPage { - // TODO(crbug.com/849932): re-enable this test on iOS 10 once the share button - // is implemented. - // TODO(crbug.com/747622): re-enable this test on iOS 11 once earl grey can - // interact with the share menu. - EARL_GREY_TEST_DISABLED( - @"Disabled until share button is implemented and " - "EG interaction is fixed on iOS 11"); - if (base::ios::IsRunningOnIOS11OrLater() && IsUIRefreshPhase1Enabled()) { EARL_GREY_TEST_SKIPPED( @"Dispatcher-based printing does not work on iOS11 when the " @@ -93,14 +85,6 @@ // Tests that the AirPrint menu successfully loads when a PDF is loaded. // TODO(crbug.com/683280): Does this test serve any purpose on iOS11? - (void)testPrintPDF { - // TODO(crbug.com/849932): re-enable this test on iOS 10 once the share button - // is implemented. - // TODO(crbug.com/747622): re-enable this test on iOS 11 once earl grey can - // interact with the share menu. - EARL_GREY_TEST_DISABLED( - @"Disabled until share button is implemented and " - "EG interaction is fixed on iOS 11"); - if (base::ios::IsRunningOnIOS11OrLater() && IsUIRefreshPhase1Enabled()) { EARL_GREY_TEST_SKIPPED( @"Dispatcher-based printing does not work on iOS11 when the "
diff --git a/ios/chrome/browser/ui/settings/BUILD.gn b/ios/chrome/browser/ui/settings/BUILD.gn index af8deb2..4ebd16b 100644 --- a/ios/chrome/browser/ui/settings/BUILD.gn +++ b/ios/chrome/browser/ui/settings/BUILD.gn
@@ -186,6 +186,7 @@ "//ios/chrome/browser/ui/table_view:styler", "//ios/chrome/browser/ui/table_view/cells", "//ios/chrome/browser/ui/table_view/cells/resources:table_view_cell_check_mark", + "//ios/chrome/browser/unified_consent", "//ios/chrome/browser/voice", "//ios/chrome/browser/web:web", "//ios/chrome/common",
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_switcher_controller_egtest.mm b/ios/chrome/browser/ui/tab_switcher/tab_switcher_controller_egtest.mm index 8f01f7c..51d9900 100644 --- a/ios/chrome/browser/ui/tab_switcher/tab_switcher_controller_egtest.mm +++ b/ios/chrome/browser/ui/tab_switcher/tab_switcher_controller_egtest.mm
@@ -439,6 +439,8 @@ [SigninEarlGreyUI signinWithIdentity:identity]; [ChromeEarlGreyUI openSettingsMenu]; + [ChromeEarlGreyUI + tapSettingsMenuButton:chrome_test_util::SettingsAccountButton()]; // Sign-out. [ChromeEarlGreyUI tapSettingsMenuButton:chrome_test_util::SignOutAccountsButton()];
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_switcher_transition_egtest.mm b/ios/chrome/browser/ui/tab_switcher/tab_switcher_transition_egtest.mm index ad5779c..92c9890 100644 --- a/ios/chrome/browser/ui/tab_switcher/tab_switcher_transition_egtest.mm +++ b/ios/chrome/browser/ui/tab_switcher/tab_switcher_transition_egtest.mm
@@ -6,7 +6,6 @@ #import <UIKit/UIKit.h> #import <XCTest/XCTest.h> -#include "base/ios/ios_util.h" #include "base/strings/stringprintf.h" #include "base/strings/sys_string_conversions.h" #import "base/test/ios/wait_util.h" @@ -261,11 +260,6 @@ // Tests exiting the switcher by tapping the new tab button or selecting new tab // from the menu (on phone only). - (void)testLeaveSwitcherByOpeningNewNormalTab { - // TODO(crbug.com/849937): re-enable this test on iOS 10. - if (!base::ios::IsRunningOnIOS11OrLater()) { - EARL_GREY_TEST_DISABLED(@"Disabled on iOS10"); - } - NSString* tab1_title = @"NormalTab1"; NSString* tab2_title = @"NormalTab2"; [self setUpTestServer];
diff --git a/ios/chrome/browser/ui/toolbar/clean/toolbar_mediator.mm b/ios/chrome/browser/ui/toolbar/clean/toolbar_mediator.mm index cd1baf5..ebb0a3b1 100644 --- a/ios/chrome/browser/ui/toolbar/clean/toolbar_mediator.mm +++ b/ios/chrome/browser/ui/toolbar/clean/toolbar_mediator.mm
@@ -260,7 +260,7 @@ } } -// Uodates the Share Menu button of the consumer. +// Updates the Share Menu button of the consumer. - (void)updateShareMenuForWebState:(web::WebState*)webState { const GURL& URL = webState->GetLastCommittedURL(); BOOL shareMenuEnabled =
diff --git a/ios/chrome/browser/ui/toolbar/toolbar_egtest.mm b/ios/chrome/browser/ui/toolbar/toolbar_egtest.mm index df0e9fc..edc8d26 100644 --- a/ios/chrome/browser/ui/toolbar/toolbar_egtest.mm +++ b/ios/chrome/browser/ui/toolbar/toolbar_egtest.mm
@@ -288,11 +288,6 @@ // location bar is supported. EARL_GREY_TEST_SKIPPED(@"Test not supported yet in UI Refresh."); } - // TODO(crbug.com/849932): re-enable this test on iOS 10 once the share button - // is implemented. - if (!base::ios::IsRunningOnIOS11OrLater()) { - EARL_GREY_TEST_DISABLED(@"Disabled until share button is implemented."); - } // Clear generalPasteboard before and after the test. [UIPasteboard generalPasteboard].string = @"";
diff --git a/ios/chrome/browser/unified_consent/BUILD.gn b/ios/chrome/browser/unified_consent/BUILD.gn new file mode 100644 index 0000000..37f1254 --- /dev/null +++ b/ios/chrome/browser/unified_consent/BUILD.gn
@@ -0,0 +1,25 @@ +# 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. + +source_set("unified_consent") { + configs += [ "//build/config/compiler:enable_arc" ] + sources = [ + "unified_consent_service_client_impl.cc", + "unified_consent_service_client_impl.h", + "unified_consent_service_factory.cc", + "unified_consent_service_factory.h", + ] + deps = [ + "//components/browser_sync", + "//components/keyed_service/ios", + "//components/metrics", + "//components/signin/core/browser", + "//components/sync", + "//components/unified_consent", + "//ios/chrome/browser", + "//ios/chrome/browser/browser_state", + "//ios/chrome/browser/signin", + "//ios/chrome/browser/sync", + ] +}
diff --git a/ios/chrome/browser/unified_consent/OWNERS b/ios/chrome/browser/unified_consent/OWNERS new file mode 100644 index 0000000..c4301ca --- /dev/null +++ b/ios/chrome/browser/unified_consent/OWNERS
@@ -0,0 +1,4 @@ +msarda@chromium.org +jlebel@chromium.org + +# TEAM: chrome-signin@chromium.org
diff --git a/ios/chrome/browser/unified_consent/unified_consent_service_client_impl.cc b/ios/chrome/browser/unified_consent/unified_consent_service_client_impl.cc new file mode 100644 index 0000000..835e5070 --- /dev/null +++ b/ios/chrome/browser/unified_consent/unified_consent_service_client_impl.cc
@@ -0,0 +1,54 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ios/chrome/browser/unified_consent/unified_consent_service_client_impl.h" + +#include "components/metrics/metrics_pref_names.h" +#include "components/prefs/pref_member.h" +#include "components/prefs/pref_service.h" +#include "ios/chrome/browser/pref_names.h" + +UnifiedConsentServiceClientImpl::UnifiedConsentServiceClientImpl( + PrefService* pref_service) + : pref_service_(pref_service) {} + +void UnifiedConsentServiceClientImpl::SetAlternateErrorPagesEnabled( + bool enabled) { + // Feature not available on iOS. + NOTREACHED(); +} + +void UnifiedConsentServiceClientImpl::SetMetricsReportingEnabled(bool enabled) { + BooleanPrefMember basePreference; + basePreference.Init(metrics::prefs::kMetricsReportingEnabled, pref_service_); + basePreference.SetValue(enabled); + BooleanPrefMember wifiPreference; + wifiPreference.Init(prefs::kMetricsReportingWifiOnly, pref_service_); + wifiPreference.SetValue(enabled); +} + +void UnifiedConsentServiceClientImpl::SetSearchSuggestEnabled(bool enabled) { + pref_service_->SetBoolean(prefs::kSearchSuggestEnabled, enabled); +} + +void UnifiedConsentServiceClientImpl::SetSafeBrowsingEnabled(bool enabled) { + // Feature not available on iOS. + NOTREACHED(); +} + +void UnifiedConsentServiceClientImpl::SetSafeBrowsingExtendedReportingEnabled( + bool enabled) { + // Feature not available on iOS. + NOTREACHED(); +} + +void UnifiedConsentServiceClientImpl::SetNetworkPredictionEnabled( + bool enabled) { + BooleanPrefMember basePreference; + basePreference.Init(prefs::kNetworkPredictionEnabled, pref_service_); + basePreference.SetValue(enabled); + BooleanPrefMember wifiPreference; + wifiPreference.Init(prefs::kNetworkPredictionWifiOnly, pref_service_); + wifiPreference.SetValue(enabled); +}
diff --git a/ios/chrome/browser/unified_consent/unified_consent_service_client_impl.h b/ios/chrome/browser/unified_consent/unified_consent_service_client_impl.h new file mode 100644 index 0000000..88339c0 --- /dev/null +++ b/ios/chrome/browser/unified_consent/unified_consent_service_client_impl.h
@@ -0,0 +1,33 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef IOS_CHROME_BROWSER_UNIFIED_CONSENT_UNIFIED_CONSENT_SERVICE_CLIENT_IMPL_H_ +#define IOS_CHROME_BROWSER_UNIFIED_CONSENT_UNIFIED_CONSENT_SERVICE_CLIENT_IMPL_H_ + +#include "base/macros.h" +#include "components/unified_consent/unified_consent_service_client.h" + +class PrefService; + +// iOS implementation for UnifiedConsentServiceClient. +class UnifiedConsentServiceClientImpl + : public unified_consent::UnifiedConsentServiceClient { + public: + explicit UnifiedConsentServiceClientImpl(PrefService* pref_service); + ~UnifiedConsentServiceClientImpl() override = default; + + void SetAlternateErrorPagesEnabled(bool enabled) override; + void SetMetricsReportingEnabled(bool enabled) override; + void SetSearchSuggestEnabled(bool enabled) override; + void SetSafeBrowsingEnabled(bool enabled) override; + void SetSafeBrowsingExtendedReportingEnabled(bool enabled) override; + void SetNetworkPredictionEnabled(bool enabled) override; + + private: + PrefService* pref_service_; + + DISALLOW_COPY_AND_ASSIGN(UnifiedConsentServiceClientImpl); +}; + +#endif // IOS_CHROME_BROWSER_UNIFIED_CONSENT_UNIFIED_CONSENT_SERVICE_CLIENT_IMPL_H_
diff --git a/ios/chrome/browser/unified_consent/unified_consent_service_factory.cc b/ios/chrome/browser/unified_consent/unified_consent_service_factory.cc new file mode 100644 index 0000000..714d4ba --- /dev/null +++ b/ios/chrome/browser/unified_consent/unified_consent_service_factory.cc
@@ -0,0 +1,65 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ios/chrome/browser/unified_consent/unified_consent_service_factory.h" + +#include "components/browser_sync/profile_sync_service.h" +#include "components/keyed_service/ios/browser_state_dependency_manager.h" +#include "components/sync/driver/sync_service.h" +#include "components/unified_consent/unified_consent_service.h" +#include "ios/chrome/browser/browser_state/chrome_browser_state.h" +#include "ios/chrome/browser/signin/identity_manager_factory.h" +#include "ios/chrome/browser/sync/profile_sync_service_factory.h" +#include "ios/chrome/browser/unified_consent/unified_consent_service_client_impl.h" + +UnifiedConsentServiceFactory::UnifiedConsentServiceFactory() + : BrowserStateKeyedServiceFactory( + "UnifiedConsentService", + BrowserStateDependencyManager::GetInstance()) { + DependsOn(IdentityManagerFactory::GetInstance()); + DependsOn(ProfileSyncServiceFactory::GetInstance()); +} + +UnifiedConsentServiceFactory::~UnifiedConsentServiceFactory() = default; + +// static +unified_consent::UnifiedConsentService* +UnifiedConsentServiceFactory::GetForBrowserState( + ios::ChromeBrowserState* browser_state) { + return static_cast<unified_consent::UnifiedConsentService*>( + GetInstance()->GetServiceForBrowserState(browser_state, true)); +} + +// static +unified_consent::UnifiedConsentService* +UnifiedConsentServiceFactory::GetForBrowserStateIfExists( + ios::ChromeBrowserState* browser_state) { + return static_cast<unified_consent::UnifiedConsentService*>( + GetInstance()->GetServiceForBrowserState(browser_state, false)); +} + +// static +UnifiedConsentServiceFactory* UnifiedConsentServiceFactory::GetInstance() { + return base::Singleton<UnifiedConsentServiceFactory>::get(); +} + +std::unique_ptr<KeyedService> +UnifiedConsentServiceFactory::BuildServiceInstanceFor( + web::BrowserState* context) const { + if (signin::GetUnifiedConsentFeatureState() == + signin::UnifiedConsentFeatureState::kDisabled) + return nullptr; + + ios::ChromeBrowserState* browser_state = + ios::ChromeBrowserState::FromBrowserState(context); + PrefService* pref_service = browser_state->GetPrefs(); + std::unique_ptr<unified_consent::UnifiedConsentServiceClient> service_client = + std::make_unique<UnifiedConsentServiceClientImpl>(pref_service); + identity::IdentityManager* identity_manager = + IdentityManagerFactory::GetForBrowserState(browser_state); + syncer::SyncService* sync_service = + ProfileSyncServiceFactory::GetForBrowserState(browser_state); + return std::make_unique<unified_consent::UnifiedConsentService>( + std::move(service_client), pref_service, identity_manager, sync_service); +}
diff --git a/ios/chrome/browser/unified_consent/unified_consent_service_factory.h b/ios/chrome/browser/unified_consent/unified_consent_service_factory.h new file mode 100644 index 0000000..f53c9e58 --- /dev/null +++ b/ios/chrome/browser/unified_consent/unified_consent_service_factory.h
@@ -0,0 +1,41 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef IOS_CHROME_BROWSER_UNIFIED_CONSENT_UNIFIED_CONSENT_SERVICE_FACTORY_H_ +#define IOS_CHROME_BROWSER_UNIFIED_CONSENT_UNIFIED_CONSENT_SERVICE_FACTORY_H_ + +#include "base/memory/singleton.h" +#include "components/keyed_service/ios/browser_state_keyed_service_factory.h" + +namespace unified_consent { +class UnifiedConsentService; +} +namespace ios { +class ChromeBrowserState; +} + +class UnifiedConsentServiceFactory : public BrowserStateKeyedServiceFactory { + public: + static unified_consent::UnifiedConsentService* GetForBrowserState( + ios::ChromeBrowserState* browser_state); + + static unified_consent::UnifiedConsentService* GetForBrowserStateIfExists( + ios::ChromeBrowserState* browser_state); + + static UnifiedConsentServiceFactory* GetInstance(); + + private: + friend struct base::DefaultSingletonTraits<UnifiedConsentServiceFactory>; + + UnifiedConsentServiceFactory(); + ~UnifiedConsentServiceFactory() override; + + // BrowserStateKeyedServiceFactory implementation. + std::unique_ptr<KeyedService> BuildServiceInstanceFor( + web::BrowserState* context) const override; + + DISALLOW_COPY_AND_ASSIGN(UnifiedConsentServiceFactory); +}; + +#endif // IOS_CHROME_BROWSER_UNIFIED_CONSENT_UNIFIED_CONSENT_SERVICE_FACTORY_H_
diff --git a/ios/chrome/test/earl_grey/chrome_earl_grey_ui.mm b/ios/chrome/test/earl_grey/chrome_earl_grey_ui.mm index 2be03f1..f7814a9 100644 --- a/ios/chrome/test/earl_grey/chrome_earl_grey_ui.mm +++ b/ios/chrome/test/earl_grey/chrome_earl_grey_ui.mm
@@ -144,7 +144,7 @@ } + (void)openShareMenu { - if (IsCompactWidth()) { + if (IsCompactWidth() && !IsRefreshLocationBarEnabled()) { [ChromeEarlGreyUI openToolsMenu]; } [[EarlGrey selectElementWithMatcher:chrome_test_util::ShareButton()]
diff --git a/ios/chrome/test/earl_grey/chrome_matchers.mm b/ios/chrome/test/earl_grey/chrome_matchers.mm index dc1292c..69739cb 100644 --- a/ios/chrome/test/earl_grey/chrome_matchers.mm +++ b/ios/chrome/test/earl_grey/chrome_matchers.mm
@@ -242,7 +242,8 @@ } id<GREYMatcher> ShareButton() { - return ButtonWithAccessibilityLabelId(IDS_IOS_TOOLS_MENU_SHARE); + return grey_allOf(ButtonWithAccessibilityLabelId(IDS_IOS_TOOLS_MENU_SHARE), + grey_sufficientlyVisible(), nil); } id<GREYMatcher> ShowTabsButton() {
diff --git a/ios/web/BUILD.gn b/ios/web/BUILD.gn index f31ecfe..3f9886a6 100644 --- a/ios/web/BUILD.gn +++ b/ios/web/BUILD.gn
@@ -559,6 +559,7 @@ "//ios/web/test:test_constants", "//ios/web/test:test_support", "//ios/web/web_state", + "//ios/web/web_state:web_frame", "//mojo/core/embedder", "//net:test_support", "//services/network/public/cpp", @@ -577,6 +578,7 @@ "web_state/favicon_callbacks_inttest.mm", "web_state/http_auth_inttest.mm", "web_state/navigation_and_load_callbacks_inttest.mm", + "web_state/web_frames_manager_inttest.mm", "webui/web_ui_mojo_inttest.mm", ] @@ -625,6 +627,17 @@ ] } +js_compile_bundle("all_frames_document_end_web_bundle") { + visibility = [ ":js_resources" ] + closure_entry_point = "__crWeb.allFramesDocumentEndWebBundle" + + sources = [ + "web_state/js/resources/all_frames_document_end_web_bundle.js", + "web_state/js/resources/plugin_placeholder.js", + "web_state/js/resources/setup_frame.js", + ] +} + js_compile_bundle("nav_bundle") { visibility = [ ":js_resources" ] closure_entry_point = "__crWeb.legacynavigation" @@ -636,6 +649,7 @@ js_compile_checked("js_resources") { public_deps = [ + ":all_frames_document_end_web_bundle", ":all_frames_web_bundle", ":main_frame_web_bundle", ":nav_bundle", @@ -643,7 +657,6 @@ ] sources = [ - "web_state/js/resources/plugin_placeholder.js", "web_state/js/resources/post_request.js", "web_state/js/resources/window_id.js", ]
diff --git a/ios/web/js_compile.gni b/ios/web/js_compile.gni index 83bcc0b..9fd25b1 100644 --- a/ios/web/js_compile.gni +++ b/ios/web/js_compile.gni
@@ -122,6 +122,7 @@ _js_modules = [ "//ios/web/web_state/js/resources/base.js", "//ios/web/web_state/js/resources/common.js", + "//ios/web/web_state/js/resources/frame_messaging.js", "//ios/web/web_state/js/resources/message.js", ] if (defined(invoker.js_modules)) {
diff --git a/ios/web/web_state/js/page_script_util.mm b/ios/web/web_state/js/page_script_util.mm index 6a3d8307..b6aa8f0e 100644 --- a/ios/web/web_state/js/page_script_util.mm +++ b/ios/web/web_state/js/page_script_util.mm
@@ -107,7 +107,7 @@ NSString* plugin_not_supported_text = base::SysUTF16ToNSString(GetWebClient()->GetPluginNotSupportedText()); - NSString* script = [GetPageScript(@"plugin_placeholder") + NSString* script = [GetPageScript(@"all_frames_document_end_web_bundle") stringByReplacingOccurrencesOfString:@"$(PLUGIN_NOT_SUPPORTED_TEXT)" withString:EscapedQuotedString( plugin_not_supported_text)];
diff --git a/ios/web/web_state/js/resources/all_frames_document_end_web_bundle.js b/ios/web/web_state/js/resources/all_frames_document_end_web_bundle.js new file mode 100644 index 0000000..3e11f329 --- /dev/null +++ b/ios/web/web_state/js/resources/all_frames_document_end_web_bundle.js
@@ -0,0 +1,9 @@ +// 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. + +// Set of scripts injected into WKWebView at Document End. +goog.provide('__crWeb.allFramesDocumentEndWebBundle'); + +goog.require('__crWeb.pluginPlaceholder'); +goog.require('__crWeb.setupFrame');
diff --git a/ios/web/web_state/js/resources/frame_messaging.js b/ios/web/web_state/js/resources/frame_messaging.js index bcc2e4f..36a9969b 100644 --- a/ios/web/web_state/js/resources/frame_messaging.js +++ b/ios/web/web_state/js/resources/frame_messaging.js
@@ -28,7 +28,7 @@ * @type {string} * @private */ -var frameId_ = undefined; +var frameId_ = ""; /** * Returns the frameId associated with this frame. A new value will be created @@ -36,8 +36,7 @@ * @return {string} A string representing a unique identifier for this frame. */ __gCrWeb.frameMessaging['getFrameId'] = function() { - if (!frameId_) { - frameId_ = ""; + if (frameId_ === "") { // Generate 128 bit unique identifier. var components = new Uint32Array(4); window.crypto.getRandomValues(components);
diff --git a/ios/web/web_state/js/resources/message.js b/ios/web/web_state/js/resources/message.js index f744e69..f1fb71e 100644 --- a/ios/web/web_state/js/resources/message.js +++ b/ios/web/web_state/js/resources/message.js
@@ -7,6 +7,7 @@ goog.provide('__crWeb.message'); goog.require('__crWeb.common'); +goog.require('__crWeb.frameMessaging'); /** * Namespace for this module. @@ -77,6 +78,7 @@ queueObject.queue.forEach(function(command) { __gCrWeb.common.sendWebKitMessage(queueObject.scheme, { 'crwCommand': command, + 'crwFrameId': __gCrWeb.frameMessaging['getFrameId'](), 'crwWindowId': window.top.__gCrWeb['windowId'] }); });
diff --git a/ios/web/web_state/js/resources/plugin_placeholder.js b/ios/web/web_state/js/resources/plugin_placeholder.js index 54af7a98..df4b235 100644 --- a/ios/web/web_state/js/resources/plugin_placeholder.js +++ b/ios/web/web_state/js/resources/plugin_placeholder.js
@@ -7,6 +7,8 @@ // // Inserts placeholders into the DOM on top of unsupported plugins. +goog.provide('__crWeb.pluginPlaceholder'); + /* Beginning of anonymous object. */ (function() {
diff --git a/ios/web/web_state/js/resources/setup_frame.js b/ios/web/web_state/js/resources/setup_frame.js new file mode 100644 index 0000000..2a32015 --- /dev/null +++ b/ios/web/web_state/js/resources/setup_frame.js
@@ -0,0 +1,30 @@ +// 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. + +// This file adheres to closure-compiler conventions in order to enable +// compilation with ADVANCED_OPTIMIZATIONS. See http://goo.gl/FwOgy + +/** + * @fileoverview Notifies the application that this frame has loaded. This file + * must be included at document end time. + */ + +goog.provide('__crWeb.setupFrame'); + +// Requires __crWeb.common and __crWeb.frameMessaging provided by +// __crWeb.allFramesWebBundle. + +/* Beginning of anonymous object. */ +(function() { + +window.addEventListener('unload', function(event) { + __gCrWeb.common.sendWebKitMessage('FrameBecameUnavailable', + __gCrWeb.frameMessaging.getFrameId()); +}); + +__gCrWeb.common.sendWebKitMessage('FrameBecameAvailable', { + 'crwFrameId': __gCrWeb.frameMessaging.getFrameId() +}); + +}()); // End of anonymous object
diff --git a/ios/web/web_state/ui/BUILD.gn b/ios/web/web_state/ui/BUILD.gn index 95e7a35b..c7092aa 100644 --- a/ios/web/web_state/ui/BUILD.gn +++ b/ios/web/web_state/ui/BUILD.gn
@@ -24,6 +24,7 @@ "//ios/web/web_state:error_translation_util", "//ios/web/web_state:navigation_context", "//ios/web/web_state:page_viewport_state", + "//ios/web/web_state:web_frame", "//ios/web/web_state:web_state_impl_header", "//ios/web/web_state:web_view_internal_creation_util", "//ios/web/web_state:wk_web_view_security_util",
diff --git a/ios/web/web_state/ui/crw_web_controller.mm b/ios/web/web_state/ui/crw_web_controller.mm index 7408a70..576617f 100644 --- a/ios/web/web_state/ui/crw_web_controller.mm +++ b/ios/web/web_state/ui/crw_web_controller.mm
@@ -96,6 +96,8 @@ #import "ios/web/web_state/ui/wk_back_forward_list_item_holder.h" #import "ios/web/web_state/ui/wk_navigation_action_util.h" #import "ios/web/web_state/ui/wk_web_view_configuration_provider.h" +#import "ios/web/web_state/web_frame_impl.h" +#import "ios/web/web_state/web_frames_manager_impl.h" #import "ios/web/web_state/web_state_impl.h" #import "ios/web/web_state/web_view_internal_creation_util.h" #import "ios/web/web_state/wk_web_view_security_util.h" @@ -145,6 +147,11 @@ // URL scheme for messages sent from javascript for asynchronous processing. NSString* const kScriptMessageName = @"crwebinvoke"; +// Message command sent when a frame becomes available. +NSString* const kFrameBecameAvailableMessageName = @"FrameBecameAvailable"; +// Message command sent when a frame is unloading. +NSString* const kFrameBecameUnavailableMessageName = @"FrameBecameUnavailable"; + // Standard User Defaults key for "Log JS" debug setting. NSString* const kLogJavaScript = @"LogJavascript"; @@ -604,6 +611,10 @@ - (NSString*)scriptByAddingWindowIDCheckForScript:(NSString*)script; // Attempts to handle a script message. Returns YES on success, NO otherwise. - (BOOL)respondToWKScriptMessage:(WKScriptMessage*)scriptMessage; +// Handles frame became available message. +- (void)frameBecameAvailableWithMessage:(WKScriptMessage*)message; +// Handles frame became unavailable message. +- (void)frameBecameUnavailableWithMessage:(WKScriptMessage*)message; // Registers load request with empty referrer and link or client redirect // transition based on user interaction state. Returns navigation context for // this request. @@ -928,6 +939,7 @@ _certVerificationErrors = std::make_unique<CertVerificationErrorsCacheType>(kMaxCertErrorsCount); _navigationStates = [[CRWWKNavigationStates alloc] init]; + web::WebFramesManagerImpl::CreateForWebState(_webStateImpl); [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(orientationDidChange) @@ -1885,6 +1897,9 @@ DCHECK(!_isHalted); _webStateImpl->ClearTransientContent(); + // Reset tracked frames because JavaScript unload handler will not be called. + web::WebFramesManagerImpl::FromWebState(self.webState)->RemoveAllWebFrames(); + web::NavigationItem* item = self.currentNavItem; const GURL currentURL = item ? item->GetURL() : GURL::EmptyGURL(); const bool isCurrentURLAppSpecific = @@ -2393,6 +2408,37 @@ return NO; } +- (void)frameBecameAvailableWithMessage:(WKScriptMessage*)message { + if (_isBeingDestroyed || ![message.body isKindOfClass:[NSDictionary class]] || + ![message.body[@"crwFrameId"] isKindOfClass:[NSString class]]) { + // WebController is being destroyed or message is invalid. + return; + } + + web::WebFramesManagerImpl* framesManager = + web::WebFramesManagerImpl::FromWebState([self webState]); + + std::string frameID = base::SysNSStringToUTF8(message.body[@"crwFrameId"]); + if (!framesManager->GetFrameWithId(frameID)) { + GURL messageFrameOrigin = + web::GURLOriginWithWKSecurityOrigin(message.frameInfo.securityOrigin); + auto newFrame = std::make_unique<web::WebFrameImpl>( + frameID, message.frameInfo.mainFrame, messageFrameOrigin, + self.webState); + framesManager->AddFrame(std::move(newFrame)); + } +} + +- (void)frameBecameUnavailableWithMessage:(WKScriptMessage*)message { + if (_isBeingDestroyed || ![message.body isKindOfClass:[NSString class]]) { + // WebController is being destroyed or message is invalid. + return; + } + std::string frameID = base::SysNSStringToUTF8(message.body); + web::WebFramesManagerImpl::FromWebState(self.webState) + ->RemoveFrameWithId(frameID); +} + #pragma mark - #pragma mark JavaScript message handlers @@ -3850,6 +3896,18 @@ } name:kScriptMessageName webView:webView]; + + [messageRouter setScriptMessageHandler:^(WKScriptMessage* message) { + [weakSelf frameBecameAvailableWithMessage:message]; + } + name:kFrameBecameAvailableMessageName + webView:webView]; + [messageRouter setScriptMessageHandler:^(WKScriptMessage* message) { + [weakSelf frameBecameUnavailableWithMessage:message]; + } + name:kFrameBecameUnavailableMessageName + webView:webView]; + _windowIDJSManager = [[CRWJSWindowIDManager alloc] initWithWebView:webView]; } else { _windowIDJSManager = nil; @@ -4791,12 +4849,15 @@ } } - // Handle error display states. - web::ErrorRetryCommand command = - item->error_retry_state_machine().DidFinishNavigation(webViewURL); - [self handleErrorRetryCommand:command - navigationItem:item - navigationContext:context]; + // Handle error display states. For reasons not fully understood, |item| may + // be nullptr. TODO(crbug.com/864769): Figure out the cause. + if (item) { + web::ErrorRetryCommand command = + item->error_retry_state_machine().DidFinishNavigation(webViewURL); + [self handleErrorRetryCommand:command + navigationItem:item + navigationContext:context]; + } } [_navigationStates setState:web::WKNavigationState::FINISHED
diff --git a/ios/web/web_state/web_frames_manager_inttest.mm b/ios/web/web_state/web_frames_manager_inttest.mm new file mode 100644 index 0000000..1eb11e9 --- /dev/null +++ b/ios/web/web_state/web_frames_manager_inttest.mm
@@ -0,0 +1,35 @@ +// 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 "ios/web/public/test/web_test_with_web_state.h" + +#include "ios/web/public/web_state/web_frame.h" +#include "ios/web/web_state/web_frames_manager_impl.h" + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +namespace web { + +typedef WebTestWithWebState WebFramesManagerTest; + +// Tests that the WebFramesManager correctly adds a WebFrame for a webpage with +// a single frame. +TEST_F(WebFramesManagerTest, SingleWebFrameAdded) { + LoadHtml(@"<p></p>"); + + WebFramesManagerImpl* frames_manager = + WebFramesManagerImpl::FromWebState(web_state()); + EXPECT_EQ(1ul, frames_manager->GetAllWebFrames().size()); + + WebFrame* main_web_frame = frames_manager->GetMainWebFrame(); + ASSERT_TRUE(main_web_frame); + EXPECT_TRUE(main_web_frame->IsMainFrame()); + EXPECT_FALSE(main_web_frame->GetFrameId().empty()); + + EXPECT_EQ(GURL(BaseUrl()).GetOrigin(), main_web_frame->GetSecurityOrigin()); +}; + +} // namespace web
diff --git a/ios/web_view/internal/autofill/web_view_personal_data_manager_factory.cc b/ios/web_view/internal/autofill/web_view_personal_data_manager_factory.cc index 2a629b1..fc64bf2 100644 --- a/ios/web_view/internal/autofill/web_view_personal_data_manager_factory.cc +++ b/ios/web_view/internal/autofill/web_view_personal_data_manager_factory.cc
@@ -51,10 +51,11 @@ std::unique_ptr<autofill::PersonalDataManager> service( new autofill::PersonalDataManager( ApplicationContext::GetInstance()->GetApplicationLocale())); - service->Init( + auto autofill_db = WebViewWebDataServiceWrapperFactory::GetAutofillWebDataForBrowserState( - browser_state, ServiceAccessType::EXPLICIT_ACCESS), - browser_state->GetPrefs(), + browser_state, ServiceAccessType::EXPLICIT_ACCESS); + service->Init( + autofill_db, nullptr, browser_state->GetPrefs(), WebViewIdentityManagerFactory::GetForBrowserState(browser_state), browser_state->IsOffTheRecord()); return service;
diff --git a/ios/web_view/internal/signin/web_view_identity_manager_factory.mm b/ios/web_view/internal/signin/web_view_identity_manager_factory.mm index 9320bc44..d095b88531 100644 --- a/ios/web_view/internal/signin/web_view_identity_manager_factory.mm +++ b/ios/web_view/internal/signin/web_view_identity_manager_factory.mm
@@ -10,6 +10,7 @@ #include "components/keyed_service/ios/browser_state_dependency_manager.h" #include "components/signin/core/browser/signin_manager.h" #include "ios/web_view/internal/signin/web_view_account_tracker_service_factory.h" +#include "ios/web_view/internal/signin/web_view_gaia_cookie_manager_service_factory.h" #include "ios/web_view/internal/signin/web_view_oauth2_token_service_factory.h" #include "ios/web_view/internal/signin/web_view_signin_manager_factory.h" #include "ios/web_view/internal/web_view_browser_state.h" @@ -36,6 +37,8 @@ WebViewSigninManagerFactory::GetForBrowserState(browser_state), WebViewOAuth2TokenServiceFactory::GetForBrowserState(browser_state), WebViewAccountTrackerServiceFactory::GetForBrowserState( + browser_state), + WebViewGaiaCookieManagerServiceFactory::GetForBrowserState( browser_state)) {} }; @@ -44,6 +47,7 @@ "IdentityManager", BrowserStateDependencyManager::GetInstance()) { DependsOn(WebViewAccountTrackerServiceFactory::GetInstance()); + DependsOn(WebViewGaiaCookieManagerServiceFactory::GetInstance()); DependsOn(WebViewOAuth2TokenServiceFactory::GetInstance()); DependsOn(WebViewSigninManagerFactory::GetInstance()); }
diff --git a/media/audio/mac/audio_manager_mac.cc b/media/audio/mac/audio_manager_mac.cc index 66a5622..74dd21d 100644 --- a/media/audio/mac/audio_manager_mac.cc +++ b/media/audio/mac/audio_manager_mac.cc
@@ -131,7 +131,11 @@ std::vector<AudioObjectID> device_ids = core_audio_mac::GetAllAudioDeviceIDs(); for (AudioObjectID device_id : device_ids) { - if (core_audio_mac::GetNumStreams(device_id, is_input) == 0) + const bool is_valid_for_direction = + (is_input ? core_audio_mac::IsInputDevice(device_id) + : core_audio_mac::IsOutputDevice(device_id)); + + if (!is_valid_for_direction) continue; base::Optional<std::string> unique_id =
diff --git a/media/audio/mac/core_audio_util_mac.cc b/media/audio/mac/core_audio_util_mac.cc index 3c050b4b..fa6dcf2 100644 --- a/media/audio/mac/core_audio_util_mac.cc +++ b/media/audio/mac/core_audio_util_mac.cc
@@ -4,6 +4,8 @@ #include "media/audio/mac/core_audio_util_mac.h" +#include <IOKit/audio/IOAudioTypes.h> + #include <utility> #include "base/mac/mac_logging.h" @@ -84,7 +86,7 @@ return size; } -std::vector<AudioObjectID> GetAudioDeviceIDs( +std::vector<AudioObjectID> GetAudioObjectIDs( AudioObjectID audio_object_id, AudioObjectPropertySelector property_selector) { AudioObjectPropertyAddress property_address = { @@ -113,7 +115,7 @@ nullptr /* inQualifierData */, &size, device_ids.data()); if (result != noErr) { OSSTATUS_DLOG(WARNING, result) - << "Failed to read devuce IDs for property " << property_selector + << "Failed to read object IDs from property " << property_selector << " for device/object " << audio_object_id; return {}; } @@ -206,12 +208,12 @@ } // namespace std::vector<AudioObjectID> GetAllAudioDeviceIDs() { - return GetAudioDeviceIDs(kAudioObjectSystemObject, + return GetAudioObjectIDs(kAudioObjectSystemObject, kAudioHardwarePropertyDevices); } std::vector<AudioObjectID> GetRelatedDeviceIDs(AudioObjectID device_id) { - return GetAudioDeviceIDs(device_id, kAudioDevicePropertyRelatedDevices); + return GetAudioObjectIDs(device_id, kAudioDevicePropertyRelatedDevices); } base::Optional<std::string> GetDeviceUniqueID(AudioObjectID device_id) { @@ -307,5 +309,50 @@ return is_private; } +bool IsInputDevice(AudioObjectID device_id) { + std::vector<AudioObjectID> streams = + GetAudioObjectIDs(device_id, kAudioDevicePropertyStreams); + + int num_undefined_input_streams = 0; + int num_defined_input_streams = 0; + int num_output_streams = 0; + + for (auto stream_id : streams) { + auto direction = + GetDeviceUint32Property(stream_id, kAudioStreamPropertyDirection, + kAudioObjectPropertyScopeGlobal); + DCHECK(direction.has_value()); + const UInt32 kDirectionOutput = 0; + const UInt32 kDirectionInput = 1; + if (direction == kDirectionOutput) { + ++num_output_streams; + } else if (direction == kDirectionInput) { + // Filter input streams based on what terminal it claims to be attached + // to. Note that INPUT_UNDEFINED comes from a set of terminals declared + // in IOKit. CoreAudio defines a number of terminals in + // AudioHardwareBase.h but none of them match any of the values I've + // seen used in practice, though I've only tested a few devices. + auto terminal = + GetDeviceUint32Property(stream_id, kAudioStreamPropertyTerminalType, + kAudioObjectPropertyScopeGlobal); + if (terminal.has_value() && terminal == INPUT_UNDEFINED) { + ++num_undefined_input_streams; + } else { + ++num_defined_input_streams; + } + } + } + + // I've only seen INPUT_UNDEFINED introduced by the VoiceProcessing AudioUnit, + // but to err on the side of caution, let's allow a device with only undefined + // input streams and no output streams as well. + return num_defined_input_streams > 0 || + (num_undefined_input_streams > 0 && num_output_streams == 0); +} + +bool IsOutputDevice(AudioObjectID device_id) { + return GetNumStreams(device_id, false) > 0; +} + } // namespace core_audio_mac } // namespace media
diff --git a/media/audio/mac/core_audio_util_mac.h b/media/audio/mac/core_audio_util_mac.h index 39c3756..b8301be 100644 --- a/media/audio/mac/core_audio_util_mac.h +++ b/media/audio/mac/core_audio_util_mac.h
@@ -52,6 +52,16 @@ // AudioUnit. bool IsPrivateAggregateDevice(AudioObjectID device_id); +// Returns whether or not the |device_id| corresponds to a device that has valid +// input streams. When the VoiceProcessing AudioUnit is active, some output +// devices get an input stream as well. This function tries to filter those out, +// based on the value of the stream's kAudioStreamPropertyTerminalType value. +bool IsInputDevice(AudioObjectID device_id); + +// Returns whether or not the |device_id| corresponds to a device with output +// streams. +bool IsOutputDevice(AudioObjectID device_id); + } // namespace core_audio_mac } // namespace media
diff --git a/media/gpu/video_decode_accelerator_unittest.cc b/media/gpu/video_decode_accelerator_unittest.cc index 1bf42525..3285989 100644 --- a/media/gpu/video_decode_accelerator_unittest.cc +++ b/media/gpu/video_decode_accelerator_unittest.cc
@@ -702,7 +702,10 @@ case DONE_RESET_AFTER_FIRST_CONFIG_INFO: case MID_STREAM_RESET: reset_point_ = END_OF_STREAM_RESET; - DecodeNextFragment(); + // Because VDA::Decode() is executed if |reset_point_| is + // MID_STREAM_RESET or RESET_AFTER_FIRST_CONFIG_INFO, + // NotifyEndOfBitstreamBuffer() will be invoked. Next VDA::Decode() is + // triggered from NotifyEndOfBitstreamBuffer(). return; case START_OF_STREAM_RESET: EXPECT_EQ(num_decoded_frames_, 0u); @@ -772,6 +775,8 @@ initialize_done_ticks_ = base::TimeTicks::Now(); EXPECT_EQ(encoded_data_helper_->AtHeadOfStream(), true); num_decoded_frames_ = 0; + if (decoder_deleted()) + return; if (reset_point_ == START_OF_STREAM_RESET) { decoder_->Reset(); @@ -780,7 +785,7 @@ for (size_t i = 0; i < config_.num_in_flight_decodes; ++i) DecodeNextFragment(); - DCHECK_EQ(outstanding_decodes_, config_.num_in_flight_decodes); + EXPECT_EQ(outstanding_decodes_, config_.num_in_flight_decodes); } void GLRenderingVDAClient::DeleteDecoder() { @@ -854,6 +859,12 @@ FROM_HERE, base::Bind(&GLRenderingVDAClient::DecodeNextFragment, AsWeakPtr()), base::TimeDelta::FromSeconds(1) / config_.decode_calls_per_second); + } else { + // Unless DecodeNextFragment() is posted from the above PostDelayedTask(), + // all the DecodeNextFragment() will be executed from + // NotifyEndOfBitstreamBuffer(). The number of Decode()s in flight must be + // less than or equal to the specified times. + EXPECT_LE(outstanding_decodes_, config_.num_in_flight_decodes); } }
diff --git a/net/base/network_interfaces_fuchsia.cc b/net/base/network_interfaces_fuchsia.cc index 7f3fdc34..abca21b 100644 --- a/net/base/network_interfaces_fuchsia.cc +++ b/net/base/network_interfaces_fuchsia.cc
@@ -99,15 +99,15 @@ bool GetNetworkList(NetworkInterfaceList* networks, int policy) { DCHECK(networks); - fuchsia::netstack::NetstackSync2Ptr netstack = + fuchsia::netstack::NetstackSyncPtr netstack = base::fuchsia::ComponentContext::GetDefault() ->ConnectToServiceSync<fuchsia::netstack::Netstack>(); // TODO(kmarshall): Use NetworkChangeNotifier's cached interface list. fidl::VectorPtr<fuchsia::netstack::NetInterface> interfaces; - auto status = netstack->GetInterfaces(&interfaces); - if (status.statvs != ZX_OK) { - ZX_LOG(ERROR, status.statvs) << "fuchsia::netstack::GetInterfaces()"; + zx_status_t status = netstack->GetInterfaces(&interfaces); + if (status != ZX_OK) { + ZX_LOG(ERROR, status) << "fuchsia::netstack::GetInterfaces()"; return false; }
diff --git a/net/third_party/quic/core/quic_framer.cc b/net/third_party/quic/core/quic_framer.cc index 2e103059..aa4b529 100644 --- a/net/third_party/quic/core/quic_framer.cc +++ b/net/third_party/quic/core/quic_framer.cc
@@ -3101,11 +3101,13 @@ // Account for the remaining Intervals, if any. while (ack_block_count != 0) { QuicPacketNumber gap_size = ack_block_smallest - itr->max(); - size_t size_of_gap_size = QuicDataWriter::GetVarInt62Len(gap_size); + // Decrement per the protocol specification + size_t size_of_gap_size = QuicDataWriter::GetVarInt62Len(gap_size - 1); ack_frame_size += size_of_gap_size; QuicPacketNumber block_size = itr->max() - itr->min(); - size_t size_of_block_size = QuicDataWriter::GetVarInt62Len(block_size); + // Decrement per the protocol specification + size_t size_of_block_size = QuicDataWriter::GetVarInt62Len(block_size - 1); ack_frame_size += size_of_block_size; ack_block_smallest = itr->min();
diff --git a/net/third_party/quic/core/quic_ietf_framer_test.cc b/net/third_party/quic/core/quic_ietf_framer_test.cc index 5a15137..8ecf4d87 100644 --- a/net/third_party/quic/core/quic_ietf_framer_test.cc +++ b/net/third_party/quic/core/quic_ietf_framer_test.cc
@@ -267,10 +267,14 @@ EXPECT_TRUE(QuicFramerPeer::AppendIetfAckFrameAndTypeByte( &framer_, transmit_frame, &writer)); - // Better have something in the packet buffer. - EXPECT_NE(0u, writer.length()); + size_t expected_frame_length = QuicFramerPeer::ComputeFrameLength( + &framer_, QuicFrame(&transmit_frame), false, + static_cast<QuicPacketNumberLength>(123456u)); + + // Encoded length should match what ComputeFrameLength returns + EXPECT_EQ(expected_frame_length, writer.length()); // and what is in the buffer should be the expected size. - EXPECT_EQ(expected_size, writer.length()); + EXPECT_EQ(expected_size, writer.length()) << "Frame is " << transmit_frame; // Now set up a reader to read in the frame. QuicDataReader reader(packet_buffer, writer.length(), NETWORK_BYTE_ORDER); @@ -646,6 +650,15 @@ { 100000000, {{1, 2}, {3, 4}, {5, 6}, {7, 8}, {9, 10}, {11, 12}}}, { 0, {{1, 65}} }, { 9223372036854775807, {{1, 11}, {74, 138}} }, + // This ack is for packets 60 & 125. There are 64 packets in the gap. + // The encoded value is gap_size - 1, or 63. Crosses a VarInt62 encoding + // boundary... + { 1, {{60, 61}, {125, 126}} }, + { 2, {{ 1, 65}, {129, 130}} }, + { 3, {{ 1, 65}, {129, 195}} }, + { 4, {{ 1, 65}, {129, 194}} }, + { 5, {{ 1, 65}, {129, 193}} }, + { 6, {{ 1, 65}, {129, 192}} }, }; // clang-format on
diff --git a/net/third_party/quic/core/quic_packet_creator_test.cc b/net/third_party/quic/core/quic_packet_creator_test.cc index 565b269..269d6fd 100644 --- a/net/third_party/quic/core/quic_packet_creator_test.cc +++ b/net/third_party/quic/core/quic_packet_creator_test.cc
@@ -1183,6 +1183,17 @@ creator_.Flush(); } +// Test for error found in +// https://bugs.chromium.org/p/chromium/issues/detail?id=859949 where a gap +// length that crosses an IETF VarInt length boundary would cause a +// failure. While this test is not applicable to versions other than version 99, +// it should still work. Hence, it is not made version-specific. +TEST_P(QuicPacketCreatorTest, IetfAckGapErrorRegression) { + QuicAckFrame ack_frame = InitAckFrame({{60, 61}, {125, 126}}); + frames_.push_back(QuicFrame(&ack_frame)); + SerializeAllFrames(frames_); +} + } // namespace } // namespace test } // namespace quic
diff --git a/net/third_party/quic/test_tools/quic_framer_peer.cc b/net/third_party/quic/test_tools/quic_framer_peer.cc index 35d06af..c0fd76d0 100644 --- a/net/third_party/quic/test_tools/quic_framer_peer.cc +++ b/net/third_party/quic/test_tools/quic_framer_peer.cc
@@ -303,5 +303,15 @@ framer->last_packet_is_ietf_quic_ = last_packet_is_ietf_quic; } +// static +size_t QuicFramerPeer::ComputeFrameLength( + QuicFramer* framer, + const QuicFrame& frame, + bool last_frame_in_packet, + QuicPacketNumberLength packet_number_length) { + return framer->ComputeFrameLength(frame, last_frame_in_packet, + packet_number_length); +} + } // namespace test } // namespace quic
diff --git a/net/third_party/quic/test_tools/quic_framer_peer.h b/net/third_party/quic/test_tools/quic_framer_peer.h index 2c3bbdd..71255db 100644 --- a/net/third_party/quic/test_tools/quic_framer_peer.h +++ b/net/third_party/quic/test_tools/quic_framer_peer.h
@@ -143,6 +143,10 @@ static bool ProcessNewConnectionIdFrame(QuicFramer* framer, QuicDataReader* reader, QuicNewConnectionIdFrame* frame); + static size_t ComputeFrameLength(QuicFramer* framer, + const QuicFrame& frame, + bool last_frame_in_packet, + QuicPacketNumberLength packet_number_length); private: DISALLOW_COPY_AND_ASSIGN(QuicFramerPeer);
diff --git a/services/identity/public/cpp/DEPS b/services/identity/public/cpp/DEPS index 81ceb59..6bb1884 100644 --- a/services/identity/public/cpp/DEPS +++ b/services/identity/public/cpp/DEPS
@@ -1,6 +1,8 @@ include_rules = [ "+components/prefs/testing_pref_service.h", "+components/signin/core/browser/account_info.h", + "+components/signin/core/browser/fake_gaia_cookie_manager_service.h", + "+components/signin/core/browser/gaia_cookie_manager_service.h", "+components/signin/core/browser/profile_management_switches.h", "+google_apis/gaia/gaia_auth_util.h", "+google_apis/gaia/google_service_auth_error.h",
diff --git a/services/identity/public/cpp/identity_manager.cc b/services/identity/public/cpp/identity_manager.cc index e79d46a3..7ec0f246 100644 --- a/services/identity/public/cpp/identity_manager.cc +++ b/services/identity/public/cpp/identity_manager.cc
@@ -25,12 +25,15 @@ } // namespace -IdentityManager::IdentityManager(SigninManagerBase* signin_manager, - ProfileOAuth2TokenService* token_service, - AccountTrackerService* account_tracker_service) +IdentityManager::IdentityManager( + SigninManagerBase* signin_manager, + ProfileOAuth2TokenService* token_service, + AccountTrackerService* account_tracker_service, + GaiaCookieManagerService* gaia_cookie_manager_service) : signin_manager_(signin_manager), token_service_(token_service), - account_tracker_service_(account_tracker_service) { + account_tracker_service_(account_tracker_service), + gaia_cookie_manager_service_(gaia_cookie_manager_service) { // Initialize the state of the primary account. primary_account_info_ = signin_manager_->GetAuthenticatedAccountInfo();
diff --git a/services/identity/public/cpp/identity_manager.h b/services/identity/public/cpp/identity_manager.h index 387d76c..3f944ed 100644 --- a/services/identity/public/cpp/identity_manager.h +++ b/services/identity/public/cpp/identity_manager.h
@@ -8,6 +8,7 @@ #include "base/observer_list.h" #include "components/signin/core/browser/account_info.h" #include "components/signin/core/browser/account_tracker_service.h" +#include "components/signin/core/browser/gaia_cookie_manager_service.h" #include "components/signin/core/browser/profile_oauth2_token_service.h" #include "components/signin/core/browser/signin_manager_base.h" #include "services/identity/public/cpp/access_token_fetcher.h" @@ -107,7 +108,8 @@ IdentityManager(SigninManagerBase* signin_manager, ProfileOAuth2TokenService* token_service, - AccountTrackerService* account_tracker_service); + AccountTrackerService* account_tracker_service, + GaiaCookieManagerService* gaia_cookie_manager_service); ~IdentityManager() override; // Provides access to the latest cached information of the user's primary @@ -219,6 +221,7 @@ SigninManagerBase* signin_manager_; ProfileOAuth2TokenService* token_service_; AccountTrackerService* account_tracker_service_; + GaiaCookieManagerService* gaia_cookie_manager_service_; // The latest (cached) value of the primary account. #if defined(OS_CHROMEOS)
diff --git a/services/identity/public/cpp/identity_manager_unittest.cc b/services/identity/public/cpp/identity_manager_unittest.cc index 538fe84e..da3ffb8 100644 --- a/services/identity/public/cpp/identity_manager_unittest.cc +++ b/services/identity/public/cpp/identity_manager_unittest.cc
@@ -7,6 +7,7 @@ #include "base/run_loop.h" #include "build/build_config.h" #include "components/signin/core/browser/account_tracker_service.h" +#include "components/signin/core/browser/fake_gaia_cookie_manager_service.h" #include "components/signin/core/browser/fake_profile_oauth2_token_service.h" #include "components/signin/core/browser/fake_signin_manager.h" #include "components/signin/core/browser/profile_management_switches.h" @@ -337,14 +338,16 @@ IdentityManagerTest() : signin_client_(&pref_service_), #if defined(OS_CHROMEOS) - signin_manager_(&signin_client_, &account_tracker_) + signin_manager_(&signin_client_, &account_tracker_), #else signin_manager_(&signin_client_, &token_service_, &account_tracker_, - nullptr) + nullptr), #endif - { + gaia_cookie_manager_service_(&token_service_, + "identity_manager_unittest", + &signin_client_) { AccountTrackerService::RegisterPrefs(pref_service_.registry()); SigninManagerBase::RegisterProfilePrefs(pref_service_.registry()); SigninManagerBase::RegisterPrefs(pref_service_.registry()); @@ -380,8 +383,9 @@ identity_manager_diagnostics_observer_.reset(); identity_manager_.reset(); - identity_manager_.reset(new IdentityManager( - &signin_manager_, &token_service_, &account_tracker_)); + identity_manager_.reset( + new IdentityManager(&signin_manager_, &token_service_, + &account_tracker_, &gaia_cookie_manager_service_)); identity_manager_observer_.reset( new TestIdentityManagerObserver(identity_manager_.get())); identity_manager_diagnostics_observer_.reset( @@ -395,6 +399,7 @@ TestSigninClient signin_client_; SigninManagerForTest signin_manager_; CustomFakeProfileOAuth2TokenService token_service_; + FakeGaiaCookieManagerService gaia_cookie_manager_service_; std::unique_ptr<IdentityManager> identity_manager_; std::unique_ptr<TestIdentityManagerObserver> identity_manager_observer_; std::unique_ptr<TestIdentityManagerDiagnosticsObserver>
diff --git a/services/identity/public/cpp/identity_test_environment.cc b/services/identity/public/cpp/identity_test_environment.cc index dc5fa3d..86b00b7 100644 --- a/services/identity/public/cpp/identity_test_environment.cc +++ b/services/identity/public/cpp/identity_test_environment.cc
@@ -6,6 +6,7 @@ #include "base/run_loop.h" #include "components/signin/core/browser/account_tracker_service.h" +#include "components/signin/core/browser/fake_gaia_cookie_manager_service.h" #include "components/signin/core/browser/fake_profile_oauth2_token_service.h" #include "components/signin/core/browser/fake_signin_manager.h" #include "components/signin/core/browser/profile_management_switches.h" @@ -42,6 +43,7 @@ TestSigninClient signin_client_; SigninManagerForTest signin_manager_; FakeProfileOAuth2TokenService token_service_; + FakeGaiaCookieManagerService gaia_cookie_manager_service_; std::unique_ptr<IdentityManager> identity_manager_; DISALLOW_COPY_AND_ASSIGN(IdentityTestEnvironmentInternal); @@ -50,14 +52,28 @@ IdentityTestEnvironmentInternal::IdentityTestEnvironmentInternal() : signin_client_(&pref_service_), #if defined(OS_CHROMEOS) - signin_manager_(&signin_client_, &account_tracker_) + signin_manager_(&signin_client_, &account_tracker_), #else signin_manager_(&signin_client_, &token_service_, &account_tracker_, - nullptr) + nullptr), #endif -{ + // NOTE: Some unittests set up their own TestURLFetcherFactory. In these + // contexts FakeGaiaCookieManagerService can't set up its own + // FakeURLFetcherFactory, as {Test, Fake}URLFetcherFactory allow only one + // instance to be alive at a time. If some users of + // IdentityTestEnvironment require that GaiaCookieManagerService have a + // FakeURLFetcherFactory, we'll need to pass a config param in to + // IdentityTestEnvironment to specify this. If some users want that + // behavior while *also* having their own FakeURLFetcherFactory, we'll + // need to pass the actual object in and have GaiaCookieManagerService + // have a reference to the object (or figure out the sharing some other + // way). Contact blundell@chromium.org if you come up against this issue. + gaia_cookie_manager_service_(&token_service_, + "identity_test_environment", + &signin_client_, + /*use_fake_url_fetcher=*/false) { AccountTrackerService::RegisterPrefs(pref_service_.registry()); SigninManagerBase::RegisterProfilePrefs(pref_service_.registry()); SigninManagerBase::RegisterPrefs(pref_service_.registry()); @@ -65,7 +81,8 @@ account_tracker_.Initialize(&signin_client_); identity_manager_.reset(new IdentityManager(&signin_manager_, &token_service_, - &account_tracker_)); + &account_tracker_, + &gaia_cookie_manager_service_)); } IdentityTestEnvironmentInternal::~IdentityTestEnvironmentInternal() {}
diff --git a/services/ui/gpu_host/gpu_client.cc b/services/ui/gpu_host/gpu_client.cc index 8785f4aa..cd60c36b 100644 --- a/services/ui/gpu_host/gpu_client.cc +++ b/services/ui/gpu_host/gpu_client.cc
@@ -54,8 +54,9 @@ gpu::GpuFeatureInfo()); } establish_callback_ = std::move(callback); + const bool cache_shaders_on_disk = true; gpu_service_->EstablishGpuChannel( - client_id_, client_tracing_id, is_gpu_host, + client_id_, client_tracing_id, is_gpu_host, cache_shaders_on_disk, base::Bind(&GpuClient::OnGpuChannelEstablished, weak_factory_.GetWeakPtr())); }
diff --git a/services/video_capture/device_media_to_mojo_adapter.cc b/services/video_capture/device_media_to_mojo_adapter.cc index 16f23c8a..abfd571 100644 --- a/services/video_capture/device_media_to_mojo_adapter.cc +++ b/services/video_capture/device_media_to_mojo_adapter.cc
@@ -145,7 +145,12 @@ device_started_ = false; weak_factory_.InvalidateWeakPtrs(); device_->StopAndDeAllocate(); - receiver_.reset(); + // We need to post the deletion of receiver to the end of the message queue, + // because |device_->StopAndDeAllocate()| may post messages (e.g. + // OnBufferRetired()) to a WeakPtr to |receiver_| to this queue, and we need + // those messages to be sent before we invalidate the WeakPtr. + base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE, + std::move(receiver_)); } void DeviceMediaToMojoAdapter::OnClientConnectionErrorOrClose() {
diff --git a/services/video_capture/test/fake_device_unittest.cc b/services/video_capture/test/fake_device_unittest.cc index 7e145d1..ef00a32 100644 --- a/services/video_capture/test/fake_device_unittest.cc +++ b/services/video_capture/test/fake_device_unittest.cc
@@ -78,6 +78,51 @@ ASSERT_LE(num_buffers_created, kMaxBufferPoolBuffers); } +// Tests that OnBufferRetired() events get sent out to the receiver when the +// device is stopped. +TEST_F(FakeVideoCaptureDeviceTest, BuffersGetRetiredWhenDeviceIsStopped) { + base::RunLoop wait_for_frames_loop; + static const int kNumFramesToWaitFor = 2; + std::vector<int32_t> received_buffer_ids; + int num_frames_arrived = 0; + mojom::ReceiverPtr receiver_proxy; + MockReceiver receiver(mojo::MakeRequest(&receiver_proxy)); + EXPECT_CALL(receiver, DoOnNewBuffer(_, _)) + .WillRepeatedly( + Invoke([&received_buffer_ids](int32_t buffer_id, + media::mojom::VideoBufferHandlePtr*) { + received_buffer_ids.push_back(buffer_id); + })); + EXPECT_CALL(receiver, DoOnFrameReadyInBuffer(_, _, _, _)) + .WillRepeatedly( + InvokeWithoutArgs([&wait_for_frames_loop, &num_frames_arrived]() { + if (++num_frames_arrived >= kNumFramesToWaitFor) { + wait_for_frames_loop.Quit(); + } + })); + + fake_device_proxy_->Start(requestable_settings_, std::move(receiver_proxy)); + wait_for_frames_loop.Run(); + + base::RunLoop wait_for_buffers_retired_loop; + EXPECT_CALL(receiver, OnBufferRetired(_)) + .WillRepeatedly( + Invoke([&received_buffer_ids, + &wait_for_buffers_retired_loop](int32_t buffer_id) { + auto iter = std::find(received_buffer_ids.begin(), + received_buffer_ids.end(), buffer_id); + ASSERT_TRUE(iter != received_buffer_ids.end()); + received_buffer_ids.erase(iter); + if (received_buffer_ids.empty()) { + wait_for_buffers_retired_loop.Quit(); + } + })); + + // Stop the device + fake_device_proxy_.reset(); + wait_for_buffers_retired_loop.Run(); +} + // This requires platforms where base::SharedMemoryHandle is backed by a // file descriptor. #if defined(OS_LINUX)
diff --git a/services/viz/privileged/interfaces/gl/gpu_service.mojom b/services/viz/privileged/interfaces/gl/gpu_service.mojom index 2e1faf24..2541925 100644 --- a/services/viz/privileged/interfaces/gl/gpu_service.mojom +++ b/services/viz/privileged/interfaces/gl/gpu_service.mojom
@@ -23,7 +23,8 @@ // client. The GPU service responds with an IPC handle. EstablishGpuChannel(int32 client_id, uint64 client_tracing_id, - bool is_gpu_host) + bool is_gpu_host, + bool cache_shaders_on_disk) => (handle<message_pipe>? channel_handle); // Tells the GPU process to close the channel identified by |client_id|. @@ -81,7 +82,7 @@ // Notify GPU that a shader program was loaded from disk. Key is an // SHA-1 hash, and data a binary blob with serialized program info. // Note that this method is used only from a trusted process. - LoadedShader(string key, string data); + LoadedShader(int32 client_id, string key, string data); // Tells GPU to wake up the GPU because we're about to draw. WakeUpGpu();
diff --git a/skia/ext/fontmgr_fuchsia.cc b/skia/ext/fontmgr_fuchsia.cc index 3c95d44..3aa0f6d6 100644 --- a/skia/ext/fontmgr_fuchsia.cc +++ b/skia/ext/fontmgr_fuchsia.cc
@@ -204,7 +204,7 @@ } FuchsiaFontManager::FuchsiaFontManager( - fuchsia::fonts::FontProviderSync2Ptr font_provider) + fuchsia::fonts::FontProviderSyncPtr font_provider) : font_provider_(std::move(font_provider)), font_cache_(new FontCache()) { for (auto& m : kFontMap) { font_map_[m.font_name_in] = m.font_name_out; @@ -254,9 +254,9 @@ request.slant = ToFontSlant(style.slant()); fuchsia::fonts::FontResponsePtr response; - auto status = font_provider_->GetFont(std::move(request), &response); - if (status.statvs != ZX_OK) { - ZX_DLOG(ERROR, status.statvs) << "Failed to query font provider."; + zx_status_t status = font_provider_->GetFont(std::move(request), &response); + if (status != ZX_OK) { + ZX_DLOG(ERROR, status) << "Failed to query font provider."; } else if (response) { sk_sp<SkTypeface> result = font_cache_->GetTypefaceFromFontData(std::move(response->data));
diff --git a/skia/ext/fontmgr_fuchsia.h b/skia/ext/fontmgr_fuchsia.h index 6c1fe27..bd3ab155 100644 --- a/skia/ext/fontmgr_fuchsia.h +++ b/skia/ext/fontmgr_fuchsia.h
@@ -20,7 +20,7 @@ class SK_API FuchsiaFontManager : public SkFontMgr { public: explicit FuchsiaFontManager( - fuchsia::fonts::FontProviderSync2Ptr font_provider); + fuchsia::fonts::FontProviderSyncPtr font_provider); ~FuchsiaFontManager() override; @@ -52,7 +52,7 @@ private: class FontCache; - fuchsia::fonts::FontProviderSync2Ptr font_provider_; + fuchsia::fonts::FontProviderSyncPtr font_provider_; // Map applied to font family name before sending requests to the FontService. base::flat_map<std::string, std::string> font_map_;
diff --git a/skia/ext/fontmgr_fuchsia_unittest.cc b/skia/ext/fontmgr_fuchsia_unittest.cc index 74c0216c..e3d3748b 100644 --- a/skia/ext/fontmgr_fuchsia_unittest.cc +++ b/skia/ext/fontmgr_fuchsia_unittest.cc
@@ -115,7 +115,7 @@ class FuchsiaFontManagerTest : public testing::Test { public: FuchsiaFontManagerTest() { - fuchsia::fonts::FontProviderSync2Ptr font_provider; + fuchsia::fonts::FontProviderSyncPtr font_provider; font_provider_service_.Bind(font_provider.NewRequest()); font_manager_ = sk_make_sp<FuchsiaFontManager>(std::move(font_provider)); } @@ -160,4 +160,4 @@ EXPECT_TRUE(serif2); } -} // namespace skia \ No newline at end of file +} // namespace skia
diff --git a/testing/buildbot/chromium.perf.json b/testing/buildbot/chromium.perf.json index b157228e..5c1ab31 100644 --- a/testing/buildbot/chromium.perf.json +++ b/testing/buildbot/chromium.perf.json
@@ -443,55 +443,6 @@ "chromedriver" ] }, - "Win 10 High-DPI Perf": { - "isolated_scripts": [ - { - "args": [ - "-v", - "--browser=release_x64", - "--upload-results", - "--run-ref-build", - "--test-shard-map-filename=win10_highdpi_shard_map.json", - "--assert-gpu-compositing" - ], - "isolate_name": "performance_test_suite", - "merge": { - "args": [ - "--service-account-file", - "C:\\creds\\service_accounts\\service-account-chromium-perf-histograms.json" - ], - "script": "//tools/perf/process_perf_results.py" - }, - "name": "performance_test_suite", - "override_compile_targets": [ - "performance_test_suite" - ], - "swarming": { - "can_use_on_swarming_builders": true, - "dimension_sets": [ - { - "gpu": "8086:1616", - "os": "Windows-10", - "pool": "chrome.tests.perf" - } - ], - "expiration": 21600, - "hard_timeout": 25200, - "ignore_task_failure": false, - "io_timeout": 1800, - "shards": 5, - "upload_test_results": true - }, - "trigger_script": { - "args": [ - "--multiple-dimension-script-verbose", - "True" - ], - "script": "//testing/trigger_scripts/perf_device_trigger.py" - } - } - ] - }, "Win 7 Nvidia GPU Perf": { "isolated_scripts": [ {
diff --git a/testing/buildbot/filters/surface_sync.content_browsertests.filter b/testing/buildbot/filters/surface_sync.content_browsertests.filter index dca9230..45bc2dc 100644 --- a/testing/buildbot/filters/surface_sync.content_browsertests.filter +++ b/testing/buildbot/filters/surface_sync.content_browsertests.filter
@@ -1,7 +1,3 @@ -# Frame submission failures, and invalid compositor locking state. -# https://crbug.com/855533 --*CompositorImplLowEndBrowserTest.* - # Flaky failures with incorrect Input event acks. https://crbug.com/855532 -TouchInputBrowserTest.TouchHandlerConsume -TouchInputBrowserTest.TouchHandlerNoConsume
diff --git a/testing/scripts/common.py b/testing/scripts/common.py index 786e98f..28758fe 100644 --- a/testing/scripts/common.py +++ b/testing/scripts/common.py
@@ -167,24 +167,6 @@ def get_gtest_summary_passes(output): """Returns a mapping of test to boolean indicating if the test passed. - """ - if not output: - return {} - - mapping = {} - - for test_suite in output.get('testsuites', []): - suite_name = test_suite['name'] - for test in test_suite['testsuite']: - full_name = '%s.%s' % (suite_name, test['name']) - - mapping[full_name] = 'failures' in test - - return mapping - - -def get_chromium_gtest_summary_passes(output): - """Returns a mapping of test to boolean indicating if the test passed. Only partially parses the format. This code is based on code in tools/build, specifically
diff --git a/testing/scripts/run_gtest_perf_test.py b/testing/scripts/run_gtest_perf_test.py index e1dd0d5..83198f5c 100755 --- a/testing/scripts/run_gtest_perf_test.py +++ b/testing/scripts/run_gtest_perf_test.py
@@ -32,7 +32,6 @@ import os import shutil import sys -import time import tempfile import traceback @@ -83,7 +82,7 @@ args, rest_args = parser.parse_known_args() - rc, charts, output_json = execute_perf_test(args, rest_args, True) + rc, charts, output_json = execute_perf_test(args, rest_args) # TODO(eakuefner): Make isolated_script_test_perf_output mandatory after # flipping flag in swarming. @@ -101,7 +100,7 @@ return rc -def execute_perf_test(args, rest_args, chromium_gtest): +def execute_perf_test(args, rest_args): env = os.environ.copy() # Assume we want to set up the sandbox environment variables all the # time; doing so is harmless on non-Linux platforms and is needed @@ -109,95 +108,53 @@ env[CHROME_SANDBOX_ENV] = CHROME_SANDBOX_PATH rc = 0 - start_time = time.time() - test_results = {} - with common.temporary_file() as results_path: - try: - executable = rest_args[0] - extra_flags = [] - if len(rest_args) > 1: - extra_flags = rest_args[1:] + try: + executable = rest_args[0] + extra_flags = [] + if len(rest_args) > 1: + extra_flags = rest_args[1:] - if chromium_gtest: - output_flag = '--test-launcher-summary-output' - output_file = results_path + # These flags are to make sure that test output perf metrics in the log. + if not '--verbose' in extra_flags: + extra_flags.append('--verbose') + if not '--test-launcher-print-test-stdio=always' in extra_flags: + extra_flags.append('--test-launcher-print-test-stdio=always') + if args.isolated_script_test_filter: + filter_list = common.extract_filter_list( + args.isolated_script_test_filter) + extra_flags.append('--gtest_filter=' + ':'.join(filter_list)) + + if IsWindows(): + executable = '.\%s.exe' % executable + else: + executable = './%s' % executable + with common.temporary_file() as tempfile_path: + env['CHROME_HEADLESS'] = '1' + cmd = [executable] + extra_flags + + if args.xvfb: + rc = xvfb.run_executable(cmd, env, stdoutfile=tempfile_path) else: - output_flag = '--gtest_output' - output_file = 'json:%s' % results_path + rc = test_env.run_command_with_output(cmd, env=env, + stdoutfile=tempfile_path) - assert not any(output_flag in flag for flag in extra_flags), ( - 'Duplicate %s flag detected.' % output_flag) - extra_flags.append('%s=%s' % (output_flag, output_file)) + # Now get the correct json format from the stdout to write to the perf + # results file + results_processor = ( + generate_legacy_perf_dashboard_json.LegacyResultsProcessor()) + charts = results_processor.GenerateJsonResults(tempfile_path) + except Exception: + traceback.print_exc() + rc = 1 - # These flags are to make sure that test output perf metrics in the log. - if not '--verbose' in extra_flags: - extra_flags.append('--verbose') - if not '--test-launcher-print-test-stdio=always' in extra_flags: - extra_flags.append('--test-launcher-print-test-stdio=always') - if args.isolated_script_test_filter: - filter_list = common.extract_filter_list( - args.isolated_script_test_filter) - extra_flags.append('--gtest_filter=' + ':'.join(filter_list)) - - if IsWindows(): - executable = '.\%s.exe' % executable - else: - executable = './%s' % executable - with common.temporary_file() as tempfile_path: - env['CHROME_HEADLESS'] = '1' - cmd = [executable] + extra_flags - - print ' '.join(cmd) - if args.xvfb: - rc = xvfb.run_executable(cmd, env, stdoutfile=tempfile_path) - else: - rc = test_env.run_command_with_output(cmd, env=env, - stdoutfile=tempfile_path) - - # Now get the correct json format from the stdout to write to the perf - # results file - results_processor = ( - generate_legacy_perf_dashboard_json.LegacyResultsProcessor()) - charts = results_processor.GenerateJsonResults(tempfile_path) - except Exception: - traceback.print_exc() - rc = 1 - - if os.path.exists(results_path): - with open(results_path) as f: - if chromium_gtest: - func = common.get_chromium_gtest_summary_passes - else: - func = common.get_gtest_summary_passes - test_results = func(json.load(f)) - + valid = (rc == 0) + failures = [] if valid else ['(entire test suite)'] output_json = { - 'version': 3, - 'interrupted': False, - 'path_delimiter': '/', - 'seconds_since_epoch': start_time, - 'num_failures_by_type': { - 'PASS': sum(1 for success in test_results.values() if success), - 'FAIL': sum(1 for success in test_results.values() if not success), - }, - 'tests': { - test: test_result_entry(success) for ( - test, success) in test_results.items() + 'valid': valid, + 'failures': failures, } - } - return rc, charts, output_json - -def test_result_entry(success): - test = { - 'expected': 'PASS', - 'actual': 'PASS' if success else 'FAIL', - } - if not success: - test['unexpected'] = True - return test - # This is not really a "script test" so does not need to manually add # any additional compile targets. def main_compile_targets(args):
diff --git a/testing/scripts/run_performance_tests.py b/testing/scripts/run_performance_tests.py index f6b5339..a491fcd 100755 --- a/testing/scripts/run_performance_tests.py +++ b/testing/scripts/run_performance_tests.py
@@ -59,19 +59,6 @@ import run_gtest_perf_test -# List of gtests which are somewhat specialized for chromium; they accept the -# --test-launcher-summary-output argument. -CHROMIUM_GTESTS = { - 'angle_perftests', - 'components_perftests', - 'cc_perftests', - 'gpu_perftests', - 'media_perftests', - 'performance_browser_tests', - 'views_perftests', -} - - def get_sharding_map_path(args): return os.path.join( os.path.dirname(__file__), '..', '..', 'tools', 'perf', 'core', @@ -210,7 +197,7 @@ # For non telemetry tests the benchmark name is the name of the executable. benchmark_name = rest_args[0] return_code, charts, output_json = run_gtest_perf_test.execute_perf_test( - args, rest_args, benchmark_name in CHROMIUM_GTESTS) + args, rest_args) write_results(benchmark_name, charts, output_json, benchmark_log='Not available for C++ perf test',
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG b/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG index ee6fcf8d..da2b5b1a 100644 --- a/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG +++ b/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG
@@ -37,6 +37,7 @@ crbug.com/636993 external/wpt/css/css-text-decor/text-decoration-color.html [ Failure ] # rightsizing-grid.html is truly flaky, show flakiness on reload +crbug.com/591099 svg/wicd/rightsizing-grid.html [ Failure Pass ] # New passes crbug.com/774229 editing/pasteboard/copy-paste-white-space.html [ Pass ] @@ -71,8 +72,6 @@ crbug.com/591099 compositing/squashing/squash-same-transform-ancestor.html [ Failure ] crbug.com/591099 compositing/squashing/squashing-inside-perspective.html [ Failure ] crbug.com/591099 css1/box_properties/float_on_text_elements.html [ Failure ] -crbug.com/591099 css2.1/t1202-counter-04-b.html [ Failure ] -crbug.com/591099 css2.1/t1202-counters-04-b.html [ Failure ] crbug.com/591099 css3/blending/mix-blend-mode-composited-reason-children.html [ Failure ] crbug.com/591099 css3/blending/mix-blend-mode-isolation-2-stacking-contexts.html [ Failure ] crbug.com/591099 css3/filters/composited-layer-child-bounds-after-composited-to-sw-shadow-change.html [ Failure ] @@ -324,16 +323,23 @@ crbug.com/591099 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-border-box-border-radius-009.html [ Failure ] crbug.com/591099 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-circle-042.html [ Pass ] crbug.com/591099 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-circle-044.html [ Pass ] +crbug.com/591099 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-circle-050.html [ Pass ] +crbug.com/591099 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-circle-054.html [ Pass ] +crbug.com/591099 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-circle-055.html [ Pass ] crbug.com/591099 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-content-box-border-radius-002.html [ Pass ] crbug.com/591099 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-ellipse-038.html [ Pass ] crbug.com/591099 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-ellipse-039.html [ Pass ] crbug.com/591099 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-ellipse-048.html [ Failure ] crbug.com/591099 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-ellipse-051.html [ Failure ] +crbug.com/591099 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-inset-020.html [ Pass ] +crbug.com/591099 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-inset-021.html [ Pass ] +crbug.com/591099 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-inset-024.html [ Pass ] crbug.com/591099 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-margin-box-border-radius-003.html [ Pass ] crbug.com/591099 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-margin-box-border-radius-004.html [ Pass ] crbug.com/591099 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-margin-box-border-radius-008.html [ Pass ] crbug.com/591099 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-padding-box-border-radius-002.html [ Pass ] crbug.com/591099 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-polygon-021.html [ Failure ] +crbug.com/591099 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-polygon-022.html [ Pass ] crbug.com/591099 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/writing-modes-3/text-combine-upright-break-inside-001.html [ Failure ] crbug.com/591099 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/writing-modes-3/text-combine-upright-break-inside-001a.html [ Failure ] crbug.com/591099 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/writing-modes-3/text-combine-upright-compression-007.html [ Failure ] @@ -356,6 +362,7 @@ crbug.com/591099 external/wpt/editing/run/unlink.html [ Pass ] crbug.com/591099 external/wpt/encoding/eof-utf-8-three.html [ Failure ] crbug.com/591099 external/wpt/encoding/eof-utf-8-two.html [ Failure ] +crbug.com/591099 external/wpt/encoding/legacy-mb-korean/euc-kr/euckr-decode-ksc_5601.html [ Timeout ] crbug.com/591099 external/wpt/fetch/cross-origin-resource-policy/fetch.any.html [ Timeout ] crbug.com/591099 external/wpt/fetch/http-cache/basic-auth-cache-test.html [ Timeout ] crbug.com/591099 external/wpt/fullscreen/api/document-exit-fullscreen-nested-in-iframe-manual.html [ Pass ] @@ -412,12 +419,17 @@ crbug.com/591099 external/wpt/requestidlecallback/callback-iframe.html [ Pass ] crbug.com/591099 external/wpt/requestidlecallback/callback-timeout-when-busy.html [ Timeout ] crbug.com/591099 external/wpt/requestidlecallback/callback-timeout.html [ Timeout ] -crbug.com/591099 external/wpt/requestidlecallback/cancel-invoked.html [ Pass Timeout ] crbug.com/591099 external/wpt/service-workers/service-worker/navigation-preload/broken-chunked-encoding.https.html [ Failure ] crbug.com/591099 external/wpt/speech-api/SpeechSynthesis-speak-twice.html [ Timeout ] crbug.com/591099 external/wpt/svg/painting/reftests/paint-context-001.svg [ Failure ] crbug.com/591099 external/wpt/svg/painting/reftests/paint-context-002.svg [ Failure ] crbug.com/591099 external/wpt/svg/path/bearing/zero.svg [ Failure ] +crbug.com/591099 external/wpt/trusted-types/HTMLImageElement-src.tentative.html [ Failure ] +crbug.com/591099 external/wpt/trusted-types/HTMLMediaElement-src.tentative.html [ Failure ] +crbug.com/591099 external/wpt/trusted-types/HTMLSourceElement-src.tentative.html [ Failure ] +crbug.com/591099 external/wpt/trusted-types/block-string-assignment-to-HTMLImageElement-src.tentative.html [ Failure ] +crbug.com/591099 external/wpt/trusted-types/block-string-assignment-to-HTMLMediaElement-src.tentative.html [ Failure ] +crbug.com/591099 external/wpt/trusted-types/block-string-assignment-to-HTMLSourceElement-src.tentative.html [ Failure ] crbug.com/591099 external/wpt/user-timing/invoke_with_timing_attributes.worker.html [ Failure ] crbug.com/591099 external/wpt/webrtc/interfaces.html [ Pass Timeout ] crbug.com/591099 external/wpt/websockets/Create-Secure-extensions-empty.any.worker.html [ Timeout ] @@ -498,7 +510,7 @@ crbug.com/591099 fast/box-shadow/box-shadow.html [ Failure ] crbug.com/591099 fast/box-shadow/inset-subpixel.html [ Failure ] crbug.com/591099 fast/box-shadow/inset.html [ Failure ] -crbug.com/591099 fast/box-sizing/replaced.html [ Failure ] +crbug.com/591099 fast/box-sizing/replaced.html [ Failure Pass ] crbug.com/591099 fast/css-generated-content/first-letter-next-sibling-crash.html [ Crash ] crbug.com/591099 fast/css-generated-content/float-first-letter-siblings-convert-to-inline.html [ Crash ] crbug.com/591099 fast/css-grid-layout/grid-align-baseline-vertical.html [ Failure ] @@ -545,7 +557,7 @@ crbug.com/591099 fast/events/background-tab-on-submit-synthesized-ctrl-click.html [ Failure ] crbug.com/591099 fast/events/touch/compositor-touch-hit-rects.html [ Failure ] crbug.com/591099 fast/events/wheel/mainthread-touchpad-fling-latching.html [ Pass ] -crbug.com/591099 fast/events/wheel/wheel-scroll-latching-on-scrollbar.html [ Pass ] +crbug.com/591099 fast/events/wheel/wheel-scroll-latching-on-scrollbar.html [ Failure Pass ] crbug.com/591099 fast/forms/form-hides-table.html [ Failure ] crbug.com/591099 fast/forms/select/select-initial-position.html [ Failure ] crbug.com/591099 fast/forms/select/select-style.html [ Failure ] @@ -655,7 +667,6 @@ crbug.com/591099 http/tests/local/fileapi/select-dragged-file-input.html [ Skip ] crbug.com/591099 http/tests/misc/acid3.html [ Failure ] crbug.com/591099 http/tests/misc/object-embedding-svg-delayed-size-negotiation.xhtml [ Failure ] -crbug.com/591099 http/tests/misc/window-dot-stop.html [ Failure Pass ] crbug.com/591099 http/tests/navigation/form-targets-cross-site-frame-get.html [ Failure ] crbug.com/591099 http/tests/navigation/form-targets-cross-site-frame-no-referrer.html [ Failure ] crbug.com/591099 http/tests/navigation/form-targets-cross-site-frame-post.html [ Failure ] @@ -666,7 +677,7 @@ crbug.com/591099 http/tests/security/setDomainRelaxationForbiddenForURLScheme.html [ Crash ] crbug.com/591099 http/tests/security/xssAuditor/block-does-not-leak-location.html [ Failure ] crbug.com/591099 http/tests/websocket/invalid-subprotocol-characters.html [ Pass Timeout ] -crbug.com/591099 idle-callback/test-runner-run-idle-tasks.html [ Crash Pass Timeout ] +crbug.com/591099 idle-callback/test-runner-run-idle-tasks.html [ Crash Pass ] crbug.com/714962 images/color-profile-background-clip-text.html [ Failure ] crbug.com/591099 images/color-profile-image-filter-all.html [ Failure ] crbug.com/714962 inspector-protocol/css/css-get-platform-fonts.js [ Failure ] @@ -678,7 +689,6 @@ crbug.com/591099 inspector-protocol/timeline/page-frames.js [ Failure Pass ] crbug.com/591099 paint/inline/focus-ring-under-absolute-with-relative-continuation.html [ Failure ] crbug.com/591099 paint/invalidation/background/background-misaligned.html [ Failure ] -crbug.com/591099 paint/invalidation/background/background-resize-height.html [ Failure ] crbug.com/591099 paint/invalidation/background/backgroundSizeRepaint.html [ Failure ] crbug.com/591099 paint/invalidation/block-layout-inline-children-replaced.html [ Failure ] crbug.com/591099 paint/invalidation/box/border-radius-repaint.html [ Failure ] @@ -691,18 +701,10 @@ crbug.com/591099 paint/invalidation/bugzilla-6473.html [ Failure ] crbug.com/591099 paint/invalidation/bugzilla-7235.html [ Failure ] crbug.com/591099 paint/invalidation/clip/caret-ancestor-clip-change.html [ Failure ] -crbug.com/591099 paint/invalidation/clip/clipped-relative.html [ Failure ] crbug.com/591099 paint/invalidation/clip/control-clip.html [ Failure ] crbug.com/591099 paint/invalidation/clip/outline-clip-change.html [ Failure ] -crbug.com/591099 paint/invalidation/clip/replaced-clipped-positioned-not-wrong-incremental-repainting.html [ Failure ] crbug.com/591099 paint/invalidation/compositing/clipping-should-not-repaint-composited-descendants.html [ Failure ] -crbug.com/591099 paint/invalidation/compositing/containing-block-added-individual.html [ Failure ] -crbug.com/591099 paint/invalidation/compositing/containing-block-added.html [ Failure ] -crbug.com/591099 paint/invalidation/compositing/containing-block-removed-individual.html [ Failure ] -crbug.com/591099 paint/invalidation/compositing/containing-block-removed.html [ Failure ] -crbug.com/591099 paint/invalidation/compositing/invalidations-with-large-negative-margin.html [ Failure ] crbug.com/591099 paint/invalidation/compositing/subpixel-offset-scaled-transform-composited.html [ Failure ] -crbug.com/591099 paint/invalidation/crbug-371640-3.html [ Failure ] crbug.com/591099 paint/invalidation/css-grid-layout/grid-element-change-columns-repaint.html [ Failure ] crbug.com/591099 paint/invalidation/css-grid-layout/grid-element-change-rows-repaint.html [ Failure ] crbug.com/591099 paint/invalidation/css-grid-layout/grid-item-change-column-repaint.html [ Failure ] @@ -724,7 +726,6 @@ crbug.com/591099 paint/invalidation/flexbox/justify-items-legacy-change.html [ Failure ] crbug.com/591099 paint/invalidation/flexbox/justify-self-change-keeping-geometry.html [ Failure ] crbug.com/591099 paint/invalidation/flexbox/justify-self-change.html [ Failure ] -crbug.com/591099 paint/invalidation/flexbox/repaint-on-layout.html [ Failure ] crbug.com/591099 paint/invalidation/flexbox/repaint-on-margin-change.html [ Failure ] crbug.com/591099 paint/invalidation/flexbox/scrollbars-changed.html [ Failure ] crbug.com/591099 paint/invalidation/float-move-during-layout.html [ Failure ] @@ -732,7 +733,6 @@ crbug.com/591099 paint/invalidation/forms/slider-thumb-float.html [ Failure ] crbug.com/591099 paint/invalidation/iframe-display-block-to-display-none.html [ Failure ] crbug.com/591099 paint/invalidation/iframe-display-none-to-display-block.html [ Failure ] -crbug.com/591099 paint/invalidation/invisible-objects.html [ Failure ] crbug.com/591099 paint/invalidation/line-flow-with-floats-1.html [ Failure ] crbug.com/591099 paint/invalidation/line-flow-with-floats-10.html [ Failure ] crbug.com/591099 paint/invalidation/line-flow-with-floats-2.html [ Failure ] @@ -743,10 +743,8 @@ crbug.com/591099 paint/invalidation/line-flow-with-floats-7.html [ Failure ] crbug.com/591099 paint/invalidation/line-flow-with-floats-8.html [ Failure ] crbug.com/591099 paint/invalidation/line-flow-with-floats-9.html [ Failure ] -crbug.com/591099 paint/invalidation/lines-with-layout-delta.html [ Failure ] crbug.com/591099 paint/invalidation/list-marker-2.html [ Failure ] crbug.com/591099 paint/invalidation/make-children-non-inline.html [ Failure ] -crbug.com/591099 paint/invalidation/mix-blend-mode-separate-stacking-context.html [ Failure ] crbug.com/591099 paint/invalidation/multicol/column-rules-fixed-height.html [ Failure ] crbug.com/824918 paint/invalidation/multicol/multicol-repaint.html [ Failure ] crbug.com/824918 paint/invalidation/multicol/multicol-with-text.html [ Failure ] @@ -762,43 +760,28 @@ crbug.com/591099 paint/invalidation/outline/outline-continuations.html [ Failure ] crbug.com/591099 paint/invalidation/overflow/align-items-overflow-change.html [ Failure ] crbug.com/591099 paint/invalidation/overflow/align-self-overflow-change.html [ Failure ] -crbug.com/591099 paint/invalidation/overflow/clipped-overflow-visible-subtree.html [ Failure ] -crbug.com/591099 paint/invalidation/overflow/content-into-overflow.html [ Failure ] -crbug.com/591099 paint/invalidation/overflow/float-overflow-right.html [ Failure ] -crbug.com/591099 paint/invalidation/overflow/float-overflow.html [ Failure ] crbug.com/591099 paint/invalidation/overflow/inline-box-overflow-repaint.html [ Failure ] crbug.com/591099 paint/invalidation/overflow/justify-items-overflow-change.html [ Failure ] crbug.com/591099 paint/invalidation/overflow/justify-self-overflow-change.html [ Failure ] crbug.com/591099 paint/invalidation/overflow/line-overflow.html [ Failure ] -crbug.com/591099 paint/invalidation/overflow/overflow-into-content.html [ Failure ] crbug.com/591099 paint/invalidation/overflow/repaint-resized-overflow.html [ Failure ] crbug.com/591099 paint/invalidation/paint-invalidation-with-reparent-across-frame-boundaries.html [ Failure ] crbug.com/591099 paint/invalidation/position/absolute-margin-change-repaint.html [ Failure ] -crbug.com/591099 paint/invalidation/position/absolute-position-change-containing-block.html [ Failure ] -crbug.com/591099 paint/invalidation/position/absolute-position-moved.html [ Failure ] crbug.com/591099 paint/invalidation/position/align-content-position-change-grid.html [ Failure ] crbug.com/591099 paint/invalidation/position/containing-block-position-change.html [ Failure ] -crbug.com/591099 paint/invalidation/position/fixed-to-relative-position-with-absolute-child.html [ Failure ] crbug.com/591099 paint/invalidation/position/intermediate-layout-position.html [ Failure ] crbug.com/591099 paint/invalidation/position/justify-content-position-change-grid.html [ Failure ] crbug.com/591099 paint/invalidation/position/justify-content-position-change.html [ Failure ] -crbug.com/591099 paint/invalidation/position/layout-state-only-positioned.html [ Failure ] crbug.com/591099 paint/invalidation/position/layout-state-relative.html [ Failure ] crbug.com/591099 paint/invalidation/position/layoutstate-invalid-invalidation-inline-relative-positioned.html [ Failure ] crbug.com/591099 paint/invalidation/position/position-change-keeping-geometry.html [ Failure ] -crbug.com/591099 paint/invalidation/position/positioned-great-grandparent-change-location.html [ Failure ] crbug.com/591099 paint/invalidation/position/positioned-list-offset-change-repaint.html [ Failure ] crbug.com/714962 paint/invalidation/position/relative-positioned-movement-repaint.html [ Failure ] crbug.com/591099 paint/invalidation/position/relayout-fixed-position-after-scale.html [ Failure ] crbug.com/591099 paint/invalidation/quotes.html [ Failure ] crbug.com/591099 paint/invalidation/remove-inline-after-layout.html [ Failure ] -crbug.com/591099 paint/invalidation/repaint-across-writing-mode-boundary.html [ Failure ] -crbug.com/591099 paint/invalidation/repaint-descandant-on-ancestor-layer-move.html [ Failure ] crbug.com/591099 paint/invalidation/resize-iframe-text.html [ Failure ] -crbug.com/591099 paint/invalidation/scroll/fixed-after-scroll.html [ Failure ] crbug.com/591099 paint/invalidation/scroll/fixed-child-of-transformed-move-after-scroll.html [ Failure ] -crbug.com/591099 paint/invalidation/scroll/overflow-move-after-scroll.html [ Failure ] -crbug.com/591099 paint/invalidation/scroll/overflow-scroll-composited-non-stacking-child.html [ Failure ] crbug.com/591099 paint/invalidation/scroll/repaint-composited-child-in-scrolled-container.html [ Failure ] crbug.com/591099 paint/invalidation/scroll/resize-scrollable-div.html [ Failure ] crbug.com/591099 paint/invalidation/scroll/resize-scrollable-iframe.html [ Failure ] @@ -834,12 +817,8 @@ crbug.com/591099 paint/invalidation/text-append-dirty-lines.html [ Failure ] crbug.com/591099 paint/invalidation/text-match-document-change.html [ Failure ] crbug.com/591099 paint/invalidation/transform/change-transform.html [ Failure ] -crbug.com/591099 paint/invalidation/transform/invalidation-with-scale-transform.html [ Failure ] -crbug.com/591099 paint/invalidation/window-resize/window-resize-centered-inline-under-fixed-pos.html [ Failure ] crbug.com/591099 paint/invalidation/window-resize/window-resize-no-layout-change2.html [ Failure ] crbug.com/591099 paint/invalidation/window-resize/window-resize-percent-width-height.html [ Failure ] -crbug.com/591099 paint/invalidation/window-resize/window-resize-positioned-bottom.html [ Failure ] -crbug.com/591099 paint/invalidation/window-resize/window-resize-positioned-percent-top.html [ Failure ] crbug.com/591099 paint/invalidation/window-resize/window-resize-viewport-percent.html [ Failure ] crbug.com/591099 paint/overflow/background-mask-should-be-recorded-full.html [ Failure ] crbug.com/591099 paint/pagination/pagination-change-clip-crash.html [ Failure ] @@ -852,14 +831,13 @@ crbug.com/591099 storage/indexeddb/mozilla/indexes.html [ Timeout ] crbug.com/591099 storage/indexeddb/mozilla/test_objectStore_openKeyCursor.html [ Timeout ] crbug.com/591099 storage/indexeddb/objectstore-cursor.html [ Pass Timeout ] -crbug.com/591099 storage/indexeddb/objectstore-keycursor.html [ Pass Timeout ] +crbug.com/591099 storage/indexeddb/objectstore-keycursor.html [ Timeout ] crbug.com/591099 svg/as-border-image/svg-as-border-image-2.html [ Failure ] crbug.com/591099 svg/as-border-image/svg-as-border-image.html [ Failure ] crbug.com/591099 svg/custom/object-sizing-no-width-height.xhtml [ Failure ] crbug.com/591099 svg/filters/feTurbulence-bad-seeds.html [ Failure ] crbug.com/591099 svg/in-html/sizing/svg-inline.html [ Failure ] crbug.com/591099 svg/transforms/text-with-pattern-inside-transformed-html.xhtml [ Failure ] -crbug.com/591099 svg/wicd/rightsizing-grid.html [ Failure Pass ] crbug.com/591099 svg/zoom/page/zoom-img-preserveAspectRatio-support-1.html [ Failure ] crbug.com/591099 svg/zoom/page/zoom-svg-through-object-with-absolute-size-2.xhtml [ Failure ] crbug.com/591099 svg/zoom/page/zoom-svg-through-object-with-absolute-size.xhtml [ Failure ]
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations index 96b13cf9..5fd2e2f 100644 --- a/third_party/WebKit/LayoutTests/TestExpectations +++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -1260,7 +1260,6 @@ crbug.com/591099 virtual/layout_ng_experimental/css3/flexbox/flexitem.html [ Skip ] crbug.com/591099 virtual/layout_ng_experimental/css3/flexbox/float-inside-flexitem.html [ Skip ] crbug.com/591099 virtual/layout_ng_experimental/css3/flexbox/floated-flexbox.html [ Skip ] -crbug.com/591099 virtual/layout_ng_experimental/css3/flexbox/floated-flexitem.html [ Failure ] crbug.com/591099 virtual/layout_ng_experimental/css3/flexbox/inline-flex.html [ Skip ] crbug.com/591099 virtual/layout_ng_experimental/css3/flexbox/inline-flexbox-ignore-firstLine.html [ Skip ] crbug.com/591099 virtual/layout_ng_experimental/css3/flexbox/inline-flexbox-wrap-vertically-width-calculation.html [ Skip ] @@ -1417,17 +1416,13 @@ crbug.com/249112 virtual/layout_ng_experimental/external/wpt/css/css-flexbox/flex-minimum-width-flex-items-007.xht [ Failure ] crbug.com/467127 virtual/layout_ng_experimental/external/wpt/css/css-flexbox/flex-minimum-width-flex-items-008.xht [ Failure ] crbug.com/591099 virtual/layout_ng_experimental/external/wpt/css/css-flexbox/flex-order.html [ Failure ] -crbug.com/591099 virtual/layout_ng_experimental/external/wpt/css/css-flexbox/flex-vertical-align-effect.html [ Failure ] +crbug.com/591099 [ Mac ] virtual/layout_ng_experimental/external/wpt/css/css-flexbox/flex-vertical-align-effect.html [ Failure ] crbug.com/591099 virtual/layout_ng_experimental/external/wpt/css/css-flexbox/flexbox-flex-direction-column-reverse.htm [ Skip ] crbug.com/591099 virtual/layout_ng_experimental/external/wpt/css/css-flexbox/flexbox-flex-direction-column.htm [ Skip ] -crbug.com/591099 virtual/layout_ng_experimental/external/wpt/css/css-flexbox/flexbox-flex-direction-default.htm [ Failure ] -crbug.com/591099 virtual/layout_ng_experimental/external/wpt/css/css-flexbox/flexbox-flex-direction-row-reverse.htm [ Failure ] -crbug.com/591099 virtual/layout_ng_experimental/external/wpt/css/css-flexbox/flexbox-flex-direction-row.htm [ Failure ] crbug.com/591099 virtual/layout_ng_experimental/external/wpt/css/css-flexbox/flexbox-flex-wrap-default.htm [ Failure ] crbug.com/591099 virtual/layout_ng_experimental/external/wpt/css/css-flexbox/flexbox-flex-wrap-flexing.html [ Failure ] crbug.com/591099 virtual/layout_ng_experimental/external/wpt/css/css-flexbox/flexbox-flex-wrap-nowrap.htm [ Failure ] crbug.com/591099 virtual/layout_ng_experimental/external/wpt/css/css-flexbox/flexbox-flex-wrap-wrap-reverse.htm [ Failure ] -crbug.com/591099 virtual/layout_ng_experimental/external/wpt/css/css-flexbox/flexbox-flex-wrap-wrap.htm [ Failure ] crbug.com/591099 virtual/layout_ng_experimental/external/wpt/css/css-flexbox/flexbox_absolute-atomic.html [ Skip ] crbug.com/591099 virtual/layout_ng_experimental/external/wpt/css/css-flexbox/flexbox_align-content-center.html [ Failure ] crbug.com/591099 virtual/layout_ng_experimental/external/wpt/css/css-flexbox/flexbox_align-content-flexend.html [ Failure ] @@ -1452,7 +1447,6 @@ crbug.com/591099 virtual/layout_ng_experimental/external/wpt/css/css-flexbox/flexbox_align-self-stretch.html [ Failure ] crbug.com/591099 virtual/layout_ng_experimental/external/wpt/css/css-flexbox/flexbox_direction-column-reverse.html [ Skip ] crbug.com/591099 virtual/layout_ng_experimental/external/wpt/css/css-flexbox/flexbox_direction-column.html [ Skip ] -crbug.com/591099 virtual/layout_ng_experimental/external/wpt/css/css-flexbox/flexbox_first-line.html [ Failure ] crbug.com/591099 virtual/layout_ng_experimental/external/wpt/css/css-flexbox/flexbox_flex-0-0-0-unitless.html [ Failure ] crbug.com/591099 virtual/layout_ng_experimental/external/wpt/css/css-flexbox/flexbox_flex-0-0-0.html [ Failure ] crbug.com/591099 virtual/layout_ng_experimental/external/wpt/css/css-flexbox/flexbox_flex-0-0-1-unitless-basis.html [ Failure ] @@ -4636,3 +4630,7 @@ crbug.com/864887 [ Linux ] fast/scroll-snap/snaps-after-scrollbar-scrolling.html [ Failure Pass ] crbug.com/864888 [ Mac ] editing/selection/offset-from-point-complex-scripts.html [ Failure Pass ] crbug.com/864891 [ Mac ] virtual/scroll_customization/fast/scroll-behavior/scroll-customization/scroll-customization-property.html [ Timeout Pass ] + +# Sheriff 2018-07-18 +crbug.com/863067 [ Win10 ] fast/dom/Window/window-focus-self.html [ Failure Pass ] +crbug.com/864994 [ Mac ] external/wpt/encoding/legacy-mb-korean/euc-kr/euckr-decode-ksc_5601.html [ Timeout Failure Pass ]
diff --git a/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json b/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json index d2c64524..3f28c5d 100644 --- a/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json +++ b/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json
@@ -103416,6 +103416,11 @@ {} ] ], + "background-fetch/get-ids.https.js": [ + [ + {} + ] + ], "background-fetch/interfaces-worker.https-expected.txt": [ [ {} @@ -103441,6 +103446,11 @@ {} ] ], + "background-fetch/resources/feature-name.txt": [ + [ + {} + ] + ], "background-fetch/resources/sw.js": [ [ {} @@ -141516,11 +141526,6 @@ {} ] ], - "encoding/legacy-mb-korean/euc-kr/euckr-decode-ksc_5601-expected.txt": [ - [ - {} - ] - ], "encoding/legacy-mb-korean/euc-kr/euckr-decode-windows-949-expected.txt": [ [ {} @@ -154646,11 +154651,6 @@ {} ] ], - "html/semantics/forms/form-submission-0/form-data-set-empty-file.window-expected.txt": [ - [ - {} - ] - ], "html/semantics/forms/form-submission-0/form-data-set-usv-form.html": [ [ {} @@ -183895,6 +183895,12 @@ {} ] ], + "background-fetch/fetch.https.window.js": [ + [ + "/background-fetch/fetch.https.window.html", + {} + ] + ], "background-fetch/interfaces.https.any.js": [ [ "/background-fetch/interfaces.https.any.html", @@ -252085,6 +252091,18 @@ {} ] ], + "trusted-types/HTMLImageElement-src.tentative.html": [ + [ + "/trusted-types/HTMLImageElement-src.tentative.html", + {} + ] + ], + "trusted-types/HTMLSourceElement-src.tentative.html": [ + [ + "/trusted-types/HTMLSourceElement-src.tentative.html", + {} + ] + ], "trusted-types/TrustedHTML.tentative.html": [ [ "/trusted-types/TrustedHTML.tentative.html", @@ -252109,6 +252127,18 @@ {} ] ], + "trusted-types/block-string-assignment-to-HTMLImageElement-src.tentative.html": [ + [ + "/trusted-types/block-string-assignment-to-HTMLImageElement-src.tentative.html", + {} + ] + ], + "trusted-types/block-string-assignment-to-HTMLSourceElement-src.tentative.html": [ + [ + "/trusted-types/block-string-assignment-to-HTMLSourceElement-src.tentative.html", + {} + ] + ], "trusted-types/block-string-assignment-to-createContextualFragment.tentative.html": [ [ "/trusted-types/block-string-assignment-to-createContextualFragment.tentative.html", @@ -274945,7 +274975,7 @@ "support" ], "apng/META.yml": [ - "ad3fb4311e96c76e3c9d3ab7be32dfc9d2708b7e", + "714075388ee3d8f4cf698f0291aebb34f62badff", "support" ], "apng/OWNERS": [ @@ -274973,7 +275003,7 @@ "testharness" ], "async-local-storage/META.yml": [ - "9fcc91fa7224a8cb8b7322d607d5a02da459af3d", + "8f1032eff89e5e9128aad0b324e816e569850979", "support" ], "async-local-storage/storage-smoke-test.https.tentative.html": [ @@ -275024,6 +275054,14 @@ "63049632a7ddca3eac182885b516471dcb85e75b", "testharness" ], + "background-fetch/fetch.https.window.js": [ + "a62e7ea112a381f24dd2c9cdec6a012dec9e41c3", + "testharness" + ], + "background-fetch/get-ids.https.js": [ + "6177ea08e069fd1aca85fd3d772a53022b07f519", + "support" + ], "background-fetch/interfaces-worker.https-expected.txt": [ "88211a0d44012d229af146366440ced5d12b0988", "support" @@ -275056,12 +275094,16 @@ "f46ead816fe9c7f9d7c3e75357aa77a71a44ca1b", "testharness" ], + "background-fetch/resources/feature-name.txt": [ + "d93e330118c50918b3205c3ea1e9ea371937aaaf", + "support" + ], "background-fetch/resources/sw.js": [ - "9b4fbabe0209a5367186e8c5717be88f32530027", + "c7b6e87115da4051980837838900fee80df9cb27", "support" ], "background-fetch/resources/utils.js": [ - "8c269c1c115f471bcd6b7551136fcad69d4cddc4", + "1602f7a4e12d8470974c706ae3b2e90f8920f854", "support" ], "battery-status/META.yml": [ @@ -278777,7 +278819,7 @@ "support" ], "content-security-policy/support/checkReport.sub.js": [ - "5a67d7d16562a8925a916214bb1d0ab27f52ddf3", + "9349d682e7ebb6a7dc504d720733f6130d253855", "support" ], "content-security-policy/support/dedicated-worker-helper.js": [ @@ -300577,7 +300619,7 @@ "support" ], "css/css-filter/META.yml": [ - "4cf686b2a40c2c89e5e557d667fb3580657f9aa7", + "edddb36dd8335f59f963701dc92c66742f4bf66c", "support" ], "css/css-filter/filtered-block-is-container-ref.html": [ @@ -351217,7 +351259,7 @@ "testharness" ], "domxpath/META.yml": [ - "e307eb75427dcc46a6613e2118a66bb854f6a164", + "4abe032da356ca26e4efe90a4815e318d487d04e", "support" ], "domxpath/OWNERS": [ @@ -352480,12 +352522,8 @@ "a49f47163ba6bcc51c9f3422b8be537ebdc2675d", "testharness" ], - "encoding/legacy-mb-korean/euc-kr/euckr-decode-ksc_5601-expected.txt": [ - "736dbee66d64147883e2e42334b28a882320f8c7", - "support" - ], "encoding/legacy-mb-korean/euc-kr/euckr-decode-ksc_5601.html": [ - "70d7606a3c067b5c3d1c6f718ffd797427b5e687", + "fc04f9c19c600fbd29596b6999b2f3b42492f8b6", "testharness" ], "encoding/legacy-mb-korean/euc-kr/euckr-decode-windows-949-expected.txt": [ @@ -357001,7 +357039,7 @@ "testharness" ], "graphics-aam/META.yml": [ - "521d90b87283645c22b8752fdb69031cc08cf3f7", + "d38103192c94766770023b35bf18fdde733765f8", "support" ], "graphics-aam/graphics-document_on_html_element-manual-expected.txt": [ @@ -370764,10 +370802,6 @@ "5cb7e225df89561a1137933615b93c9912444006", "support" ], - "html/semantics/forms/form-submission-0/form-data-set-empty-file.window-expected.txt": [ - "e4d90a03c9c0c9bf265fc1cdb58141275c423459", - "support" - ], "html/semantics/forms/form-submission-0/form-data-set-empty-file.window.js": [ "727543085a822c9976f443b2e5adbf9708aecec2", "testharness" @@ -405028,6 +405062,14 @@ "5b387ae813ec359650618081128001955c7024e2", "testharness" ], + "trusted-types/HTMLImageElement-src.tentative.html": [ + "1ac2d898da5cb684f642250760dce70b0e130489", + "testharness" + ], + "trusted-types/HTMLSourceElement-src.tentative.html": [ + "bc816aea73f1d1eaf7fbd7516360430483608fee", + "testharness" + ], "trusted-types/META.yml": [ "4966e7a03e815dac333218faf57875b57b9dd535", "support" @@ -405052,6 +405094,14 @@ "1a3857314ef43c75bcf398f2429d34adce135edb", "testharness" ], + "trusted-types/block-string-assignment-to-HTMLImageElement-src.tentative.html": [ + "29af914e035e196635be0568d8536f037c179c28", + "testharness" + ], + "trusted-types/block-string-assignment-to-HTMLSourceElement-src.tentative.html": [ + "1a403b7f4d9a0614e3fb7f41b95a0cf1e5fbb3ff", + "testharness" + ], "trusted-types/block-string-assignment-to-createContextualFragment.tentative.html": [ "aa2e78ed37b9b04378e0cbfc93a510bf428f6f78", "testharness" @@ -406817,7 +406867,7 @@ "testharness" ], "web-locks/META.yml": [ - "b0d8c58c78cfd2dcc8a81b83fb17afadeabfb375", + "d54d4f23e137a6c6ac2a40d943fafae5dea66758", "support" ], "web-locks/OWNERS": [ @@ -410873,7 +410923,7 @@ "testharness" ], "webvr/META.yml": [ - "58bfdf395aa56d3292d85f1a3b0b1545c2b19c57", + "ed6e1e542838eaeaed1e527b57ea9fcf1e0f1fce", "support" ], "webvr/OWNERS": [ @@ -415437,7 +415487,7 @@ "support" ], "x-frame-options/META.yml": [ - "382438af20cfed5bdd980fdd3bc39771472c5547", + "956e7abe295e8eb9198ff2a8208f95f4147edbbc", "support" ], "x-frame-options/OWNERS": [
diff --git a/third_party/WebKit/LayoutTests/external/wpt/apng/META.yml b/third_party/WebKit/LayoutTests/external/wpt/apng/META.yml index 89834729..a660c7e1 100644 --- a/third_party/WebKit/LayoutTests/external/wpt/apng/META.yml +++ b/third_party/WebKit/LayoutTests/external/wpt/apng/META.yml
@@ -1,3 +1,4 @@ +spec: https://wiki.mozilla.org/APNG_Specification suggested_reviewers: - stuartparmenter - svgeesus
diff --git a/third_party/WebKit/LayoutTests/external/wpt/async-local-storage/META.yml b/third_party/WebKit/LayoutTests/external/wpt/async-local-storage/META.yml index 0f3cf59..1bbe9e5ac 100644 --- a/third_party/WebKit/LayoutTests/external/wpt/async-local-storage/META.yml +++ b/third_party/WebKit/LayoutTests/external/wpt/async-local-storage/META.yml
@@ -1,2 +1,3 @@ +spec: https://domenic.github.io/async-local-storage/ suggested_reviewers: - domenic
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-filter/META.yml b/third_party/WebKit/LayoutTests/external/wpt/css/css-filter/META.yml index a477613d..8d06833 100644 --- a/third_party/WebKit/LayoutTests/external/wpt/css/css-filter/META.yml +++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-filter/META.yml
@@ -1,2 +1,3 @@ +spec: https://drafts.fxtf.org/filter-effects/ suggested_reviewers: - chrishtr
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-logical/logical-box-inset-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/css/css-logical/logical-box-inset-expected.txt index 62e3fc56..89988c38 100644 --- a/third_party/WebKit/LayoutTests/external/wpt/css/css-logical/logical-box-inset-expected.txt +++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-logical/logical-box-inset-expected.txt
@@ -1,44 +1,44 @@ This is a testharness.js-based test. -FAIL Test that logical inset-* properties are supported. assert_equals: logical properties in inline style, inset-inline-start expected "1px" but got "" -FAIL Test that logical inset-* properties share computed values with their physical associates, with 'writing-mode: horizontal-tb; direction: ltr; '. assert_equals: logical properties on one declaration, writing mode properties on another, 'writing-mode: horizontal-tb; direction: ltr; ', inset-inline-start expected "1px" but got "" -FAIL Test that inset-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: horizontal-tb; direction: ltr; '. assert_equals: shorthand properties on one declaration, writing mode properties on another, 'writing-mode: horizontal-tb; direction: ltr; ', inset-inline-start expected "1px" but got "" -FAIL Test that inset-* properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: horizontal-tb; direction: ltr; '. assert_equals: 'inset-inline-start' last on single declaration, 'writing-mode: horizontal-tb; direction: ltr; ', left expected "5px" but got "1px" -FAIL Test that inset-* properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: horizontal-tb; direction: ltr; '. assert_equals: 'inset-inline-start', two declarations, 'writing-mode: horizontal-tb; direction: ltr; ', left expected "5px" but got "1px" -FAIL Test that logical inset-* properties share computed values with their physical associates, with 'writing-mode: horizontal-tb; direction: rtl; '. assert_equals: logical properties on one declaration, writing mode properties on another, 'writing-mode: horizontal-tb; direction: rtl; ', inset-inline-start expected "1px" but got "" -FAIL Test that inset-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: horizontal-tb; direction: rtl; '. assert_equals: shorthand properties on one declaration, writing mode properties on another, 'writing-mode: horizontal-tb; direction: rtl; ', inset-inline-start expected "1px" but got "" -FAIL Test that inset-* properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: horizontal-tb; direction: rtl; '. assert_equals: 'inset-inline-start' last on single declaration, 'writing-mode: horizontal-tb; direction: rtl; ', inset-inline-end expected "1px" but got "" -FAIL Test that inset-* properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: horizontal-tb; direction: rtl; '. assert_equals: 'inset-inline-start', two declarations, 'writing-mode: horizontal-tb; direction: rtl; ', inset-inline-end expected "1px" but got "" -FAIL Test that logical inset-* properties share computed values with their physical associates, with 'writing-mode: vertical-rl; direction: rtl; '. assert_equals: logical properties on one declaration, writing mode properties on another, 'writing-mode: vertical-rl; direction: rtl; ', inset-inline-start expected "1px" but got "" -FAIL Test that inset-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: vertical-rl; direction: rtl; '. assert_equals: shorthand properties on one declaration, writing mode properties on another, 'writing-mode: vertical-rl; direction: rtl; ', inset-inline-start expected "1px" but got "" -FAIL Test that inset-* properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: vertical-rl; direction: rtl; '. assert_equals: 'inset-inline-start' last on single declaration, 'writing-mode: vertical-rl; direction: rtl; ', inset-block-end expected "1px" but got "" -FAIL Test that inset-* properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: vertical-rl; direction: rtl; '. assert_equals: 'inset-inline-start', two declarations, 'writing-mode: vertical-rl; direction: rtl; ', inset-block-end expected "1px" but got "" -FAIL Test that logical inset-* properties share computed values with their physical associates, with 'writing-mode: sideways-rl; direction: rtl; '. assert_equals: logical properties on one declaration, writing mode properties on another, 'writing-mode: sideways-rl; direction: rtl; ', inset-inline-start expected "1px" but got "" -FAIL Test that inset-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: sideways-rl; direction: rtl; '. assert_equals: shorthand properties on one declaration, writing mode properties on another, 'writing-mode: sideways-rl; direction: rtl; ', inset-inline-start expected "1px" but got "" -FAIL Test that inset-* properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: sideways-rl; direction: rtl; '. assert_equals: 'inset-inline-start' last on single declaration, 'writing-mode: sideways-rl; direction: rtl; ', inset-block-end expected "1px" but got "" -FAIL Test that inset-* properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: sideways-rl; direction: rtl; '. assert_equals: 'inset-inline-start', two declarations, 'writing-mode: sideways-rl; direction: rtl; ', inset-block-end expected "1px" but got "" -FAIL Test that logical inset-* properties share computed values with their physical associates, with 'writing-mode: vertical-rl; direction: ltr; '. assert_equals: logical properties on one declaration, writing mode properties on another, 'writing-mode: vertical-rl; direction: ltr; ', inset-inline-start expected "1px" but got "" -FAIL Test that inset-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: vertical-rl; direction: ltr; '. assert_equals: shorthand properties on one declaration, writing mode properties on another, 'writing-mode: vertical-rl; direction: ltr; ', inset-inline-start expected "1px" but got "" -FAIL Test that inset-* properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: vertical-rl; direction: ltr; '. assert_equals: 'inset-inline-start' last on single declaration, 'writing-mode: vertical-rl; direction: ltr; ', inset-block-end expected "1px" but got "" -FAIL Test that inset-* properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: vertical-rl; direction: ltr; '. assert_equals: 'inset-inline-start', two declarations, 'writing-mode: vertical-rl; direction: ltr; ', inset-block-end expected "1px" but got "" -FAIL Test that logical inset-* properties share computed values with their physical associates, with 'writing-mode: sideways-rl; direction: ltr; '. assert_equals: logical properties on one declaration, writing mode properties on another, 'writing-mode: sideways-rl; direction: ltr; ', inset-inline-start expected "1px" but got "" -FAIL Test that inset-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: sideways-rl; direction: ltr; '. assert_equals: shorthand properties on one declaration, writing mode properties on another, 'writing-mode: sideways-rl; direction: ltr; ', inset-inline-start expected "1px" but got "" -FAIL Test that inset-* properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: sideways-rl; direction: ltr; '. assert_equals: 'inset-inline-start' last on single declaration, 'writing-mode: sideways-rl; direction: ltr; ', inset-block-end expected "1px" but got "" -FAIL Test that inset-* properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: sideways-rl; direction: ltr; '. assert_equals: 'inset-inline-start', two declarations, 'writing-mode: sideways-rl; direction: ltr; ', inset-block-end expected "1px" but got "" -FAIL Test that logical inset-* properties share computed values with their physical associates, with 'writing-mode: vertical-lr; direction: rtl; '. assert_equals: logical properties on one declaration, writing mode properties on another, 'writing-mode: vertical-lr; direction: rtl; ', inset-inline-start expected "1px" but got "" -FAIL Test that inset-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: vertical-lr; direction: rtl; '. assert_equals: shorthand properties on one declaration, writing mode properties on another, 'writing-mode: vertical-lr; direction: rtl; ', inset-inline-start expected "1px" but got "" -FAIL Test that inset-* properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: vertical-lr; direction: rtl; '. assert_equals: 'inset-inline-start' last on single declaration, 'writing-mode: vertical-lr; direction: rtl; ', inset-block-start expected "1px" but got "" -FAIL Test that inset-* properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: vertical-lr; direction: rtl; '. assert_equals: 'inset-inline-start', two declarations, 'writing-mode: vertical-lr; direction: rtl; ', inset-block-start expected "1px" but got "" -FAIL Test that logical inset-* properties share computed values with their physical associates, with 'writing-mode: sideways-lr; direction: ltr; '. assert_equals: logical properties on one declaration, writing mode properties on another, 'writing-mode: sideways-lr; direction: ltr; ', inset-inline-start expected "1px" but got "" -FAIL Test that inset-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: sideways-lr; direction: ltr; '. assert_equals: shorthand properties on one declaration, writing mode properties on another, 'writing-mode: sideways-lr; direction: ltr; ', inset-inline-start expected "1px" but got "" -FAIL Test that inset-* properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: sideways-lr; direction: ltr; '. assert_equals: 'inset-inline-start' last on single declaration, 'writing-mode: sideways-lr; direction: ltr; ', inset-block-start expected "1px" but got "" -FAIL Test that inset-* properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: sideways-lr; direction: ltr; '. assert_equals: 'inset-inline-start', two declarations, 'writing-mode: sideways-lr; direction: ltr; ', inset-block-start expected "1px" but got "" -FAIL Test that logical inset-* properties share computed values with their physical associates, with 'writing-mode: vertical-lr; direction: ltr; '. assert_equals: logical properties on one declaration, writing mode properties on another, 'writing-mode: vertical-lr; direction: ltr; ', inset-inline-start expected "1px" but got "" -FAIL Test that inset-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: vertical-lr; direction: ltr; '. assert_equals: shorthand properties on one declaration, writing mode properties on another, 'writing-mode: vertical-lr; direction: ltr; ', inset-inline-start expected "1px" but got "" -FAIL Test that inset-* properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: vertical-lr; direction: ltr; '. assert_equals: 'inset-inline-start' last on single declaration, 'writing-mode: vertical-lr; direction: ltr; ', inset-block-start expected "1px" but got "" -FAIL Test that inset-* properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: vertical-lr; direction: ltr; '. assert_equals: 'inset-inline-start', two declarations, 'writing-mode: vertical-lr; direction: ltr; ', inset-block-start expected "1px" but got "" -FAIL Test that logical inset-* properties share computed values with their physical associates, with 'writing-mode: sideways-lr; direction: rtl; '. assert_equals: logical properties on one declaration, writing mode properties on another, 'writing-mode: sideways-lr; direction: rtl; ', inset-inline-start expected "1px" but got "" -FAIL Test that inset-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: sideways-lr; direction: rtl; '. assert_equals: shorthand properties on one declaration, writing mode properties on another, 'writing-mode: sideways-lr; direction: rtl; ', inset-inline-start expected "1px" but got "" -FAIL Test that inset-* properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: sideways-lr; direction: rtl; '. assert_equals: 'inset-inline-start' last on single declaration, 'writing-mode: sideways-lr; direction: rtl; ', inset-block-start expected "1px" but got "" -FAIL Test that inset-* properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: sideways-lr; direction: rtl; '. assert_equals: 'inset-inline-start', two declarations, 'writing-mode: sideways-lr; direction: rtl; ', inset-block-start expected "1px" but got "" +PASS Test that logical inset-* properties are supported. +PASS Test that logical inset-* properties share computed values with their physical associates, with 'writing-mode: horizontal-tb; direction: ltr; '. +PASS Test that inset-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: horizontal-tb; direction: ltr; '. +PASS Test that inset-* properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: horizontal-tb; direction: ltr; '. +PASS Test that inset-* properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: horizontal-tb; direction: ltr; '. +PASS Test that logical inset-* properties share computed values with their physical associates, with 'writing-mode: horizontal-tb; direction: rtl; '. +PASS Test that inset-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: horizontal-tb; direction: rtl; '. +PASS Test that inset-* properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: horizontal-tb; direction: rtl; '. +PASS Test that inset-* properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: horizontal-tb; direction: rtl; '. +PASS Test that logical inset-* properties share computed values with their physical associates, with 'writing-mode: vertical-rl; direction: rtl; '. +PASS Test that inset-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: vertical-rl; direction: rtl; '. +PASS Test that inset-* properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: vertical-rl; direction: rtl; '. +PASS Test that inset-* properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: vertical-rl; direction: rtl; '. +FAIL Test that logical inset-* properties share computed values with their physical associates, with 'writing-mode: sideways-rl; direction: rtl; '. assert_equals: logical properties on one declaration, writing mode properties on another, 'writing-mode: sideways-rl; direction: rtl; ', bottom expected "1px" but got "4px" +FAIL Test that inset-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: sideways-rl; direction: rtl; '. assert_equals: shorthand properties on one declaration, writing mode properties on another, 'writing-mode: sideways-rl; direction: rtl; ', bottom expected "1px" but got "0px" +FAIL Test that inset-* properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: sideways-rl; direction: rtl; '. assert_equals: 'inset-inline-start' last on single declaration, 'writing-mode: sideways-rl; direction: rtl; ', inset-block-end expected "1px" but got "4px" +FAIL Test that inset-* properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: sideways-rl; direction: rtl; '. assert_equals: 'inset-inline-start', two declarations, 'writing-mode: sideways-rl; direction: rtl; ', inset-block-end expected "1px" but got "4px" +PASS Test that logical inset-* properties share computed values with their physical associates, with 'writing-mode: vertical-rl; direction: ltr; '. +PASS Test that inset-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: vertical-rl; direction: ltr; '. +PASS Test that inset-* properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: vertical-rl; direction: ltr; '. +PASS Test that inset-* properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: vertical-rl; direction: ltr; '. +FAIL Test that logical inset-* properties share computed values with their physical associates, with 'writing-mode: sideways-rl; direction: ltr; '. assert_equals: logical properties on one declaration, writing mode properties on another, 'writing-mode: sideways-rl; direction: ltr; ', top expected "1px" but got "3px" +FAIL Test that inset-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: sideways-rl; direction: ltr; '. assert_equals: shorthand properties on one declaration, writing mode properties on another, 'writing-mode: sideways-rl; direction: ltr; ', top expected "1px" but got "0px" +FAIL Test that inset-* properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: sideways-rl; direction: ltr; '. assert_equals: 'inset-inline-start' last on single declaration, 'writing-mode: sideways-rl; direction: ltr; ', left expected "1px" but got "5px" +FAIL Test that inset-* properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: sideways-rl; direction: ltr; '. assert_equals: 'inset-inline-start', two declarations, 'writing-mode: sideways-rl; direction: ltr; ', left expected "1px" but got "5px" +PASS Test that logical inset-* properties share computed values with their physical associates, with 'writing-mode: vertical-lr; direction: rtl; '. +PASS Test that inset-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: vertical-lr; direction: rtl; '. +PASS Test that inset-* properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: vertical-lr; direction: rtl; '. +PASS Test that inset-* properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: vertical-lr; direction: rtl; '. +FAIL Test that logical inset-* properties share computed values with their physical associates, with 'writing-mode: sideways-lr; direction: ltr; '. assert_equals: logical properties on one declaration, writing mode properties on another, 'writing-mode: sideways-lr; direction: ltr; ', bottom expected "1px" but got "4px" +FAIL Test that inset-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: sideways-lr; direction: ltr; '. assert_equals: shorthand properties on one declaration, writing mode properties on another, 'writing-mode: sideways-lr; direction: ltr; ', bottom expected "1px" but got "0px" +FAIL Test that inset-* properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: sideways-lr; direction: ltr; '. assert_equals: 'inset-inline-start' last on single declaration, 'writing-mode: sideways-lr; direction: ltr; ', left expected "1px" but got "5px" +FAIL Test that inset-* properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: sideways-lr; direction: ltr; '. assert_equals: 'inset-inline-start', two declarations, 'writing-mode: sideways-lr; direction: ltr; ', left expected "1px" but got "5px" +PASS Test that logical inset-* properties share computed values with their physical associates, with 'writing-mode: vertical-lr; direction: ltr; '. +PASS Test that inset-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: vertical-lr; direction: ltr; '. +PASS Test that inset-* properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: vertical-lr; direction: ltr; '. +PASS Test that inset-* properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: vertical-lr; direction: ltr; '. +FAIL Test that logical inset-* properties share computed values with their physical associates, with 'writing-mode: sideways-lr; direction: rtl; '. assert_equals: logical properties on one declaration, writing mode properties on another, 'writing-mode: sideways-lr; direction: rtl; ', top expected "1px" but got "3px" +FAIL Test that inset-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: sideways-lr; direction: rtl; '. assert_equals: shorthand properties on one declaration, writing mode properties on another, 'writing-mode: sideways-lr; direction: rtl; ', top expected "1px" but got "0px" +FAIL Test that inset-* properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: sideways-lr; direction: rtl; '. assert_equals: 'inset-inline-start' last on single declaration, 'writing-mode: sideways-lr; direction: rtl; ', inset-block-start expected "1px" but got "3px" +FAIL Test that inset-* properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: sideways-lr; direction: rtl; '. assert_equals: 'inset-inline-start', two declarations, 'writing-mode: sideways-lr; direction: rtl; ', inset-block-start expected "1px" but got "3px" Harness: the test ran to completion.
diff --git a/third_party/WebKit/LayoutTests/external/wpt/domxpath/META.yml b/third_party/WebKit/LayoutTests/external/wpt/domxpath/META.yml index a03fca23..6c9963a8 100644 --- a/third_party/WebKit/LayoutTests/external/wpt/domxpath/META.yml +++ b/third_party/WebKit/LayoutTests/external/wpt/domxpath/META.yml
@@ -1,3 +1,4 @@ +spec: https://www.w3.org/TR/DOM-Level-3-XPath/ suggested_reviewers: - gsnedders - zqzhang
diff --git a/third_party/WebKit/LayoutTests/external/wpt/encoding/legacy-mb-korean/euc-kr/euckr-decode-ksc_5601.html b/third_party/WebKit/LayoutTests/external/wpt/encoding/legacy-mb-korean/euc-kr/euckr-decode-ksc_5601.html index 6499ab5..afda7233 100644 --- a/third_party/WebKit/LayoutTests/external/wpt/encoding/legacy-mb-korean/euc-kr/euckr-decode-ksc_5601.html +++ b/third_party/WebKit/LayoutTests/external/wpt/encoding/legacy-mb-korean/euc-kr/euckr-decode-ksc_5601.html
@@ -6,6 +6,7 @@ <meta name="timeout" content="long"> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script> +<script src="/common/subset-tests.js"></script> <link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org"> <link rel="help" href="https://encoding.spec.whatwg.org/#names-and-labels"> <meta name="assert" content="The browser produces the same decoding behavior for a document labeled 'ksc_5601' as for a document labeled 'euc-kr'.">
diff --git a/third_party/WebKit/LayoutTests/external/wpt/graphics-aam/META.yml b/third_party/WebKit/LayoutTests/external/wpt/graphics-aam/META.yml index d99ed62b8..69446b20 100644 --- a/third_party/WebKit/LayoutTests/external/wpt/graphics-aam/META.yml +++ b/third_party/WebKit/LayoutTests/external/wpt/graphics-aam/META.yml
@@ -1,3 +1,4 @@ +spec: https://w3c.github.io/graphics-aam/ suggested_reviewers: - halindrome - joanmarie
diff --git a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/DOMParser-requiresTrustedTypes.tentative.html b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/DOMParser-requiresTrustedTypes.tentative.html index 2541c496..7e21fea 100644 --- a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/DOMParser-requiresTrustedTypes.tentative.html +++ b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/DOMParser-requiresTrustedTypes.tentative.html
@@ -1,7 +1,7 @@ <!DOCTYPE html> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script> -<script src="./support/helper.js"></script> +<script src="support/helper.sub.js"></script> <meta http-equiv="Content-Security-Policy" content="require-trusted-types"> <body>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/DOMParser.tentative.html b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/DOMParser.tentative.html index 9541ba6..53d2b44f 100644 --- a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/DOMParser.tentative.html +++ b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/DOMParser.tentative.html
@@ -1,7 +1,7 @@ <!DOCTYPE html> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script> -<script src="./support/helper.js"></script> +<script src="support/helper.sub.js"></script> <body> <script> test(t => {
diff --git a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/HTMLBaseElement-href.tentative.html b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/HTMLBaseElement-href.tentative.html index 322d7405..80bed80 100644 --- a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/HTMLBaseElement-href.tentative.html +++ b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/HTMLBaseElement-href.tentative.html
@@ -3,7 +3,7 @@ <head> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script> - <script src="support/helper.js"></script> + <script src="support/helper.sub.js"></script> <script> //helper function for the tests
diff --git a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/HTMLImageElement-src.tentative.html b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/HTMLImageElement-src.tentative.html new file mode 100644 index 0000000..e4c0a3e --- /dev/null +++ b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/HTMLImageElement-src.tentative.html
@@ -0,0 +1,24 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="./support/helper.js"></script> + +<body> +<script> + test(t => { + var url = TrustedURL.create(URLS.safe); + + var d = document.createElement('image'); + d.src = url; + assert_equals("" + d.src, URLS.safe); + }, "src = TrustedURL.create()."); + + test(t => { + var url = TrustedURL.unsafelyCreate(URLS.safe); + + var d = document.createElement('image'); + d.src = url; + assert_equals("" + d.src, URLS.safe); + }, "src = TrustedURL.unsafelyCreate()."); +</script> +
diff --git a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/HTMLMediaElement-src.tentative.html b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/HTMLMediaElement-src.tentative.html new file mode 100644 index 0000000..ece95eaf --- /dev/null +++ b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/HTMLMediaElement-src.tentative.html
@@ -0,0 +1,23 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="./support/helper.js"></script> + +<body> +<script> + test(t => { + var url = TrustedURL.create(URLS.safe); + + var d = document.createElement('video'); + d.src = url; + assert_equals("" + d.src, URLS.safe); + }, "src = TrustedURL.create()."); + + test(t => { + var url = TrustedURL.unsafelyCreate(URLS.safe); + + var d = document.createElement('video'); + d.src = url; + assert_equals("" + d.src, URLS.safe); + }, "src = TrustedURL.unsafelyCreate()."); +</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/HTMLSourceElement-src.tentative.html b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/HTMLSourceElement-src.tentative.html new file mode 100644 index 0000000..e32a1fcb --- /dev/null +++ b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/HTMLSourceElement-src.tentative.html
@@ -0,0 +1,22 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="./support/helper.js"></script> +<body> +<script> + test(t => { + var url = TrustedURL.create(URLS.safe); + + var d = document.createElement('source'); + d.src = url; + assert_equals(d.src, URLS.safe); + }, "src = TrustedURL.create()."); + + test(t => { + var url = TrustedURL.unsafelyCreate(URLS.safe); + + var d = document.createElement('source'); + d.src = url; + assert_equals(d.src, URLS.safe); + }, "src = TrustedURL.unsafelyCreate()."); +</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/TrustedHTML.tentative.html b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/TrustedHTML.tentative.html index 2a28f1b..a8d4e78 100644 --- a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/TrustedHTML.tentative.html +++ b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/TrustedHTML.tentative.html
@@ -1,7 +1,7 @@ <!DOCTYPE html> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script> -<script src="./support/helper.js"></script> +<script src="support/helper.sub.js"></script> <script> test(t => { var html = TrustedHTML.escape(STRINGS.unescapedHTML);
diff --git a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/TrustedScriptURL.tentative.html b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/TrustedScriptURL.tentative.html index 39a920a0..92bc87f1 100644 --- a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/TrustedScriptURL.tentative.html +++ b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/TrustedScriptURL.tentative.html
@@ -1,7 +1,7 @@ <!DOCTYPE html> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script> -<script src="./support/helper.js"></script> +<script src="support/helper.sub.js"></script> <script> test(t => { var url = TrustedScriptURL.unsafelyCreate(URLS.safe);
diff --git a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/TrustedURL.tentative.html b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/TrustedURL.tentative.html index 599a0c69b..5048326 100644 --- a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/TrustedURL.tentative.html +++ b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/TrustedURL.tentative.html
@@ -1,7 +1,7 @@ <!DOCTYPE html> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script> -<script src="./support/helper.js"></script> +<script src="support/helper.sub.js"></script> <script> test(t => { var url = TrustedURL.create(URLS.safe);
diff --git a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-HTMLBaseElement-href.tentative.html b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-HTMLBaseElement-href.tentative.html index 88aeb5b..a973e29 100644 --- a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-HTMLBaseElement-href.tentative.html +++ b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-HTMLBaseElement-href.tentative.html
@@ -3,7 +3,7 @@ <head> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script> - <script src="support/helper.js"></script> + <script src="support/helper.sub.js"></script> <meta http-equiv="Content-Security-Policy" content="require-trusted-types">
diff --git a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-HTMLImageElement-src.tentative.html b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-HTMLImageElement-src.tentative.html new file mode 100644 index 0000000..e2c7dcb --- /dev/null +++ b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-HTMLImageElement-src.tentative.html
@@ -0,0 +1,33 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="./support/helper.js"></script> + +<meta http-equiv="Content-Security-Policy" content="require-trusted-types"> +<body> +<script> + //URL assignments don't throw + test(t => { + var url = TrustedURL.create(URLS.safe); + + var d = document.createElement('img'); + d.src = url; + assert_equals("" + d.src, URLS.safe); + }, "src = TrustedURL.create()."); + + test(t => { + var url = TrustedURL.unsafelyCreate(URLS.safe); + + var d = document.createElement('img'); + d.src = url; + assert_equals("" + d.src, URLS.safe); + }, "src = TrustedURL.unsafelyCreate()."); + + //String assignment throws + test(t => { + var d = document.createElement('img'); + assert_throws(new TypeError(), _ => { + d.src = "Fail."; + }); + }, "'src = string' throws."); +</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-HTMLMediaElement-src.tentative.html b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-HTMLMediaElement-src.tentative.html new file mode 100644 index 0000000..8e4b039 --- /dev/null +++ b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-HTMLMediaElement-src.tentative.html
@@ -0,0 +1,33 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="./support/helper.js"></script> + +<meta http-equiv="Content-Security-Policy" content="require-trusted-types"> +<body> +<script> + //URL assignments don't throw + test(t => { + var url = TrustedURL.create(URLS.safe); + + var d = document.createElement('video'); + d.src = url; + assert_equals("" + d.src, URLS.safe); + }, "src = TrustedURL.create()."); + + test(t => { + var url = TrustedURL.unsafelyCreate(URLS.safe); + + var d = document.createElement('video'); + d.src = url; + assert_equals("" + d.src, URLS.safe); + }, "src = TrustedURL.unsafelyCreate()."); + + //String assignment throws + test(t => { + var d = document.createElement('video'); + assert_throws(new TypeError(), _ => { + d.src = "Fail."; + }); + }, "'src = string' throws."); +</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-HTMLSourceElement-src.tentative.html b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-HTMLSourceElement-src.tentative.html new file mode 100644 index 0000000..06262ff --- /dev/null +++ b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-HTMLSourceElement-src.tentative.html
@@ -0,0 +1,36 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="./support/helper.js"></script> + +<meta http-equiv="Content-Security-Policy" content="require-trusted-types"> +<body> +<script> + //URL assignments don't throw + test(t => { + var url = TrustedURL.create(URLS.safe); + + var d = document.createElement('source'); + d.src = url; + assert_equals(d.src, URLS.safe); + }, "src = TrustedURL.create()."); + + test(t => { + var url = TrustedURL.unsafelyCreate(URLS.safe); + + var d = document.createElement('source'); + d.src = url; + assert_equals(d.src, URLS.safe); + }, "src = TrustedURL.unsafelyCreate()."); + + //String assignment throws + test(t => { + var url = "Fail." + + var d = document.createElement('source'); + assert_throws(new TypeError(), _ => { + d.src = url; + }); + }, "'src = string' throws."); +</script> +
diff --git a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-createContextualFragment.tentative.html b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-createContextualFragment.tentative.html index 0840623..1d67a51 100644 --- a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-createContextualFragment.tentative.html +++ b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-createContextualFragment.tentative.html
@@ -1,7 +1,7 @@ <!DOCTYPE html> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script> -<script src="support/helper.js"></script> +<script src="support/helper.sub.js"></script> <meta http-equiv="Content-Security-Policy" content="require-trusted-types"> <body>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-embed-src.tentative.html b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-embed-src.tentative.html new file mode 100644 index 0000000..51d553f --- /dev/null +++ b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-embed-src.tentative.html
@@ -0,0 +1,56 @@ +<!DOCTYPE html> +<html> +<head> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="./support/helper.js"></script> + + <meta http-equiv="Content-Security-Policy" content="require-trusted-types"> +</head> +<body> +<script> + // String assignments throw. + test(t => { + var s = document.createElement('embed'); + assert_throws(new TypeError(), _ => { + s.src = "Fail."; + }); + assert_equals('', s.src); + }, "src = 'string' assignment throws."); + + // TrustedURL assignments throw. + test(t => { + var url = TrustedURL.unsafelyCreate(URLS.safe); + + var s = document.createElement('embed'); + assert_throws(new TypeError(), _ => { + s.src = url; + }); + assert_equals('', s.src); + }, "src = TrustedURL.unsafelyCreate(URLS.safe) assignment throws"); + + // TrustedScriptURL assignments work. + test(t => { + var url = TrustedScriptURL.unsafelyCreate(URLS.safe); + + var s = document.createElement('embed'); + s.src = url; + assert_equals(url + '', s.src); + }, "src = TrustedScriptURL.unsafelyCreate(URLS.safe)"); + + test(t => { + var url = TrustedScriptURL.unsafelyCreate(URLS.javascript); + + var s = document.createElement('embed'); + s.src = url; + assert_equals(url + '', s.src); + }, "src = TrustedScriptURL.unsafelyCreate(URLS.javascript)"); + + test(t => { + var url = TrustedScriptURL.unsafelyCreate(URLS.external); + + var s = document.createElement('embed'); + s.src = url; + assert_equals(url + '', s.src); + }, "src = TrustedScriptURL.unsafelyCreate(URLS.external)"); +</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-innerHTML.tentative.html b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-innerHTML.tentative.html index 698bf9dc..67faf6ea 100644 --- a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-innerHTML.tentative.html +++ b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-innerHTML.tentative.html
@@ -3,7 +3,7 @@ <head> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script> - <script src="./support/helper.js"></script> + <script src="support/helper.sub.js"></script> <meta http-equiv="Content-Security-Policy" content="require-trusted-types"> </head>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-insertAdjacentHTML.tentative.html b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-insertAdjacentHTML.tentative.html index 5b3bea8..70bb8034 100644 --- a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-insertAdjacentHTML.tentative.html +++ b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-insertAdjacentHTML.tentative.html
@@ -3,7 +3,7 @@ <head> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script> - <script src="./support/helper.js"></script> + <script src="support/helper.sub.js"></script> <meta http-equiv="Content-Security-Policy" content="require-trusted-types"> </head>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-location-assign.tentative.html b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-location-assign.tentative.html index 63fa9b4..76725da 100644 --- a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-location-assign.tentative.html +++ b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-location-assign.tentative.html
@@ -3,7 +3,7 @@ <head> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script> - <script src="./support/helper.js"></script> + <script src="support/helper.sub.js"></script> <meta http-equiv="Content-Security-Policy" content="require-trusted-types"> </head>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-location-href.tentative.html b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-location-href.tentative.html index 6581ac29..07cc4d5 100644 --- a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-location-href.tentative.html +++ b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-location-href.tentative.html
@@ -3,7 +3,7 @@ <head> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script> - <script src="./support/helper.js"></script> + <script src="support/helper.sub.js"></script> <meta http-equiv="Content-Security-Policy" content="require-trusted-types"> </head>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-location-replace.tentative.html b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-location-replace.tentative.html index ae8bdcd..9736a84b 100644 --- a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-location-replace.tentative.html +++ b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-location-replace.tentative.html
@@ -3,7 +3,7 @@ <head> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script> - <script src="./support/helper.js"></script> + <script src="support/helper.sub.js"></script> <meta http-equiv="Content-Security-Policy" content="require-trusted-types"> </head>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-outerHTML.tentative.html b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-outerHTML.tentative.html index 0e420e5b..8cf6c4b0 100644 --- a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-outerHTML.tentative.html +++ b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-outerHTML.tentative.html
@@ -3,7 +3,7 @@ <head> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script> - <script src="./support/helper.js"></script> + <script src="support/helper.sub.js"></script> <meta http-equiv="Content-Security-Policy" content="require-trusted-types"> </head>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-script-src.tentative.html b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-script-src.tentative.html index e0d2bc7e6..ade76848 100644 --- a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-script-src.tentative.html +++ b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-script-src.tentative.html
@@ -3,7 +3,7 @@ <head> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script> - <script src="./support/helper.js"></script> + <script src="support/helper.sub.js"></script> <meta http-equiv="Content-Security-Policy" content="require-trusted-types"> </head>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/createContextualFragment.tentative.html b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/createContextualFragment.tentative.html index 7e60f0b..5e50acc8 100644 --- a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/createContextualFragment.tentative.html +++ b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/createContextualFragment.tentative.html
@@ -1,7 +1,7 @@ <!DOCTYPE html> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script> -<script src="./support/helper.js"></script> +<script src="support/helper.sub.js"></script> <body> <script> test(t => {
diff --git a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/document-write.tentative.html b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/document-write.tentative.html index 0934db9..12794199 100644 --- a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/document-write.tentative.html +++ b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/document-write.tentative.html
@@ -1,7 +1,7 @@ <!DOCTYPE html> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script> -<script src="./support/helper.js"></script> +<script src="support/helper.sub.js"></script> <body> <script> promise_test(t => {
diff --git a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/embed-src.tentative.html b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/embed-src.tentative.html new file mode 100644 index 0000000..a06adc08 --- /dev/null +++ b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/embed-src.tentative.html
@@ -0,0 +1,30 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="./support/helper.js"></script> +<body> +<script> + test(t => { + var url = TrustedScriptURL.unsafelyCreate(URLS.safe); + + var s = document.createElement('embed'); + s.src = url; + assert_equals(url + '', s.src); + }, "src = TrustedScriptURL.unsafelyCreate(URLS.safe)"); + + test(t => { + var url = TrustedScriptURL.unsafelyCreate(URLS.javascript); + + var s = document.createElement('embed'); + s.src = url; + assert_equals(url + '', s.src); + }, "src = TrustedScriptURL.unsafelyCreate(URLS.javascript)"); + + test(t => { + var url = TrustedScriptURL.unsafelyCreate(URLS.external); + + var s = document.createElement('embed'); + s.src = url; + assert_equals(url + '', s.src); + }, "src = TrustedScriptURL.unsafelyCreate(URLS.external)"); +</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/innerHTML.tentative.html b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/innerHTML.tentative.html index 6a72dfa..f9f32d4 100644 --- a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/innerHTML.tentative.html +++ b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/innerHTML.tentative.html
@@ -1,7 +1,7 @@ <!DOCTYPE html> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script> -<script src="./support/helper.js"></script> +<script src="support/helper.sub.js"></script> <body> <script> test(t => {
diff --git a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/insertAdjacentHTML.tentative.html b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/insertAdjacentHTML.tentative.html index 5341f3b0..a95dd6c 100644 --- a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/insertAdjacentHTML.tentative.html +++ b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/insertAdjacentHTML.tentative.html
@@ -1,7 +1,7 @@ <!DOCTYPE html> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script> -<script src="./support/helper.js"></script> +<script src="support/helper.sub.js"></script> <body> <div id="container"></div> <script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/location-assign.tentative.html b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/location-assign.tentative.html index 1eb47dd..07cb4a8 100644 --- a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/location-assign.tentative.html +++ b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/location-assign.tentative.html
@@ -1,7 +1,7 @@ <!DOCTYPE html> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script> -<script src="support/helper.js"></script> +<script src="support/helper.sub.js"></script> <body> <script> test(t => {
diff --git a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/location-href.tentative.html b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/location-href.tentative.html index 6dd4816..2527fbf 100644 --- a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/location-href.tentative.html +++ b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/location-href.tentative.html
@@ -1,7 +1,7 @@ <!DOCTYPE html> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script> -<script src="./support/helper.js"></script> +<script src="support/helper.sub.js"></script> <body> <script> promise_test(t => {
diff --git a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/location-replace.tentative.html b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/location-replace.tentative.html index acde7ff3..097c24d 100644 --- a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/location-replace.tentative.html +++ b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/location-replace.tentative.html
@@ -1,7 +1,7 @@ <!DOCTYPE html> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script> -<script src="support/helper.js"></script> +<script src="support/helper.sub.js"></script> <body> <script> test(t => {
diff --git a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/outerHTML.tentative.html b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/outerHTML.tentative.html index 0a25dc2..1deb46b 100644 --- a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/outerHTML.tentative.html +++ b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/outerHTML.tentative.html
@@ -1,7 +1,7 @@ <!DOCTYPE html> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script> -<script src="./support/helper.js"></script> +<script src="support/helper.sub.js"></script> <body> <div id="container"></div> <script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/script-src.tentative.html b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/script-src.tentative.html index bc8c332b..7235cc3 100644 --- a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/script-src.tentative.html +++ b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/script-src.tentative.html
@@ -1,7 +1,7 @@ <!DOCTYPE html> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script> -<script src="./support/helper.js"></script> +<script src="support/helper.sub.js"></script> <body> <script> test(t => {
diff --git a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/srcDoc-requiresTrustedTypes.tentative.html b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/srcDoc-requiresTrustedTypes.tentative.html index c1a560e..b957488 100644 --- a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/srcDoc-requiresTrustedTypes.tentative.html +++ b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/srcDoc-requiresTrustedTypes.tentative.html
@@ -1,7 +1,7 @@ <!DOCTYPE html> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script> -<script src="./support/helper.js"></script> +<script src="support/helper.sub.js"></script> <meta http-equiv="Content-Security-Policy" content="require-trusted-types"> <body>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/srcDoc.tentative.html b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/srcDoc.tentative.html index 1521615..b23703e 100644 --- a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/srcDoc.tentative.html +++ b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/srcDoc.tentative.html
@@ -1,7 +1,7 @@ <!DOCTYPE html> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script> -<script src="./support/helper.js"></script> +<script src="support/helper.sub.js"></script> <body> <script> async_test(t => {
diff --git a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/support/helper.js b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/support/helper.sub.js similarity index 95% rename from third_party/WebKit/LayoutTests/external/wpt/trusted-types/support/helper.js rename to third_party/WebKit/LayoutTests/external/wpt/trusted-types/support/helper.sub.js index 1094e72..036dbed7 100644 --- a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/support/helper.js +++ b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/support/helper.sub.js
@@ -5,7 +5,7 @@ }; var URLS = { - safe: "https://example.test/", + safe: "http://{{host}}:{{ports[http][0]}}/", javascript: "javascript:'scripted'", external: "custom-handler:whatever", sanitized: "about:invalid"
diff --git a/third_party/WebKit/LayoutTests/external/wpt/web-locks/META.yml b/third_party/WebKit/LayoutTests/external/wpt/web-locks/META.yml index eeb57de..39acca8 100644 --- a/third_party/WebKit/LayoutTests/external/wpt/web-locks/META.yml +++ b/third_party/WebKit/LayoutTests/external/wpt/web-locks/META.yml
@@ -1,3 +1,4 @@ +spec: https://inexorabletash.github.io/web-locks/ suggested_reviewers: - inexorabletash - pwnall
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webvr/META.yml b/third_party/WebKit/LayoutTests/external/wpt/webvr/META.yml index 546f5b32..b50e559 100644 --- a/third_party/WebKit/LayoutTests/external/wpt/webvr/META.yml +++ b/third_party/WebKit/LayoutTests/external/wpt/webvr/META.yml
@@ -1,2 +1,3 @@ +spec: https://immersive-web.github.io/webvr/spec/1.1/ suggested_reviewers: - klausw
diff --git a/third_party/WebKit/LayoutTests/external/wpt/x-frame-options/META.yml b/third_party/WebKit/LayoutTests/external/wpt/x-frame-options/META.yml index 3c7f551..674a164 100644 --- a/third_party/WebKit/LayoutTests/external/wpt/x-frame-options/META.yml +++ b/third_party/WebKit/LayoutTests/external/wpt/x-frame-options/META.yml
@@ -1,3 +1,4 @@ +spec: https://tools.ietf.org/html/rfc7034 suggested_reviewers: - annevk - mikewest
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/css2.1/t1202-counter-04-b-expected.png b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/css2.1/t1202-counter-04-b-expected.png new file mode 100644 index 0000000..2fddef0 --- /dev/null +++ b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/css2.1/t1202-counter-04-b-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/css2.1/t1202-counter-04-b-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/css2.1/t1202-counter-04-b-expected.txt new file mode 100644 index 0000000..9bfed86a --- /dev/null +++ b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/css2.1/t1202-counter-04-b-expected.txt
@@ -0,0 +1,88 @@ +layer at (0,0) size 800x600 + LayoutView at (0,0) size 800x600 +layer at (0,0) size 800x100 + LayoutNGBlockFlow {HTML} at (0,0) size 800x100 + LayoutNGBlockFlow {BODY} at (8,16) size 784x76 + LayoutNGBlockFlow {P} at (0,0) size 784x20 + LayoutText {#text} at (0,0) size 233x19 + text run at (0,0) width 233: "The following two lines should look " + LayoutInline {EM} at (0,0) size 90x19 + LayoutText {#text} at (233,0) size 90x19 + text run at (233,0) width 90: "approximately" + LayoutText {#text} at (323,0) size 63x19 + text run at (323,0) width 63: " the same:" + LayoutNGBlockFlow {DIV} at (0,36) size 784x20 + LayoutInline {SPAN} at (0,0) size 10x19 + LayoutInline {<pseudo:before>} at (0,0) size 10x19 + LayoutCounter (anonymous) at (0,0) size 10x19 + text run at (0,0) width 10: "\x{25A0}" + LayoutText {#text} at (10,0) size 4x19 + text run at (10,0) width 4: " " + LayoutInline {SPAN} at (0,0) size 10x19 + LayoutInline {<pseudo:before>} at (0,0) size 10x19 + LayoutCounter (anonymous) at (14,0) size 10x19 + text run at (14,0) width 10: "\x{25A0}" + LayoutText {#text} at (24,0) size 4x19 + text run at (24,0) width 4: " " + LayoutInline {SPAN} at (0,0) size 10x19 + LayoutInline {<pseudo:before>} at (0,0) size 10x19 + LayoutCounter (anonymous) at (28,0) size 10x19 + text run at (28,0) width 10: "\x{25A0}" + LayoutText {#text} at (38,0) size 4x19 + text run at (38,0) width 4: " " + LayoutInline {SPAN} at (0,0) size 10x19 + LayoutInline {<pseudo:before>} at (0,0) size 10x19 + LayoutCounter (anonymous) at (42,0) size 10x19 + text run at (42,0) width 10: "\x{25A0}" + LayoutText {#text} at (52,0) size 4x19 + text run at (52,0) width 4: " " + LayoutInline {SPAN} at (0,0) size 10x19 + LayoutInline {<pseudo:before>} at (0,0) size 10x19 + LayoutCounter (anonymous) at (56,0) size 10x19 + text run at (56,0) width 10: "\x{25A0}" + LayoutText {#text} at (66,0) size 4x19 + text run at (66,0) width 4: " " + LayoutInline {SPAN} at (0,0) size 10x19 + LayoutInline {<pseudo:before>} at (0,0) size 10x19 + LayoutCounter (anonymous) at (70,0) size 10x19 + text run at (70,0) width 10: "\x{25A0}" + LayoutText {#text} at (80,0) size 4x19 + text run at (80,0) width 4: " " + LayoutInline {SPAN} at (0,0) size 10x19 + LayoutInline {<pseudo:before>} at (0,0) size 10x19 + LayoutCounter (anonymous) at (84,0) size 10x19 + text run at (84,0) width 10: "\x{25A0}" + LayoutText {#text} at (94,0) size 4x19 + text run at (94,0) width 4: " " + LayoutInline {SPAN} at (0,0) size 10x19 + LayoutInline {<pseudo:before>} at (0,0) size 10x19 + LayoutCounter (anonymous) at (98,0) size 10x19 + text run at (98,0) width 10: "\x{25A0}" + LayoutText {#text} at (108,0) size 4x19 + text run at (108,0) width 4: " " + LayoutInline {SPAN} at (0,0) size 10x19 + LayoutInline {<pseudo:before>} at (0,0) size 10x19 + LayoutCounter (anonymous) at (112,0) size 10x19 + text run at (112,0) width 10: "\x{25A0}" + LayoutText {#text} at (122,0) size 4x19 + text run at (122,0) width 4: " " + LayoutInline {SPAN} at (0,0) size 10x19 + LayoutInline {<pseudo:before>} at (0,0) size 10x19 + LayoutCounter (anonymous) at (126,0) size 10x19 + text run at (126,0) width 10: "\x{25A0}" + LayoutText {#text} at (136,0) size 4x19 + text run at (136,0) width 4: " " + LayoutInline {SPAN} at (0,0) size 10x19 + LayoutInline {<pseudo:before>} at (0,0) size 10x19 + LayoutCounter (anonymous) at (140,0) size 10x19 + text run at (140,0) width 10: "\x{25A0}" + LayoutText {#text} at (150,0) size 4x19 + text run at (150,0) width 4: " " + LayoutInline {SPAN} at (0,0) size 10x19 + LayoutInline {<pseudo:before>} at (0,0) size 10x19 + LayoutCounter (anonymous) at (154,0) size 10x19 + text run at (154,0) width 10: "\x{25A0}" + LayoutText {#text} at (0,0) size 0x0 + LayoutNGBlockFlow {DIV} at (0,56) size 784x20 + LayoutText {#text} at (0,0) size 144x19 + text run at (0,0) width 144: "\x{25FE}\x{25FE}\x{25FE}\x{25FE}\x{25FE}\x{25FE}\x{25FE}\x{25FE}\x{25FE}\x{25FE}\x{25FE}\x{25FE}"
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/css2.1/t1202-counters-04-b-expected.png b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/css2.1/t1202-counters-04-b-expected.png new file mode 100644 index 0000000..e7ecc33e --- /dev/null +++ b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/css2.1/t1202-counters-04-b-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/css2.1/t1202-counters-04-b-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/css2.1/t1202-counters-04-b-expected.txt new file mode 100644 index 0000000..6e169f7 --- /dev/null +++ b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/css2.1/t1202-counters-04-b-expected.txt
@@ -0,0 +1,112 @@ +layer at (0,0) size 800x600 + LayoutView at (0,0) size 800x600 +layer at (0,0) size 800x100 + LayoutNGBlockFlow {HTML} at (0,0) size 800x100 + LayoutNGBlockFlow {BODY} at (8,16) size 784x76 + LayoutNGBlockFlow {P} at (0,0) size 784x20 + LayoutText {#text} at (0,0) size 233x19 + text run at (0,0) width 233: "The following two lines should look " + LayoutInline {EM} at (0,0) size 90x19 + LayoutText {#text} at (233,0) size 90x19 + text run at (233,0) width 90: "approximately" + LayoutText {#text} at (323,0) size 63x19 + text run at (323,0) width 63: " the same:" + LayoutNGBlockFlow {DIV} at (0,36) size 784x20 + LayoutInline {SPAN} at (0,0) size 24x19 + LayoutInline {<pseudo:before>} at (0,0) size 24x19 + LayoutCounter (anonymous) at (0,0) size 24x19 + text run at (0,0) width 24: "\x{25A0}.\x{25A0}" + LayoutText {#text} at (24,0) size 4x19 + text run at (24,0) width 4: " " + LayoutInline {SPAN} at (0,0) size 24x19 + LayoutInline {<pseudo:before>} at (0,0) size 24x19 + LayoutCounter (anonymous) at (28,0) size 24x19 + text run at (28,0) width 24: "\x{25A0}.\x{25A0}" + LayoutText {#text} at (52,0) size 4x19 + text run at (52,0) width 4: " " + LayoutInline {SPAN} at (0,0) size 24x19 + LayoutInline {<pseudo:before>} at (0,0) size 24x19 + LayoutCounter (anonymous) at (56,0) size 24x19 + text run at (56,0) width 24: "\x{25A0}.\x{25A0}" + LayoutText {#text} at (80,0) size 4x19 + text run at (80,0) width 4: " " + LayoutInline {SPAN} at (0,0) size 24x19 + LayoutInline {<pseudo:before>} at (0,0) size 24x19 + LayoutCounter (anonymous) at (84,0) size 24x19 + text run at (84,0) width 24: "\x{25A0}.\x{25A0}" + LayoutText {#text} at (108,0) size 4x19 + text run at (108,0) width 4: " " + LayoutInline {SPAN} at (0,0) size 24x19 + LayoutInline {<pseudo:before>} at (0,0) size 24x19 + LayoutCounter (anonymous) at (112,0) size 24x19 + text run at (112,0) width 24: "\x{25A0}.\x{25A0}" + LayoutText {#text} at (136,0) size 4x19 + text run at (136,0) width 4: " " + LayoutInline {SPAN} at (0,0) size 24x19 + LayoutInline {<pseudo:before>} at (0,0) size 24x19 + LayoutCounter (anonymous) at (140,0) size 24x19 + text run at (140,0) width 24: "\x{25A0}.\x{25A0}" + LayoutText {#text} at (164,0) size 4x19 + text run at (164,0) width 4: " " + LayoutInline {SPAN} at (0,0) size 24x19 + LayoutInline {<pseudo:before>} at (0,0) size 24x19 + LayoutCounter (anonymous) at (168,0) size 24x19 + text run at (168,0) width 24: "\x{25A0}.\x{25A0}" + LayoutText {#text} at (192,0) size 4x19 + text run at (192,0) width 4: " " + LayoutInline {SPAN} at (0,0) size 24x19 + LayoutInline {<pseudo:before>} at (0,0) size 24x19 + LayoutCounter (anonymous) at (196,0) size 24x19 + text run at (196,0) width 24: "\x{25A0}.\x{25A0}" + LayoutText {#text} at (220,0) size 4x19 + text run at (220,0) width 4: " " + LayoutInline {SPAN} at (0,0) size 24x19 + LayoutInline {<pseudo:before>} at (0,0) size 24x19 + LayoutCounter (anonymous) at (224,0) size 24x19 + text run at (224,0) width 24: "\x{25A0}.\x{25A0}" + LayoutText {#text} at (248,0) size 4x19 + text run at (248,0) width 4: " " + LayoutInline {SPAN} at (0,0) size 24x19 + LayoutInline {<pseudo:before>} at (0,0) size 24x19 + LayoutCounter (anonymous) at (252,0) size 24x19 + text run at (252,0) width 24: "\x{25A0}.\x{25A0}" + LayoutText {#text} at (276,0) size 4x19 + text run at (276,0) width 4: " " + LayoutInline {SPAN} at (0,0) size 24x19 + LayoutInline {<pseudo:before>} at (0,0) size 24x19 + LayoutCounter (anonymous) at (280,0) size 24x19 + text run at (280,0) width 24: "\x{25A0}.\x{25A0}" + LayoutText {#text} at (304,0) size 4x19 + text run at (304,0) width 4: " " + LayoutInline {SPAN} at (0,0) size 24x19 + LayoutInline {<pseudo:before>} at (0,0) size 24x19 + LayoutCounter (anonymous) at (308,0) size 24x19 + text run at (308,0) width 24: "\x{25A0}.\x{25A0}" + LayoutText {#text} at (0,0) size 0x0 + LayoutNGBlockFlow {DIV} at (0,56) size 784x20 + LayoutText {#text} at (0,0) size 336x19 + text run at (0,0) width 12: "\x{25FE}" + text run at (12,0) width 4: "." + text run at (16,0) width 24: "\x{25FE}\x{25FE}" + text run at (40,0) width 4: "." + text run at (44,0) width 24: "\x{25FE}\x{25FE}" + text run at (68,0) width 4: "." + text run at (72,0) width 24: "\x{25FE}\x{25FE}" + text run at (96,0) width 4: "." + text run at (100,0) width 24: "\x{25FE}\x{25FE}" + text run at (124,0) width 4: "." + text run at (128,0) width 24: "\x{25FE}\x{25FE}" + text run at (152,0) width 4: "." + text run at (156,0) width 24: "\x{25FE}\x{25FE}" + text run at (180,0) width 4: "." + text run at (184,0) width 24: "\x{25FE}\x{25FE}" + text run at (208,0) width 4: "." + text run at (212,0) width 24: "\x{25FE}\x{25FE}" + text run at (236,0) width 4: "." + text run at (240,0) width 24: "\x{25FE}\x{25FE}" + text run at (264,0) width 4: "." + text run at (268,0) width 24: "\x{25FE}\x{25FE}" + text run at (292,0) width 4: "." + text run at (296,0) width 24: "\x{25FE}\x{25FE}" + text run at (320,0) width 4: "." + text run at (324,0) width 12: "\x{25FE}"
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/background/background-resize-height-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/background/background-resize-height-expected.txt new file mode 100644 index 0000000..2440419 --- /dev/null +++ b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/background/background-resize-height-expected.txt
@@ -0,0 +1,402 @@ +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [800, 600], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [800, 600], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [800, 600], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + }, + { + "name": "LayoutNGBlockFlow (positioned) DIV class='test image'", + "position": [8, 8], + "bounds": [60, 44], + "contentsOpaque": true, + "backfaceVisibility": "hidden", + "paintInvalidations": [ + { + "object": "LayoutNGBlockFlow (positioned) DIV class='test image'", + "rect": [0, 40, 60, 4], + "reason": "incremental" + } + ] + }, + { + "name": "LayoutNGBlockFlow (positioned) DIV class='test image size-cover'", + "position": [58, 8], + "bounds": [60, 44], + "contentsOpaque": true, + "backfaceVisibility": "hidden", + "paintInvalidations": [ + { + "object": "LayoutNGBlockFlow (positioned) DIV class='test image size-cover'", + "rect": [0, 0, 60, 44], + "reason": "geometry" + } + ] + }, + { + "name": "LayoutNGBlockFlow (positioned) DIV class='test image size-contain'", + "position": [108, 8], + "bounds": [60, 44], + "contentsOpaque": true, + "backfaceVisibility": "hidden", + "paintInvalidations": [ + { + "object": "LayoutNGBlockFlow (positioned) DIV class='test image size-contain'", + "rect": [0, 0, 60, 44], + "reason": "geometry" + } + ] + }, + { + "name": "LayoutNGBlockFlow (positioned) DIV class='test image fixed-height'", + "position": [158, 8], + "bounds": [60, 44], + "contentsOpaque": true, + "backfaceVisibility": "hidden", + "paintInvalidations": [ + { + "object": "LayoutNGBlockFlow (positioned) DIV class='test image fixed-height'", + "rect": [0, 40, 60, 4], + "reason": "incremental" + } + ] + }, + { + "name": "LayoutNGBlockFlow (positioned) DIV class='test image percent-height'", + "position": [208, 8], + "bounds": [60, 44], + "contentsOpaque": true, + "backfaceVisibility": "hidden", + "paintInvalidations": [ + { + "object": "LayoutNGBlockFlow (positioned) DIV class='test image percent-height'", + "rect": [0, 0, 60, 44], + "reason": "geometry" + } + ] + }, + { + "name": "LayoutNGBlockFlow (positioned) DIV class='test image top'", + "position": [258, 8], + "bounds": [60, 44], + "contentsOpaque": true, + "backfaceVisibility": "hidden", + "paintInvalidations": [ + { + "object": "LayoutNGBlockFlow (positioned) DIV class='test image top'", + "rect": [0, 40, 60, 4], + "reason": "incremental" + } + ] + }, + { + "name": "LayoutNGBlockFlow (positioned) DIV class='test image bottom'", + "position": [308, 8], + "bounds": [60, 44], + "contentsOpaque": true, + "backfaceVisibility": "hidden", + "paintInvalidations": [ + { + "object": "LayoutNGBlockFlow (positioned) DIV class='test image bottom'", + "rect": [0, 0, 60, 44], + "reason": "geometry" + } + ] + }, + { + "name": "LayoutNGBlockFlow (positioned) DIV class='test image center'", + "position": [358, 8], + "bounds": [60, 44], + "contentsOpaque": true, + "backfaceVisibility": "hidden", + "paintInvalidations": [ + { + "object": "LayoutNGBlockFlow (positioned) DIV class='test image center'", + "rect": [0, 0, 60, 44], + "reason": "geometry" + } + ] + }, + { + "name": "LayoutNGBlockFlow (positioned) DIV class='test image no-repeat'", + "position": [408, 8], + "bounds": [60, 44], + "backfaceVisibility": "hidden", + "paintInvalidations": [ + { + "object": "LayoutNGBlockFlow (positioned) DIV class='test image no-repeat'", + "rect": [0, 40, 60, 4], + "reason": "incremental" + } + ] + }, + { + "name": "LayoutNGBlockFlow (positioned) DIV class='test image repeat-space'", + "position": [458, 8], + "bounds": [60, 44], + "backfaceVisibility": "hidden", + "paintInvalidations": [ + { + "object": "LayoutNGBlockFlow (positioned) DIV class='test image repeat-space'", + "rect": [0, 0, 60, 44], + "reason": "geometry" + } + ] + }, + { + "name": "LayoutNGBlockFlow (positioned) DIV class='test image repeat-round'", + "position": [508, 8], + "bounds": [60, 44], + "contentsOpaque": true, + "backfaceVisibility": "hidden", + "paintInvalidations": [ + { + "object": "LayoutNGBlockFlow (positioned) DIV class='test image repeat-round'", + "rect": [0, 0, 60, 44], + "reason": "geometry" + } + ] + }, + { + "name": "LayoutNGBlockFlow (positioned) DIV class='test generated'", + "position": [8, 108], + "bounds": [60, 44], + "contentsOpaque": true, + "backfaceVisibility": "hidden", + "paintInvalidations": [ + { + "object": "LayoutNGBlockFlow (positioned) DIV class='test generated'", + "rect": [0, 0, 60, 44], + "reason": "geometry" + } + ] + }, + { + "name": "LayoutNGBlockFlow (positioned) DIV class='test generated size-cover'", + "position": [58, 108], + "bounds": [60, 44], + "contentsOpaque": true, + "backfaceVisibility": "hidden", + "paintInvalidations": [ + { + "object": "LayoutNGBlockFlow (positioned) DIV class='test generated size-cover'", + "rect": [0, 0, 60, 44], + "reason": "geometry" + } + ] + }, + { + "name": "LayoutNGBlockFlow (positioned) DIV class='test generated size-contain'", + "position": [108, 108], + "bounds": [60, 44], + "contentsOpaque": true, + "backfaceVisibility": "hidden", + "paintInvalidations": [ + { + "object": "LayoutNGBlockFlow (positioned) DIV class='test generated size-contain'", + "rect": [0, 0, 60, 44], + "reason": "geometry" + } + ] + }, + { + "name": "LayoutNGBlockFlow (positioned) DIV class='test generated percent-height'", + "position": [208, 108], + "bounds": [60, 44], + "contentsOpaque": true, + "backfaceVisibility": "hidden", + "paintInvalidations": [ + { + "object": "LayoutNGBlockFlow (positioned) DIV class='test generated percent-height'", + "rect": [0, 0, 60, 44], + "reason": "geometry" + } + ] + }, + { + "name": "LayoutNGBlockFlow (positioned) DIV class='test generated top'", + "position": [258, 108], + "bounds": [60, 44], + "contentsOpaque": true, + "backfaceVisibility": "hidden", + "paintInvalidations": [ + { + "object": "LayoutNGBlockFlow (positioned) DIV class='test generated top'", + "rect": [0, 0, 60, 44], + "reason": "geometry" + } + ] + }, + { + "name": "LayoutNGBlockFlow (positioned) DIV class='test generated bottom'", + "position": [308, 108], + "bounds": [60, 44], + "contentsOpaque": true, + "backfaceVisibility": "hidden", + "paintInvalidations": [ + { + "object": "LayoutNGBlockFlow (positioned) DIV class='test generated bottom'", + "rect": [0, 0, 60, 44], + "reason": "geometry" + } + ] + }, + { + "name": "LayoutNGBlockFlow (positioned) DIV class='test generated center'", + "position": [358, 108], + "bounds": [60, 44], + "contentsOpaque": true, + "backfaceVisibility": "hidden", + "paintInvalidations": [ + { + "object": "LayoutNGBlockFlow (positioned) DIV class='test generated center'", + "rect": [0, 0, 60, 44], + "reason": "geometry" + } + ] + }, + { + "name": "LayoutNGBlockFlow (positioned) DIV class='test generated no-repeat'", + "position": [408, 108], + "bounds": [60, 44], + "backfaceVisibility": "hidden", + "paintInvalidations": [ + { + "object": "LayoutNGBlockFlow (positioned) DIV class='test generated no-repeat'", + "rect": [0, 0, 60, 44], + "reason": "geometry" + } + ] + }, + { + "name": "LayoutNGBlockFlow (positioned) DIV class='test generated repeat-space'", + "position": [458, 108], + "bounds": [60, 44], + "backfaceVisibility": "hidden", + "paintInvalidations": [ + { + "object": "LayoutNGBlockFlow (positioned) DIV class='test generated repeat-space'", + "rect": [0, 0, 60, 44], + "reason": "geometry" + } + ] + }, + { + "name": "LayoutNGBlockFlow (positioned) DIV class='test generated repeat-round'", + "position": [508, 108], + "bounds": [60, 44], + "contentsOpaque": true, + "backfaceVisibility": "hidden", + "paintInvalidations": [ + { + "object": "LayoutNGBlockFlow (positioned) DIV class='test generated repeat-round'", + "rect": [0, 0, 60, 44], + "reason": "geometry" + } + ] + } + ], + "objectPaintInvalidations": [ + { + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow (positioned) DIV id='container'", + "reason": "subtree" + }, + { + "object": "LayoutNGBlockFlow (positioned) DIV class='test image'", + "reason": "incremental" + }, + { + "object": "LayoutNGBlockFlow (positioned) DIV class='test image size-cover'", + "reason": "geometry" + }, + { + "object": "LayoutNGBlockFlow (positioned) DIV class='test image size-contain'", + "reason": "geometry" + }, + { + "object": "LayoutNGBlockFlow (positioned) DIV class='test image fixed-height'", + "reason": "incremental" + }, + { + "object": "LayoutNGBlockFlow (positioned) DIV class='test image percent-height'", + "reason": "geometry" + }, + { + "object": "LayoutNGBlockFlow (positioned) DIV class='test image top'", + "reason": "incremental" + }, + { + "object": "LayoutNGBlockFlow (positioned) DIV class='test image bottom'", + "reason": "geometry" + }, + { + "object": "LayoutNGBlockFlow (positioned) DIV class='test image center'", + "reason": "geometry" + }, + { + "object": "LayoutNGBlockFlow (positioned) DIV class='test image no-repeat'", + "reason": "incremental" + }, + { + "object": "LayoutNGBlockFlow (positioned) DIV class='test image repeat-space'", + "reason": "geometry" + }, + { + "object": "LayoutNGBlockFlow (positioned) DIV class='test image repeat-round'", + "reason": "geometry" + }, + { + "object": "LayoutNGBlockFlow (positioned) DIV class='test generated'", + "reason": "geometry" + }, + { + "object": "LayoutNGBlockFlow (positioned) DIV class='test generated size-cover'", + "reason": "geometry" + }, + { + "object": "LayoutNGBlockFlow (positioned) DIV class='test generated size-contain'", + "reason": "geometry" + }, + { + "object": "LayoutNGBlockFlow (positioned) DIV class='test generated percent-height'", + "reason": "geometry" + }, + { + "object": "LayoutNGBlockFlow (positioned) DIV class='test generated top'", + "reason": "geometry" + }, + { + "object": "LayoutNGBlockFlow (positioned) DIV class='test generated bottom'", + "reason": "geometry" + }, + { + "object": "LayoutNGBlockFlow (positioned) DIV class='test generated center'", + "reason": "geometry" + }, + { + "object": "LayoutNGBlockFlow (positioned) DIV class='test generated no-repeat'", + "reason": "geometry" + }, + { + "object": "LayoutNGBlockFlow (positioned) DIV class='test generated repeat-space'", + "reason": "geometry" + }, + { + "object": "LayoutNGBlockFlow (positioned) DIV class='test generated repeat-round'", + "reason": "geometry" + } + ] +} +
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/clip/clipped-relative-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/clip/clipped-relative-expected.txt index 22a0415..0a9a3f2e 100644 --- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/clip/clipped-relative-expected.txt +++ b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/clip/clipped-relative-expected.txt
@@ -32,6 +32,10 @@ ], "objectPaintInvalidations": [ { + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow (relative positioned) DIV id='i'", + "reason": "subtree" + }, + { "object": "NGPhysicalBoxFragment LayoutNGBlockFlow (positioned) DIV id='inner'", "reason": "subtree" },
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/clip/replaced-clipped-positioned-not-wrong-incremental-repainting-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/clip/replaced-clipped-positioned-not-wrong-incremental-repainting-expected.txt index d3cb80ad..db4867f 100644 --- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/clip/replaced-clipped-positioned-not-wrong-incremental-repainting-expected.txt +++ b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/clip/replaced-clipped-positioned-not-wrong-incremental-repainting-expected.txt
@@ -32,6 +32,10 @@ ], "objectPaintInvalidations": [ { + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow (relative positioned) DIV id='shiftMe'", + "reason": "subtree" + }, + { "object": "NGPhysicalBoxFragment LayoutNGBlockFlow (relative positioned) (floating) DIV class='imgContainer'", "reason": "subtree" },
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/compositing/containing-block-added-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/compositing/containing-block-added-expected.txt new file mode 100644 index 0000000..4a0cc22 --- /dev/null +++ b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/compositing/containing-block-added-expected.txt
@@ -0,0 +1,62 @@ +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [800, 600], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [800, 600], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [800, 600], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF", + "paintInvalidations": [ + { + "object": "LayoutNGBlockFlow (positioned) DIV class='fixed'", + "rect": [50, 50, 75, 75], + "reason": "chunk disappeared" + } + ] + }, + { + "name": "LayoutNGBlockFlow (positioned) DIV id='container'", + "position": [200, 100], + "bounds": [125, 125], + "backgroundColor": "#0000FF", + "paintInvalidations": [ + { + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow (positioned) DIV id='container'", + "rect": [0, 0, 100, 100], + "reason": "appeared" + }, + { + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow (positioned) DIV id='container'", + "rect": [0, 0, 100, 100], + "reason": "disappeared" + }, + { + "object": "LayoutNGBlockFlow (positioned) DIV class='fixed'", + "rect": [50, 50, 75, 75], + "reason": "chunk appeared" + } + ] + } + ], + "objectPaintInvalidations": [ + { + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow (positioned) DIV id='container'", + "reason": "style change" + }, + { + "object": "LayoutNGBlockFlow (positioned) DIV class='fixed'", + "reason": "style change" + } + ] +} +
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/compositing/containing-block-added-individual-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/compositing/containing-block-added-individual-expected.txt new file mode 100644 index 0000000..4a0cc22 --- /dev/null +++ b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/compositing/containing-block-added-individual-expected.txt
@@ -0,0 +1,62 @@ +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [800, 600], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [800, 600], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [800, 600], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF", + "paintInvalidations": [ + { + "object": "LayoutNGBlockFlow (positioned) DIV class='fixed'", + "rect": [50, 50, 75, 75], + "reason": "chunk disappeared" + } + ] + }, + { + "name": "LayoutNGBlockFlow (positioned) DIV id='container'", + "position": [200, 100], + "bounds": [125, 125], + "backgroundColor": "#0000FF", + "paintInvalidations": [ + { + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow (positioned) DIV id='container'", + "rect": [0, 0, 100, 100], + "reason": "appeared" + }, + { + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow (positioned) DIV id='container'", + "rect": [0, 0, 100, 100], + "reason": "disappeared" + }, + { + "object": "LayoutNGBlockFlow (positioned) DIV class='fixed'", + "rect": [50, 50, 75, 75], + "reason": "chunk appeared" + } + ] + } + ], + "objectPaintInvalidations": [ + { + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow (positioned) DIV id='container'", + "reason": "style change" + }, + { + "object": "LayoutNGBlockFlow (positioned) DIV class='fixed'", + "reason": "style change" + } + ] +} +
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/compositing/containing-block-removed-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/compositing/containing-block-removed-expected.txt new file mode 100644 index 0000000..c9784c8 --- /dev/null +++ b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/compositing/containing-block-removed-expected.txt
@@ -0,0 +1,63 @@ +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [800, 600], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [800, 600], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [800, 600], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF", + "paintInvalidations": [ + { + "object": "LayoutNGBlockFlow (positioned) DIV class='fixed'", + "rect": [50, 50, 75, 75], + "reason": "chunk appeared" + } + ] + }, + { + "name": "LayoutNGBlockFlow (positioned) DIV id='container'", + "position": [200, 100], + "bounds": [100, 100], + "contentsOpaque": true, + "backgroundColor": "#0000FF", + "paintInvalidations": [ + { + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow (positioned) DIV id='container'", + "rect": [0, 0, 100, 100], + "reason": "appeared" + }, + { + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow (positioned) DIV id='container'", + "rect": [0, 0, 100, 100], + "reason": "disappeared" + }, + { + "object": "LayoutNGBlockFlow (positioned) DIV class='fixed'", + "rect": [50, 50, 75, 75], + "reason": "chunk disappeared" + } + ] + } + ], + "objectPaintInvalidations": [ + { + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow (positioned) DIV id='container'", + "reason": "style change" + }, + { + "object": "LayoutNGBlockFlow (positioned) DIV class='fixed'", + "reason": "style change" + } + ] +} +
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/compositing/containing-block-removed-individual-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/compositing/containing-block-removed-individual-expected.txt new file mode 100644 index 0000000..e59ed70a --- /dev/null +++ b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/compositing/containing-block-removed-individual-expected.txt
@@ -0,0 +1,64 @@ +CONSOLE MESSAGE: line 30: debug +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [800, 600], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [800, 600], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [800, 600], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF", + "paintInvalidations": [ + { + "object": "LayoutNGBlockFlow (positioned) DIV class='fixed'", + "rect": [50, 50, 75, 75], + "reason": "chunk appeared" + } + ] + }, + { + "name": "LayoutNGBlockFlow (positioned) DIV id='container'", + "position": [200, 100], + "bounds": [100, 100], + "contentsOpaque": true, + "backgroundColor": "#0000FF", + "paintInvalidations": [ + { + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow (positioned) DIV id='container'", + "rect": [0, 0, 100, 100], + "reason": "appeared" + }, + { + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow (positioned) DIV id='container'", + "rect": [0, 0, 100, 100], + "reason": "disappeared" + }, + { + "object": "LayoutNGBlockFlow (positioned) DIV class='fixed'", + "rect": [50, 50, 75, 75], + "reason": "chunk disappeared" + } + ] + } + ], + "objectPaintInvalidations": [ + { + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow (positioned) DIV id='container'", + "reason": "style change" + }, + { + "object": "LayoutNGBlockFlow (positioned) DIV class='fixed'", + "reason": "style change" + } + ] +} +
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/compositing/invalidations-with-large-negative-margin-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/compositing/invalidations-with-large-negative-margin-expected.txt new file mode 100644 index 0000000..6f8a5958 --- /dev/null +++ b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/compositing/invalidations-with-large-negative-margin-expected.txt
@@ -0,0 +1,44 @@ +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [800, 600], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [800, 600], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [800, 600], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF", + "paintInvalidations": [ + { + "object": "LayoutNGBlockFlow (positioned) DIV id='chip'", + "rect": [408, 8, 50, 50], + "reason": "subtree" + }, + { + "object": "LayoutNGBlockFlow (positioned) DIV id='chip'", + "rect": [8, 8, 50, 50], + "reason": "subtree" + } + ] + } + ], + "objectPaintInvalidations": [ + { + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow (relative positioned) DIV", + "reason": "subtree" + }, + { + "object": "LayoutNGBlockFlow (positioned) DIV id='chip'", + "reason": "subtree" + } + ] +} +
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/crbug-371640-3-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/crbug-371640-3-expected.txt new file mode 100644 index 0000000..a75284f8 --- /dev/null +++ b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/crbug-371640-3-expected.txt
@@ -0,0 +1,65 @@ +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [800, 600], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [800, 600], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [800, 600], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF", + "paintInvalidations": [ + { + "object": "LayoutNGBlockFlow DIV id='keep_child'", + "rect": [508, 208, 100, 100], + "reason": "subtree" + }, + { + "object": "LayoutNGBlockFlow (positioned) DIV id='remove_child'", + "rect": [508, 88, 100, 100], + "reason": "disappeared" + }, + { + "object": "LayoutNGBlockFlow DIV id='keep_child'", + "rect": [408, 208, 100, 100], + "reason": "subtree" + } + ] + } + ], + "objectPaintInvalidations": [ + { + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow (positioned) DIV id='wrapper'", + "reason": "subtree" + }, + { + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow (floating) DIV id='container'", + "reason": "subtree" + }, + { + "object": "LayoutNGBlockFlow (floating) DIV id='keep_outer' class='outer'", + "reason": "subtree" + }, + { + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow (relative positioned) DIV id='keep_inner'", + "reason": "subtree" + }, + { + "object": "LayoutNGBlockFlow (positioned) DIV id='abs_pos'", + "reason": "subtree" + }, + { + "object": "LayoutNGBlockFlow DIV id='keep_child'", + "reason": "subtree" + } + ] +} +
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/flexbox/repaint-on-layout-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/flexbox/repaint-on-layout-expected.txt index 76e5d2ac..7f72a8a 100644 --- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/flexbox/repaint-on-layout-expected.txt +++ b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/flexbox/repaint-on-layout-expected.txt
@@ -24,6 +24,16 @@ } ] } + ], + "objectPaintInvalidations": [ + { + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow DIV class='item'", + "reason": "subtree" + }, + { + "object": "LayoutNGBlockFlow (floating) DIV class='state'", + "reason": "subtree" + } ] }
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/invisible-objects-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/invisible-objects-expected.txt new file mode 100644 index 0000000..023fcd3 --- /dev/null +++ b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/invisible-objects-expected.txt
@@ -0,0 +1,32 @@ +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [800, 600], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [800, 600], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [800, 600], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + } + ], + "objectPaintInvalidations": [ + { + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow (relative positioned) DIV", + "reason": "subtree" + }, + { + "object": "NGPhysicalTextFragment 'is invisible'", + "reason": "subtree" + } + ] +} +
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/lines-with-layout-delta-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/lines-with-layout-delta-expected.txt index 7ef20d9..0441e3b 100644 --- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/lines-with-layout-delta-expected.txt +++ b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/lines-with-layout-delta-expected.txt
@@ -32,6 +32,10 @@ ], "objectPaintInvalidations": [ { + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow DIV", + "reason": "subtree" + }, + { "object": "NGPhysicalBoxFragment LayoutNGBlockFlow (floating) DIV id='target'", "reason": "subtree" },
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/mix-blend-mode-separate-stacking-context-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/mix-blend-mode-separate-stacking-context-expected.txt new file mode 100644 index 0000000..7db456d --- /dev/null +++ b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/mix-blend-mode-separate-stacking-context-expected.txt
@@ -0,0 +1,117 @@ +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [800, 600], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [800, 600], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [800, 600], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF", + "paintInvalidations": [ + { + "object": "LayoutNGBlockFlow (relative positioned) (floating) DIV", + "rect": [128, 28, 80, 80], + "reason": "chunk appeared" + }, + { + "object": "LayoutNGBlockFlow (relative positioned) (floating) DIV", + "rect": [28, 28, 80, 80], + "reason": "chunk disappeared" + }, + { + "object": "LayoutNGBlockFlow (floating) DIV", + "rect": [348, 48, 60, 60], + "reason": "paint property change" + }, + { + "object": "LayoutNGBlockFlow (relative positioned) (floating) DIV id='fourth'", + "rect": [328, 28, 60, 60], + "reason": "chunk appeared" + }, + { + "object": "LayoutNGBlockFlow HTML", + "rect": [328, 28, 60, 60], + "reason": "chunk disappeared" + }, + { + "object": "LayoutNGBlockFlow (floating) DIV id='third'", + "rect": [248, 48, 60, 60], + "reason": "paint property change" + }, + { + "object": "LayoutNGBlockFlow (relative positioned) (floating) DIV", + "rect": [228, 28, 60, 60], + "reason": "paint property change" + }, + { + "object": "LayoutNGBlockFlow (floating) DIV id='second'", + "rect": [148, 48, 60, 60], + "reason": "chunk disappeared" + }, + { + "object": "LayoutNGBlockFlow (relative positioned) (floating) DIV", + "rect": [128, 28, 60, 60], + "reason": "chunk disappeared" + }, + { + "object": "LayoutNGBlockFlow (floating) DIV id='first'", + "rect": [48, 48, 60, 60], + "reason": "chunk appeared" + }, + { + "object": "LayoutNGBlockFlow (relative positioned) (floating) DIV", + "rect": [28, 28, 60, 60], + "reason": "chunk appeared" + } + ] + } + ], + "objectPaintInvalidations": [ + { + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow BODY", + "reason": "subtree" + }, + { + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow (relative positioned) (floating) DIV", + "reason": "subtree" + }, + { + "object": "LayoutNGBlockFlow (floating) DIV id='first'", + "reason": "subtree" + }, + { + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow (relative positioned) (floating) DIV", + "reason": "subtree" + }, + { + "object": "LayoutNGBlockFlow (floating) DIV id='second'", + "reason": "subtree" + }, + { + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow (relative positioned) (floating) DIV", + "reason": "subtree" + }, + { + "object": "LayoutNGBlockFlow (floating) DIV id='third'", + "reason": "subtree" + }, + { + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow (relative positioned) (floating) DIV id='fourth'", + "reason": "subtree" + }, + { + "object": "LayoutNGBlockFlow (floating) DIV", + "reason": "subtree" + } + ] +} +
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/overflow/clipped-overflow-visible-subtree-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/overflow/clipped-overflow-visible-subtree-expected.txt new file mode 100644 index 0000000..c95cc5d2 --- /dev/null +++ b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/overflow/clipped-overflow-visible-subtree-expected.txt
@@ -0,0 +1,56 @@ +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [800, 600], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [800, 600], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [800, 600], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF", + "paintInvalidations": [ + { + "object": "LayoutNGBlockFlow DIV", + "rect": [308, 8, 100, 100], + "reason": "subtree" + }, + { + "object": "LayoutNGBlockFlow DIV", + "rect": [8, 8, 100, 100], + "reason": "subtree" + } + ] + } + ], + "objectPaintInvalidations": [ + { + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow DIV", + "reason": "subtree" + }, + { + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow (floating) DIV", + "reason": "subtree" + }, + { + "object": "LayoutNGBlockFlow (floating) DIV", + "reason": "subtree" + }, + { + "object": "LayoutNGBlockFlow DIV", + "reason": "subtree" + }, + { + "object": "LayoutNGBlockFlow DIV", + "reason": "subtree" + } + ] +} +
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/overflow/content-into-overflow-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/overflow/content-into-overflow-expected.txt new file mode 100644 index 0000000..1d5dcff --- /dev/null +++ b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/overflow/content-into-overflow-expected.txt
@@ -0,0 +1,70 @@ +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [800, 600], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [800, 600], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [800, 600], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF", + "paintInvalidations": [ + { + "object": "LayoutNGBlockFlow (positioned) DIV", + "rect": [8, 158, 106, 106], + "reason": "geometry" + }, + { + "object": "LayoutNGBlockFlow (positioned) DIV", + "rect": [8, 8, 106, 106], + "reason": "geometry" + }, + { + "object": "LayoutNGBlockFlow DIV", + "rect": [8, 308, 100, 100], + "reason": "appeared" + }, + { + "object": "LayoutNGBlockFlow DIV id='target3'", + "rect": [8, 388, 100, 20], + "reason": "appeared" + } + ] + } + ], + "objectPaintInvalidations": [ + { + "object": "LayoutNGBlockFlow (positioned) DIV", + "reason": "geometry" + }, + { + "object": "LayoutNGBlockFlow (positioned) DIV", + "reason": "geometry" + }, + { + "object": "LayoutNGBlockFlow DIV", + "reason": "appeared" + }, + { + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow (relative positioned) DIV", + "reason": "subtree" + }, + { + "object": "LayoutNGBlockFlow (floating) DIV id='main-content'", + "reason": "subtree" + }, + { + "object": "LayoutNGBlockFlow DIV id='target3'", + "reason": "appeared" + } + ] +} +
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/overflow/float-overflow-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/overflow/float-overflow-expected.txt index c465cdc..32c2602 100644 --- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/overflow/float-overflow-expected.txt +++ b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/overflow/float-overflow-expected.txt
@@ -158,34 +158,34 @@ "reason": "geometry" }, { - "object": "LayoutNGBlockFlow DIV", + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow DIV", "rect": [67, 504, 50, 10], - "reason": "geometry" + "reason": "appeared" }, { - "object": "LayoutNGBlockFlow DIV", + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow DIV", "rect": [67, 498, 50, 10], - "reason": "geometry" + "reason": "disappeared" }, { - "object": "LayoutNGBlockFlow DIV", + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow DIV", "rect": [67, 465, 50, 10], - "reason": "geometry" + "reason": "appeared" }, { - "object": "LayoutNGBlockFlow DIV", + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow DIV", "rect": [67, 459, 50, 10], - "reason": "geometry" + "reason": "disappeared" }, { - "object": "LayoutNGBlockFlow DIV", + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow DIV", "rect": [67, 426, 50, 10], - "reason": "geometry" + "reason": "appeared" }, { - "object": "LayoutNGBlockFlow DIV", + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow DIV", "rect": [67, 420, 50, 10], - "reason": "geometry" + "reason": "disappeared" }, { "object": "LayoutBlockFlow DIV", @@ -198,82 +198,82 @@ "reason": "geometry" }, { - "object": "LayoutNGBlockFlow DIV", + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow DIV", "rect": [67, 177, 50, 10], - "reason": "geometry" + "reason": "appeared" }, { - "object": "LayoutNGBlockFlow DIV", + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow DIV", "rect": [67, 171, 50, 10], - "reason": "geometry" + "reason": "disappeared" }, { - "object": "LayoutNGBlockFlow DIV", + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow DIV", "rect": [67, 138, 50, 10], - "reason": "geometry" + "reason": "appeared" }, { - "object": "LayoutNGBlockFlow DIV", + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow DIV", "rect": [67, 132, 50, 10], - "reason": "geometry" + "reason": "disappeared" }, { - "object": "LayoutNGBlockFlow DIV", + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow DIV", "rect": [67, 99, 50, 10], - "reason": "geometry" + "reason": "appeared" }, { - "object": "LayoutNGBlockFlow DIV", + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow DIV", "rect": [67, 93, 50, 10], - "reason": "geometry" + "reason": "disappeared" }, { - "object": "LayoutNGBlockFlow DIV", + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow DIV", "rect": [67, 60, 50, 10], - "reason": "geometry" + "reason": "appeared" }, { - "object": "LayoutNGBlockFlow DIV", + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow DIV", "rect": [67, 54, 50, 10], - "reason": "geometry" + "reason": "disappeared" }, { - "object": "LayoutNGBlockFlow DIV", + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow DIV", "rect": [67, 21, 50, 10], - "reason": "geometry" + "reason": "appeared" }, { - "object": "LayoutNGBlockFlow DIV", + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow DIV", "rect": [67, 15, 50, 10], - "reason": "geometry" + "reason": "disappeared" }, { - "object": "LayoutNGTableCell TD", + "object": "NGPhysicalBoxFragment LayoutNGTableCell TD", "rect": [69, 374, 42, 12], "reason": "subtree" }, { - "object": "LayoutNGTableCell TD", + "object": "NGPhysicalBoxFragment LayoutNGTableCell TD", "rect": [69, 368, 42, 12], "reason": "subtree" }, { - "object": "LayoutNGTableCell TD", + "object": "NGPhysicalBoxFragment LayoutNGTableCell TD", "rect": [69, 335, 42, 12], "reason": "geometry" }, { - "object": "LayoutNGTableCell TD", + "object": "NGPhysicalBoxFragment LayoutNGTableCell TD", "rect": [69, 329, 42, 12], "reason": "geometry" }, { - "object": "LayoutNGTableCell TD", + "object": "NGPhysicalBoxFragment LayoutNGTableCell TD", "rect": [69, 296, 42, 12], "reason": "geometry" }, { - "object": "LayoutNGTableCell TD", + "object": "NGPhysicalBoxFragment LayoutNGTableCell TD", "rect": [69, 290, 42, 12], "reason": "geometry" }, @@ -418,14 +418,14 @@ "reason": "disappeared" }, { - "object": "LayoutNGBlockFlow DIV", + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow DIV", "rect": [67, 543, 32, 20], - "reason": "subtree" + "reason": "appeared" }, { - "object": "LayoutNGBlockFlow DIV", + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow DIV", "rect": [67, 537, 32, 20], - "reason": "subtree" + "reason": "disappeared" }, { "object": "InlineTextBox 'x'", @@ -576,8 +576,8 @@ "reason": "geometry" }, { - "object": "LayoutNGBlockFlow DIV", - "reason": "geometry" + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow DIV", + "reason": "subtree" }, { "object": "NGPhysicalBoxFragment LayoutNGBlockFlow (floating) DIV", @@ -596,8 +596,8 @@ "reason": "geometry" }, { - "object": "LayoutNGBlockFlow DIV", - "reason": "geometry" + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow DIV", + "reason": "subtree" }, { "object": "NGPhysicalBoxFragment LayoutNGBlockFlow (floating) DIV", @@ -616,8 +616,8 @@ "reason": "geometry" }, { - "object": "LayoutNGBlockFlow DIV", - "reason": "geometry" + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow DIV", + "reason": "subtree" }, { "object": "NGPhysicalBoxFragment LayoutNGBlockFlow (floating) DIV", @@ -636,8 +636,8 @@ "reason": "geometry" }, { - "object": "LayoutNGBlockFlow DIV", - "reason": "geometry" + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow DIV", + "reason": "subtree" }, { "object": "NGPhysicalBoxFragment LayoutNGBlockFlow (floating) DIV", @@ -656,8 +656,8 @@ "reason": "geometry" }, { - "object": "LayoutNGBlockFlow DIV", - "reason": "geometry" + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow DIV", + "reason": "subtree" }, { "object": "NGPhysicalBoxFragment LayoutNGBlockFlow (floating) DIV", @@ -736,7 +736,7 @@ "reason": "geometry" }, { - "object": "LayoutNGTableCell TD", + "object": "NGPhysicalBoxFragment LayoutNGTableCell TD", "reason": "geometry" }, { @@ -756,7 +756,7 @@ "reason": "geometry" }, { - "object": "LayoutNGTableCell TD", + "object": "NGPhysicalBoxFragment LayoutNGTableCell TD", "reason": "geometry" }, { @@ -788,7 +788,7 @@ "reason": "subtree" }, { - "object": "LayoutNGTableCell TD", + "object": "NGPhysicalBoxFragment LayoutNGTableCell TD", "reason": "subtree" }, { @@ -808,8 +808,8 @@ "reason": "geometry" }, { - "object": "LayoutNGBlockFlow DIV", - "reason": "geometry" + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow DIV", + "reason": "subtree" }, { "object": "NGPhysicalBoxFragment LayoutNGBlockFlow (floating) DIV", @@ -824,8 +824,8 @@ "reason": "geometry" }, { - "object": "LayoutNGBlockFlow DIV", - "reason": "geometry" + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow DIV", + "reason": "subtree" }, { "object": "NGPhysicalBoxFragment LayoutNGBlockFlow (floating) DIV", @@ -840,8 +840,8 @@ "reason": "geometry" }, { - "object": "LayoutNGBlockFlow DIV", - "reason": "geometry" + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow DIV", + "reason": "subtree" }, { "object": "NGPhysicalBoxFragment LayoutNGBlockFlow (floating) DIV", @@ -856,7 +856,7 @@ "reason": "subtree" }, { - "object": "LayoutNGBlockFlow DIV", + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow DIV", "reason": "subtree" }, {
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/overflow/float-overflow-right-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/overflow/float-overflow-right-expected.txt index b4e0fd5..01b6082b 100644 --- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/overflow/float-overflow-right-expected.txt +++ b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/overflow/float-overflow-right-expected.txt
@@ -158,34 +158,34 @@ "reason": "geometry" }, { - "object": "LayoutNGBlockFlow DIV", + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow DIV", "rect": [683, 504, 50, 10], - "reason": "geometry" + "reason": "appeared" }, { - "object": "LayoutNGBlockFlow DIV", + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow DIV", "rect": [683, 498, 50, 10], - "reason": "geometry" + "reason": "disappeared" }, { - "object": "LayoutNGBlockFlow DIV", + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow DIV", "rect": [683, 465, 50, 10], - "reason": "geometry" + "reason": "appeared" }, { - "object": "LayoutNGBlockFlow DIV", + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow DIV", "rect": [683, 459, 50, 10], - "reason": "geometry" + "reason": "disappeared" }, { - "object": "LayoutNGBlockFlow DIV", + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow DIV", "rect": [683, 426, 50, 10], - "reason": "geometry" + "reason": "appeared" }, { - "object": "LayoutNGBlockFlow DIV", + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow DIV", "rect": [683, 420, 50, 10], - "reason": "geometry" + "reason": "disappeared" }, { "object": "LayoutBlockFlow DIV", @@ -198,82 +198,82 @@ "reason": "geometry" }, { - "object": "LayoutNGBlockFlow DIV", + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow DIV", "rect": [683, 177, 50, 10], - "reason": "geometry" + "reason": "appeared" }, { - "object": "LayoutNGBlockFlow DIV", + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow DIV", "rect": [683, 171, 50, 10], - "reason": "geometry" + "reason": "disappeared" }, { - "object": "LayoutNGBlockFlow DIV", + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow DIV", "rect": [683, 138, 50, 10], - "reason": "geometry" + "reason": "appeared" }, { - "object": "LayoutNGBlockFlow DIV", + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow DIV", "rect": [683, 132, 50, 10], - "reason": "geometry" + "reason": "disappeared" }, { - "object": "LayoutNGBlockFlow DIV", + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow DIV", "rect": [683, 99, 50, 10], - "reason": "geometry" + "reason": "appeared" }, { - "object": "LayoutNGBlockFlow DIV", + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow DIV", "rect": [683, 93, 50, 10], - "reason": "geometry" + "reason": "disappeared" }, { - "object": "LayoutNGBlockFlow DIV", + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow DIV", "rect": [683, 60, 50, 10], - "reason": "geometry" + "reason": "appeared" }, { - "object": "LayoutNGBlockFlow DIV", + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow DIV", "rect": [683, 54, 50, 10], - "reason": "geometry" + "reason": "disappeared" }, { - "object": "LayoutNGBlockFlow DIV", + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow DIV", "rect": [683, 21, 50, 10], - "reason": "geometry" + "reason": "appeared" }, { - "object": "LayoutNGBlockFlow DIV", + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow DIV", "rect": [683, 15, 50, 10], - "reason": "geometry" + "reason": "disappeared" }, { - "object": "LayoutNGTableCell TD", + "object": "NGPhysicalBoxFragment LayoutNGTableCell TD", "rect": [689, 374, 42, 12], "reason": "subtree" }, { - "object": "LayoutNGTableCell TD", + "object": "NGPhysicalBoxFragment LayoutNGTableCell TD", "rect": [689, 368, 42, 12], "reason": "subtree" }, { - "object": "LayoutNGTableCell TD", + "object": "NGPhysicalBoxFragment LayoutNGTableCell TD", "rect": [689, 335, 42, 12], "reason": "geometry" }, { - "object": "LayoutNGTableCell TD", + "object": "NGPhysicalBoxFragment LayoutNGTableCell TD", "rect": [689, 329, 42, 12], "reason": "geometry" }, { - "object": "LayoutNGTableCell TD", + "object": "NGPhysicalBoxFragment LayoutNGTableCell TD", "rect": [689, 296, 42, 12], "reason": "geometry" }, { - "object": "LayoutNGTableCell TD", + "object": "NGPhysicalBoxFragment LayoutNGTableCell TD", "rect": [689, 290, 42, 12], "reason": "geometry" }, @@ -418,14 +418,14 @@ "reason": "geometry" }, { - "object": "LayoutNGBlockFlow DIV", + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow DIV", "rect": [701, 543, 32, 20], - "reason": "subtree" + "reason": "appeared" }, { - "object": "LayoutNGBlockFlow DIV", + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow DIV", "rect": [701, 537, 32, 20], - "reason": "subtree" + "reason": "disappeared" }, { "object": "InlineTextBox 'x'", @@ -576,8 +576,8 @@ "reason": "geometry" }, { - "object": "LayoutNGBlockFlow DIV", - "reason": "geometry" + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow DIV", + "reason": "subtree" }, { "object": "NGPhysicalBoxFragment LayoutNGBlockFlow (floating) DIV", @@ -596,8 +596,8 @@ "reason": "geometry" }, { - "object": "LayoutNGBlockFlow DIV", - "reason": "geometry" + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow DIV", + "reason": "subtree" }, { "object": "NGPhysicalBoxFragment LayoutNGBlockFlow (floating) DIV", @@ -616,8 +616,8 @@ "reason": "geometry" }, { - "object": "LayoutNGBlockFlow DIV", - "reason": "geometry" + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow DIV", + "reason": "subtree" }, { "object": "NGPhysicalBoxFragment LayoutNGBlockFlow (floating) DIV", @@ -636,8 +636,8 @@ "reason": "geometry" }, { - "object": "LayoutNGBlockFlow DIV", - "reason": "geometry" + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow DIV", + "reason": "subtree" }, { "object": "NGPhysicalBoxFragment LayoutNGBlockFlow (floating) DIV", @@ -656,8 +656,8 @@ "reason": "geometry" }, { - "object": "LayoutNGBlockFlow DIV", - "reason": "geometry" + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow DIV", + "reason": "subtree" }, { "object": "NGPhysicalBoxFragment LayoutNGBlockFlow (floating) DIV", @@ -736,7 +736,7 @@ "reason": "geometry" }, { - "object": "LayoutNGTableCell TD", + "object": "NGPhysicalBoxFragment LayoutNGTableCell TD", "reason": "geometry" }, { @@ -756,7 +756,7 @@ "reason": "geometry" }, { - "object": "LayoutNGTableCell TD", + "object": "NGPhysicalBoxFragment LayoutNGTableCell TD", "reason": "geometry" }, { @@ -788,7 +788,7 @@ "reason": "subtree" }, { - "object": "LayoutNGTableCell TD", + "object": "NGPhysicalBoxFragment LayoutNGTableCell TD", "reason": "subtree" }, { @@ -808,8 +808,8 @@ "reason": "geometry" }, { - "object": "LayoutNGBlockFlow DIV", - "reason": "geometry" + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow DIV", + "reason": "subtree" }, { "object": "NGPhysicalBoxFragment LayoutNGBlockFlow (floating) DIV", @@ -824,8 +824,8 @@ "reason": "geometry" }, { - "object": "LayoutNGBlockFlow DIV", - "reason": "geometry" + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow DIV", + "reason": "subtree" }, { "object": "NGPhysicalBoxFragment LayoutNGBlockFlow (floating) DIV", @@ -840,8 +840,8 @@ "reason": "geometry" }, { - "object": "LayoutNGBlockFlow DIV", - "reason": "geometry" + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow DIV", + "reason": "subtree" }, { "object": "NGPhysicalBoxFragment LayoutNGBlockFlow (floating) DIV", @@ -856,7 +856,7 @@ "reason": "subtree" }, { - "object": "LayoutNGBlockFlow DIV", + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow DIV", "reason": "subtree" }, {
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/overflow/overflow-into-content-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/overflow/overflow-into-content-expected.txt new file mode 100644 index 0000000..124509b --- /dev/null +++ b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/overflow/overflow-into-content-expected.txt
@@ -0,0 +1,66 @@ +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [800, 600], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [800, 600], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [800, 600], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF", + "paintInvalidations": [ + { + "object": "LayoutNGBlockFlow (positioned) DIV", + "rect": [8, 158, 106, 106], + "reason": "geometry" + }, + { + "object": "LayoutNGBlockFlow (positioned) DIV", + "rect": [8, 8, 106, 106], + "reason": "geometry" + }, + { + "object": "LayoutNGBlockFlow DIV", + "rect": [8, 308, 100, 100], + "reason": "disappeared" + }, + { + "object": "LayoutNGBlockFlow DIV id='target3'", + "rect": [8, 388, 100, 20], + "reason": "disappeared" + } + ] + } + ], + "objectPaintInvalidations": [ + { + "object": "LayoutNGBlockFlow (positioned) DIV", + "reason": "geometry" + }, + { + "object": "LayoutNGBlockFlow (positioned) DIV", + "reason": "geometry" + }, + { + "object": "LayoutNGBlockFlow DIV", + "reason": "disappeared" + }, + { + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow (relative positioned) DIV", + "reason": "subtree" + }, + { + "object": "LayoutNGBlockFlow (floating) DIV id='main-content'", + "reason": "subtree" + } + ] +} +
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/position/absolute-position-change-containing-block-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/position/absolute-position-change-containing-block-expected.txt index 84e64434..e1f9051a 100644 --- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/position/absolute-position-change-containing-block-expected.txt +++ b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/position/absolute-position-change-containing-block-expected.txt
@@ -32,7 +32,11 @@ ], "objectPaintInvalidations": [ { - "object": "LayoutNGBlockFlow DIV id='container' class='fixed blue'", + "object": "LayoutNGBlockFlow BODY", + "reason": "style change" + }, + { + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow DIV id='container' class='fixed blue'", "reason": "style change" }, {
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/position/absolute-position-moved-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/position/absolute-position-moved-expected.txt new file mode 100644 index 0000000..c1d9945 --- /dev/null +++ b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/position/absolute-position-moved-expected.txt
@@ -0,0 +1,58 @@ +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [800, 600], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [800, 600], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [800, 2016], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF", + "paintInvalidations": [ + { + "object": "LayoutNGBlockFlow (positioned) DIV class='fixed red'", + "rect": [108, 1100, 100, 100], + "reason": "chunk disappeared" + }, + { + "object": "LayoutNGBlockFlow (positioned) DIV id='absoluteDiv' class='absolute green'", + "rect": [100, 100, 100, 100], + "reason": "chunk appeared" + }, + { + "object": "LayoutNGBlockFlow (positioned) DIV class='fixed red'", + "rect": [8, 1000, 100, 100], + "reason": "chunk appeared" + }, + { + "object": "LayoutNGBlockFlow (positioned) DIV class='fixed red'", + "rect": [8, 1000, 100, 100], + "reason": "chunk disappeared" + } + ] + } + ], + "objectPaintInvalidations": [ + { + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow BODY", + "reason": "subtree" + }, + { + "object": "LayoutNGBlockFlow (positioned) DIV class='fixed red'", + "reason": "subtree" + }, + { + "object": "LayoutNGBlockFlow (positioned) DIV id='absoluteDiv' class='absolute green'", + "reason": "subtree" + } + ] +} +
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/position/fixed-to-relative-position-with-absolute-child-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/position/fixed-to-relative-position-with-absolute-child-expected.txt index 095e586..cd204fb1 100644 --- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/position/fixed-to-relative-position-with-absolute-child-expected.txt +++ b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/position/fixed-to-relative-position-with-absolute-child-expected.txt
@@ -37,7 +37,11 @@ ], "objectPaintInvalidations": [ { - "object": "LayoutNGBlockFlow (relative positioned) DIV id='container' class='fixed blue'", + "object": "LayoutNGBlockFlow BODY", + "reason": "style change" + }, + { + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow (relative positioned) DIV id='container' class='fixed blue'", "reason": "style change" }, {
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/position/layout-state-only-positioned-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/position/layout-state-only-positioned-expected.txt new file mode 100644 index 0000000..96bceb6 --- /dev/null +++ b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/position/layout-state-only-positioned-expected.txt
@@ -0,0 +1,52 @@ +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [800, 600], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [800, 600], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [800, 600], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF", + "paintInvalidations": [ + { + "object": "LayoutNGBlockFlow (positioned) DIV", + "rect": [0, 50, 106, 106], + "reason": "chunk appeared" + }, + { + "object": "LayoutNGBlockFlow HTML", + "rect": [0, 50, 106, 106], + "reason": "chunk disappeared" + } + ] + } + ], + "objectPaintInvalidations": [ + { + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow (positioned) DIV", + "reason": "subtree" + }, + { + "object": "LayoutNGBlockFlow (positioned) DIV", + "reason": "geometry" + }, + { + "object": "VerticalScrollbar", + "reason": "scroll control" + }, + { + "object": "LayoutNGBlockFlow (positioned) DIV id='q'", + "reason": "subtree" + } + ] +} +
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/position/positioned-great-grandparent-change-location-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/position/positioned-great-grandparent-change-location-expected.txt index dd193b8..67c6cb1 100644 --- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/position/positioned-great-grandparent-change-location-expected.txt +++ b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/position/positioned-great-grandparent-change-location-expected.txt
@@ -20,34 +20,46 @@ { "object": "NGPhysicalBoxFragment LayoutNGBlockFlow (positioned) DIV", "rect": [100, 200, 100, 100], - "reason": "geometry" + "reason": "subtree" }, { "object": "NGPhysicalBoxFragment LayoutNGBlockFlow (positioned) DIV", "rect": [100, 100, 100, 100], - "reason": "geometry" + "reason": "subtree" }, { "object": "NGPhysicalTextFragment 'Target'", "rect": [100, 200, 41, 19], - "reason": "geometry" + "reason": "subtree" }, { "object": "NGPhysicalTextFragment 'Target'", "rect": [100, 100, 41, 19], - "reason": "geometry" + "reason": "subtree" } ] } ], "objectPaintInvalidations": [ { + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow (positioned) DIV id='great-grandparent'", + "reason": "subtree" + }, + { "object": "NGPhysicalBoxFragment LayoutNGBlockFlow (positioned) DIV", - "reason": "geometry" + "reason": "subtree" + }, + { + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow (positioned) DIV", + "reason": "subtree" + }, + { + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow (positioned) DIV", + "reason": "subtree" }, { "object": "NGPhysicalTextFragment 'Target'", - "reason": "geometry" + "reason": "subtree" } ] }
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/repaint-across-writing-mode-boundary-expected.png b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/repaint-across-writing-mode-boundary-expected.png index 0818885..ab6a5dc0 100644 --- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/repaint-across-writing-mode-boundary-expected.png +++ b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/repaint-across-writing-mode-boundary-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/repaint-across-writing-mode-boundary-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/repaint-across-writing-mode-boundary-expected.txt index 7cef0b9..8f87759a 100644 --- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/repaint-across-writing-mode-boundary-expected.txt +++ b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/repaint-across-writing-mode-boundary-expected.txt
@@ -18,8 +18,8 @@ "backgroundColor": "#FFFFFF", "paintInvalidations": [ { - "object": "NGPaintFragment", - "rect": [528, 79, 63, 219], + "object": "NGPhysicalTextFragment 'paragraph 1'", + "rect": [548, 165, 23, 97], "reason": "selection" } ] @@ -27,7 +27,7 @@ ], "objectPaintInvalidations": [ { - "object": "NGPaintFragment", + "object": "NGPhysicalTextFragment 'paragraph 1'", "reason": "selection" } ]
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/repaint-descandant-on-ancestor-layer-move-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/repaint-descandant-on-ancestor-layer-move-expected.txt index bb6ae35..d4cfcc9 100644 --- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/repaint-descandant-on-ancestor-layer-move-expected.txt +++ b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/repaint-descandant-on-ancestor-layer-move-expected.txt
@@ -42,6 +42,10 @@ ], "objectPaintInvalidations": [ { + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow (positioned) BODY", + "reason": "subtree" + }, + { "object": "NGPhysicalBoxFragment LayoutNGBlockFlow (positioned) DIV id='container'", "reason": "subtree" },
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/scroll/fixed-after-scroll-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/scroll/fixed-after-scroll-expected.txt new file mode 100644 index 0000000..ad7fb0f --- /dev/null +++ b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/scroll/fixed-after-scroll-expected.txt
@@ -0,0 +1,66 @@ +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [800, 600], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [800, 600], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [800, 2016], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF", + "paintInvalidations": [ + { + "object": "LayoutNGBlockFlow (positioned) DIV class='red fixed'", + "rect": [8, 700, 100, 100], + "reason": "subtree" + }, + { + "object": "LayoutNGBlockFlow (positioned) DIV id='t' class='green absolute'", + "rect": [8, 700, 100, 100], + "reason": "paint property change" + }, + { + "object": "LayoutNGBlockFlow (positioned) DIV id='t' class='green absolute'", + "rect": [8, 200, 100, 100], + "reason": "paint property change" + } + ], + "transform": 1 + } + ], + "transforms": [ + { + "id": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [0, -500, 0, 1] + ], + "flattenInheritedTransform": false + } + ], + "objectPaintInvalidations": [ + { + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow BODY", + "reason": "subtree" + }, + { + "object": "LayoutNGBlockFlow (positioned) DIV class='red fixed'", + "reason": "subtree" + }, + { + "object": "LayoutNGBlockFlow (positioned) DIV id='t' class='green absolute'", + "reason": "subtree" + } + ] +} +
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/scroll/overflow-move-after-scroll-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/scroll/overflow-move-after-scroll-expected.txt new file mode 100644 index 0000000..5aa67ae --- /dev/null +++ b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/scroll/overflow-move-after-scroll-expected.txt
@@ -0,0 +1,103 @@ +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [800, 600], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [800, 600], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [800, 600], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + }, + { + "name": "LayoutNGBlockFlow (positioned) DIV id='scroller' class='scroller'", + "position": [10, 60], + "bounds": [700, 400] + }, + { + "name": "Scrolling Layer", + "position": [10, 60], + "bounds": [685, 385], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "position": [10, 60], + "bounds": [685, 600], + "paintInvalidations": [ + { + "object": "LayoutNGBlockFlow (positioned) DIV id='block'", + "rect": [300, 200, 120, 50], + "reason": "subtree" + }, + { + "object": "LayoutNGBlockFlow (positioned) DIV id='block'", + "rect": [50, 200, 120, 50], + "reason": "subtree" + } + ], + "transform": 1 + }, + { + "name": "Overflow Controls Host Layer", + "position": [10, 60], + "bounds": [700, 400], + "drawsContent": false + }, + { + "name": "Horizontal Scrollbar Layer", + "position": [10, 445], + "bounds": [685, 15], + "drawsContent": false + }, + { + "name": "Vertical Scrollbar Layer", + "position": [695, 60], + "bounds": [15, 385], + "drawsContent": false, + "paintInvalidations": [ + { + "object": "Vertical Scrollbar Layer", + "rect": [0, 0, 15, 385], + "reason": "full layer" + } + ] + }, + { + "name": "Scroll Corner Layer", + "position": [695, 445], + "bounds": [15, 15] + } + ], + "transforms": [ + { + "id": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [0, -100, 0, 1] + ], + "flattenInheritedTransform": false + } + ], + "objectPaintInvalidations": [ + { + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow (positioned) DIV", + "reason": "subtree" + }, + { + "object": "LayoutNGBlockFlow (positioned) DIV id='block'", + "reason": "subtree" + } + ] +} +
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/scroll/overflow-scroll-composited-non-stacking-child-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/scroll/overflow-scroll-composited-non-stacking-child-expected.txt new file mode 100644 index 0000000..bc326775 --- /dev/null +++ b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/scroll/overflow-scroll-composited-non-stacking-child-expected.txt
@@ -0,0 +1,89 @@ +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [800, 600], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [800, 600], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [800, 600], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF", + "paintInvalidations": [ + { + "object": "LayoutNGBlockFlow DIV class='scroller'", + "rect": [18, 60, 310, 200], + "reason": "full" + }, + { + "object": "LayoutNGBlockFlow (positioned) DIV class='back'", + "rect": [93, 125, 180, 100], + "reason": "paint property change" + }, + { + "object": "LayoutNGBlockFlow (positioned) DIV class='back'", + "rect": [93, 75, 180, 100], + "reason": "paint property change" + }, + { + "object": "VerticalScrollbar", + "rect": [308, 65, 15, 175], + "reason": "scroll control" + } + ] + }, + { + "name": "LayoutNGBlockFlow (positioned) DIV class='icon'", + "bounds": [40, 40], + "contentsOpaque": true, + "backgroundColor": "#FFDDBB", + "transform": 1 + }, + { + "name": "Ancestor Clipping Layer", + "position": [23, 65], + "bounds": [285, 175], + "drawsContent": false + }, + { + "name": "LayoutNGBlockFlow (relative positioned) DIV class='list'", + "position": [43, 35], + "bounds": [180, 250], + "drawsContent": false + }, + { + "name": "Squashing Layer (first squashed layer: LayoutNGBlockFlow (relative positioned) DIV class='commit')", + "position": [43, 35], + "bounds": [230, 250] + } + ], + "transforms": [ + { + "id": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [200, 10, 0, 1] + ] + } + ], + "objectPaintInvalidations": [ + { + "object": "LayoutNGBlockFlow DIV class='scroller'", + "reason": "full" + }, + { + "object": "VerticalScrollbar", + "reason": "scroll control" + } + ] +} +
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/transform/invalidation-with-scale-transform-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/transform/invalidation-with-scale-transform-expected.txt new file mode 100644 index 0000000..476e6839 --- /dev/null +++ b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/transform/invalidation-with-scale-transform-expected.txt
@@ -0,0 +1,44 @@ +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [800, 600], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [800, 600], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [800, 600], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF", + "paintInvalidations": [ + { + "object": "LayoutNGBlockFlow (positioned) DIV id='target'", + "rect": [85, 70, 91, 92], + "reason": "subtree" + }, + { + "object": "LayoutNGBlockFlow (positioned) DIV id='target'", + "rect": [84, 70, 91, 92], + "reason": "subtree" + } + ] + } + ], + "objectPaintInvalidations": [ + { + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow DIV id='zoom' class='zoom-div'", + "reason": "subtree" + }, + { + "object": "LayoutNGBlockFlow (positioned) DIV id='target'", + "reason": "subtree" + } + ] +} +
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/window-resize/window-resize-centered-inline-under-fixed-pos-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/window-resize/window-resize-centered-inline-under-fixed-pos-expected.txt index 4f07a4ba..9d8fe7f 100644 --- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/window-resize/window-resize-centered-inline-under-fixed-pos-expected.txt +++ b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/window-resize/window-resize-centered-inline-under-fixed-pos-expected.txt
@@ -20,7 +20,7 @@ { "object": "LayoutNGBlockFlow (positioned) DIV class='container'", "rect": [0, 0, 600, 250], - "reason": "geometry" + "reason": "subtree" }, { "object": "NGPhysicalBoxFragment LayoutNGBlockFlow DIV class='parent'", @@ -50,8 +50,12 @@ "reason": "incremental" }, { + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow BODY", + "reason": "subtree" + }, + { "object": "LayoutNGBlockFlow (positioned) DIV class='container'", - "reason": "geometry" + "reason": "subtree" }, { "object": "NGPhysicalBoxFragment LayoutNGBlockFlow DIV class='parent'", @@ -85,7 +89,7 @@ { "object": "LayoutNGBlockFlow (positioned) DIV class='container'", "rect": [0, 0, 400, 250], - "reason": "geometry" + "reason": "subtree" }, { "object": "NGPhysicalBoxFragment LayoutNGBlockFlow DIV class='parent'", @@ -115,8 +119,12 @@ "reason": "incremental" }, { + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow BODY", + "reason": "subtree" + }, + { "object": "LayoutNGBlockFlow (positioned) DIV class='container'", - "reason": "geometry" + "reason": "subtree" }, { "object": "NGPhysicalBoxFragment LayoutNGBlockFlow DIV class='parent'", @@ -150,7 +158,7 @@ { "object": "LayoutNGBlockFlow (positioned) DIV class='container'", "rect": [0, 0, 400, 600], - "reason": "geometry" + "reason": "subtree" }, { "object": "Scrolling Contents Layer", @@ -190,8 +198,12 @@ "reason": "incremental" }, { + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow BODY", + "reason": "subtree" + }, + { "object": "LayoutNGBlockFlow (positioned) DIV class='container'", - "reason": "geometry" + "reason": "subtree" }, { "object": "NGPhysicalBoxFragment LayoutNGBlockFlow DIV class='parent'", @@ -225,7 +237,7 @@ { "object": "LayoutNGBlockFlow (positioned) DIV class='container'", "rect": [0, 0, 800, 600], - "reason": "geometry" + "reason": "subtree" }, { "object": "Scrolling Contents Layer", @@ -260,8 +272,12 @@ "reason": "incremental" }, { + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow BODY", + "reason": "subtree" + }, + { "object": "LayoutNGBlockFlow (positioned) DIV class='container'", - "reason": "geometry" + "reason": "subtree" }, { "object": "NGPhysicalBoxFragment LayoutNGBlockFlow DIV class='parent'",
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/window-resize/window-resize-positioned-bottom-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/window-resize/window-resize-positioned-bottom-expected.txt new file mode 100644 index 0000000..2a195de --- /dev/null +++ b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/window-resize/window-resize-positioned-bottom-expected.txt
@@ -0,0 +1,205 @@ +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [600, 250], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [600, 250], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [600, 250], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF", + "paintInvalidations": [ + { + "object": "LayoutNGBlockFlow (positioned) DIV", + "rect": [0, 210, 20, 20], + "reason": "subtree" + } + ] + } + ], + "objectPaintInvalidations": [ + { + "object": "Scrolling Contents Layer", + "reason": "incremental" + }, + { + "object": "LayoutView #document", + "reason": "incremental" + }, + { + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow BODY", + "reason": "subtree" + }, + { + "object": "LayoutNGBlockFlow (positioned) DIV", + "reason": "subtree" + } + ] +} +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [400, 250], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [400, 250], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [400, 250], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF", + "paintInvalidations": [ + { + "object": "Scrolling Contents Layer", + "rect": [0, 0, 400, 250], + "reason": "background on scrolling contents layer" + }, + { + "object": "LayoutNGBlockFlow (positioned) DIV", + "rect": [0, 210, 20, 20], + "reason": "subtree" + } + ] + } + ], + "objectPaintInvalidations": [ + { + "object": "Scrolling Contents Layer", + "reason": "background on scrolling contents layer" + }, + { + "object": "LayoutView #document", + "reason": "incremental" + }, + { + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow BODY", + "reason": "subtree" + }, + { + "object": "LayoutNGBlockFlow (positioned) DIV", + "reason": "subtree" + } + ] +} +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [400, 600], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [400, 600], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [400, 600], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF", + "paintInvalidations": [ + { + "object": "Scrolling Contents Layer", + "rect": [0, 250, 400, 350], + "reason": "incremental" + }, + { + "object": "LayoutNGBlockFlow (positioned) DIV", + "rect": [0, 560, 20, 20], + "reason": "subtree" + }, + { + "object": "LayoutNGBlockFlow (positioned) DIV", + "rect": [0, 210, 20, 20], + "reason": "subtree" + } + ] + } + ], + "objectPaintInvalidations": [ + { + "object": "Scrolling Contents Layer", + "reason": "incremental" + }, + { + "object": "LayoutView #document", + "reason": "incremental" + }, + { + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow BODY", + "reason": "subtree" + }, + { + "object": "LayoutNGBlockFlow (positioned) DIV", + "reason": "subtree" + } + ] +} +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [800, 600], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [800, 600], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [800, 600], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF", + "paintInvalidations": [ + { + "object": "Scrolling Contents Layer", + "rect": [0, 0, 800, 600], + "reason": "background on scrolling contents layer" + }, + { + "object": "LayoutNGBlockFlow (positioned) DIV", + "rect": [0, 560, 20, 20], + "reason": "subtree" + } + ] + } + ], + "objectPaintInvalidations": [ + { + "object": "Scrolling Contents Layer", + "reason": "background on scrolling contents layer" + }, + { + "object": "LayoutView #document", + "reason": "incremental" + }, + { + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow BODY", + "reason": "subtree" + }, + { + "object": "LayoutNGBlockFlow (positioned) DIV", + "reason": "subtree" + } + ] +} +
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/window-resize/window-resize-positioned-percent-top-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/window-resize/window-resize-positioned-percent-top-expected.txt new file mode 100644 index 0000000..452890c --- /dev/null +++ b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/window-resize/window-resize-positioned-percent-top-expected.txt
@@ -0,0 +1,205 @@ +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [600, 250], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [600, 250], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [600, 250], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF", + "paintInvalidations": [ + { + "object": "LayoutNGBlockFlow (positioned) DIV", + "rect": [0, 125, 20, 20], + "reason": "subtree" + } + ] + } + ], + "objectPaintInvalidations": [ + { + "object": "Scrolling Contents Layer", + "reason": "incremental" + }, + { + "object": "LayoutView #document", + "reason": "incremental" + }, + { + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow BODY", + "reason": "subtree" + }, + { + "object": "LayoutNGBlockFlow (positioned) DIV", + "reason": "subtree" + } + ] +} +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [400, 250], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [400, 250], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [400, 250], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF", + "paintInvalidations": [ + { + "object": "Scrolling Contents Layer", + "rect": [0, 0, 400, 250], + "reason": "background on scrolling contents layer" + }, + { + "object": "LayoutNGBlockFlow (positioned) DIV", + "rect": [0, 125, 20, 20], + "reason": "subtree" + } + ] + } + ], + "objectPaintInvalidations": [ + { + "object": "Scrolling Contents Layer", + "reason": "background on scrolling contents layer" + }, + { + "object": "LayoutView #document", + "reason": "incremental" + }, + { + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow BODY", + "reason": "subtree" + }, + { + "object": "LayoutNGBlockFlow (positioned) DIV", + "reason": "subtree" + } + ] +} +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [400, 600], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [400, 600], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [400, 600], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF", + "paintInvalidations": [ + { + "object": "Scrolling Contents Layer", + "rect": [0, 250, 400, 350], + "reason": "incremental" + }, + { + "object": "LayoutNGBlockFlow (positioned) DIV", + "rect": [0, 300, 20, 20], + "reason": "subtree" + }, + { + "object": "LayoutNGBlockFlow (positioned) DIV", + "rect": [0, 125, 20, 20], + "reason": "subtree" + } + ] + } + ], + "objectPaintInvalidations": [ + { + "object": "Scrolling Contents Layer", + "reason": "incremental" + }, + { + "object": "LayoutView #document", + "reason": "incremental" + }, + { + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow BODY", + "reason": "subtree" + }, + { + "object": "LayoutNGBlockFlow (positioned) DIV", + "reason": "subtree" + } + ] +} +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [800, 600], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [800, 600], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [800, 600], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF", + "paintInvalidations": [ + { + "object": "Scrolling Contents Layer", + "rect": [0, 0, 800, 600], + "reason": "background on scrolling contents layer" + }, + { + "object": "LayoutNGBlockFlow (positioned) DIV", + "rect": [0, 300, 20, 20], + "reason": "subtree" + } + ] + } + ], + "objectPaintInvalidations": [ + { + "object": "Scrolling Contents Layer", + "reason": "background on scrolling contents layer" + }, + { + "object": "LayoutView #document", + "reason": "incremental" + }, + { + "object": "NGPhysicalBoxFragment LayoutNGBlockFlow BODY", + "reason": "subtree" + }, + { + "object": "LayoutNGBlockFlow (positioned) DIV", + "reason": "subtree" + } + ] +} +
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/audits2/audits2-successful-run-expected.txt b/third_party/WebKit/LayoutTests/http/tests/devtools/audits2/audits2-successful-run-expected.txt index 4cd7bf54..ab81cff 100644 --- a/third_party/WebKit/LayoutTests/http/tests/devtools/audits2/audits2-successful-run-expected.txt +++ b/third_party/WebKit/LayoutTests/http/tests/devtools/audits2/audits2-successful-run-expected.txt
@@ -26,9 +26,9 @@ Retrieving: ChromeConsoleMessages Retrieving: ImageUsage Retrieving: Accessibility -Retrieving: EventListeners Retrieving: AnchorsWithNoRelNoopener Retrieving: AppCacheManifest +Retrieving: Doctype Retrieving: DOMStats Retrieving: JSLibraries Retrieving: OptimizedImages @@ -78,7 +78,7 @@ Evaluating: User can be prompted to Install the Web App Evaluating: Configured for a custom splash screen Evaluating: Address bar matches brand colors -Evaluating: Manifest's `short_name` won't be truncated when displayed on homescreen +Evaluating: The `short_name` won't be truncated on the homescreen Evaluating: Content is sized correctly for the viewport Evaluating: Displays images with correct aspect ratio Evaluating: Avoids deprecated APIs @@ -150,11 +150,11 @@ Evaluating: Properly size images Evaluating: Use video formats for animated content Evaluating: Avoids Application Cache +Evaluating: Page has the HTML doctype Evaluating: Avoids an excessive DOM size Evaluating: Links to cross-origin destinations are safe Evaluating: Avoids requesting the geolocation permission on page load Evaluating: Avoids `document.write()` -Evaluating: Avoids Mutation Events in its own scripts Evaluating: Avoids front-end JavaScript libraries with known security vulnerabilities Evaluating: Avoids WebSQL DB Evaluating: Avoids requesting the notification permission on page load @@ -176,7 +176,7 @@ =============== Lighthouse Results =============== URL: http://127.0.0.1:8000/devtools/resources/inspected-page.html -Version: 3.0.0-beta.0 +Version: 3.0.3 accesskeys: not-applicable @@ -201,6 +201,7 @@ definition-list: not-applicable deprecations: pass dlitem: not-applicable +doctype: fail document-title: fail dom-size: numeric duplicate-id: not-applicable @@ -238,7 +239,7 @@ logical-tab-order: manual mainthread-work-breakdown: numeric managed-focus: manual -manifest-short-name-length: fail +manifest-short-name-length: not-applicable meta-description: fail meta-refresh: not-applicable meta-viewport: not-applicable @@ -246,7 +247,6 @@ mobile-friendly: manual network-requests: informative no-document-write: pass -no-mutation-events: pass no-vulnerable-libraries: pass no-websql: pass notification-on-start: pass
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.12/paint/invalidation/flexbox/scrollbars-changed-expected.txt b/third_party/WebKit/LayoutTests/platform/mac-mac10.10/paint/invalidation/flexbox/scrollbars-changed-expected.txt similarity index 100% rename from third_party/WebKit/LayoutTests/platform/mac-mac10.12/paint/invalidation/flexbox/scrollbars-changed-expected.txt rename to third_party/WebKit/LayoutTests/platform/mac-mac10.10/paint/invalidation/flexbox/scrollbars-changed-expected.txt
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.12/paint/invalidation/overflow/inline-vertical-lr-overflow-expected.txt b/third_party/WebKit/LayoutTests/platform/mac-mac10.10/paint/invalidation/overflow/inline-vertical-lr-overflow-expected.txt similarity index 100% rename from third_party/WebKit/LayoutTests/platform/mac-mac10.12/paint/invalidation/overflow/inline-vertical-lr-overflow-expected.txt rename to third_party/WebKit/LayoutTests/platform/mac-mac10.10/paint/invalidation/overflow/inline-vertical-lr-overflow-expected.txt
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.12/paint/invalidation/overflow/inline-vertical-rl-overflow-expected.txt b/third_party/WebKit/LayoutTests/platform/mac-mac10.10/paint/invalidation/overflow/inline-vertical-rl-overflow-expected.txt similarity index 100% rename from third_party/WebKit/LayoutTests/platform/mac-mac10.12/paint/invalidation/overflow/inline-vertical-rl-overflow-expected.txt rename to third_party/WebKit/LayoutTests/platform/mac-mac10.10/paint/invalidation/overflow/inline-vertical-rl-overflow-expected.txt
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.12/paint/invalidation/vertical-rl-as-paint-container-expected.txt b/third_party/WebKit/LayoutTests/platform/mac-mac10.10/paint/invalidation/vertical-rl-as-paint-container-expected.txt similarity index 100% rename from third_party/WebKit/LayoutTests/platform/mac-mac10.12/paint/invalidation/vertical-rl-as-paint-container-expected.txt rename to third_party/WebKit/LayoutTests/platform/mac-mac10.10/paint/invalidation/vertical-rl-as-paint-container-expected.txt
diff --git a/third_party/WebKit/LayoutTests/external/wpt/encoding/legacy-mb-korean/euc-kr/euckr-decode-ksc_5601-expected.txt b/third_party/WebKit/LayoutTests/platform/mac-mac10.11/external/wpt/encoding/legacy-mb-korean/euc-kr/euckr-decode-ksc_5601-expected.txt similarity index 100% rename from third_party/WebKit/LayoutTests/external/wpt/encoding/legacy-mb-korean/euc-kr/euckr-decode-ksc_5601-expected.txt rename to third_party/WebKit/LayoutTests/platform/mac-mac10.11/external/wpt/encoding/legacy-mb-korean/euc-kr/euckr-decode-ksc_5601-expected.txt
diff --git a/third_party/WebKit/LayoutTests/external/wpt/encoding/legacy-mb-korean/euc-kr/euckr-decode-ksc_5601-expected.txt b/third_party/WebKit/LayoutTests/platform/mac-retina/external/wpt/encoding/legacy-mb-korean/euc-kr/euckr-decode-ksc_5601-expected.txt similarity index 100% copy from third_party/WebKit/LayoutTests/external/wpt/encoding/legacy-mb-korean/euc-kr/euckr-decode-ksc_5601-expected.txt copy to third_party/WebKit/LayoutTests/platform/mac-retina/external/wpt/encoding/legacy-mb-korean/euc-kr/euckr-decode-ksc_5601-expected.txt
diff --git a/third_party/WebKit/LayoutTests/webexposed/css-properties-as-js-properties-expected.txt b/third_party/WebKit/LayoutTests/webexposed/css-properties-as-js-properties-expected.txt index 6bce4802..c47863e 100644 --- a/third_party/WebKit/LayoutTests/webexposed/css-properties-as-js-properties-expected.txt +++ b/third_party/WebKit/LayoutTests/webexposed/css-properties-as-js-properties-expected.txt
@@ -186,6 +186,13 @@ imageOrientation imageRendering inlineSize +inset +insetBlock +insetBlockEnd +insetBlockStart +insetInline +insetInlineEnd +insetInlineStart isolation item justifyContent
diff --git a/third_party/WebKit/LayoutTests/webexposed/css-property-listing-expected.txt b/third_party/WebKit/LayoutTests/webexposed/css-property-listing-expected.txt index 0bb1df04..1049450 100644 --- a/third_party/WebKit/LayoutTests/webexposed/css-property-listing-expected.txt +++ b/third_party/WebKit/LayoutTests/webexposed/css-property-listing-expected.txt
@@ -201,6 +201,10 @@ image-orientation image-rendering inline-size + inset-block-end + inset-block-start + inset-inline-end + inset-inline-start isolation justify-content justify-items @@ -591,6 +595,17 @@ grid-template-areas grid-template-columns grid-template-rows + inset + bottom + left + right + top + inset-block + inset-block-end + inset-block-start + inset-inline + inset-inline-end + inset-inline-start list-style list-style-image list-style-position
diff --git a/third_party/blink/public/mojom/use_counter/css_property_id.mojom b/third_party/blink/public/mojom/use_counter/css_property_id.mojom index 0dec4c15..1d7a72d 100644 --- a/third_party/blink/public/mojom/use_counter/css_property_id.mojom +++ b/third_party/blink/public/mojom/use_counter/css_property_id.mojom
@@ -4,7 +4,7 @@ module blink.mojom; -const int32 kMaximumCSSSampleId = 629; +const int32 kMaximumCSSSampleId = 636; // This CSSSampleId represents page load for CSS histograms. It is recorded once // per page visit for each CSS histogram being logged on the blink side and the
diff --git a/third_party/blink/public/platform/web_rtc_rtp_transceiver.h b/third_party/blink/public/platform/web_rtc_rtp_transceiver.h index 03e2357..435c1fcc 100644 --- a/third_party/blink/public/platform/web_rtc_rtp_transceiver.h +++ b/third_party/blink/public/platform/web_rtc_rtp_transceiver.h
@@ -53,9 +53,11 @@ virtual std::unique_ptr<WebRTCRtpReceiver> Receiver() const = 0; virtual bool Stopped() const = 0; virtual webrtc::RtpTransceiverDirection Direction() const = 0; + virtual void SetDirection(webrtc::RtpTransceiverDirection) = 0; virtual base::Optional<webrtc::RtpTransceiverDirection> CurrentDirection() const = 0; - virtual void Stop() = 0; + virtual base::Optional<webrtc::RtpTransceiverDirection> FiredDirection() + const = 0; }; } // namespace blink
diff --git a/third_party/blink/renderer/core/css/BUILD.gn b/third_party/blink/renderer/core/css/BUILD.gn index 6826801..8a3c38f 100644 --- a/third_party/blink/renderer/core/css/BUILD.gn +++ b/third_party/blink/renderer/core/css/BUILD.gn
@@ -523,6 +523,10 @@ "properties/longhands/image_orientation_custom.cc", "properties/longhands/image_rendering_custom.cc", "properties/longhands/inline_size_custom.cc", + "properties/longhands/inset_block_end_custom.cc", + "properties/longhands/inset_block_start_custom.cc", + "properties/longhands/inset_inline_end_custom.cc", + "properties/longhands/inset_inline_start_custom.cc", "properties/longhands/isolation_custom.cc", "properties/longhands/justify_content_custom.cc", "properties/longhands/justify_items_custom.cc", @@ -788,6 +792,9 @@ "properties/shorthands/grid_row_custom.cc", "properties/shorthands/grid_row_gap_custom.cc", "properties/shorthands/grid_template_custom.cc", + "properties/shorthands/inset_block_custom.cc", + "properties/shorthands/inset_custom.cc", + "properties/shorthands/inset_inline_custom.cc", "properties/shorthands/list_style_custom.cc", "properties/shorthands/margin_block_custom.cc", "properties/shorthands/margin_custom.cc",
diff --git a/third_party/blink/renderer/core/css/CSSProperties.json5 b/third_party/blink/renderer/core/css/CSSProperties.json5 index fcd5b73a..c4478b93 100644 --- a/third_party/blink/renderer/core/css/CSSProperties.json5 +++ b/third_party/blink/renderer/core/css/CSSProperties.json5
@@ -4279,6 +4279,42 @@ shorthand_for_physical_side: "borderColorShorthand", }, }, + { + name: "inset-inline-start", + property_methods: ["ParseSingleValue"], + runtime_flag: "CSSLogical", + direction_aware_options: { + logical_side: "start", + shorthand_for_physical_side: "insetShorthand", + } + }, + { + name: "inset-inline-end", + property_methods: ["ParseSingleValue"], + runtime_flag: "CSSLogical", + direction_aware_options: { + logical_side: "end", + shorthand_for_physical_side: "insetShorthand", + } + }, + { + name: "inset-block-start", + property_methods: ["ParseSingleValue"], + runtime_flag: "CSSLogical", + direction_aware_options: { + logical_side: "before", + shorthand_for_physical_side: "insetShorthand", + } + }, + { + name: "inset-block-end", + property_methods: ["ParseSingleValue"], + runtime_flag: "CSSLogical", + direction_aware_options: { + logical_side: "after", + shorthand_for_physical_side: "insetShorthand", + } + }, // Non-standard direction aware properties @@ -4766,6 +4802,24 @@ layout_dependent: true, }, { + name: "inset-block", + longhands: ["inset-block-start", "inset-block-end"], + property_methods: ["ParseShorthand"], + runtime_flag: "CSSLogical", + }, + { + name: "inset-inline", + longhands: ["inset-inline-start", "inset-inline-end"], + property_methods: ["ParseShorthand"], + runtime_flag: "CSSLogical", + }, + { + name: "inset", + longhands: ["top", "right", "bottom", "left"], + property_methods: ["ParseShorthand"], + runtime_flag: "CSSLogical", + }, + { name: "list-style", longhands: ["list-style-type", "list-style-position", "list-style-image"], property_methods: ["ParseShorthand", "CSSValueFromComputedStyleInternal"],
diff --git a/third_party/blink/renderer/core/css/parser/css_proto_converter.cc b/third_party/blink/renderer/core/css/parser/css_proto_converter.cc index 4fce488..9f911947 100644 --- a/third_party/blink/renderer/core/css/parser/css_proto_converter.cc +++ b/third_party/blink/renderer/core/css/parser/css_proto_converter.cc
@@ -1470,6 +1470,13 @@ "border-inline-color", "border-block", "border-inline", + "inset-block-start", + "inset-block-end", + "inset-block", + "inset-inline-start", + "inset-inline-end", + "inset-inline", + "inset", "INVALID_PROPERTY", };
diff --git a/third_party/blink/renderer/core/css/properties/longhands/inset_block_end_custom.cc b/third_party/blink/renderer/core/css/properties/longhands/inset_block_end_custom.cc new file mode 100644 index 0000000..631aa57 --- /dev/null +++ b/third_party/blink/renderer/core/css/properties/longhands/inset_block_end_custom.cc
@@ -0,0 +1,24 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/core/css/properties/longhands/inset_block_end.h" + +#include "third_party/blink/renderer/core/css/parser/css_parser_context.h" +#include "third_party/blink/renderer/core/css/parser/css_property_parser_helpers.h" +#include "third_party/blink/renderer/core/css/properties/css_parsing_utils.h" +#include "third_party/blink/renderer/core/style_property_shorthand.h" + +namespace blink { +namespace CSSLonghand { + +const CSSValue* InsetBlockEnd::ParseSingleValue( + CSSParserTokenRange& range, + const CSSParserContext& context, + const CSSParserLocalContext&) const { + return CSSParsingUtils::ConsumeMarginOrOffset( + range, context.Mode(), CSSPropertyParserHelpers::UnitlessQuirk::kForbid); +} + +} // namespace CSSLonghand +} // namespace blink
diff --git a/third_party/blink/renderer/core/css/properties/longhands/inset_block_start_custom.cc b/third_party/blink/renderer/core/css/properties/longhands/inset_block_start_custom.cc new file mode 100644 index 0000000..27cbd32 --- /dev/null +++ b/third_party/blink/renderer/core/css/properties/longhands/inset_block_start_custom.cc
@@ -0,0 +1,24 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/core/css/properties/longhands/inset_block_start.h" + +#include "third_party/blink/renderer/core/css/parser/css_parser_context.h" +#include "third_party/blink/renderer/core/css/parser/css_property_parser_helpers.h" +#include "third_party/blink/renderer/core/css/properties/css_parsing_utils.h" +#include "third_party/blink/renderer/core/style_property_shorthand.h" + +namespace blink { +namespace CSSLonghand { + +const CSSValue* InsetBlockStart::ParseSingleValue( + CSSParserTokenRange& range, + const CSSParserContext& context, + const CSSParserLocalContext&) const { + return CSSParsingUtils::ConsumeMarginOrOffset( + range, context.Mode(), CSSPropertyParserHelpers::UnitlessQuirk::kForbid); +} + +} // namespace CSSLonghand +} // namespace blink
diff --git a/third_party/blink/renderer/core/css/properties/longhands/inset_inline_end_custom.cc b/third_party/blink/renderer/core/css/properties/longhands/inset_inline_end_custom.cc new file mode 100644 index 0000000..6b1113d --- /dev/null +++ b/third_party/blink/renderer/core/css/properties/longhands/inset_inline_end_custom.cc
@@ -0,0 +1,24 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/core/css/properties/longhands/inset_inline_end.h" + +#include "third_party/blink/renderer/core/css/parser/css_parser_context.h" +#include "third_party/blink/renderer/core/css/parser/css_property_parser_helpers.h" +#include "third_party/blink/renderer/core/css/properties/css_parsing_utils.h" +#include "third_party/blink/renderer/core/style_property_shorthand.h" + +namespace blink { +namespace CSSLonghand { + +const CSSValue* InsetInlineEnd::ParseSingleValue( + CSSParserTokenRange& range, + const CSSParserContext& context, + const CSSParserLocalContext&) const { + return CSSParsingUtils::ConsumeMarginOrOffset( + range, context.Mode(), CSSPropertyParserHelpers::UnitlessQuirk::kForbid); +} + +} // namespace CSSLonghand +} // namespace blink
diff --git a/third_party/blink/renderer/core/css/properties/longhands/inset_inline_start_custom.cc b/third_party/blink/renderer/core/css/properties/longhands/inset_inline_start_custom.cc new file mode 100644 index 0000000..2a394eb --- /dev/null +++ b/third_party/blink/renderer/core/css/properties/longhands/inset_inline_start_custom.cc
@@ -0,0 +1,24 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/core/css/properties/longhands/inset_inline_start.h" + +#include "third_party/blink/renderer/core/css/parser/css_parser_context.h" +#include "third_party/blink/renderer/core/css/parser/css_property_parser_helpers.h" +#include "third_party/blink/renderer/core/css/properties/css_parsing_utils.h" +#include "third_party/blink/renderer/core/style_property_shorthand.h" + +namespace blink { +namespace CSSLonghand { + +const CSSValue* InsetInlineStart::ParseSingleValue( + CSSParserTokenRange& range, + const CSSParserContext& context, + const CSSParserLocalContext&) const { + return CSSParsingUtils::ConsumeMarginOrOffset( + range, context.Mode(), CSSPropertyParserHelpers::UnitlessQuirk::kForbid); +} + +} // namespace CSSLonghand +} // namespace blink
diff --git a/third_party/blink/renderer/core/css/properties/shorthands/inset_block_custom.cc b/third_party/blink/renderer/core/css/properties/shorthands/inset_block_custom.cc new file mode 100644 index 0000000..d9616b44 --- /dev/null +++ b/third_party/blink/renderer/core/css/properties/shorthands/inset_block_custom.cc
@@ -0,0 +1,24 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/core/css/properties/shorthands/inset_block.h" + +#include "third_party/blink/renderer/core/css/parser/css_property_parser_helpers.h" +#include "third_party/blink/renderer/core/style_property_shorthand.h" + +namespace blink { +namespace CSSShorthand { + +bool InsetBlock::ParseShorthand( + bool important, + CSSParserTokenRange& range, + const CSSParserContext& context, + const CSSParserLocalContext&, + HeapVector<CSSPropertyValue, 256>& properties) const { + return CSSPropertyParserHelpers::ConsumeShorthandVia2Longhands( + insetBlockShorthand(), important, context, range, properties); +} + +} // namespace CSSShorthand +} // namespace blink
diff --git a/third_party/blink/renderer/core/css/properties/shorthands/inset_custom.cc b/third_party/blink/renderer/core/css/properties/shorthands/inset_custom.cc new file mode 100644 index 0000000..06744cb --- /dev/null +++ b/third_party/blink/renderer/core/css/properties/shorthands/inset_custom.cc
@@ -0,0 +1,24 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/core/css/properties/shorthands/inset.h" + +#include "third_party/blink/renderer/core/css/parser/css_property_parser_helpers.h" +#include "third_party/blink/renderer/core/style_property_shorthand.h" + +namespace blink { +namespace CSSShorthand { + +bool Inset::ParseShorthand( + bool important, + CSSParserTokenRange& range, + const CSSParserContext& context, + const CSSParserLocalContext&, + HeapVector<CSSPropertyValue, 256>& properties) const { + return CSSPropertyParserHelpers::ConsumeShorthandVia4Longhands( + insetShorthand(), important, context, range, properties); +} + +} // namespace CSSShorthand +} // namespace blink
diff --git a/third_party/blink/renderer/core/css/properties/shorthands/inset_inline_custom.cc b/third_party/blink/renderer/core/css/properties/shorthands/inset_inline_custom.cc new file mode 100644 index 0000000..2ae7dce --- /dev/null +++ b/third_party/blink/renderer/core/css/properties/shorthands/inset_inline_custom.cc
@@ -0,0 +1,24 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/core/css/properties/shorthands/inset_inline.h" + +#include "third_party/blink/renderer/core/css/parser/css_property_parser_helpers.h" +#include "third_party/blink/renderer/core/style_property_shorthand.h" + +namespace blink { +namespace CSSShorthand { + +bool InsetInline::ParseShorthand( + bool important, + CSSParserTokenRange& range, + const CSSParserContext& context, + const CSSParserLocalContext&, + HeapVector<CSSPropertyValue, 256>& properties) const { + return CSSPropertyParserHelpers::ConsumeShorthandVia2Longhands( + insetInlineShorthand(), important, context, range, properties); +} + +} // namespace CSSShorthand +} // namespace blink
diff --git a/third_party/blink/renderer/core/dom/element.cc b/third_party/blink/renderer/core/dom/element.cc index a93c5341..54a70a4 100644 --- a/third_party/blink/renderer/core/dom/element.cc +++ b/third_party/blink/renderer/core/dom/element.cc
@@ -33,6 +33,7 @@ #include "third_party/blink/renderer/bindings/core/v8/scroll_into_view_options_or_boolean.h" #include "third_party/blink/renderer/bindings/core/v8/string_or_trusted_html.h" #include "third_party/blink/renderer/bindings/core/v8/string_or_trusted_script_url.h" +#include "third_party/blink/renderer/bindings/core/v8/usv_string_or_trusted_url.h" #include "third_party/blink/renderer/core/accessibility/ax_object_cache.h" #include "third_party/blink/renderer/core/animation/css/css_animations.h" #include "third_party/blink/renderer/core/aom/computed_accessible_node.h" @@ -143,6 +144,7 @@ #include "third_party/blink/renderer/core/svg_names.h" #include "third_party/blink/renderer/core/trustedtypes/trusted_html.h" #include "third_party/blink/renderer/core/trustedtypes/trusted_script_url.h" +#include "third_party/blink/renderer/core/trustedtypes/trusted_url.h" #include "third_party/blink/renderer/core/xml_names.h" #include "third_party/blink/renderer/platform/bindings/dom_data_store.h" #include "third_party/blink/renderer/platform/bindings/exception_state.h" @@ -1612,6 +1614,24 @@ setAttribute(name, AtomicString(valueString)); } +void Element::setAttribute(const QualifiedName& name, + const USVStringOrTrustedURL& stringOrURL, + ExceptionState& exception_state) { + DCHECK(stringOrURL.IsUSVString() || + RuntimeEnabledFeatures::TrustedDOMTypesEnabled()); + if (stringOrURL.IsUSVString() && GetDocument().RequireTrustedTypes()) { + exception_state.ThrowTypeError( + "This document requires `TrustedURL` assignment."); + return; + } + + String valueString = stringOrURL.IsUSVString() + ? stringOrURL.GetAsUSVString() + : stringOrURL.GetAsTrustedURL()->toString(); + + setAttribute(name, AtomicString(valueString)); +} + ALWAYS_INLINE void Element::SetAttributeInternal( size_t index, const QualifiedName& name, @@ -4229,6 +4249,18 @@ result.SetString(url.GetString()); } +void Element::GetURLAttribute(const QualifiedName& name, + USVStringOrTrustedURL& result) const { + String url = GetURLAttribute(name); + result.SetUSVString(url); +} + +void Element::FastGetAttribute(const QualifiedName& name, + USVStringOrTrustedURL& result) const { + String attr = FastGetAttribute(name); + result.SetUSVString(attr); +} + void Element::FastGetAttribute(const QualifiedName& name, StringOrTrustedHTML& result) const { String html = FastGetAttribute(name);
diff --git a/third_party/blink/renderer/core/dom/element.h b/third_party/blink/renderer/core/dom/element.h index 479d4d8..f7d4037 100644 --- a/third_party/blink/renderer/core/dom/element.h +++ b/third_party/blink/renderer/core/dom/element.h
@@ -81,6 +81,7 @@ class StringOrTrustedScriptURL; class StylePropertyMap; class StylePropertyMapReadOnly; +class USVStringOrTrustedURL; class V0CustomElementDefinition; class V8ScrollStateCallback; @@ -216,6 +217,11 @@ const StringOrTrustedHTML&, ExceptionState&); + // Trusted Type URL variant + void setAttribute(const QualifiedName&, + const USVStringOrTrustedURL&, + ExceptionState&); + static bool ParseAttributeName(QualifiedName&, const AtomicString& namespace_uri, const AtomicString& qualified_name, @@ -583,6 +589,8 @@ KURL GetURLAttribute(const QualifiedName&) const; void GetURLAttribute(const QualifiedName&, StringOrTrustedScriptURL&) const; + void GetURLAttribute(const QualifiedName&, USVStringOrTrustedURL&) const; + void FastGetAttribute(const QualifiedName&, USVStringOrTrustedURL&) const; void FastGetAttribute(const QualifiedName&, StringOrTrustedHTML&) const; KURL GetNonEmptyURLAttribute(const QualifiedName&) const;
diff --git a/third_party/blink/renderer/core/editing/spellcheck/idle_spell_check_callback_test.cc b/third_party/blink/renderer/core/editing/spellcheck/idle_spell_check_callback_test.cc index 4f46e643..74179ea 100644 --- a/third_party/blink/renderer/core/editing/spellcheck/idle_spell_check_callback_test.cc +++ b/third_party/blink/renderer/core/editing/spellcheck/idle_spell_check_callback_test.cc
@@ -4,10 +4,13 @@ #include "third_party/blink/renderer/core/editing/spellcheck/idle_spell_check_callback.h" +#include "third_party/blink/renderer/core/dom/element.h" #include "third_party/blink/renderer/core/editing/spellcheck/spell_check_test_base.h" #include "third_party/blink/renderer/core/editing/spellcheck/spell_checker.h" #include "third_party/blink/renderer/core/frame/local_frame.h" +#include "third_party/blink/renderer/core/html/html_object_element.h" #include "third_party/blink/renderer/platform/runtime_enabled_features.h" +#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h" namespace blink { @@ -181,4 +184,28 @@ EXPECT_EQ(State::kInactive, IdleChecker().GetState()); } +// crbug.com/863784 +TEST_F(IdleSpellCheckCallbackTest, ColdModeRangeCrossesShadow) { + ScopedIdleTimeColdModeSpellCheckingForTest cold_mode_scope(true); + SetBodyContent( + "<div contenteditable style=\"width:800px\">" + "foo" + "<menu style=\"all: initial\">1127</menu>" + "<object><optgroup></optgroup></object>" + "</div>"); + ToHTMLObjectElement(GetDocument().QuerySelector("object")) + ->RenderFallbackContent(); + GetDocument().QuerySelector("div")->focus(); + UpdateAllLifecyclePhases(); + + // Advance to cold mode invocation + IdleChecker().ForceInvocationForTesting(); + IdleChecker().SkipColdModeTimerForTesting(); + ASSERT_EQ(State::kColdModeRequested, IdleChecker().GetState()); + + // Shouldn't crash + IdleChecker().ForceInvocationForTesting(); + EXPECT_EQ(State::kInactive, IdleChecker().GetState()); +} + } // namespace blink
diff --git a/third_party/blink/renderer/core/editing/spellcheck/spell_check_requester.cc b/third_party/blink/renderer/core/editing/spellcheck/spell_check_requester.cc index 08604403..706f274d 100644 --- a/third_party/blink/renderer/core/editing/spellcheck/spell_check_requester.cc +++ b/third_party/blink/renderer/core/editing/spellcheck/spell_check_requester.cc
@@ -88,7 +88,6 @@ request_number_(request_number) { DCHECK(checking_range_); DCHECK(checking_range_->IsConnected()); - DCHECK(root_editable_element_); } SpellCheckRequest::~SpellCheckRequest() = default; @@ -123,7 +122,15 @@ Range* checking_range_object = CreateRange(checking_range); - return new SpellCheckRequest(checking_range_object, text, request_number); + SpellCheckRequest* request = + new SpellCheckRequest(checking_range_object, text, request_number); + if (request->RootEditableElement()) + return request; + + // We may reach here if |checking_range| crosses shadow boundary, in which + // case we don't want spellchecker to crash renderer. + request->Dispose(); + return nullptr; } bool SpellCheckRequest::IsValid() const {
diff --git a/third_party/blink/renderer/core/frame/use_counter.cc b/third_party/blink/renderer/core/frame/use_counter.cc index fc8cd4e3..50cf45b 100644 --- a/third_party/blink/renderer/core/frame/use_counter.cc +++ b/third_party/blink/renderer/core/frame/use_counter.cc
@@ -1210,6 +1210,20 @@ return 628; case CSSPropertyBorderInline: return 629; + case CSSPropertyInsetBlockStart: + return 630; + case CSSPropertyInsetBlockEnd: + return 631; + case CSSPropertyInsetBlock: + return 632; + case CSSPropertyInsetInlineStart: + return 633; + case CSSPropertyInsetInlineEnd: + return 634; + case CSSPropertyInsetInline: + return 635; + case CSSPropertyInset: + return 636; // 1. Add new features above this line (don't change the assigned numbers of // the existing items). // 2. Update kMaximumCSSSampleId (defined in
diff --git a/third_party/blink/renderer/core/html/html_embed_element.idl b/third_party/blink/renderer/core/html/html_embed_element.idl index 020794f..fc16063 100644 --- a/third_party/blink/renderer/core/html/html_embed_element.idl +++ b/third_party/blink/renderer/core/html/html_embed_element.idl
@@ -20,13 +20,17 @@ // https://html.spec.whatwg.org/#the-embed-element +// The `ScriptURLString` reference below is from Trusted Types: +// https://github.com/WICG/trusted-types/, which is still WIP. +// https://crbug.com/739170. + // TODO(yukishiino): HTMLEmbedElement should not have [OverrideBuiltins]. [ OverrideBuiltins, ActiveScriptWrappable, HTMLConstructor ] interface HTMLEmbedElement : HTMLElement { - [CEReactions, Reflect, URL] attribute DOMString src; + [CEReactions, Reflect, URL, RaisesException=Setter] attribute ScriptURLString src; [CEReactions, Reflect] attribute DOMString type; [CEReactions, Reflect] attribute DOMString width; [CEReactions, Reflect] attribute DOMString height;
diff --git a/third_party/blink/renderer/core/html/html_image_element.cc b/third_party/blink/renderer/core/html/html_image_element.cc index 2553e8d..8c29fcd 100644 --- a/third_party/blink/renderer/core/html/html_image_element.cc +++ b/third_party/blink/renderer/core/html/html_image_element.cc
@@ -58,6 +58,7 @@ #include "third_party/blink/renderer/core/page/page.h" #include "third_party/blink/renderer/core/style/content_data.h" #include "third_party/blink/renderer/core/svg/graphics/svg_image_for_container.h" +#include "third_party/blink/renderer/core/trustedtypes/trusted_url.h" #include "third_party/blink/renderer/platform/network/mime/content_type.h" #include "third_party/blink/renderer/platform/network/mime/mime_type_registry.h" #include "third_party/blink/renderer/platform/weborigin/security_policy.h" @@ -564,6 +565,11 @@ setAttribute(srcAttr, AtomicString(value)); } +void HTMLImageElement::SetSrc(const USVStringOrTrustedURL& value, + ExceptionState& exception_state) { + setAttribute(srcAttr, value, exception_state); +} + void HTMLImageElement::setWidth(unsigned value) { SetUnsignedIntegralAttribute(widthAttr, value); }
diff --git a/third_party/blink/renderer/core/html/html_image_element.h b/third_party/blink/renderer/core/html/html_image_element.h index 8f8524d..14a6964d 100644 --- a/third_party/blink/renderer/core/html/html_image_element.h +++ b/third_party/blink/renderer/core/html/html_image_element.h
@@ -40,7 +40,9 @@ class HTMLFormElement; class ImageCandidate; +class ExceptionState; class ShadowRoot; +class USVStringOrTrustedURL; class CORE_EXPORT HTMLImageElement final : public HTMLElement, @@ -95,6 +97,7 @@ KURL Src() const; void SetSrc(const String&); + void SetSrc(const USVStringOrTrustedURL&, ExceptionState&); void setWidth(unsigned);
diff --git a/third_party/blink/renderer/core/html/html_image_element.idl b/third_party/blink/renderer/core/html/html_image_element.idl index 201caa6..c3e7980 100644 --- a/third_party/blink/renderer/core/html/html_image_element.idl +++ b/third_party/blink/renderer/core/html/html_image_element.idl
@@ -27,7 +27,7 @@ NamedConstructor=Image(optional unsigned long width, optional unsigned long height) ] interface HTMLImageElement : HTMLElement { [CEReactions, Reflect] attribute DOMString alt; - [CEReactions, Reflect, URL] attribute DOMString src; + [CEReactions, Reflect, URL, RaisesException=Setter] attribute URLString src; [CEReactions, Reflect] attribute DOMString srcset; [CEReactions, Reflect] attribute DOMString sizes; [CEReactions, Reflect, ReflectOnly=("anonymous","use-credentials"), ReflectEmpty="anonymous", ReflectInvalid="anonymous"] attribute DOMString? crossOrigin;
diff --git a/third_party/blink/renderer/core/html/html_source_element.cc b/third_party/blink/renderer/core/html/html_source_element.cc index 58327a3..ff13fd9 100644 --- a/third_party/blink/renderer/core/html/html_source_element.cc +++ b/third_party/blink/renderer/core/html/html_source_element.cc
@@ -26,6 +26,7 @@ #include "third_party/blink/renderer/core/html/html_source_element.h" #include "third_party/blink/public/platform/task_type.h" +#include "third_party/blink/renderer/bindings/core/v8/usv_string_or_trusted_url.h" #include "third_party/blink/renderer/core/css/media_list.h" #include "third_party/blink/renderer/core/css/media_query_list.h" #include "third_party/blink/renderer/core/css/media_query_matcher.h" @@ -34,6 +35,7 @@ #include "third_party/blink/renderer/core/html/html_picture_element.h" #include "third_party/blink/renderer/core/html/media/html_media_element.h" #include "third_party/blink/renderer/core/html_names.h" +#include "third_party/blink/renderer/core/trustedtypes/trusted_url.h" #define SOURCE_LOG_LEVEL 3 @@ -124,6 +126,11 @@ setAttribute(srcAttr, AtomicString(url)); } +void HTMLSourceElement::SetSrc(const USVStringOrTrustedURL& usvStringOrURL, + ExceptionState& exception_state) { + setAttribute(srcAttr, usvStringOrURL, exception_state); +} + const AtomicString& HTMLSourceElement::type() const { return getAttribute(typeAttr); }
diff --git a/third_party/blink/renderer/core/html/html_source_element.h b/third_party/blink/renderer/core/html/html_source_element.h index 6d2cabd..5d6fe8c 100644 --- a/third_party/blink/renderer/core/html/html_source_element.h +++ b/third_party/blink/renderer/core/html/html_source_element.h
@@ -33,6 +33,8 @@ namespace blink { +class USVStringOrTrustedURL; +class ExceptionState; class HTMLSourceElement final : public HTMLElement { DEFINE_WRAPPERTYPEINFO(); @@ -44,6 +46,7 @@ const AtomicString& type() const; void SetSrc(const String&); + void SetSrc(const USVStringOrTrustedURL&, ExceptionState&); void setType(const AtomicString&); void ScheduleErrorEvent();
diff --git a/third_party/blink/renderer/core/html/html_source_element.idl b/third_party/blink/renderer/core/html/html_source_element.idl index 82d94e1..1c51502 100644 --- a/third_party/blink/renderer/core/html/html_source_element.idl +++ b/third_party/blink/renderer/core/html/html_source_element.idl
@@ -26,7 +26,7 @@ // https://html.spec.whatwg.org/#the-source-element [HTMLConstructor] interface HTMLSourceElement : HTMLElement { - [CEReactions, Reflect, URL] attribute USVString src; + [CEReactions, Reflect, URL, RaisesException=Setter] attribute URLString src; [CEReactions] attribute DOMString type; // https://html.spec.whatwg.org/#the-source-element-when-used-with-the-picture-element
diff --git a/third_party/blink/renderer/core/html/media/html_media_element.cc b/third_party/blink/renderer/core/html/media/html_media_element.cc index 42e327b6..ea46fb8 100644 --- a/third_party/blink/renderer/core/html/media/html_media_element.cc +++ b/third_party/blink/renderer/core/html/media/html_media_element.cc
@@ -789,6 +789,11 @@ setAttribute(srcAttr, url); } +void HTMLMediaElement::SetSrc(const USVStringOrTrustedURL& stringOrURL, + ExceptionState& exception_state) { + setAttribute(srcAttr, stringOrURL, exception_state); +} + void HTMLMediaElement::SetSrcObject(MediaStreamDescriptor* src_object) { BLINK_MEDIA_LOG << "setSrcObject(" << (void*)this << ")"; src_object_ = src_object;
diff --git a/third_party/blink/renderer/core/html/media/html_media_element.h b/third_party/blink/renderer/core/html/media/html_media_element.h index a8e8d4d..dd65972 100644 --- a/third_party/blink/renderer/core/html/media/html_media_element.h +++ b/third_party/blink/renderer/core/html/media/html_media_element.h
@@ -139,6 +139,7 @@ // network state void SetSrc(const AtomicString&); + void SetSrc(const USVStringOrTrustedURL&, ExceptionState&); const KURL& currentSrc() const { return current_src_; } void SetSrcObject(MediaStreamDescriptor*); MediaStreamDescriptor* GetSrcObject() const { return src_object_.Get(); }
diff --git a/third_party/blink/renderer/core/html/media/html_media_element.idl b/third_party/blink/renderer/core/html/media/html_media_element.idl index b1bfe6e..61972ef 100644 --- a/third_party/blink/renderer/core/html/media/html_media_element.idl +++ b/third_party/blink/renderer/core/html/media/html_media_element.idl
@@ -25,6 +25,9 @@ // https://html.spec.whatwg.org/#media-elements +// The `URLString` reference below is from Trusted Types: +// https://github.com/WICG/trusted-types/, which is still WIP. +// https://crbug.com/739170. enum CanPlayTypeResult { "" /* empty string */, "maybe", "probably" }; [ ActiveScriptWrappable @@ -34,7 +37,7 @@ readonly attribute MediaError? error; // network state - [CEReactions, Reflect, URL] attribute DOMString src; + [CEReactions, Reflect, URL, RaisesException=Setter] attribute URLString src; // FIXME: attribute MediaProvider? srcObject; crbug.com/387740 readonly attribute DOMString currentSrc; [CEReactions, Reflect, ReflectOnly=("anonymous","use-credentials"), ReflectEmpty="anonymous", ReflectInvalid="anonymous"] attribute DOMString? crossOrigin;
diff --git a/third_party/blink/renderer/core/layout/flexible_box_algorithm.h b/third_party/blink/renderer/core/layout/flexible_box_algorithm.h index aabff561..05635caa 100644 --- a/third_party/blink/renderer/core/layout/flexible_box_algorithm.h +++ b/third_party/blink/renderer/core/layout/flexible_box_algorithm.h
@@ -63,6 +63,8 @@ class FlexItem { public: FlexItem(LayoutBox*, + // flex_base_content_size includes scrollbar width but not border or + // padding. LayoutUnit flex_base_content_size, MinMaxSize min_max_sizes, LayoutUnit main_axis_border_and_padding,
diff --git a/third_party/blink/renderer/core/layout/layout_flexible_box.cc b/third_party/blink/renderer/core/layout/layout_flexible_box.cc index ffacb574..054c5a5a 100644 --- a/third_party/blink/renderer/core/layout/layout_flexible_box.cc +++ b/third_party/blink/renderer/core/layout/layout_flexible_box.cc
@@ -1300,12 +1300,11 @@ void LayoutFlexibleBox::SetOverrideMainAxisContentSizeForChild( LayoutBox& child, LayoutUnit child_preferred_size) { + // child_preferred_size includes scrollbar width. if (HasOrthogonalFlow(child)) { - // TODO(rego): Shouldn't we add the scrollbar height too? child.SetOverrideLogicalHeight(child_preferred_size + child.BorderAndPaddingLogicalHeight()); } else { - // TODO(rego): Shouldn't we add the scrollbar width too? child.SetOverrideLogicalWidth(child_preferred_size + child.BorderAndPaddingLogicalWidth()); }
diff --git a/third_party/blink/renderer/core/layout/ng/ng_block_node.h b/third_party/blink/renderer/core/layout/ng/ng_block_node.h index 6060051..f0968873 100644 --- a/third_party/blink/renderer/core/layout/ng/ng_block_node.h +++ b/third_party/blink/renderer/core/layout/ng/ng_block_node.h
@@ -35,7 +35,8 @@ NGBreakToken* break_token = nullptr); NGLayoutInputNode NextSibling() const; - // Computes the value of min-content and max-content for this box. + // Computes the value of min-content and max-content for this node's border + // box. // If the underlying layout algorithm's ComputeMinMaxSize returns // no value, this function will synthesize these sizes using Layout with // special constraint spaces -- infinite available size for max content, zero @@ -49,6 +50,9 @@ // calculating min/max for orthogonal flows. This constraint space will not be // passed on to children. If no constraint space is specified, a zero-sized // one will be used. + // The constraint space is also used to perform layout when this block's + // writing mode is orthogonal to its parent's, in which case the constraint + // space is not optional. MinMaxSize ComputeMinMaxSize(WritingMode container_writing_mode, const MinMaxSizeInput&, const NGConstraintSpace* = nullptr);
diff --git a/third_party/blink/renderer/core/layout/ng/ng_flex_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/ng_flex_layout_algorithm.cc index 1bd2690..c6e35df 100644 --- a/third_party/blink/renderer/core/layout/ng/ng_flex_layout_algorithm.cc +++ b/third_party/blink/renderer/core/layout/ng/ng_flex_layout_algorithm.cc
@@ -33,8 +33,9 @@ CalculateBorderScrollbarPadding(ConstraintSpace(), Node()).InlineSum(); Vector<FlexItem> flex_items; - for (NGLayoutInputNode child = Node().FirstChild(); child; - child = child.NextSibling()) { + for (NGLayoutInputNode generic_child = Node().FirstChild(); generic_child; + generic_child = generic_child.NextSibling()) { + NGBlockNode child = ToNGBlockNode(generic_child); if (child.IsOutOfFlowPositioned()) continue; @@ -47,17 +48,22 @@ scoped_refptr<NGConstraintSpace> child_space = space_builder.ToConstraintSpace(child.Style().GetWritingMode()); + LayoutUnit main_axis_border_and_padding = + ComputeBorders(*child_space, child.Style()).InlineSum() + + ComputePadding(*child_space, child.Style()).InlineSum(); // ComputeMinMaxSize will layout the child if it has an orthogonal writing // mode. MinMaxSize will be in the container's inline direction. MinMaxSizeInput zero_input; - MinMaxSize min_max_sizes = child.ComputeMinMaxSize( + MinMaxSize min_max_sizes_border_box = child.ComputeMinMaxSize( ConstraintSpace().GetWritingMode(), zero_input, child_space.get()); // Spec calls this "flex base size" // https://www.w3.org/TR/css-flexbox-1/#algo-main-item - LayoutUnit flex_base_content_size; + // Blink's FlexibleBoxAlgorithm expects it to be content + scrollbar widths, + // but no padding or border. + LayoutUnit flex_base_border_box; if (child.Style().FlexBasis().IsAuto() && child.Style().Width().IsAuto()) { - flex_base_content_size = min_max_sizes.max_size; + flex_base_border_box = min_max_sizes_border_box.max_size; } else { Length length_to_resolve = child.Style().FlexBasis(); if (length_to_resolve.IsAuto()) @@ -66,14 +72,15 @@ // TODO(dgrogan): Use ResolveBlockLength here for column flex boxes. - flex_base_content_size = ResolveInlineLength( - *child_space, child.Style(), min_max_sizes, length_to_resolve, - LengthResolveType::kContentSize, LengthResolvePhase::kLayout); + flex_base_border_box = ResolveInlineLength( + *child_space, child.Style(), min_max_sizes_border_box, + length_to_resolve, LengthResolveType::kContentSize, + LengthResolvePhase::kLayout); } - LayoutUnit main_axis_border_and_padding = - ComputeBorders(*child_space, child.Style()).InlineSum() + - ComputePadding(*child_space, child.Style()).InlineSum(); + LayoutUnit flex_base_content_size = + flex_base_border_box - main_axis_border_and_padding; + LayoutUnit main_axis_margin = ComputeMarginsForSelf(*child_space, child.Style()).InlineSum(); @@ -113,7 +120,9 @@ NGConstraintSpaceBuilder space_builder(ConstraintSpace()); // TODO(dgrogan): Set the percentage size also. space_builder.SetAvailableSize( - {flex_item.flexed_content_size, NGSizeIndefinite}); + {flex_item.flexed_content_size + + flex_item.main_axis_border_and_padding, + NGSizeIndefinite}); space_builder.SetIsFixedSizeInline(true); scoped_refptr<NGConstraintSpace> child_space = space_builder.ToConstraintSpace(
diff --git a/third_party/blink/renderer/core/layout/ng/ng_layout_input_node.h b/third_party/blink/renderer/core/layout/ng/ng_layout_input_node.h index 896a4bd..c19c680 100644 --- a/third_party/blink/renderer/core/layout/ng/ng_layout_input_node.h +++ b/third_party/blink/renderer/core/layout/ng/ng_layout_input_node.h
@@ -72,6 +72,7 @@ // Performs layout on this input node, will return the layout result. scoped_refptr<NGLayoutResult> Layout(const NGConstraintSpace&, NGBreakToken*); + // Returns border box. MinMaxSize ComputeMinMaxSize(WritingMode, const MinMaxSizeInput&, const NGConstraintSpace* = nullptr);
diff --git a/third_party/blink/renderer/devtools/front_end/audits2/lighthouse/renderer/category-renderer.js b/third_party/blink/renderer/devtools/front_end/audits2/lighthouse/renderer/category-renderer.js index 1646e6ee..4daf750 100644 --- a/third_party/blink/renderer/devtools/front_end/audits2/lighthouse/renderer/category-renderer.js +++ b/third_party/blink/renderer/devtools/front_end/audits2/lighthouse/renderer/category-renderer.js
@@ -9,9 +9,6 @@ /** @typedef {import('./dom.js')} DOM */ /** @typedef {import('./report-renderer.js')} ReportRenderer */ -/** @typedef {import('./report-renderer.js').AuditJSON} AuditJSON */ -/** @typedef {import('./report-renderer.js').CategoryJSON} CategoryJSON */ -/** @typedef {import('./report-renderer.js').GroupJSON} GroupJSON */ /** @typedef {import('./details-renderer.js')} DetailsRenderer */ /** @typedef {import('./util.js')} Util */ @@ -32,7 +29,7 @@ } /** - * @param {AuditJSON} audit + * @param {LH.ReportResult.AuditRef} audit * @param {number} index * @return {Element} */ @@ -43,7 +40,7 @@ /** * Populate an DOM tree with audit details. Used by renderAudit and renderOpportunity - * @param {AuditJSON} audit + * @param {LH.ReportResult.AuditRef} audit * @param {number} index * @param {DocumentFragment} tmpl * @return {Element} @@ -105,7 +102,7 @@ } /** - * @return {!HTMLElement} + * @return {HTMLElement} */ _createChevron() { const chevronTmpl = this.dom.cloneTemplate('#tmpl-lh-chevron', this.templateContext); @@ -114,7 +111,7 @@ } /** - * @param {!Element} element DOM node to populate with values. + * @param {Element} element DOM node to populate with values. * @param {number|null} score * @param {string} scoreDisplayMode * @return {Element} @@ -126,7 +123,7 @@ } /** - * @param {CategoryJSON} category + * @param {LH.ReportResult.Category} category * @return {Element} */ renderCategoryHeader(category) { @@ -149,7 +146,7 @@ /** * Renders the group container for a group of audits. Individual audit elements can be added * directly to the returned element. - * @param {GroupJSON} group + * @param {LH.Result.ReportGroup} group * @param {{expandable: boolean, itemCount?: number}} opts * @return {Element} */ @@ -235,8 +232,8 @@ } /** - * @param {Array<AuditJSON>} manualAudits - * @param {string} manualDescription + * @param {Array<LH.ReportResult.AuditRef>} manualAudits + * @param {string} [manualDescription] * @return {Element} */ _renderManualAudits(manualAudits, manualDescription) { @@ -259,7 +256,7 @@ } /** - * @param {CategoryJSON} category + * @param {LH.ReportResult.Category} category * @return {DocumentFragment} */ renderScoreGauge(category) { @@ -293,8 +290,8 @@ } /** - * @param {CategoryJSON} category - * @param {Object<string, GroupJSON>} groupDefinitions + * @param {LH.ReportResult.Category} category + * @param {Object<string, LH.Result.ReportGroup>} [groupDefinitions] * @return {Element} */ render(category, groupDefinitions) { @@ -306,7 +303,7 @@ const manualAudits = auditRefs.filter(audit => audit.result.scoreDisplayMode === 'manual'); const nonManualAudits = auditRefs.filter(audit => !manualAudits.includes(audit)); - /** @type {Object<string, {passed: Array<AuditJSON>, failed: Array<AuditJSON>, notApplicable: Array<AuditJSON>}>} */ + /** @type {Object<string, {passed: Array<LH.ReportResult.AuditRef>, failed: Array<LH.ReportResult.AuditRef>, notApplicable: Array<LH.ReportResult.AuditRef>}>} */ const auditsGroupedByGroup = {}; const auditsUngrouped = {passed: [], failed: [], notApplicable: []}; @@ -339,14 +336,14 @@ const passedElements = /** @type {Array<Element>} */ ([]); const notApplicableElements = /** @type {Array<Element>} */ ([]); - auditsUngrouped.failed.forEach((/** @type {AuditJSON} */ audit, i) => - failedElements.push(this.renderAudit(audit, i))); - auditsUngrouped.passed.forEach((/** @type {AuditJSON} */ audit, i) => - passedElements.push(this.renderAudit(audit, i))); - auditsUngrouped.notApplicable.forEach((/** @type {AuditJSON} */ audit, i) => - notApplicableElements.push(this.renderAudit(audit, i))); + auditsUngrouped.failed.forEach((audit, i) => failedElements.push(this.renderAudit(audit, i))); + auditsUngrouped.passed.forEach((audit, i) => passedElements.push(this.renderAudit(audit, i))); + auditsUngrouped.notApplicable.forEach((audit, i) => notApplicableElements.push( + this.renderAudit(audit, i))); Object.keys(auditsGroupedByGroup).forEach(groupId => { + if (!groupDefinitions) return; // We never reach here if there aren't groups, but TSC needs convincing + const group = groupDefinitions[groupId]; const groups = auditsGroupedByGroup[groupId];
diff --git a/third_party/blink/renderer/devtools/front_end/audits2/lighthouse/renderer/details-renderer.js b/third_party/blink/renderer/devtools/front_end/audits2/lighthouse/renderer/details-renderer.js index b908209..9f48a3e 100644 --- a/third_party/blink/renderer/devtools/front_end/audits2/lighthouse/renderer/details-renderer.js +++ b/third_party/blink/renderer/devtools/front_end/audits2/lighthouse/renderer/details-renderer.js
@@ -9,6 +9,10 @@ /** @typedef {import('./dom.js')} DOM */ /** @typedef {import('./crc-details-renderer.js')} CRCDetailsJSON */ +/** @typedef {LH.Result.Audit.OpportunityDetails} OpportunityDetails */ + +/** @type {Array<string>} */ +const URL_PREFIXES = ['http://', 'https://', 'data:']; class DetailsRenderer { /** @@ -29,7 +33,7 @@ } /** - * @param {DetailsJSON} details + * @param {DetailsJSON|OpportunityDetails} details * @return {Element} */ render(details) { @@ -55,13 +59,16 @@ // @ts-ignore - TODO(bckenny): Fix type hierarchy return this._renderTable(/** @type {TableDetailsJSON} */ (details)); case 'code': - return this._renderCode(details); + return this._renderCode(/** @type {DetailsJSON} */ (details)); case 'node': return this.renderNode(/** @type {NodeDetailsJSON} */(details)); case 'criticalrequestchain': return CriticalRequestChainRenderer.render(this._dom, this._templateContext, // @ts-ignore - TODO(bckenny): Fix type hierarchy /** @type {CRCDetailsJSON} */ (details)); + case 'opportunity': + // @ts-ignore - TODO(bckenny): Fix type hierarchy + return this._renderOpportunityTable(details); default: { throw new Error(`Unknown type: ${details.type}`); } @@ -69,17 +76,17 @@ } /** - * @param {NumericUnitDetailsJSON} details + * @param {{value: number, granularity?: number}} details * @return {Element} */ _renderBytes(details) { // TODO: handle displayUnit once we have something other than 'kb' const value = Util.formatBytesToKB(details.value, details.granularity); - return this._renderText({type: 'text', value}); + return this._renderText({value}); } /** - * @param {NumericUnitDetailsJSON} details + * @param {{value: number, granularity?: number, displayUnit?: string}} details * @return {Element} */ _renderMilliseconds(details) { @@ -88,11 +95,11 @@ value = Util.formatDuration(details.value); } - return this._renderText({type: 'text', value}); + return this._renderText({value}); } /** - * @param {StringDetailsJSON} text + * @param {{value: string}} text * @return {HTMLElement} */ _renderTextURL(text) { @@ -107,7 +114,7 @@ displayedHost = parsed.file === '/' ? '' : `(${parsed.hostname})`; title = url; } catch (/** @type {!Error} */ e) { - if (!(e instanceof TypeError)) { + if (!e.name.startsWith('TypeError')) { throw e; } displayedPath = url; @@ -116,13 +123,11 @@ const element = /** @type {HTMLElement} */ (this._dom.createElement('div', 'lh-text__url')); element.appendChild(this._renderText({ value: displayedPath, - type: 'text', })); if (displayedHost) { const hostElem = this._renderText({ value: displayedHost, - type: 'text', }); hostElem.classList.add('lh-text__url-host'); element.appendChild(hostElem); @@ -142,7 +147,6 @@ if (!allowedProtocols.includes(url.protocol)) { // Fall back to just the link text if protocol not allowed. return this._renderText({ - type: 'text', value: details.text, }); } @@ -157,7 +161,7 @@ } /** - * @param {StringDetailsJSON} text + * @param {{value: string}} text * @return {Element} */ _renderText(text) { @@ -169,13 +173,11 @@ /** * Create small thumbnail with scaled down image asset. * If the supplied details doesn't have an image/* mimeType, then an empty span is returned. - * @param {ThumbnailDetails} details + * @param {{value: string}} details * @return {Element} */ _renderThumbnail(details) { const element = /** @type {HTMLImageElement}*/ (this._dom.createElement('img', 'lh-thumbnail')); - /** @type {string} */ - // @ts-ignore - type should have a value if we get here. const strValue = details.value; element.src = strValue; element.title = strValue; @@ -243,6 +245,82 @@ } /** + * TODO(bckenny): migrate remaining table rendering to this function, then rename + * back to _renderTable and replace the original. + * @param {OpportunityDetails} details + * @return {Element} + */ + _renderOpportunityTable(details) { + if (!details.items.length) return this._dom.createElement('span'); + + const tableElem = this._dom.createElement('table', 'lh-table'); + const theadElem = this._dom.createChildOf(tableElem, 'thead'); + const theadTrElem = this._dom.createChildOf(theadElem, 'tr'); + + for (const heading of details.headings) { + const valueType = heading.valueType || 'text'; + const classes = `lh-table-column--${valueType}`; + const labelEl = this._dom.createElement('div', 'lh-text'); + labelEl.textContent = heading.label; + this._dom.createChildOf(theadTrElem, 'th', classes).appendChild(labelEl); + } + + const tbodyElem = this._dom.createChildOf(tableElem, 'tbody'); + for (const row of details.items) { + const rowElem = this._dom.createChildOf(tbodyElem, 'tr'); + for (const heading of details.headings) { + const key = /** @type {keyof LH.Result.Audit.OpportunityDetailsItem} */ (heading.key); + const value = row[key]; + + if (typeof value === 'undefined' || value === null) { + this._dom.createChildOf(rowElem, 'td', 'lh-table-column--empty'); + continue; + } + + const valueType = heading.valueType; + let itemElement; + + // TODO(bckenny): as we add more table types, split out into _renderTableItem fn. + switch (valueType) { + case 'url': { + // Fall back to <pre> rendering if not actually a URL. + const strValue = /** @type {string} */ (value); + if (URL_PREFIXES.some(prefix => strValue.startsWith(prefix))) { + itemElement = this._renderTextURL({value: strValue}); + } else { + const codeValue = /** @type {(number|string|undefined)} */ (value); + itemElement = this._renderCode({value: codeValue}); + } + break; + } + case 'timespanMs': { + const numValue = /** @type {number} */ (value); + itemElement = this._renderMilliseconds({value: numValue}); + break; + } + case 'bytes': { + const numValue = /** @type {number} */ (value); + itemElement = this._renderBytes({value: numValue, granularity: 1}); + break; + } + case 'thumbnail': { + const strValue = /** @type {string} */ (value); + itemElement = this._renderThumbnail({value: strValue}); + break; + } + default: { + throw new Error(`Unknown valueType: ${valueType}`); + } + } + + const classes = `lh-table-column--${valueType}`; + this._dom.createChildOf(rowElem, 'td', classes).appendChild(itemElement); + } + } + return tableElem; + } + + /** * @param {NodeDetailsJSON} item * @return {Element} * @protected @@ -279,7 +357,7 @@ } /** - * @param {DetailsJSON} details + * @param {{value?: string|number}} details * @return {Element} */ _renderCode(details) { @@ -300,7 +378,6 @@ * @typedef {{ type: string, value: (string|number|undefined), - summary?: OpportunitySummary, granularity?: number, displayUnit?: string }} DetailsJSON @@ -352,7 +429,7 @@ /** @typedef {{ type: string, - value?: string, + value: string, }} ThumbnailDetails */ @@ -369,10 +446,3 @@ items: Array<{timing: number, timestamp: number, data: string}>, }} FilmstripDetails */ - - -/** @typedef {{ - wastedMs?: number, - wastedBytes?: number - }} OpportunitySummary - */
diff --git a/third_party/blink/renderer/devtools/front_end/audits2/lighthouse/renderer/performance-category-renderer.js b/third_party/blink/renderer/devtools/front_end/audits2/lighthouse/renderer/performance-category-renderer.js index 71b1667..3b25314 100644 --- a/third_party/blink/renderer/devtools/front_end/audits2/lighthouse/renderer/performance-category-renderer.js +++ b/third_party/blink/renderer/devtools/front_end/audits2/lighthouse/renderer/performance-category-renderer.js
@@ -8,15 +8,12 @@ /* globals self, Util, CategoryRenderer */ /** @typedef {import('./dom.js')} DOM */ -/** @typedef {import('./report-renderer.js').CategoryJSON} CategoryJSON */ -/** @typedef {import('./report-renderer.js').GroupJSON} GroupJSON */ -/** @typedef {import('./report-renderer.js').AuditJSON} AuditJSON */ -/** @typedef {import('./details-renderer.js').OpportunitySummary} OpportunitySummary */ /** @typedef {import('./details-renderer.js').FilmstripDetails} FilmstripDetails */ +/** @typedef {LH.Result.Audit.OpportunityDetails} OpportunityDetails */ class PerformanceCategoryRenderer extends CategoryRenderer { /** - * @param {AuditJSON} audit + * @param {LH.ReportResult.AuditRef} audit * @return {Element} */ _renderMetric(audit) { @@ -46,7 +43,7 @@ } /** - * @param {AuditJSON} audit + * @param {LH.ReportResult.AuditRef} audit * @param {number} index * @param {number} scale * @return {Element} @@ -56,20 +53,20 @@ const element = this.populateAuditValues(audit, index, oppTmpl); element.id = audit.result.id; - const details = audit.result.details; - if (!details) { + if (!audit.result.details || audit.result.scoreDisplayMode === 'error') { return element; } - const summaryInfo = /** @type {OpportunitySummary} */ (details.summary); - if (!summaryInfo || !summaryInfo.wastedMs || audit.result.scoreDisplayMode === 'error') { + // TODO(bckenny): remove cast when details is fully discriminated based on `type`. + const details = /** @type {OpportunityDetails} */ (audit.result.details); + if (details.type !== 'opportunity') { return element; } // Overwrite the displayValue with opportunity's wastedMs const displayEl = this.dom.find('.lh-audit__display-text', element); - const sparklineWidthPct = `${summaryInfo.wastedMs / scale * 100}%`; + const sparklineWidthPct = `${details.overallSavingsMs / scale * 100}%`; this.dom.find('.lh-sparkline__bar', element).style.width = sparklineWidthPct; - displayEl.textContent = Util.formatSeconds(summaryInfo.wastedMs, 0.01); + displayEl.textContent = Util.formatSeconds(details.overallSavingsMs, 0.01); // Set [title] tooltips if (audit.result.displayValue) { @@ -83,26 +80,27 @@ /** * Get an audit's wastedMs to sort the opportunity by, and scale the sparkline width - * Opportunties with an error won't have a summary object, so MIN_VALUE is returned to keep any + * Opportunties with an error won't have a details object, so MIN_VALUE is returned to keep any * erroring opportunities last in sort order. - * @param {AuditJSON} audit + * @param {LH.ReportResult.AuditRef} audit * @return {number} */ _getWastedMs(audit) { - if ( - audit.result.details && - audit.result.details.summary && - typeof audit.result.details.summary.wastedMs === 'number' - ) { - return audit.result.details.summary.wastedMs; + if (audit.result.details && audit.result.details.type === 'opportunity') { + // TODO(bckenny): remove cast when details is fully discriminated based on `type`. + const details = /** @type {OpportunityDetails} */ (audit.result.details); + if (typeof details.overallSavingsMs !== 'number') { + throw new Error('non-opportunity details passed to _getWastedMs'); + } + return details.overallSavingsMs; } else { return Number.MIN_VALUE; } } /** - * @param {CategoryJSON} category - * @param {Object<string, GroupJSON>} groups + * @param {LH.ReportResult.Category} category + * @param {Object<string, LH.Result.ReportGroup>} groups * @return {Element} * @override */
diff --git a/third_party/blink/renderer/devtools/front_end/audits2/lighthouse/renderer/report-renderer.js b/third_party/blink/renderer/devtools/front_end/audits2/lighthouse/renderer/report-renderer.js index 011dfe8..de990d2 100644 --- a/third_party/blink/renderer/devtools/front_end/audits2/lighthouse/renderer/report-renderer.js +++ b/third_party/blink/renderer/devtools/front_end/audits2/lighthouse/renderer/report-renderer.js
@@ -29,12 +29,12 @@ } /** - * @param {ReportJSON} report + * @param {LH.ReportResult} report * @param {Element} container Parent element to render the report into. */ renderReport(report, container) { // If any mutations happen to the report within the renderers, we want the original object untouched - const clone = /** @type {ReportJSON} */ (JSON.parse(JSON.stringify(report))); + const clone = /** @type {LH.ReportResult} */ (JSON.parse(JSON.stringify(report))); // TODO(phulce): we all agree this is technical debt we should fix if (typeof clone.categories !== 'object') throw new Error('No categories provided.'); @@ -56,7 +56,7 @@ } /** - * @param {ReportJSON} report + * @param {LH.ReportResult} report * @return {DocumentFragment} */ _renderReportHeader(report) { @@ -90,7 +90,7 @@ /** - * @param {ReportJSON} report + * @param {LH.ReportResult} report * @return {DocumentFragment} */ _renderReportFooter(report) { @@ -117,7 +117,7 @@ /** * Returns a div with a list of top-level warnings, or an empty div if no warnings. - * @param {ReportJSON} report + * @param {LH.ReportResult} report * @return {Node} */ _renderReportWarnings(report) { @@ -136,7 +136,7 @@ } /** - * @param {ReportJSON} report + * @param {LH.ReportResult} report * @return {DocumentFragment} */ _renderReport(report) { @@ -203,7 +203,7 @@ /** * Place the AuditResult into the auditDfn (which has just weight & group) * @param {Object<string, LH.Audit.Result>} audits - * @param {Array<CategoryJSON>} reportCategories + * @param {Array<LH.ReportResult.Category>} reportCategories */ static smooshAuditResultsIntoCategories(audits, reportCategories) { for (const category of reportCategories) { @@ -220,49 +220,3 @@ } else { self.ReportRenderer = ReportRenderer; } - -/** - * @typedef {{ - id: string, - score: (number|null), - weight: number, - group?: string, - result: LH.Audit.Result - }} AuditJSON - */ - -/** - * @typedef {{ - title: string, - id: string, - score: (number|null), - description?: string, - manualDescription: string, - auditRefs: Array<AuditJSON> - }} CategoryJSON - */ - -/** - * @typedef {{ - title: string, - description?: string, - }} GroupJSON - */ - -/** - * @typedef {{ - lighthouseVersion: string, - userAgent: string, - fetchTime: string, - timing: {total: number}, - requestedUrl: string, - finalUrl: string, - runWarnings?: Array<string>, - artifacts: {traces: {defaultPass: {traceEvents: Array}}}, - audits: Object<string, LH.Audit.Result>, - categories: Object<string, CategoryJSON>, - reportCategories: Array<CategoryJSON>, - categoryGroups: Object<string, GroupJSON>, - configSettings: LH.Config.Settings, - }} ReportJSON - */
diff --git a/third_party/blink/renderer/devtools/front_end/audits2/lighthouse/report-styles.css b/third_party/blink/renderer/devtools/front_end/audits2/lighthouse/report-styles.css index 4081296d1..d175f729 100644 --- a/third_party/blink/renderer/devtools/front_end/audits2/lighthouse/report-styles.css +++ b/third_party/blink/renderer/devtools/front_end/audits2/lighthouse/report-styles.css
@@ -127,10 +127,10 @@ scroll-behavior: smooth; } -.lh-root [data-keyboard-focus="true"]:focus { +.lh-root :focus { outline: -webkit-focus-ring-color auto 3px; } -.lh-root summary[data-keyboard-focus="true"]:focus { +.lh-root summary:focus { outline: 1px solid hsl(217, 89%, 61%); } @@ -864,6 +864,7 @@ .lh-table-column--text, .lh-table-column--bytes, +.lh-table-column--timespanMs, .lh-table-column--ms { text-align: right; }
diff --git a/third_party/blink/renderer/devtools/front_end/audits2_worker/lighthouse/lighthouse-background.js b/third_party/blink/renderer/devtools/front_end/audits2_worker/lighthouse/lighthouse-background.js index 21f7dcab..8f2d45c 100644 --- a/third_party/blink/renderer/devtools/front_end/audits2_worker/lighthouse/lighthouse-background.js +++ b/third_party/blink/renderer/devtools/front_end/audits2_worker/lighthouse/lighthouse-background.js
@@ -1,4 +1,4 @@ -// lighthouse, browserified. 3.0.0-beta.0 (a194e972771bcf85071a796142d50d5bfbe3cc7b) +// lighthouse, browserified. 3.0.3 (d1cae24fda4182406e02d3f4df6309d48878fc50) require=function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a;}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r);},p,p.exports,r,e,n,t);}return n[i].exports;}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o;}return r;}()({"../audits/accessibility/accesskeys":[function(require,module,exports){ @@ -20,10 +20,10 @@ static get meta(){ return{ -name:'accesskeys', -description:'`[accesskey]` values are unique', -failureDescription:'`[accesskey]` values are not unique', -helpText:'Access keys let users quickly focus a part of the page. For proper '+ +id:'accesskeys', +title:'`[accesskey]` values are unique', +failureTitle:'`[accesskey]` values are not unique', +description:'Access keys let users quickly focus a part of the page. For proper '+ 'navigation, each access key must be unique. '+ '[Learn more](https://dequeuniversity.com/rules/axe/2.2/accesskeys?application=lighthouse).', requiredArtifacts:['Accessibility']}; @@ -54,10 +54,10 @@ static get meta(){ return{ -name:'aria-allowed-attr', -description:'`[aria-*]` attributes match their roles', -failureDescription:'`[aria-*]` attributes do not match their roles', -helpText:'Each ARIA `role` supports a specific subset of `aria-*` attributes. '+ +id:'aria-allowed-attr', +title:'`[aria-*]` attributes match their roles', +failureTitle:'`[aria-*]` attributes do not match their roles', +description:'Each ARIA `role` supports a specific subset of `aria-*` attributes. '+ 'Mismatching these invalidates the `aria-*` attributes. [Learn '+ 'more](https://dequeuniversity.com/rules/axe/2.2/aria-allowed-attr?application=lighthouse).', requiredArtifacts:['Accessibility']}; @@ -88,10 +88,10 @@ static get meta(){ return{ -name:'aria-required-attr', -description:'`[role]`s have all required `[aria-*]` attributes', -failureDescription:'`[role]`s do not have all required `[aria-*]` attributes', -helpText:'Some ARIA roles have required attributes that describe the state '+ +id:'aria-required-attr', +title:'`[role]`s have all required `[aria-*]` attributes', +failureTitle:'`[role]`s do not have all required `[aria-*]` attributes', +description:'Some ARIA roles have required attributes that describe the state '+ 'of the element to screen readers. [Learn more](https://dequeuniversity.com/rules/axe/2.2/aria-required-attr?application=lighthouse).', requiredArtifacts:['Accessibility']}; @@ -122,11 +122,11 @@ static get meta(){ return{ -name:'aria-required-children', -description:'Elements with `[role]` that require specific children `[role]`s, are present', -failureDescription:'Elements with `[role]` that require specific children `[role]`s, '+ +id:'aria-required-children', +title:'Elements with `[role]` that require specific children `[role]`s, are present', +failureTitle:'Elements with `[role]` that require specific children `[role]`s, '+ 'are missing.', -helpText:'Some ARIA parent roles must contain specific child roles to perform '+ +description:'Some ARIA parent roles must contain specific child roles to perform '+ 'their intended accessibility functions. '+ '[Learn more](https://dequeuniversity.com/rules/axe/2.2/aria-required-children?application=lighthouse).', requiredArtifacts:['Accessibility']}; @@ -158,10 +158,10 @@ static get meta(){ return{ -name:'aria-required-parent', -description:'`[role]`s are contained by their required parent element', -failureDescription:'`[role]`s are not contained by their required parent element', -helpText:'Some ARIA child roles must be contained by specific parent roles to '+ +id:'aria-required-parent', +title:'`[role]`s are contained by their required parent element', +failureTitle:'`[role]`s are not contained by their required parent element', +description:'Some ARIA child roles must be contained by specific parent roles to '+ 'properly perform their intended accessibility functions. '+ '[Learn more](https://dequeuniversity.com/rules/axe/2.2/aria-required-parent?application=lighthouse).', requiredArtifacts:['Accessibility']}; @@ -192,10 +192,10 @@ static get meta(){ return{ -name:'aria-roles', -description:'`[role]` values are valid', -failureDescription:'`[role]` values are not valid', -helpText:'ARIA roles must have valid values in order to perform their '+ +id:'aria-roles', +title:'`[role]` values are valid', +failureTitle:'`[role]` values are not valid', +description:'ARIA roles must have valid values in order to perform their '+ 'intended accessibility functions. '+ '[Learn more](https://dequeuniversity.com/rules/axe/2.2/aria-roles?application=lighthouse).', requiredArtifacts:['Accessibility']}; @@ -226,10 +226,10 @@ static get meta(){ return{ -name:'aria-valid-attr-value', -description:'`[aria-*]` attributes have valid values', -failureDescription:'`[aria-*]` attributes do not have valid values', -helpText:'Assistive technologies, like screen readers, can\'t interpret ARIA '+ +id:'aria-valid-attr-value', +title:'`[aria-*]` attributes have valid values', +failureTitle:'`[aria-*]` attributes do not have valid values', +description:'Assistive technologies, like screen readers, can\'t interpret ARIA '+ 'attributes with invalid values. [Learn '+ 'more](https://dequeuniversity.com/rules/axe/2.2/aria-valid-attr-value?application=lighthouse).', requiredArtifacts:['Accessibility']}; @@ -260,10 +260,10 @@ static get meta(){ return{ -name:'aria-valid-attr', -description:'`[aria-*]` attributes are valid and not misspelled', -failureDescription:'`[aria-*]` attributes are not valid or misspelled', -helpText:'Assistive technologies, like screen readers, can\'t interpret ARIA '+ +id:'aria-valid-attr', +title:'`[aria-*]` attributes are valid and not misspelled', +failureTitle:'`[aria-*]` attributes are not valid or misspelled', +description:'Assistive technologies, like screen readers, can\'t interpret ARIA '+ 'attributes with invalid names. [Learn '+ 'more](https://dequeuniversity.com/rules/axe/2.2/aria-valid-attr?application=lighthouse).', requiredArtifacts:['Accessibility']}; @@ -294,11 +294,11 @@ static get meta(){ return{ -name:'audio-caption', -description:'`<audio>` elements contain a `<track>` element with `[kind="captions"]`', -failureDescription:'`<audio>` elements are missing a `<track>` element with '+ +id:'audio-caption', +title:'`<audio>` elements contain a `<track>` element with `[kind="captions"]`', +failureTitle:'`<audio>` elements are missing a `<track>` element with '+ '`[kind="captions"]`.', -helpText:'Captions make audio elements usable for deaf or hearing-impaired users, '+ +description:'Captions make audio elements usable for deaf or hearing-impaired users, '+ 'providing critical information such as who is talking, what they\'re saying, '+ 'and other non-speech information. '+ '[Learn more](https://dequeuniversity.com/rules/axe/2.2/audio-caption?application=lighthouse).', @@ -330,11 +330,11 @@ static get meta(){ return{ -name:'button-name', -description:'Buttons have an accessible name', -failureDescription:'Buttons do not have an accessible name', -helpText:'When a button doesn\'t have an accessible name, screen readers announce it as '+ -'"button", making it unusable for users who rely on screen readers. '+ +id:'button-name', +title:'Buttons have an accessible name', +failureTitle:'Buttons do not have an accessible name', +description:'When a button doesn\'t have an accessible name, screen readers announce it '+ +'as "button", making it unusable for users who rely on screen readers. '+ '[Learn more](https://dequeuniversity.com/rules/axe/2.2/button-name?application=lighthouse).', requiredArtifacts:['Accessibility']}; @@ -365,11 +365,11 @@ static get meta(){ return{ -name:'bypass', -description:'The page contains a heading, skip link, or landmark region', -failureDescription:'The page does not contain a heading, skip link, or landmark region', -helpText:'Adding ways to bypass repetitive content lets keyboard users navigate the page '+ -'more efficiently. '+ +id:'bypass', +title:'The page contains a heading, skip link, or landmark region', +failureTitle:'The page does not contain a heading, skip link, or landmark region', +description:'Adding ways to bypass repetitive content lets keyboard users navigate the '+ +'page more efficiently. '+ '[Learn more](https://dequeuniversity.com/rules/axe/2.2/bypass?application=lighthouse).', requiredArtifacts:['Accessibility']}; @@ -400,11 +400,11 @@ static get meta(){ return{ -name:'color-contrast', -description:'Background and foreground colors have a sufficient contrast ratio', -failureDescription:'Background and foreground colors do not have a '+ +id:'color-contrast', +title:'Background and foreground colors have a sufficient contrast ratio', +failureTitle:'Background and foreground colors do not have a '+ 'sufficient contrast ratio.', -helpText:'Low-contrast text is difficult or impossible for many users to read. '+ +description:'Low-contrast text is difficult or impossible for many users to read. '+ '[Learn more](https://dequeuniversity.com/rules/axe/2.2/color-contrast?application=lighthouse).', requiredArtifacts:['Accessibility']}; @@ -434,12 +434,12 @@ static get meta(){ return{ -name:'definition-list', -description:'`<dl>`\'s contain only properly-ordered `<dt>` and `<dd>` groups, `<script>` '+ +id:'definition-list', +title:'`<dl>`\'s contain only properly-ordered `<dt>` and `<dd>` groups, `<script>` '+ 'or `<template>` elements.', -failureDescription:'`<dl>`\'s do not contain only properly-ordered `<dt>` and `<dd>` '+ +failureTitle:'`<dl>`\'s do not contain only properly-ordered `<dt>` and `<dd>` '+ 'groups, `<script>` or `<template>` elements.', -helpText:'When definition lists are not properly marked up, screen readers may produce '+ +description:'When definition lists are not properly marked up, screen readers may produce '+ 'confusing or inaccurate output. '+ '[Learn more](https://dequeuniversity.com/rules/axe/2.2/definition-list?application=lighthouse).', requiredArtifacts:['Accessibility']}; @@ -470,10 +470,10 @@ static get meta(){ return{ -name:'dlitem', -description:'Definition list items are wrapped in `<dl>` elements', -failureDescription:'Definition list items are not wrapped in `<dl>` elements', -helpText:'Definition list items (`<dt>` and `<dd>`) must be wrapped in a '+ +id:'dlitem', +title:'Definition list items are wrapped in `<dl>` elements', +failureTitle:'Definition list items are not wrapped in `<dl>` elements', +description:'Definition list items (`<dt>` and `<dd>`) must be wrapped in a '+ 'parent `<dl>` element to ensure that screen readers can properly announce them. '+ '[Learn more](https://dequeuniversity.com/rules/axe/2.2/dlitem?application=lighthouse).', requiredArtifacts:['Accessibility']}; @@ -504,10 +504,10 @@ static get meta(){ return{ -name:'document-title', -description:'Document has a `<title>` element', -failureDescription:'Document doesn\'t have a `<title>` element', -helpText:'The title gives screen reader users an overview of the page, and search '+ +id:'document-title', +title:'Document has a `<title>` element', +failureTitle:'Document doesn\'t have a `<title>` element', +description:'The title gives screen reader users an overview of the page, and search '+ 'engine users rely on it heavily to determine if a page is relevant to their search. '+ '[Learn more](https://developers.google.com/web/tools/lighthouse/audits/title).', requiredArtifacts:['Accessibility']}; @@ -538,10 +538,10 @@ static get meta(){ return{ -name:'duplicate-id', -description:'`[id]` attributes on the page are unique', -failureDescription:'`[id]` attributes on the page are not unique', -helpText:'The value of an id attribute must be unique to prevent '+ +id:'duplicate-id', +title:'`[id]` attributes on the page are unique', +failureTitle:'`[id]` attributes on the page are not unique', +description:'The value of an id attribute must be unique to prevent '+ 'other instances from being overlooked by assistive technologies. '+ '[Learn more](https://dequeuniversity.com/rules/axe/2.2/duplicate-id?application=lighthouse).', requiredArtifacts:['Accessibility']}; @@ -572,10 +572,10 @@ static get meta(){ return{ -name:'frame-title', -description:'`<frame>` or `<iframe>` elements have a title', -failureDescription:'`<frame>` or `<iframe>` elements do not have a title', -helpText:'Screen reader users rely on frame titles to describe the contents of frames. '+ +id:'frame-title', +title:'`<frame>` or `<iframe>` elements have a title', +failureTitle:'`<frame>` or `<iframe>` elements do not have a title', +description:'Screen reader users rely on frame titles to describe the contents of frames. '+ '[Learn more](https://dequeuniversity.com/rules/axe/2.2/frame-title?application=lighthouse).', requiredArtifacts:['Accessibility']}; @@ -605,10 +605,10 @@ static get meta(){ return{ -name:'html-has-lang', -description:'`<html>` element has a `[lang]` attribute', -failureDescription:'`<html>` element does not have a `[lang]` attribute', -helpText:'If a page doesn\'t specify a lang attribute, a screen reader assumes '+ +id:'html-has-lang', +title:'`<html>` element has a `[lang]` attribute', +failureTitle:'`<html>` element does not have a `[lang]` attribute', +description:'If a page doesn\'t specify a lang attribute, a screen reader assumes '+ 'that the page is in the default language that the user chose when setting up the '+ 'screen reader. If the page isn\'t actually in the default language, then the screen '+ 'reader might not announce the page\'s text correctly. '+ @@ -641,11 +641,11 @@ static get meta(){ return{ -name:'html-lang-valid', -description:'`<html>` element has a valid value for its `[lang]` attribute', -failureDescription:'`<html>` element does not have a valid value for '+ +id:'html-lang-valid', +title:'`<html>` element has a valid value for its `[lang]` attribute', +failureTitle:'`<html>` element does not have a valid value for '+ 'its `[lang]` attribute.', -helpText:'Specifying a valid [BCP 47 language](https://www.w3.org/International/questions/qa-choosing-language-tags#question) '+ +description:'Specifying a valid [BCP 47 language](https://www.w3.org/International/questions/qa-choosing-language-tags#question) '+ 'helps screen readers announce text properly. '+ '[Learn more](https://dequeuniversity.com/rules/axe/2.2/valid-lang?application=lighthouse).', requiredArtifacts:['Accessibility']}; @@ -676,10 +676,10 @@ static get meta(){ return{ -name:'image-alt', -description:'Image elements have `[alt]` attributes', -failureDescription:'Image elements do not have `[alt]` attributes', -helpText:'Informative elements should aim for short, descriptive alternate text. '+ +id:'image-alt', +title:'Image elements have `[alt]` attributes', +failureTitle:'Image elements do not have `[alt]` attributes', +description:'Informative elements should aim for short, descriptive alternate text. '+ 'Decorative elements can be ignored with an empty alt attribute. '+ '[Learn more](https://dequeuniversity.com/rules/axe/2.2/image-alt?application=lighthouse).', requiredArtifacts:['Accessibility']}; @@ -710,11 +710,11 @@ static get meta(){ return{ -name:'input-image-alt', -description:'`<input type="image">` elements have `[alt]` text', -failureDescription:'`<input type="image">` elements do not have `[alt]` text', -helpText:'When an image is being used as an `<input>` button, providing alternative text '+ -'can help screen reader users understand the purpose of the button. '+ +id:'input-image-alt', +title:'`<input type="image">` elements have `[alt]` text', +failureTitle:'`<input type="image">` elements do not have `[alt]` text', +description:'When an image is being used as an `<input>` button, providing alternative '+ +'text can help screen reader users understand the purpose of the button. '+ '[Learn more](https://dequeuniversity.com/rules/axe/2.2/input-image-alt?application=lighthouse).', requiredArtifacts:['Accessibility']}; @@ -744,10 +744,10 @@ static get meta(){ return{ -name:'label', -description:'Form elements have associated labels', -failureDescription:'Form elements do not have associated labels', -helpText:'Labels ensure that form controls are announced properly by assistive '+ +id:'label', +title:'Form elements have associated labels', +failureTitle:'Form elements do not have associated labels', +description:'Labels ensure that form controls are announced properly by assistive '+ 'technologies, like screen readers. [Learn '+ 'more](https://dequeuniversity.com/rules/axe/2.2/label?application=lighthouse).', requiredArtifacts:['Accessibility']}; @@ -779,12 +779,12 @@ static get meta(){ return{ -name:'layout-table', -description:'Presentational `<table>` elements avoid using `<th>`, `<caption>` or the '+ +id:'layout-table', +title:'Presentational `<table>` elements avoid using `<th>`, `<caption>` or the '+ '`[summary]` attribute.', -failureDescription:'Presentational `<table>` elements do not avoid using `<th>`, '+ +failureTitle:'Presentational `<table>` elements do not avoid using `<th>`, '+ '`<caption>` or the `[summary]` attribute.', -helpText:'A table being used for layout purposes should not include data elements, '+ +description:'A table being used for layout purposes should not include data elements, '+ 'such as the th or caption elements or the summary attribute, because this can '+ 'create a confusing experience for screen reader users. '+ '[Learn more](https://dequeuniversity.com/rules/axe/2.2/layout-table?application=lighthouse).', @@ -816,10 +816,10 @@ static get meta(){ return{ -name:'link-name', -description:'Links have a discernible name', -failureDescription:'Links do not have a discernible name', -helpText:'Link text (and alternate text for images, when used as links) that is '+ +id:'link-name', +title:'Links have a discernible name', +failureTitle:'Links do not have a discernible name', +description:'Link text (and alternate text for images, when used as links) that is '+ 'discernible, unique, and focusable improves the navigation experience for '+ 'screen reader users. '+ '[Learn more](https://dequeuniversity.com/rules/axe/2.2/link-name?application=lighthouse).', @@ -851,11 +851,11 @@ static get meta(){ return{ -name:'listitem', -description:'List items (`<li>`) are contained within `<ul>` or `<ol>` parent elements', -failureDescription:'List items (`<li>`) are not contained within `<ul>` '+ +id:'listitem', +title:'List items (`<li>`) are contained within `<ul>` or `<ol>` parent elements', +failureTitle:'List items (`<li>`) are not contained within `<ul>` '+ 'or `<ol>` parent elements.', -helpText:'Screen readers require list items (`<li>`) to be contained within a '+ +description:'Screen readers require list items (`<li>`) to be contained within a '+ 'parent `<ul>` or `<ol>` to be announced properly. '+ '[Learn more](https://dequeuniversity.com/rules/axe/2.2/listitem?application=lighthouse).', requiredArtifacts:['Accessibility']}; @@ -886,12 +886,12 @@ static get meta(){ return{ -name:'list', -description:'Lists contain only `<li>` elements and script supporting elements '+ +id:'list', +title:'Lists contain only `<li>` elements and script supporting elements '+ '(`<script>` and `<template>`).', -failureDescription:'Lists do not contain only `<li>` elements and script '+ +failureTitle:'Lists do not contain only `<li>` elements and script '+ 'supporting elements (`<script>` and `<template>`).', -helpText:'Screen readers have a specific way of announcing lists. Ensuring proper list '+ +description:'Screen readers have a specific way of announcing lists. Ensuring proper list '+ 'structure aids screen reader output. '+ '[Learn more](https://dequeuniversity.com/rules/axe/2.2/list?application=lighthouse).', requiredArtifacts:['Accessibility']}; @@ -922,9 +922,9 @@ static get meta(){ return Object.assign({ -name:'custom-controls-labels', -helpText:'Custom interactive controls have associated labels, provided by aria-label or aria-labelledby. [Learn more](https://developers.google.com/web/fundamentals/accessibility/how-to-review#try_it_with_a_screen_reader).', -description:'Custom controls have associated labels'}, +id:'custom-controls-labels', +description:'Custom interactive controls have associated labels, provided by aria-label or aria-labelledby. [Learn more](https://developers.google.com/web/fundamentals/accessibility/how-to-review#try_it_with_a_screen_reader).', +title:'Custom controls have associated labels'}, super.partialMeta); }} @@ -952,9 +952,9 @@ static get meta(){ return Object.assign({ -name:'custom-controls-roles', -helpText:'Custom interactive controls have appropriate ARIA roles. [Learn more](https://developers.google.com/web/fundamentals/accessibility/how-to-review#try_it_with_a_screen_reader).', -description:'Custom controls have ARIA roles'}, +id:'custom-controls-roles', +description:'Custom interactive controls have appropriate ARIA roles. [Learn more](https://developers.google.com/web/fundamentals/accessibility/how-to-review#try_it_with_a_screen_reader).', +title:'Custom controls have ARIA roles'}, super.partialMeta); }} @@ -982,9 +982,9 @@ static get meta(){ return Object.assign({ -name:'focus-traps', -helpText:'A user can tab into and out of any control or region without accidentally trapping their focus. [Learn more](https://developers.google.com/web/fundamentals/accessibility/how-to-review#start_with_the_keyboard).', -description:'User focus is not accidentally trapped in a region'}, +id:'focus-traps', +description:'A user can tab into and out of any control or region without accidentally trapping their focus. [Learn more](https://developers.google.com/web/fundamentals/accessibility/how-to-review#start_with_the_keyboard).', +title:'User focus is not accidentally trapped in a region'}, super.partialMeta); }} @@ -1012,9 +1012,9 @@ static get meta(){ return Object.assign({ -name:'focusable-controls', -helpText:'Custom interactive controls are keyboard focusable and display a focus indicator. [Learn more](https://developers.google.com/web/fundamentals/accessibility/how-to-review#start_with_the_keyboard).', -description:'Interactive controls are keyboard focusable'}, +id:'focusable-controls', +description:'Custom interactive controls are keyboard focusable and display a focus indicator. [Learn more](https://developers.google.com/web/fundamentals/accessibility/how-to-review#start_with_the_keyboard).', +title:'Interactive controls are keyboard focusable'}, super.partialMeta); }} @@ -1042,9 +1042,9 @@ static get meta(){ return Object.assign({ -name:'heading-levels', -helpText:'Headings are used to create an outline for the page and heading levels are not skipped. [Learn more](https://developers.google.com/web/fundamentals/accessibility/how-to-review#take_advantage_of_headings_and_landmarks).', -description:'Headings don\'t skip levels'}, +id:'heading-levels', +description:'Headings are used to create an outline for the page and heading levels are not skipped. [Learn more](https://developers.google.com/web/fundamentals/accessibility/how-to-review#take_advantage_of_headings_and_landmarks).', +title:'Headings don\'t skip levels'}, super.partialMeta); }} @@ -1072,9 +1072,9 @@ static get meta(){ return Object.assign({ -name:'logical-tab-order', -helpText:'Tabbing through the page follows the visual layout. Users cannot focus elements that are offscreen. [Learn more](https://developers.google.com/web/fundamentals/accessibility/how-to-review#start_with_the_keyboard).', -description:'The page has a logical tab order'}, +id:'logical-tab-order', +description:'Tabbing through the page follows the visual layout. Users cannot focus elements that are offscreen. [Learn more](https://developers.google.com/web/fundamentals/accessibility/how-to-review#start_with_the_keyboard).', +title:'The page has a logical tab order'}, super.partialMeta); }} @@ -1102,9 +1102,9 @@ static get meta(){ return Object.assign({ -name:'managed-focus', -helpText:'If new content, such as a dialog, is added to the page, the user\'s focus is directed to it. [Learn more](https://developers.google.com/web/fundamentals/accessibility/how-to-review#start_with_the_keyboard).', -description:'The user\'s focus is directed to new content added to the page'}, +id:'managed-focus', +description:'If new content, such as a dialog, is added to the page, the user\'s focus is directed to it. [Learn more](https://developers.google.com/web/fundamentals/accessibility/how-to-review#start_with_the_keyboard).', +title:'The user\'s focus is directed to new content added to the page'}, super.partialMeta); }} @@ -1133,9 +1133,9 @@ static get meta(){ return Object.assign({ -name:'offscreen-content-hidden', -helpText:'Offscreen content is hidden with display: none or aria-hidden=true. [Learn more](https://developers.google.com/web/fundamentals/accessibility/how-to-review#try_it_with_a_screen_reader).', -description:'Offscreen content is hidden from assistive technology'}, +id:'offscreen-content-hidden', +description:'Offscreen content is hidden with display: none or aria-hidden=true. [Learn more](https://developers.google.com/web/fundamentals/accessibility/how-to-review#try_it_with_a_screen_reader).', +title:'Offscreen content is hidden from assistive technology'}, super.partialMeta); }} @@ -1163,9 +1163,9 @@ static get meta(){ return Object.assign({ -name:'use-landmarks', -helpText:'Landmark elements (<main>, <nav>, etc.) are used to improve the keyboard navigation of the page for assistive technology. [Learn more](https://developers.google.com/web/fundamentals/accessibility/how-to-review#take_advantage_of_headings_and_landmarks).', -description:'HTML5 landmark elements are used to improve navigation'}, +id:'use-landmarks', +description:'Landmark elements (<main>, <nav>, etc.) are used to improve the keyboard navigation of the page for assistive technology. [Learn more](https://developers.google.com/web/fundamentals/accessibility/how-to-review#take_advantage_of_headings_and_landmarks).', +title:'HTML5 landmark elements are used to improve navigation'}, super.partialMeta); }} @@ -1193,9 +1193,9 @@ static get meta(){ return Object.assign({ -name:'visual-order-follows-dom', -helpText:'DOM order matches the visual order, improving navigation for assistive technology. [Learn more](https://developers.google.com/web/fundamentals/accessibility/how-to-review#try_it_with_a_screen_reader).', -description:'Visual order on the page follows DOM order'}, +id:'visual-order-follows-dom', +description:'DOM order matches the visual order, improving navigation for assistive technology. [Learn more](https://developers.google.com/web/fundamentals/accessibility/how-to-review#try_it_with_a_screen_reader).', +title:'Visual order on the page follows DOM order'}, super.partialMeta); }} @@ -1223,10 +1223,10 @@ static get meta(){ return{ -name:'meta-refresh', -description:'The document does not use `<meta http-equiv="refresh">`', -failureDescription:'The document uses `<meta http-equiv="refresh">`', -helpText:'Users do not expect a page to refresh automatically, and doing so will move '+ +id:'meta-refresh', +title:'The document does not use `<meta http-equiv="refresh">`', +failureTitle:'The document uses `<meta http-equiv="refresh">`', +description:'Users do not expect a page to refresh automatically, and doing so will move '+ 'focus back to the top of the page. This may create a frustrating or '+ 'confusing experience. '+ '[Learn more](https://dequeuniversity.com/rules/axe/2.2/meta-refresh?application=lighthouse).', @@ -1258,13 +1258,13 @@ static get meta(){ return{ -name:'meta-viewport', -description:'`[user-scalable="no"]` is not used in the `<meta name="viewport">` '+ +id:'meta-viewport', +title:'`[user-scalable="no"]` is not used in the `<meta name="viewport">` '+ 'element and the `[maximum-scale]` attribute is not less than 5.', -failureDescription:'`[user-scalable="no"]` is used in the `<meta name="viewport">` '+ +failureTitle:'`[user-scalable="no"]` is used in the `<meta name="viewport">` '+ 'element or the `[maximum-scale]` attribute is less than 5.', -helpText:'Disabling zooming is problematic for users with low vision who rely on screen '+ -'magnification to properly see the contents of a web page. '+ +description:'Disabling zooming is problematic for users with low vision who rely on '+ +'screen magnification to properly see the contents of a web page. '+ '[Learn more](https://dequeuniversity.com/rules/axe/2.2/meta-viewport?application=lighthouse).', requiredArtifacts:['Accessibility']}; @@ -1294,11 +1294,11 @@ static get meta(){ return{ -name:'object-alt', -description:'`<object>` elements have `[alt]` text', -failureDescription:'`<object>` elements do not have `[alt]` text', -helpText:'Screen readers cannot translate non-text content. Adding alt text to `<object>` '+ -'elements helps screen readers convey meaning to users. '+ +id:'object-alt', +title:'`<object>` elements have `[alt]` text', +failureTitle:'`<object>` elements do not have `[alt]` text', +description:'Screen readers cannot translate non-text content. Adding alt text to '+ +'`<object>` elements helps screen readers convey meaning to users. '+ '[Learn more](https://dequeuniversity.com/rules/axe/2.2/object-alt?application=lighthouse).', requiredArtifacts:['Accessibility']}; @@ -1328,10 +1328,10 @@ static get meta(){ return{ -name:'tabindex', -description:'No element has a `[tabindex]` value greater than 0', -failureDescription:'Some elements have a `[tabindex]` value greater than 0', -helpText:'A value greater than 0 implies an explicit navigation ordering. '+ +id:'tabindex', +title:'No element has a `[tabindex]` value greater than 0', +failureTitle:'Some elements have a `[tabindex]` value greater than 0', +description:'A value greater than 0 implies an explicit navigation ordering. '+ 'Although technically valid, this often creates frustrating experiences '+ 'for users who rely on assistive technologies. [Learn more](https://dequeuniversity.com/rules/axe/2.2/tabindex?application=lighthouse).', requiredArtifacts:['Accessibility']}; @@ -1363,14 +1363,14 @@ static get meta(){ return{ -name:'td-headers-attr', -description:'Cells in a `<table>` element that use the `[headers]` attribute only refer '+ +id:'td-headers-attr', +title:'Cells in a `<table>` element that use the `[headers]` attribute only refer '+ 'to other cells of that same table.', -failureDescription:'Cells in a `<table>` element that use the `[headers]` '+ +failureTitle:'Cells in a `<table>` element that use the `[headers]` '+ 'attribute refers to other cells of that same table.', -helpText:'Screen readers have features to make navigating tables easier. Ensuring `<td>` '+ -'cells using the `[headers]` attribute only refer to other cells in the same table may '+ -'improve the experience for screen reader users. '+ +description:'Screen readers have features to make navigating tables easier. Ensuring '+ +'`<td>` cells using the `[headers]` attribute only refer to other cells in the same '+ +'table may improve the experience for screen reader users. '+ '[Learn more](https://dequeuniversity.com/rules/axe/2.2/td-headers-attr?application=lighthouse).', requiredArtifacts:['Accessibility']}; @@ -1400,13 +1400,13 @@ static get meta(){ return{ -name:'th-has-data-cells', -description:'`<th>` elements and elements with `[role="columnheader"/"rowheader"]` have '+ +id:'th-has-data-cells', +title:'`<th>` elements and elements with `[role="columnheader"/"rowheader"]` have '+ 'data cells they describe.', -failureDescription:'`<th>` elements and elements with '+ +failureTitle:'`<th>` elements and elements with '+ '`[role="columnheader"/"rowheader"]` do not have data cells they describe.', -helpText:'Screen readers have features to make navigating tables easier. Ensuring table '+ -'headers always refer to some set of cells may improve the experience for screen '+ +description:'Screen readers have features to make navigating tables easier. Ensuring '+ +'table headers always refer to some set of cells may improve the experience for screen '+ 'reader users. '+ '[Learn more](https://dequeuniversity.com/rules/axe/2.2/th-has-data-cells?application=lighthouse).', requiredArtifacts:['Accessibility']}; @@ -1437,10 +1437,10 @@ static get meta(){ return{ -name:'valid-lang', -description:'`[lang]` attributes have a valid value', -failureDescription:'`[lang]` attributes do not have a valid value', -helpText:'Specifying a valid [BCP 47 language](https://www.w3.org/International/questions/qa-choosing-language-tags#question) '+ +id:'valid-lang', +title:'`[lang]` attributes have a valid value', +failureTitle:'`[lang]` attributes do not have a valid value', +description:'Specifying a valid [BCP 47 language](https://www.w3.org/International/questions/qa-choosing-language-tags#question) '+ 'on elements helps ensure that text is pronounced correctly by a screen reader. '+ '[Learn more](https://dequeuniversity.com/rules/axe/2.2/valid-lang?application=lighthouse).', requiredArtifacts:['Accessibility']}; @@ -1471,11 +1471,11 @@ static get meta(){ return{ -name:'video-caption', -description:'`<video>` elements contain a `<track>` element with `[kind="captions"]`', -failureDescription:'`<video>` elements do not contain a `<track>` element '+ +id:'video-caption', +title:'`<video>` elements contain a `<track>` element with `[kind="captions"]`', +failureTitle:'`<video>` elements do not contain a `<track>` element '+ 'with `[kind="captions"]`.', -helpText:'When a video provides a caption it is easier for deaf and hearing impaired '+ +description:'When a video provides a caption it is easier for deaf and hearing impaired '+ 'users to access its information. '+ '[Learn more](https://dequeuniversity.com/rules/axe/2.2/video-caption?application=lighthouse).', requiredArtifacts:['Accessibility']}; @@ -1506,11 +1506,11 @@ static get meta(){ return{ -name:'video-description', -description:'`<video>` elements contain a `<track>` element with `[kind="description"]`', -failureDescription:'`<video>` elements do not contain a `<track>` element with '+ +id:'video-description', +title:'`<video>` elements contain a `<track>` element with `[kind="description"]`', +failureTitle:'`<video>` elements do not contain a `<track>` element with '+ '`[kind="description"]`.', -helpText:'Audio descriptions provide relevant information for videos that dialogue '+ +description:'Audio descriptions provide relevant information for videos that dialogue '+ 'cannot, such as facial expressions and scenes. '+ '[Learn more](https://dequeuniversity.com/rules/axe/2.2/video-description?application=lighthouse).', requiredArtifacts:['Accessibility']}; @@ -1529,9 +1529,9 @@ 'use strict'; const Audit=require('./audit'); -const WebInspector=require('../lib/web-inspector'); const Util=require('../report/html/renderer/util'); -const{groupIdToName,taskToGroup}=require('../lib/task-groups'); +const NetworkRequest=require('../lib/network-request'); +const{taskGroups}=require('../lib/task-groups'); class BootupTime extends Audit{ @@ -1539,11 +1539,11 @@ static get meta(){ return{ -name:'bootup-time', -description:'JavaScript boot-up time', -failureDescription:'JavaScript boot-up time is too high', +id:'bootup-time', +title:'JavaScript boot-up time', +failureTitle:'JavaScript boot-up time is too high', scoreDisplayMode:Audit.SCORING_MODES.NUMERIC, -helpText:'Consider reducing the time spent parsing, compiling, and executing JS. '+ +description:'Consider reducing the time spent parsing, compiling, and executing JS. '+ 'You may find delivering smaller JS payloads helps with this. [Learn '+ 'more](https://developers.google.com/web/tools/lighthouse/audits/bootup).', requiredArtifacts:['traces']}; @@ -1566,31 +1566,38 @@ +static getJavaScriptURLs(records){ +const urls=new Set(); +for(const record of records){ +if(record.resourceType===NetworkRequest.TYPES.Script){ +urls.add(record.url); +} +} -static getExecutionTimingsByURL(timelineModel){ -const bottomUpByURL=timelineModel.bottomUpGroupBy('URL'); - -const result=new Map(); - -bottomUpByURL.children.forEach((perUrlNode,url)=>{ - -if(!url||url==='about:blank'){ -return; +return urls; } -const taskGroups={}; -perUrlNode.children.forEach(perTaskPerUrlNode=>{ -const task=WebInspector.TimelineUIUtils.eventStyle(perTaskPerUrlNode.event); -const groupName=taskToGroup[task.title]||groupIdToName.other; -const groupTotal=taskGroups[groupName]||0; -taskGroups[groupName]=groupTotal+(perTaskPerUrlNode.selfTime||0); -}); -result.set(url,taskGroups); -}); + + +static getExecutionTimingsByURL(tasks,jsURLs){ + +const result=new Map(); + +for(const task of tasks){ +const jsURL=task.attributableURLs.find(url=>jsURLs.has(url)); +const fallbackURL=task.attributableURLs[0]; +const attributableURL=jsURL||fallbackURL; +if(!attributableURL||attributableURL==='about:blank')continue; + +const timingByGroupId=result.get(attributableURL)||{}; +const originalTime=timingByGroupId[task.group.id]||0; +timingByGroupId[task.group.id]=originalTime+task.selfTime; +result.set(attributableURL,timingByGroupId); +} return result; } @@ -1603,47 +1610,54 @@ static async audit(artifacts,context){ const settings=context.settings||{}; const trace=artifacts.traces[BootupTime.DEFAULT_PASS]; -const devtoolsTimelineModel=await artifacts.requestDevtoolsTimelineModel(trace); -const executionTimings=BootupTime.getExecutionTimingsByURL(devtoolsTimelineModel); -let totalBootupTime=0; - -const extendedInfo={}; - -const headings=[ -{key:'url',itemType:'url',text:'URL'}, -{key:'scripting',granularity:1,itemType:'ms',text:groupIdToName.scripting}, -{key:'scriptParseCompile',granularity:1,itemType:'ms', -text:groupIdToName.scriptParseCompile}]; - - +const devtoolsLog=artifacts.devtoolsLogs[BootupTime.DEFAULT_PASS]; +const networkRecords=await artifacts.requestNetworkRecords(devtoolsLog); +const tasks=await artifacts.requestMainThreadTasks(trace); const multiplier=settings.throttlingMethod==='simulate'? settings.throttling.cpuSlowdownMultiplier:1; -const results=Array.from(executionTimings). -map(([url,groups])=>{ +const jsURLs=BootupTime.getJavaScriptURLs(networkRecords); +const executionTimings=BootupTime.getExecutionTimingsByURL(tasks,jsURLs); -for(const[name,value]of Object.entries(groups)){ -groups[name]=value*multiplier; -totalBootupTime+=value*multiplier; +let totalBootupTime=0; +const results=Array.from(executionTimings). +map(([url,timingByGroupId])=>{ + +let bootupTimeForURL=0; +for(const[groupId,timespanMs]of Object.entries(timingByGroupId)){ +timingByGroupId[groupId]=timespanMs*multiplier; +bootupTimeForURL+=timespanMs*multiplier; } -extendedInfo[url]=groups; -const scriptingTotal=groups[groupIdToName.scripting]||0; -const parseCompileTotal=groups[groupIdToName.scriptParseCompile]||0; +if(bootupTimeForURL>=context.options.thresholdInMs){ +totalBootupTime+=bootupTimeForURL; +} + +const scriptingTotal=timingByGroupId[taskGroups.scriptEvaluation.id]||0; +const parseCompileTotal=timingByGroupId[taskGroups.scriptParseCompile.id]||0; + return{ url:url, -sum:scriptingTotal+parseCompileTotal, - +total:bootupTimeForURL, scripting:scriptingTotal, scriptParseCompile:parseCompileTotal}; }). -filter(result=>result.sum>=context.options.thresholdInMs). -sort((a,b)=>b.sum-a.sum); +filter(result=>result.total>=context.options.thresholdInMs). +sort((a,b)=>b.total-a.total); const summary={wastedMs:totalBootupTime}; + +const headings=[ +{key:'url',itemType:'url',text:'URL'}, +{key:'total',granularity:1,itemType:'ms',text:'Total'}, +{key:'scripting',granularity:1,itemType:'ms',text:taskGroups.scriptEvaluation.label}, +{key:'scriptParseCompile',granularity:1,itemType:'ms', +text:taskGroups.scriptParseCompile.label}]; + + const details=BootupTime.makeTableDetails(headings,results,summary); const score=Audit.computeLogNormalScore( @@ -1656,17 +1670,14 @@ score, rawValue:totalBootupTime, displayValue:[Util.MS_DISPLAY_VALUE,totalBootupTime], -details, -extendedInfo:{ -value:extendedInfo}}; - +details}; }} module.exports=BootupTime; -},{"../lib/task-groups":42,"../lib/web-inspector":47,"../report/html/renderer/util":48,"./audit":2}],"../audits/byte-efficiency/efficient-animated-content":[function(require,module,exports){ +},{"../lib/network-request":38,"../lib/task-groups":43,"../report/html/renderer/util":48,"./audit":2}],"../audits/byte-efficiency/efficient-animated-content":[function(require,module,exports){ @@ -1677,7 +1688,7 @@ 'use strict'; -const WebInspector=require('../../lib/web-inspector'); +const NetworkRequest=require('../../lib/network-request'); const ByteEfficiencyAudit=require('./byte-efficiency-audit'); @@ -1690,10 +1701,10 @@ static get meta(){ return{ -name:'efficient-animated-content', +id:'efficient-animated-content', scoreDisplayMode:ByteEfficiencyAudit.SCORING_MODES.NUMERIC, -description:'Use video formats for animated content', -helpText:'Large GIFs are inefficient for delivering animated content. Consider using '+ +title:'Use video formats for animated content', +description:'Large GIFs are inefficient for delivering animated content. Consider using '+ 'MPEG4/WebM videos for animations and PNG/WebP for static images instead of GIF to save '+ 'network bytes. [Learn more](https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/replace-animated-gifs-with-video/)', requiredArtifacts:['devtoolsLogs']}; @@ -1717,14 +1728,14 @@ static audit_(artifacts,networkRecords){ const unoptimizedContent=networkRecords.filter( -record=>record._mimeType==='image/gif'&& -record._resourceType===WebInspector.resourceTypes.Image&& -(record._resourceSize||0)>GIF_BYTE_THRESHOLD); +record=>record.mimeType==='image/gif'&& +record.resourceType===NetworkRequest.TYPES.Image&& +(record.resourceSize||0)>GIF_BYTE_THRESHOLD); -const results=unoptimizedContent.map(record=>{ -const resourceSize=record._resourceSize||0; +const items=unoptimizedContent.map(record=>{ +const resourceSize=record.resourceSize||0; return{ url:record.url, totalBytes:resourceSize, @@ -1733,26 +1744,15 @@ }); + const headings=[ -{key:'url',itemType:'url',text:'URL'}, -{ -key:'totalBytes', -itemType:'bytes', -displayUnit:'kb', -granularity:1, -text:'Transfer Size'}, - -{ -key:'wastedBytes', -itemType:'bytes', -displayUnit:'kb', -granularity:1, -text:'Byte Savings'}]; - +{key:'url',valueType:'url',label:'URL'}, +{key:'totalBytes',valueType:'bytes',label:'Transfer Size'}, +{key:'wastedBytes',valueType:'bytes',label:'Byte Savings'}]; return{ -results, +items, headings}; }} @@ -1760,7 +1760,7 @@ module.exports=EfficientAnimatedContent; -},{"../../lib/web-inspector":47,"./byte-efficiency-audit":3}],"../audits/byte-efficiency/offscreen-images":[function(require,module,exports){ +},{"../../lib/network-request":38,"./byte-efficiency-audit":3}],"../audits/byte-efficiency/offscreen-images":[function(require,module,exports){ @@ -1781,6 +1781,7 @@ const IGNORE_THRESHOLD_IN_BYTES=2048; const IGNORE_THRESHOLD_IN_PERCENT=75; +const IGNORE_THRESHOLD_IN_MS=50; @@ -1790,10 +1791,10 @@ static get meta(){ return{ -name:'offscreen-images', -description:'Defer offscreen images', +id:'offscreen-images', +title:'Defer offscreen images', scoreDisplayMode:ByteEfficiencyAudit.SCORING_MODES.NUMERIC, -helpText: +description: 'Consider lazy-loading offscreen and hidden images after all critical resources have '+ 'finished loading to lower time to interactive. '+ '[Learn more](https://developers.google.com/web/tools/lighthouse/audits/offscreen-images).', @@ -1855,12 +1856,59 @@ +static filterLanternResults(images,lanternMetricData){ +const nodeTimings=lanternMetricData.pessimisticEstimate.nodeTimings; + + +let lastLongTaskStartTime=0; + + +const startTimesByURL=new Map(); +for(const[node,timing]of nodeTimings){ +if(node.type==='cpu'&&timing.duration>=50){ +lastLongTaskStartTime=Math.max(lastLongTaskStartTime,timing.startTime); +}else if(node.type==='network'){ +const networkNode=node; +startTimesByURL.set(networkNode.record.url,timing.startTime); +} +} + +return images.filter(image=>{ + +if(image.wastedBytes<IGNORE_THRESHOLD_IN_BYTES)return false; +if(image.wastedPercent<IGNORE_THRESHOLD_IN_PERCENT)return false; + +const imageRequestStartTime=startTimesByURL.get(image.url)||0; +return imageRequestStartTime<lastLongTaskStartTime-IGNORE_THRESHOLD_IN_MS; +}); +} + + + + + + + +static filterObservedResults(images,interactiveTimestamp){ +return images.filter(image=>{ +if(image.wastedBytes<IGNORE_THRESHOLD_IN_BYTES)return false; +if(image.wastedPercent<IGNORE_THRESHOLD_IN_PERCENT)return false; +return image.requestStartTime<interactiveTimestamp/1e6-IGNORE_THRESHOLD_IN_MS/1000; +}); +} + + + + + + + static computeWasteWithTTIGraph(results,graph,simulator){ -return ByteEfficiencyAudit.computeWasteWithTTIGraph(results,graph,simulator, +return super.computeWasteWithTTIGraph(results,graph,simulator, {includeLoad:false}); } @@ -1887,7 +1935,7 @@ if(processed instanceof Error){ warnings.push(processed.message); -Sentry.captureException(processed,{tags:{audit:this.meta.name},level:'warning'}); +Sentry.captureException(processed,{tags:{audit:this.meta.id},level:'warning'}); return results; } @@ -1901,36 +1949,26 @@ },new Map()); const settings=context.settings; -return artifacts.requestFirstCPUIdle({trace,devtoolsLog,settings}).then(firstInteractive=>{ +return artifacts.requestInteractive({trace,devtoolsLog,settings}).then(interactive=>{ +const unfilteredResults=Array.from(resultsMap.values()); +const lanternInteractive=interactive; +const items=context.settings.throttlingMethod==='simulate'? +OffscreenImages.filterLanternResults(unfilteredResults,lanternInteractive): +OffscreenImages.filterObservedResults(unfilteredResults,interactive.timestamp); -const ttiTimestamp=firstInteractive.timestamp?firstInteractive.timestamp/1e6:Infinity; - -const results=Array.from(resultsMap.values()).filter(item=>{ -const isWasteful= -item.wastedBytes>IGNORE_THRESHOLD_IN_BYTES&& -item.wastedPercent>IGNORE_THRESHOLD_IN_PERCENT; -const loadedEarly=item.requestStartTime<ttiTimestamp; -return isWasteful&&loadedEarly; -}); const headings=[ -{key:'url',itemType:'thumbnail',text:''}, -{key:'url',itemType:'url',text:'URL'}, -{key:'totalBytes',itemType:'bytes',displayUnit:'kb',granularity:1,text:'Original'}, -{ -key:'wastedBytes', -itemType:'bytes', -displayUnit:'kb', -granularity:1, -text:'Potential Savings'}]; - +{key:'url',valueType:'thumbnail',label:''}, +{key:'url',valueType:'url',label:'URL'}, +{key:'totalBytes',valueType:'bytes',label:'Original'}, +{key:'wastedBytes',valueType:'bytes',label:'Potential Savings'}]; return{ warnings, -results, +items, headings}; }); @@ -1939,7 +1977,7 @@ module.exports=OffscreenImages; -},{"../../lib/sentry":39,"../../lib/url-shim":"url","./byte-efficiency-audit":3}],"../audits/byte-efficiency/render-blocking-resources":[function(require,module,exports){ +},{"../../lib/sentry":40,"../../lib/url-shim":"url","./byte-efficiency-audit":3}],"../audits/byte-efficiency/render-blocking-resources":[function(require,module,exports){ @@ -1952,13 +1990,14 @@ 'use strict'; const Audit=require('../audit'); -const Node=require('../../lib/dependency-graph/node'); +const BaseNode=require('../../lib/dependency-graph/base-node'); const ByteEfficiencyAudit=require('./byte-efficiency-audit'); const UnusedCSS=require('./unused-css-rules'); -const WebInspector=require('../../lib/web-inspector'); +const NetworkRequest=require('../../lib/network-request'); -const Simulator=require('../../lib/dependency-graph/simulator/simulator'); -const NetworkNode=require('../../lib/dependency-graph/network-node.js'); + + + @@ -1977,11 +2016,10 @@ const nodes=Array.from(nodeTimings.keys()); nodes.forEach(node=>{ if(node.type!=='network')return; -const networkNode=node; const nodeTiming=nodeTimings.get(node); if(!nodeTiming)return; -urlMap[networkNode.record.url]={node,nodeTiming}; +urlMap[node.record.url]={node,nodeTiming}; }); return urlMap; @@ -1993,10 +2031,10 @@ static get meta(){ return{ -name:'render-blocking-resources', -description:'Eliminate render-blocking resources', +id:'render-blocking-resources', +title:'Eliminate render-blocking resources', scoreDisplayMode:Audit.SCORING_MODES.NUMERIC, -helpText: +description: 'Resources are blocking the first paint of your page. Consider '+ 'delivering critical JS/CSS inline and deferring all non-critical '+ 'JS/styles. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/blocking-resources).', @@ -2044,8 +2082,7 @@ node.traverse(node=>deferredNodeIds.add(node.id)); - -const wastedMs=Math.round(nodeTiming.endTime-nodeTiming.startTime); +const wastedMs=Math.round(nodeTiming.duration); if(wastedMs<MINIMUM_WASTED_MS)continue; results.push({ @@ -2092,26 +2129,24 @@ const minimalFCPGraph=fcpGraph.cloneWithRelationships(node=>{ const canDeferRequest=deferredIds.has(node.id); -if(node.type!==Node.TYPES.NETWORK)return!canDeferRequest; - -const networkNode=node; +if(node.type!==BaseNode.TYPES.NETWORK)return!canDeferRequest; const isStylesheet= -networkNode.record._resourceType===WebInspector.resourceTypes.Stylesheet; +node.record.resourceType===NetworkRequest.TYPES.Stylesheet; if(canDeferRequest&&isStylesheet){ -const wastedBytes=wastedCssBytesByUrl.get(networkNode.record.url)||0; -totalChildNetworkBytes+=(networkNode.record._transferSize||0)-wastedBytes; +const wastedBytes=wastedCssBytesByUrl.get(node.record.url)||0; +totalChildNetworkBytes+=(node.record.transferSize||0)-wastedBytes; } return!canDeferRequest; }); -const originalTransferSize=minimalFCPGraph.record._transferSize; +const originalTransferSize=minimalFCPGraph.record.transferSize; const safeTransferSize=originalTransferSize||0; -minimalFCPGraph.record._transferSize=safeTransferSize+totalChildNetworkBytes; +minimalFCPGraph.record.transferSize=safeTransferSize+totalChildNetworkBytes; const estimateAfterInline=simulator.simulate(minimalFCPGraph).timeInMs; -minimalFCPGraph.record._transferSize=originalTransferSize; +minimalFCPGraph.record.transferSize=originalTransferSize; return Math.round(Math.max(originalEstimate-estimateAfterInline,0)); } @@ -2149,20 +2184,14 @@ displayValue=`${results.length} resource delayed first paint by ${wastedMs}ms`; } + const headings=[ -{key:'url',itemType:'url',text:'URL'}, -{ -key:'totalBytes', -itemType:'bytes', -displayUnit:'kb', -granularity:0.01, -text:'Size (KB)'}, - -{key:'wastedMs',itemType:'ms',text:'Download Time (ms)',granularity:1}]; +{key:'url',valueType:'url',label:'URL'}, +{key:'totalBytes',valueType:'bytes',label:'Size (KB)'}, +{key:'wastedMs',valueType:'timespanMs',label:'Download Time (ms)'}]; -const summary={wastedMs}; -const details=Audit.makeTableDetails(headings,results,summary); +const details=Audit.makeOpportunityDetails(headings,results,wastedMs); return{ displayValue, @@ -2175,7 +2204,7 @@ module.exports=RenderBlockingResources; -},{"../../lib/dependency-graph/network-node.js":24,"../../lib/dependency-graph/node":25,"../../lib/dependency-graph/simulator/simulator":28,"../../lib/web-inspector":47,"../audit":2,"./byte-efficiency-audit":3,"./unused-css-rules":"../audits/byte-efficiency/unused-css-rules"}],"../audits/byte-efficiency/total-byte-weight":[function(require,module,exports){ +},{"../../lib/dependency-graph/base-node":22,"../../lib/network-request":38,"../audit":2,"./byte-efficiency-audit":3,"./unused-css-rules":"../audits/byte-efficiency/unused-css-rules"}],"../audits/byte-efficiency/total-byte-weight":[function(require,module,exports){ @@ -2191,11 +2220,11 @@ static get meta(){ return{ -name:'total-byte-weight', -description:'Avoids enormous network payloads', -failureDescription:'Has enormous network payloads', +id:'total-byte-weight', +title:'Avoids enormous network payloads', +failureTitle:'Has enormous network payloads', scoreDisplayMode:ByteEfficiencyAudit.SCORING_MODES.NUMERIC, -helpText: +description: 'Large network payloads cost users real money and are highly correlated with '+ 'long load times. [Learn '+ 'more](https://developers.google.com/web/tools/lighthouse/audits/network-payloads).', @@ -2310,11 +2339,11 @@ static get meta(){ return{ -name:'unminified-css', -description:'Minify CSS', +id:'unminified-css', +title:'Minify CSS', scoreDisplayMode:ByteEfficiencyAudit.SCORING_MODES.NUMERIC, -helpText:'Minifying CSS files can reduce network payload sizes. '+ -'[Learn more](https://developers.google.com/speed/docs/insights/MinifyResources).', +description:'Minifying CSS files can reduce network payload sizes. '+ +'[Learn more](https://developers.google.com/web/tools/lighthouse/audits/minify-css).', requiredArtifacts:['CSSUsage','devtoolsLogs']}; } @@ -2390,15 +2419,14 @@ const content=stylesheet.content; const totalTokenLength=UnminifiedCSS.computeTokenLength(content); - let url=stylesheet.header.sourceURL; if(!url||url===pageUrl){ const contentPreview=UnusedCSSRules.determineContentPreview(stylesheet.content); -url={type:'code',value:contentPreview}; +url=contentPreview; } const totalBytes=ByteEfficiencyAudit.estimateTransferSize(networkRecord,content.length, -'stylesheet'); +'Stylesheet'); const wastedRatio=1-totalTokenLength/content.length; const wastedBytes=Math.round(totalBytes*wastedRatio); @@ -2417,7 +2445,7 @@ static audit_(artifacts,networkRecords){ const pageUrl=artifacts.URL.finalUrl; -const results=[]; +const items=[]; for(const stylesheet of artifacts.CSSUsage.stylesheets){ const networkRecord=networkRecords. find(record=>record.url===stylesheet.header.sourceURL); @@ -2430,16 +2458,15 @@ if(result.wastedPercent<IGNORE_THRESHOLD_IN_PERCENT|| result.wastedBytes<IGNORE_THRESHOLD_IN_BYTES|| !Number.isFinite(result.wastedBytes))continue; -results.push(result); +items.push(result); } return{ -results, +items, headings:[ -{key:'url',itemType:'url',text:'URL'}, -{key:'totalBytes',itemType:'bytes',displayUnit:'kb',granularity:1,text:'Original'}, -{key:'wastedBytes',itemType:'bytes',displayUnit:'kb',granularity:1, -text:'Potential Savings'}]}; +{key:'url',valueType:'url',label:'URL'}, +{key:'totalBytes',valueType:'bytes',label:'Original'}, +{key:'wastedBytes',valueType:'bytes',label:'Potential Savings'}]}; }} @@ -2478,11 +2505,11 @@ static get meta(){ return{ -name:'unminified-javascript', -description:'Minify JavaScript', +id:'unminified-javascript', +title:'Minify JavaScript', scoreDisplayMode:ByteEfficiencyAudit.SCORING_MODES.NUMERIC, -helpText:'Minifying JavaScript files can reduce payload sizes and script parse time. '+ +description:'Minifying JavaScript files can reduce payload sizes and script parse time. '+ '[Learn more](https://developers.google.com/speed/docs/insights/MinifyResources).', requiredArtifacts:['Scripts','devtoolsLogs']}; @@ -2507,7 +2534,7 @@ } const totalBytes=ByteEfficiencyAudit.estimateTransferSize(networkRecord,contentLength, -'script'); +'Script'); const wastedRatio=1-totalTokenLength/contentLength; const wastedBytes=Math.round(totalBytes*wastedRatio); @@ -2526,7 +2553,7 @@ static audit_(artifacts,networkRecords){ -const results=[]; +const items=[]; const warnings=[]; for(const requestId of Object.keys(artifacts.Scripts)){ const scriptContent=artifacts.Scripts[requestId]; @@ -2540,20 +2567,19 @@ if(result.wastedPercent<IGNORE_THRESHOLD_IN_PERCENT|| result.wastedBytes<IGNORE_THRESHOLD_IN_BYTES|| !Number.isFinite(result.wastedBytes))continue; -results.push(result); +items.push(result); }catch(err){ -warnings.push(`Unable to process ${networkRecord._url}: ${err.message}`); +warnings.push(`Unable to process ${networkRecord.url}: ${err.message}`); } } return{ -results, +items, warnings, headings:[ -{key:'url',itemType:'url',text:'URL'}, -{key:'totalBytes',itemType:'bytes',displayUnit:'kb',granularity:1,text:'Original'}, -{key:'wastedBytes',itemType:'bytes',displayUnit:'kb',granularity:1, -text:'Potential Savings'}]}; +{key:'url',valueType:'url',label:'URL'}, +{key:'totalBytes',valueType:'bytes',label:'Original'}, +{key:'wastedBytes',valueType:'bytes',label:'Potential Savings'}]}; }} @@ -2561,7 +2587,7 @@ module.exports=UnminifiedJavaScript; -},{"./byte-efficiency-audit":3,"esprima":136}],"../audits/byte-efficiency/unused-css-rules":[function(require,module,exports){ +},{"./byte-efficiency-audit":3,"esprima":118}],"../audits/byte-efficiency/unused-css-rules":[function(require,module,exports){ @@ -2582,10 +2608,10 @@ static get meta(){ return{ -name:'unused-css-rules', -description:'Defer unused CSS', +id:'unused-css-rules', +title:'Defer unused CSS', scoreDisplayMode:ByteEfficiencyAudit.SCORING_MODES.NUMERIC, -helpText:'Remove unused rules from stylesheets to reduce unnecessary '+ +description:'Remove unused rules from stylesheets to reduce unnecessary '+ 'bytes consumed by network activity. '+ '[Learn more](https://developers.google.com/speed/docs/insights/OptimizeCSSDelivery).', requiredArtifacts:['CSSUsage','URL','devtoolsLogs']}; @@ -2645,7 +2671,7 @@ } const totalTransferredBytes=ByteEfficiencyAudit.estimateTransferSize( -stylesheetInfo.networkRecord,totalUncompressedBytes,'stylesheet'); +stylesheetInfo.networkRecord,totalUncompressedBytes,'Stylesheet'); const percentUnused=(totalUncompressedBytes-usedUncompressedBytes)/totalUncompressedBytes; const wastedBytes=Math.round(percentUnused*totalTransferredBytes); @@ -2698,15 +2724,15 @@ static mapSheetToResult(stylesheetInfo,pageUrl){ - let url=stylesheetInfo.header.sourceURL; if(!url||url===pageUrl){ const contentPreview=UnusedCSSRules.determineContentPreview(stylesheetInfo.content); -url={type:'code',value:contentPreview}; +url=contentPreview; } const usage=UnusedCSSRules.computeUsage(stylesheetInfo); -return Object.assign({url},usage); +const result={url}; +return Object.assign(result,usage); } @@ -2723,19 +2749,19 @@ const indexedSheets=UnusedCSSRules.indexStylesheetsById(styles,networkRecords); UnusedCSSRules.indexUsedRules(usage,indexedSheets); -const results=Object.keys(indexedSheets). +const items=Object.keys(indexedSheets). map(sheetId=>UnusedCSSRules.mapSheetToResult(indexedSheets[sheetId],pageUrl)). filter(sheet=>sheet&&sheet.wastedBytes>IGNORE_THRESHOLD_IN_BYTES); + const headings=[ -{key:'url',itemType:'url',text:'URL'}, -{key:'totalBytes',itemType:'bytes',displayUnit:'kb',granularity:1,text:'Original'}, -{key:'wastedBytes',itemType:'bytes',displayUnit:'kb',granularity:1, -text:'Potential Savings'}]; +{key:'url',valueType:'url',label:'URL'}, +{key:'totalBytes',valueType:'bytes',label:'Original'}, +{key:'wastedBytes',valueType:'bytes',label:'Potential Savings'}]; return{ -results, +items, headings}; }); @@ -2762,10 +2788,10 @@ static get meta(){ return{ -name:'unused-javascript', -description:'Unused JavaScript', +id:'unused-javascript', +title:'Unused JavaScript', scoreDisplayMode:ByteEfficiencyAudit.SCORING_MODES.NUMERIC, -helpText:'Remove unused JavaScript to reduce bytes consumed by network activity.', +description:'Remove unused JavaScript to reduce bytes consumed by network activity.', requiredArtifacts:['JsUsage','devtoolsLogs']}; } @@ -2820,7 +2846,7 @@ } const totalBytes=ByteEfficiencyAudit.estimateTransferSize(networkRecord,contentLength, -'script'); +'Script'); const wastedRatio=unusedLength/contentLength||0; const wastedBytes=Math.round(totalBytes*wastedRatio); @@ -2846,24 +2872,22 @@ scriptsByUrl.set(script.url,scripts); } - -const results=[]; +const items=[]; for(const[url,scripts]of scriptsByUrl.entries()){ const networkRecord=networkRecords.find(record=>record.url===url); if(!networkRecord)continue; const wasteData=scripts.map(UnusedJavaScript.computeWaste); -const result=UnusedJavaScript.mergeWaste(wasteData,networkRecord); -if(result.wastedBytes<=IGNORE_THRESHOLD_IN_BYTES)continue; -results.push(result); +const item=UnusedJavaScript.mergeWaste(wasteData,networkRecord); +if(item.wastedBytes<=IGNORE_THRESHOLD_IN_BYTES)continue; +items.push(item); } return{ -results, +items, headings:[ -{key:'url',itemType:'url',text:'URL'}, -{key:'totalBytes',itemType:'bytes',displayUnit:'kb',granularity:1,text:'Original'}, -{key:'wastedBytes',itemType:'bytes',displayUnit:'kb',granularity:1, -text:'Potential Savings'}]}; +{key:'url',valueType:'url',label:'URL'}, +{key:'totalBytes',valueType:'bytes',label:'Original'}, +{key:'wastedBytes',valueType:'bytes',label:'Potential Savings'}]}; }} @@ -2883,8 +2907,9 @@ const parseCacheControl=require('parse-cache-control'); const Audit=require('../audit'); -const WebInspector=require('../../lib/web-inspector'); +const NetworkRequest=require('../../lib/network-request'); const URL=require('../../lib/url-shim'); +const linearInterpolation=require('../../lib/statistics').linearInterpolation; const IGNORE_THRESHOLD_IN_PERCENT=0.925; @@ -2895,12 +2920,12 @@ static get meta(){ return{ -name:'uses-long-cache-ttl', -description:'Uses efficient cache policy on static assets', -failureDescription:'Uses inefficient cache policy on static assets', -helpText: +id:'uses-long-cache-ttl', +title:'Uses efficient cache policy on static assets', +failureTitle:'Uses inefficient cache policy on static assets', +description: 'A long cache lifetime can speed up repeat visits to your page. '+ -'[Learn more](https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/http-caching#cache-control).', +'[Learn more](https://developers.google.com/web/tools/lighthouse/audits/cache-policy).', scoreDisplayMode:Audit.SCORING_MODES.NUMERIC, requiredArtifacts:['devtoolsLogs']}; @@ -2925,20 +2950,6 @@ - - - -static linearInterpolation(x0,y0,x1,y1,x){ -const slope=(y1-y0)/(x1-x0); -return y0+(x-x0)*slope; -} - - - - - - - static getCacheHitProbability(maxAgeInSeconds){ @@ -2964,7 +2975,7 @@ const lowerDecile=(upperDecileIndex-1)/10; -return CacheHeaders.linearInterpolation( +return linearInterpolation( lowerDecileValue, lowerDecile, upperDecileValue, @@ -3022,17 +3033,17 @@ const CACHEABLE_STATUS_CODES=new Set([200,203,206]); const STATIC_RESOURCE_TYPES=new Set([ -WebInspector.resourceTypes.Font, -WebInspector.resourceTypes.Image, -WebInspector.resourceTypes.Media, -WebInspector.resourceTypes.Script, -WebInspector.resourceTypes.Stylesheet]); +NetworkRequest.TYPES.Font, +NetworkRequest.TYPES.Image, +NetworkRequest.TYPES.Media, +NetworkRequest.TYPES.Script, +NetworkRequest.TYPES.Stylesheet]); -const resourceUrl=record._url; +const resourceUrl=record.url; return( CACHEABLE_STATUS_CODES.has(record.statusCode)&& -STATIC_RESOURCE_TYPES.has(record._resourceType)&& +STATIC_RESOURCE_TYPES.has(record.resourceType||'Other')&& !resourceUrl.includes('data:')); } @@ -3054,7 +3065,7 @@ const headers=new Map(); -for(const header of record._responseHeaders||[]){ +for(const header of record.responseHeaders||[]){ headers.set(header.name.toLowerCase(),header.value); } @@ -3071,8 +3082,8 @@ const cacheHitProbability=CacheHeaders.getCacheHitProbability(cacheLifetimeInSeconds); if(cacheHitProbability>IGNORE_THRESHOLD_IN_PERCENT)continue; -const url=URL.elideDataURI(record._url); -const totalBytes=record._transferSize||0; +const url=URL.elideDataURI(record.url); +const totalBytes=record.transferSize||0; const wastedBytes=(1-cacheHitProbability)*totalBytes; totalWastedBytes+=wastedBytes; @@ -3126,7 +3137,7 @@ module.exports=CacheHeaders; -},{"../../lib/url-shim":"url","../../lib/web-inspector":47,"../audit":2,"assert":53,"parse-cache-control":147}],"../audits/byte-efficiency/uses-optimized-images":[function(require,module,exports){ +},{"../../lib/network-request":38,"../../lib/statistics":41,"../../lib/url-shim":"url","../audit":2,"assert":53,"parse-cache-control":129}],"../audits/byte-efficiency/uses-optimized-images":[function(require,module,exports){ @@ -3149,10 +3160,10 @@ static get meta(){ return{ -name:'uses-optimized-images', -description:'Efficiently encode images', +id:'uses-optimized-images', +title:'Efficiently encode images', scoreDisplayMode:ByteEfficiencyAudit.SCORING_MODES.NUMERIC, -helpText:'Optimized images load faster and consume less cellular data. '+ +description:'Optimized images load faster and consume less cellular data. '+ '[Learn more](https://developers.google.com/web/tools/lighthouse/audits/optimize-images).', requiredArtifacts:['OptimizedImages','devtoolsLogs']}; @@ -3176,7 +3187,7 @@ const images=artifacts.OptimizedImages; -const results=[]; +const items=[]; const warnings=[]; for(const image of images){ if(image.failed){ @@ -3190,7 +3201,7 @@ const url=URL.elideDataURI(image.url); const jpegSavings=UsesOptimizedImages.computeSavings(image); -results.push({ +items.push({ url, fromProtocol:image.fromProtocol, isCrossOrigin:!image.isSameOrigin, @@ -3199,17 +3210,17 @@ } + const headings=[ -{key:'url',itemType:'thumbnail',text:''}, -{key:'url',itemType:'url',text:'URL'}, -{key:'totalBytes',itemType:'bytes',displayUnit:'kb',granularity:1,text:'Original'}, -{key:'wastedBytes',itemType:'bytes',displayUnit:'kb',granularity:1, -text:'Potential Savings'}]; +{key:'url',valueType:'thumbnail',label:''}, +{key:'url',valueType:'url',label:'URL'}, +{key:'totalBytes',valueType:'bytes',label:'Original'}, +{key:'wastedBytes',valueType:'bytes',label:'Potential Savings'}]; return{ warnings, -results, +items, headings}; }} @@ -3245,10 +3256,10 @@ static get meta(){ return{ -name:'uses-responsive-images', -description:'Properly size images', +id:'uses-responsive-images', +title:'Properly size images', scoreDisplayMode:ByteEfficiencyAudit.SCORING_MODES.NUMERIC, -helpText: +description: 'Serve images that are appropriately-sized to save cellular data '+ 'and improve load time. '+ '[Learn more](https://developers.google.com/web/tools/lighthouse/audits/oversized-images).', @@ -3316,7 +3327,7 @@ if(processed instanceof Error){ warnings.push(processed.message); -Sentry.captureException(processed,{tags:{audit:this.meta.name},level:'warning'}); +Sentry.captureException(processed,{tags:{audit:this.meta.id},level:'warning'}); return; } @@ -3327,20 +3338,20 @@ } }); -const results=Array.from(resultsMap.values()). +const items=Array.from(resultsMap.values()). filter(item=>item.wastedBytes>IGNORE_THRESHOLD_IN_BYTES); + const headings=[ -{key:'url',itemType:'thumbnail',text:''}, -{key:'url',itemType:'url',text:'URL'}, -{key:'totalBytes',itemType:'bytes',displayUnit:'kb',granularity:1,text:'Original'}, -{key:'wastedBytes',itemType:'bytes',displayUnit:'kb',granularity:1, -text:'Potential Savings'}]; +{key:'url',valueType:'thumbnail',label:''}, +{key:'url',valueType:'url',label:'URL'}, +{key:'totalBytes',valueType:'bytes',label:'Original'}, +{key:'wastedBytes',valueType:'bytes',label:'Potential Savings'}]; return{ warnings, -results, +items, headings}; }} @@ -3348,7 +3359,7 @@ module.exports=UsesResponsiveImages; -},{"../../lib/sentry":39,"../../lib/url-shim":"url","./byte-efficiency-audit":3}],"../audits/byte-efficiency/uses-text-compression":[function(require,module,exports){ +},{"../../lib/sentry":40,"../../lib/url-shim":"url","./byte-efficiency-audit":3}],"../audits/byte-efficiency/uses-text-compression":[function(require,module,exports){ @@ -3372,12 +3383,12 @@ static get meta(){ return{ -name:'uses-text-compression', +id:'uses-text-compression', scoreDisplayMode:ByteEfficiencyAudit.SCORING_MODES.NUMERIC, -description:'Enable text compression', -helpText:'Text-based responses should be served with compression (gzip, deflate or brotli)'+ -' to minimize total network bytes.'+ -' [Learn more](https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/optimize-encoding-and-transfer).', +title:'Enable text compression', +description:'Text-based responses should be served with compression (gzip, deflate or'+ +' brotli) to minimize total network bytes.'+ +' [Learn more](https://developers.google.com/web/tools/lighthouse/audits/text-compression).', requiredArtifacts:['ResponseCompression','devtoolsLogs']}; } @@ -3390,8 +3401,11 @@ const uncompressedResponses=artifacts.ResponseCompression; -const results=[]; +const items=[]; uncompressedResponses.forEach(record=>{ + +if(!record.gzipSize||record.gzipSize<0)return; + const originalSize=record.resourceSize; const gzipSize=record.gzipSize; const gzipSavings=originalSize-gzipSize; @@ -3407,28 +3421,28 @@ const url=URL.elideDataURI(record.url); -const isDuplicate=results.find(res=>res.url===url&& -res.totalBytes===record.resourceSize); +const isDuplicate=items.find(item=>item.url===url&& +item.totalBytes===record.resourceSize); if(isDuplicate){ return; } -results.push({ +items.push({ url, totalBytes:originalSize, wastedBytes:gzipSavings}); }); + const headings=[ -{key:'url',itemType:'url',text:'Uncompressed resource URL'}, -{key:'totalBytes',itemType:'bytes',displayUnit:'kb',granularity:1,text:'Original'}, -{key:'wastedBytes',itemType:'bytes',displayUnit:'kb',granularity:1, -text:'GZIP Savings'}]; +{key:'url',valueType:'url',label:'Uncompressed resource URL'}, +{key:'totalBytes',valueType:'bytes',label:'Original'}, +{key:'wastedBytes',valueType:'bytes',label:'GZIP Savings'}]; return{ -results, +items, headings}; }} @@ -3458,10 +3472,10 @@ static get meta(){ return{ -name:'uses-webp-images', -description:'Serve images in next-gen formats', +id:'uses-webp-images', +title:'Serve images in next-gen formats', scoreDisplayMode:ByteEfficiencyAudit.SCORING_MODES.NUMERIC, -helpText:'Image formats like JPEG 2000, JPEG XR, and WebP often provide better '+ +description:'Image formats like JPEG 2000, JPEG XR, and WebP often provide better '+ 'compression than PNG or JPEG, which means faster downloads and less data consumption. '+ '[Learn more](https://developers.google.com/web/tools/lighthouse/audits/webp).', requiredArtifacts:['OptimizedImages','devtoolsLogs']}; @@ -3486,7 +3500,7 @@ const images=artifacts.OptimizedImages; -const results=[]; +const items=[]; const warnings=[]; for(const image of images){ if(image.failed){ @@ -3499,7 +3513,7 @@ const url=URL.elideDataURI(image.url); const webpSavings=UsesWebPImages.computeSavings(image); -results.push({ +items.push({ url, fromProtocol:image.fromProtocol, isCrossOrigin:!image.isSameOrigin, @@ -3508,17 +3522,17 @@ } + const headings=[ -{key:'url',itemType:'thumbnail',text:''}, -{key:'url',itemType:'url',text:'URL'}, -{key:'totalBytes',itemType:'bytes',displayUnit:'kb',granularity:1,text:'Original'}, -{key:'wastedBytes',itemType:'bytes',displayUnit:'kb',granularity:1, -text:'Potential Savings'}]; +{key:'url',valueType:'thumbnail',label:''}, +{key:'url',valueType:'url',label:'URL'}, +{key:'totalBytes',valueType:'bytes',label:'Original'}, +{key:'wastedBytes',valueType:'bytes',label:'Potential Savings'}]; return{ warnings, -results, +items, headings}; }} @@ -3542,10 +3556,10 @@ static get meta(){ return{ -name:'content-width', -description:'Content is sized correctly for the viewport', -failureDescription:'Content is not sized correctly for the viewport', -helpText:'If the width of your app\'s content doesn\'t match the width '+ +id:'content-width', +title:'Content is sized correctly for the viewport', +failureTitle:'Content is not sized correctly for the viewport', +description:'If the width of your app\'s content doesn\'t match the width '+ 'of the viewport, your app might not be optimized for mobile screens. '+ '[Learn more](https://developers.google.com/web/tools/lighthouse/audits/content-sized-correctly-for-viewport).', requiredArtifacts:['ViewportDimensions']}; @@ -3601,10 +3615,10 @@ static get meta(){ return{ -name:'critical-request-chains', -description:'Critical Request Chains', +id:'critical-request-chains', +title:'Critical Request Chains', scoreDisplayMode:Audit.SCORING_MODES.INFORMATIVE, -helpText:'The Critical Request Chains below show you what resources are '+ +description:'The Critical Request Chains below show you what resources are '+ 'issued with a high priority. Consider reducing '+ 'the length of chains, reducing the download size of resources, or '+ 'deferring the download of unnecessary resources to improve page load. '+ @@ -3695,7 +3709,7 @@ url:request.url, startTime:request.startTime, endTime:request.endTime, -_responseReceivedTime:request._responseReceivedTime, +responseReceivedTime:request.responseReceivedTime, transferSize:request.transferSize}; @@ -3814,10 +3828,10 @@ static get meta(){ return{ -name:'deprecations', -description:'Avoids deprecated APIs', -failureDescription:'Uses deprecated API\'s', -helpText:'Deprecated APIs will eventually be removed from the browser. '+ +id:'deprecations', +title:'Avoids deprecated APIs', +failureTitle:'Uses deprecated API\'s', +description:'Deprecated APIs will eventually be removed from the browser. '+ '[Learn more](https://www.chromestatus.com/features#deprecated).', requiredArtifacts:['ChromeConsoleMessages']}; @@ -3887,10 +3901,10 @@ static get meta(){ return{ -name:'appcache-manifest', -description:'Avoids Application Cache', -failureDescription:'Uses Application Cache', -helpText:'Application Cache is deprecated. '+ +id:'appcache-manifest', +title:'Avoids Application Cache', +failureTitle:'Uses Application Cache', +description:'Application Cache is deprecated. '+ '[Learn more](https://developers.google.com/web/tools/lighthouse/audits/appcache).', requiredArtifacts:['AppCacheManifest']}; @@ -3913,6 +3927,81 @@ module.exports=AppCacheManifestAttr; +},{"../audit":2}],"../audits/dobetterweb/doctype":[function(require,module,exports){ + + + + + +'use strict'; + +const Audit=require('../audit'); + +class Doctype extends Audit{ + + + +static get meta(){ +return{ +id:'doctype', +title:'Page has the HTML doctype', +failureTitle:'Page is missing the HTML doctype', +description:'Specifying a doctype prevents the browser from switching to quirks-mode.'+ +'Read more on the '+ +'[MDN Web Docs page](https://developer.mozilla.org/en-US/docs/Glossary/Doctype)', +requiredArtifacts:['Doctype']}; + +} + + + + + +static audit(artifacts){ +if(!artifacts.Doctype){ +return{ +rawValue:false, +explanation:'Document must contain a doctype'}; + +} + + +const doctypeName=artifacts.Doctype.name.trim(); +const doctypePublicId=artifacts.Doctype.publicId; +const doctypeSystemId=artifacts.Doctype.systemId; + +if(doctypePublicId!==''){ +return{ +rawValue:false, +explanation:'Expected publicId to be an empty string'}; + +} + +if(doctypeSystemId!==''){ +return{ +rawValue:false, +explanation:'Expected systemId to be an empty string'}; + +} + + + + +if(doctypeName==='html'){ +return{ +rawValue:true}; + +}else{ +return{ +rawValue:false, +explanation:'Doctype name must be the lowercase string `html`'}; + +} +}} + + +module.exports=Doctype; + },{"../audit":2}],"../audits/dobetterweb/dom-size":[function(require,module,exports){ @@ -3945,10 +4034,10 @@ static get meta(){ return{ -name:'dom-size', -description:'Avoids an excessive DOM size', -failureDescription:'Uses an excessive DOM size', -helpText:'Browser engineers recommend pages contain fewer than '+ +id:'dom-size', +title:'Avoids an excessive DOM size', +failureTitle:'Uses an excessive DOM size', +description:'Browser engineers recommend pages contain fewer than '+ `~${Util.formatNumber(DOMSize.MAX_DOM_NODES)} DOM nodes. The sweet spot is a tree `+ `depth < ${MAX_DOM_TREE_DEPTH} elements and fewer than ${MAX_DOM_TREE_WIDTH} `+ 'children/parent element. A large DOM can increase memory usage, cause longer '+ @@ -4043,10 +4132,10 @@ static get meta(){ return{ -name:'external-anchors-use-rel-noopener', -description:'Links to cross-origin destinations are safe', -failureDescription:'Links to cross-origin destinations are unsafe', -helpText:'Add `rel="noopener"` or `rel="noreferrer"` to any external links to improve '+ +id:'external-anchors-use-rel-noopener', +title:'Links to cross-origin destinations are safe', +failureTitle:'Links to cross-origin destinations are unsafe', +description:'Add `rel="noopener"` or `rel="noreferrer"` to any external links to improve '+ 'performance and prevent security vulnerabilities. '+ '[Learn more](https://developers.google.com/web/tools/lighthouse/audits/noopener).', requiredArtifacts:['URL','AnchorsWithNoRelNoopener']}; @@ -4134,10 +4223,10 @@ static get meta(){ return{ -name:'geolocation-on-start', -description:'Avoids requesting the geolocation permission on page load', -failureDescription:'Requests the geolocation permission on page load', -helpText:'Users are mistrustful of or confused by sites that request their '+ +id:'geolocation-on-start', +title:'Avoids requesting the geolocation permission on page load', +failureTitle:'Requests the geolocation permission on page load', +description:'Users are mistrustful of or confused by sites that request their '+ 'location without context. Consider tying the request to user gestures instead. '+ '[Learn more](https://developers.google.com/web/tools/lighthouse/audits/geolocation-on-load).', requiredArtifacts:['ChromeConsoleMessages']}; @@ -4193,10 +4282,10 @@ static get meta(){ return{ -name:'no-document-write', -description:'Avoids `document.write()`', -failureDescription:'Uses `document.write()`', -helpText:'For users on slow connections, external scripts dynamically injected via '+ +id:'no-document-write', +title:'Avoids `document.write()`', +failureTitle:'Uses `document.write()`', +description:'For users on slow connections, external scripts dynamically injected via '+ '`document.write()` can delay page load by tens of seconds. '+ '[Learn more](https://developers.google.com/web/tools/lighthouse/audits/document-write).', requiredArtifacts:['ChromeConsoleMessages']}; @@ -4229,101 +4318,7 @@ module.exports=NoDocWriteAudit; -},{"../violation-audit":6}],"../audits/dobetterweb/no-mutation-events":[function(require,module,exports){ - - - - - - - - - - - -'use strict'; - -const URL=require('../../lib/url-shim'); -const Audit=require('../audit'); -const EventHelpers=require('../../lib/event-helpers'); - -class NoMutationEventsAudit extends Audit{ -static get MUTATION_EVENTS(){ -return[ -'DOMAttrModified', -'DOMAttributeNameChanged', -'DOMCharacterDataModified', -'DOMElementNameChanged', -'DOMNodeInserted', -'DOMNodeInsertedIntoDocument', -'DOMNodeRemoved', -'DOMNodeRemovedFromDocument', -'DOMSubtreeModified']; - -} - - - - -static get meta(){ -return{ -name:'no-mutation-events', -description:'Avoids Mutation Events in its own scripts', -failureDescription:'Uses Mutation Events in its own scripts', -helpText:'Mutation Events are deprecated and harm performance. Consider using Mutation '+ -'Observers instead. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/mutation-events).', -requiredArtifacts:['URL','EventListeners']}; - -} - - - - - -static audit(artifacts){ - -const warnings=[]; -const listeners=artifacts.EventListeners; - -const results=listeners.filter(loc=>{ -const isMutationEvent=this.MUTATION_EVENTS.includes(loc.type); -let sameHost=URL.hostsMatch(artifacts.URL.finalUrl,loc.url); - -if(!URL.isValid(loc.url)&&isMutationEvent){ -sameHost=true; -warnings.push(URL.INVALID_URL_DEBUG_STRING); -} - -return sameHost&&isMutationEvent; -}).map(EventHelpers.addFormattedCodeSnippet); - -const groupedResults=EventHelpers.groupCodeSnippetsByLocation(results); - -const headings=[ -{key:'url',itemType:'url',text:'URL'}, -{key:'type',itemType:'code',text:'Event'}, -{key:'line',itemType:'text',text:'Line'}, -{key:'col',itemType:'text',text:'Col'}, -{key:'pre',itemType:'code',text:'Snippet'}]; - -const details=NoMutationEventsAudit.makeTableDetails(headings,groupedResults); - -return{ -rawValue:groupedResults.length===0, -extendedInfo:{ -value:{ -results:groupedResults}}, - - -details, -warnings}; - -}} - - -module.exports=NoMutationEventsAudit; - -},{"../../lib/event-helpers":34,"../../lib/url-shim":"url","../audit":2}],"../audits/dobetterweb/no-vulnerable-libraries":[function(require,module,exports){ +},{"../violation-audit":6}],"../audits/dobetterweb/no-vulnerable-libraries":[function(require,module,exports){ @@ -4355,13 +4350,14 @@ static get meta(){ return{ -name:'no-vulnerable-libraries', -description:'Avoids front-end JavaScript libraries'+ +id:'no-vulnerable-libraries', +title:'Avoids front-end JavaScript libraries'+ ' with known security vulnerabilities', -failureDescription:'Includes front-end JavaScript libraries'+ +failureTitle:'Includes front-end JavaScript libraries'+ ' with known security vulnerabilities', -helpText:'Some third-party scripts may contain known security vulnerabilities '+ -' that are easily identified and exploited by attackers.', +description:'Some third-party scripts may contain known security vulnerabilities '+ +'that are easily identified and exploited by attackers. '+ +'[Learn more](https://developers.google.com/web/tools/lighthouse/audits/vulnerabilities).', requiredArtifacts:['JSLibraries']}; } @@ -4523,7 +4519,7 @@ module.exports=NoVulnerableLibrariesAudit; -},{"../../../third-party/snyk/snapshot.json":155,"../../lib/sentry":39,"../audit":2,"semver":150}],"../audits/dobetterweb/no-websql":[function(require,module,exports){ +},{"../../../third-party/snyk/snapshot.json":137,"../../lib/sentry":40,"../audit":2,"semver":132}],"../audits/dobetterweb/no-websql":[function(require,module,exports){ @@ -4545,10 +4541,10 @@ static get meta(){ return{ -name:'no-websql', -description:'Avoids WebSQL DB', -failureDescription:'Uses WebSQL DB', -helpText:'Web SQL is deprecated. Consider using IndexedDB instead. '+ +id:'no-websql', +title:'Avoids WebSQL DB', +failureTitle:'Uses WebSQL DB', +description:'Web SQL is deprecated. Consider using IndexedDB instead. '+ '[Learn more](https://developers.google.com/web/tools/lighthouse/audits/web-sql).', requiredArtifacts:['WebSQL']}; @@ -4594,10 +4590,10 @@ static get meta(){ return{ -name:'notification-on-start', -description:'Avoids requesting the notification permission on page load', -failureDescription:'Requests the notification permission on page load', -helpText:'Users are mistrustful of or confused by sites that request to send '+ +id:'notification-on-start', +title:'Avoids requesting the notification permission on page load', +failureTitle:'Requests the notification permission on page load', +description:'Users are mistrustful of or confused by sites that request to send '+ 'notifications without context. Consider tying the request to user gestures '+ 'instead. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/notifications-on-load).', requiredArtifacts:['ChromeConsoleMessages']}; @@ -4646,11 +4642,11 @@ static get meta(){ return{ -name:'password-inputs-can-be-pasted-into', -description:'Allows users to paste into password fields', -failureDescription:'Prevents users to paste into password fields', -helpText:'Preventing password pasting undermines good security policy. '+ -'[Learn more](https://www.ncsc.gov.uk/blog-post/let-them-paste-passwords).', +id:'password-inputs-can-be-pasted-into', +title:'Allows users to paste into password fields', +failureTitle:'Prevents users to paste into password fields', +description:'Preventing password pasting undermines good security policy. '+ +'[Learn more](https://developers.google.com/web/tools/lighthouse/audits/password-pasting).', requiredArtifacts:['PasswordInputsWithPreventedPaste']}; } @@ -4710,10 +4706,10 @@ static get meta(){ return{ -name:'uses-http2', -description:'Uses HTTP/2 for its own resources', -failureDescription:'Does not use HTTP/2 for all of its resources', -helpText:'HTTP/2 offers many benefits over HTTP/1.1, including binary headers, '+ +id:'uses-http2', +title:'Uses HTTP/2 for its own resources', +failureTitle:'Does not use HTTP/2 for all of its resources', +description:'HTTP/2 offers many benefits over HTTP/1.1, including binary headers, '+ 'multiplexing, and server push. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/http2).', requiredArtifacts:['URL','devtoolsLogs']}; @@ -4734,12 +4730,12 @@ const isOldHttp=/HTTP\/[01][.\d]?/i.test(record.protocol); if(!isOldHttp)return false; -const requestHost=new URL(record._url).host; +const requestHost=new URL(record.url).host; return requestHost===finalHost; }).map(record=>{ return{ protocol:record.protocol, -url:record._url}; +url:record.url}; }).filter(record=>{ if(seenURLs.has(record.url))return false; @@ -4799,10 +4795,10 @@ static get meta(){ return{ -name:'uses-passive-event-listeners', -description:'Uses passive listeners to improve scrolling performance', -failureDescription:'Does not use passive listeners to improve scrolling performance', -helpText:'Consider marking your touch and wheel event listeners as `passive` '+ +id:'uses-passive-event-listeners', +title:'Uses passive listeners to improve scrolling performance', +failureTitle:'Does not use passive listeners to improve scrolling performance', +description:'Consider marking your touch and wheel event listeners as `passive` '+ 'to improve your page\'s scroll performance. '+ '[Learn more](https://developers.google.com/web/tools/lighthouse/audits/passive-event-listeners).', requiredArtifacts:['ChromeConsoleMessages']}; @@ -4856,11 +4852,11 @@ static get meta(){ return{ -name:'errors-in-console', -description:'No browser errors logged to the console', -helpText:'Errors logged to the console indicate unresolved problems. '+ +id:'errors-in-console', +title:'No browser errors logged to the console', +description:'Errors logged to the console indicate unresolved problems. '+ 'They can come from network request failures and other browser concerns.', -failureDescription:'Browser errors were logged to the console', +failureTitle:'Browser errors were logged to the console', requiredArtifacts:['ChromeConsoleMessages','RuntimeExceptions']}; } @@ -4916,7 +4912,7 @@ module.exports=ErrorLogs; -},{"./audit":2}],"../audits/estimated-input-latency":[function(require,module,exports){ +},{"./audit":2}],"../audits/font-display":[function(require,module,exports){ @@ -4925,272 +4921,7 @@ 'use strict'; const Audit=require('./audit'); - -class EstimatedInputLatency extends Audit{ - - - -static get meta(){ -return{ -name:'estimated-input-latency', -description:'Estimated Input Latency', -helpText:'The score above is an estimate of how long your app takes to respond to user '+ -'input, in milliseconds, during the busiest 5s window of page load. If your '+ -'latency is higher than 50 ms, users may perceive your app as laggy. '+ -'[Learn more](https://developers.google.com/web/tools/lighthouse/audits/estimated-input-latency).', -scoreDisplayMode:Audit.SCORING_MODES.NUMERIC, -requiredArtifacts:['traces']}; - -} - - - - -static get defaultOptions(){ -return{ - -scorePODR:50, -scoreMedian:100}; - -} - - - - - - - - - -static async audit(artifacts,context){ -const trace=artifacts.traces[Audit.DEFAULT_PASS]; -const devtoolsLog=artifacts.devtoolsLogs[Audit.DEFAULT_PASS]; -const metricComputationData={trace,devtoolsLog,settings:context.settings}; -const metricResult=await artifacts.requestEstimatedInputLatency(metricComputationData); - -return{ -score:Audit.computeLogNormalScore( -metricResult.timing, -context.options.scorePODR, -context.options.scoreMedian), - -rawValue:metricResult.timing, -displayValue:['%d\xa0ms',metricResult.timing]}; - -}} - - -module.exports=EstimatedInputLatency; - -},{"./audit":2}],"../audits/first-contentful-paint":[function(require,module,exports){ - - - - - -'use strict'; - -const Audit=require('./audit'); -const Util=require('../report/html/renderer/util.js'); - -class FirstContentfulPaint extends Audit{ - - - -static get meta(){ -return{ -name:'first-contentful-paint', -description:'First Contentful Paint', -helpText:'First contentful paint marks the time at which the first text/image is painted. '+ -`[Learn more](https://developers.google.com/web/fundamentals/performance/user-centric-performance-metrics#first_paint_and_first_contentful_paint).`, -scoreDisplayMode:Audit.SCORING_MODES.NUMERIC, -requiredArtifacts:['traces','devtoolsLogs']}; - -} - - - - -static get defaultOptions(){ -return{ - - - -scorePODR:2900, -scoreMedian:4000}; - -} - - - - - - -static async audit(artifacts,context){ -const trace=artifacts.traces[Audit.DEFAULT_PASS]; -const devtoolsLog=artifacts.devtoolsLogs[Audit.DEFAULT_PASS]; -const metricComputationData={trace,devtoolsLog,settings:context.settings}; -const metricResult=await artifacts.requestFirstContentfulPaint(metricComputationData); - -return{ -score:Audit.computeLogNormalScore( -metricResult.timing, -context.options.scorePODR, -context.options.scoreMedian), - -rawValue:metricResult.timing, -displayValue:[Util.MS_DISPLAY_VALUE,metricResult.timing]}; - -}} - - -module.exports=FirstContentfulPaint; - -},{"../report/html/renderer/util.js":48,"./audit":2}],"../audits/first-cpu-idle":[function(require,module,exports){ - - - - - -'use strict'; - -const Audit=require('./audit'); -const Util=require('../report/html/renderer/util.js'); - -class FirstCPUIdle extends Audit{ - - - -static get meta(){ -return{ -name:'first-cpu-idle', -description:'First CPU Idle', -helpText:'First CPU Idle marks the first time at which the page\'s main thread is '+ -'quiet enough to handle input. '+ -'[Learn more](https://developers.google.com/web/tools/lighthouse/audits/first-interactive).', -scoreDisplayMode:Audit.SCORING_MODES.NUMERIC, -requiredArtifacts:['traces']}; - -} - - - - -static get defaultOptions(){ -return{ - - - -scorePODR:2900, -scoreMedian:6500}; - -} - - - - - - - - - -static async audit(artifacts,context){ -const trace=artifacts.traces[Audit.DEFAULT_PASS]; -const devtoolsLog=artifacts.devtoolsLogs[Audit.DEFAULT_PASS]; -const metricComputationData={trace,devtoolsLog,settings:context.settings}; -const metricResult=await artifacts.requestFirstCPUIdle(metricComputationData); - -return{ -score:Audit.computeLogNormalScore( -metricResult.timing, -context.options.scorePODR, -context.options.scoreMedian), - -rawValue:metricResult.timing, -displayValue:[Util.MS_DISPLAY_VALUE,metricResult.timing]}; - -}} - - -module.exports=FirstCPUIdle; - -},{"../report/html/renderer/util.js":48,"./audit":2}],"../audits/first-meaningful-paint":[function(require,module,exports){ - - - - - -'use strict'; - -const Audit=require('./audit'); -const Util=require('../report/html/renderer/util'); - -class FirstMeaningfulPaint extends Audit{ - - - -static get meta(){ -return{ -name:'first-meaningful-paint', -description:'First Meaningful Paint', -helpText:'First Meaningful Paint measures when the primary content of a page is visible. '+ -'[Learn more](https://developers.google.com/web/tools/lighthouse/audits/first-meaningful-paint).', -scoreDisplayMode:Audit.SCORING_MODES.NUMERIC, -requiredArtifacts:['traces']}; - -} - - - - -static get defaultOptions(){ -return{ - - - -scorePODR:2000, -scoreMedian:4000}; - -} - - - - - - - - - -static async audit(artifacts,context){ -const trace=artifacts.traces[Audit.DEFAULT_PASS]; -const devtoolsLog=artifacts.devtoolsLogs[Audit.DEFAULT_PASS]; -const metricComputationData={trace,devtoolsLog,settings:context.settings}; -const metricResult=await artifacts.requestFirstMeaningfulPaint(metricComputationData); - -return{ -score:Audit.computeLogNormalScore( -metricResult.timing, -context.options.scorePODR, -context.options.scoreMedian), - -rawValue:metricResult.timing, -displayValue:[Util.MS_DISPLAY_VALUE,metricResult.timing]}; - -}} - - -module.exports=FirstMeaningfulPaint; - -},{"../report/html/renderer/util":48,"./audit":2}],"../audits/font-display":[function(require,module,exports){ - - - - - -'use strict'; - -const Audit=require('./audit'); -const WebInspector=require('../lib/web-inspector'); +const NetworkRequest=require('../lib/network-request'); const allowedFontFaceDisplays=['block','fallback','optional','swap']; class FontDisplay extends Audit{ @@ -5199,10 +4930,10 @@ static get meta(){ return{ -name:'font-display', -description:'All text remains visible during webfont loads', -failureDescription:'Text is invisible while webfonts are loading', -helpText:'Leverage the font-display CSS feature to ensure text is user-visible while '+ +id:'font-display', +title:'All text remains visible during webfont loads', +failureTitle:'Text is invisible while webfonts are loading', +description:'Leverage the font-display CSS feature to ensure text is user-visible while '+ 'webfonts are loading. '+ '[Learn more](https://developers.google.com/web/updates/2016/02/font-display).', requiredArtifacts:['devtoolsLogs','Fonts']}; @@ -5224,7 +4955,7 @@ return artifacts.requestNetworkRecords(devtoolsLogs).then(networkRecords=>{ const results=networkRecords.filter(record=>{ -const isFont=record._resourceType===WebInspector.resourceTypes.Font; +const isFont=record.resourceType===NetworkRequest.TYPES.Font; return isFont; }). @@ -5263,7 +4994,7 @@ module.exports=FontDisplay; -},{"../lib/web-inspector":47,"./audit":2}],"../audits/image-aspect-ratio":[function(require,module,exports){ +},{"../lib/network-request":38,"./audit":2}],"../audits/image-aspect-ratio":[function(require,module,exports){ @@ -5280,7 +5011,7 @@ const Audit=require('./audit'); const URL=require('../lib/url-shim'); -const THRESHOLD=0.05; +const THRESHOLD_PX=2; @@ -5290,10 +5021,11 @@ static get meta(){ return{ -name:'image-aspect-ratio', -description:'Displays images with correct aspect ratio', -failureDescription:'Displays images with incorrect aspect ratio', -helpText:'Image display dimensions should match natural aspect ratio.', +id:'image-aspect-ratio', +title:'Displays images with correct aspect ratio', +failureTitle:'Displays images with incorrect aspect ratio', +description:'Image display dimensions should match natural aspect ratio. '+ +'[Learn more](https://developers.google.com/web/tools/lighthouse/audits/aspect-ratio).', requiredArtifacts:['ImageUsage']}; } @@ -5306,7 +5038,9 @@ const url=URL.elideDataURI(image.src); const actualAspectRatio=image.naturalWidth/image.naturalHeight; const displayedAspectRatio=image.width/image.height; -const doRatiosMatch=Math.abs(actualAspectRatio-displayedAspectRatio)<THRESHOLD; + +const targetDisplayHeight=image.width/actualAspectRatio; +const doRatiosMatch=Math.abs(targetDisplayHeight-image.height)<THRESHOLD_PX; if(!Number.isFinite(actualAspectRatio)|| !Number.isFinite(displayedAspectRatio)){ @@ -5371,89 +5105,7 @@ module.exports=ImageAspectRatio; -},{"../lib/url-shim":"url","./audit":2}],"../audits/interactive":[function(require,module,exports){ - - - - - -'use strict'; - -const Audit=require('./audit'); -const Util=require('../report/html/renderer/util'); - - - - - - - -class InteractiveMetric extends Audit{ - - - -static get meta(){ -return{ -name:'interactive', -description:'Time to Interactive', -helpText:'Interactive marks the time at which the page is fully interactive. '+ -'[Learn more](https://developers.google.com/web/tools/lighthouse/audits/consistently-interactive).', -scoreDisplayMode:Audit.SCORING_MODES.NUMERIC, -requiredArtifacts:['traces','devtoolsLogs']}; - -} - - - - -static get defaultOptions(){ -return{ - - - -scorePODR:2900, -scoreMedian:7300}; - -} - - - - - - -static async audit(artifacts,context){ -const trace=artifacts.traces[Audit.DEFAULT_PASS]; -const devtoolsLog=artifacts.devtoolsLogs[Audit.DEFAULT_PASS]; -const metricComputationData={trace,devtoolsLog,settings:context.settings}; -const metricResult=await artifacts.requestInteractive(metricComputationData); -const timeInMs=metricResult.timing; -const extendedInfo={ -timeInMs, -timestamp:metricResult.timestamp, - -optimistic:metricResult.optimisticEstimate&&metricResult.optimisticEstimate.timeInMs, - -pessimistic:metricResult.pessimisticEstimate&&metricResult.pessimisticEstimate.timeInMs}; - - -return{ -score:Audit.computeLogNormalScore( -timeInMs, -context.options.scorePODR, -context.options.scoreMedian), - -rawValue:timeInMs, -displayValue:[Util.MS_DISPLAY_VALUE,timeInMs], -extendedInfo:{ -value:extendedInfo}}; - - -}} - - -module.exports=InteractiveMetric; - -},{"../report/html/renderer/util":48,"./audit":2}],"../audits/is-on-https":[function(require,module,exports){ +},{"../lib/url-shim":"url","./audit":2}],"../audits/is-on-https":[function(require,module,exports){ @@ -5474,10 +5126,10 @@ static get meta(){ return{ -name:'is-on-https', -description:'Uses HTTPS', -failureDescription:'Does not use HTTPS', -helpText:'All sites should be protected with HTTPS, even ones that don\'t handle '+ +id:'is-on-https', +title:'Uses HTTPS', +failureTitle:'Does not use HTTPS', +description:'All sites should be protected with HTTPS, even ones that don\'t handle '+ 'sensitive data. HTTPS prevents intruders from tampering with or passively listening '+ 'in on the communications between your app and your users, and is a prerequisite for '+ 'HTTP/2 and many new web platform APIs. '+ @@ -5562,10 +5214,10 @@ static get meta(){ return{ -name:'load-fast-enough-for-pwa', -description:'Page load is fast enough on 3G', -failureDescription:'Page load is not fast enough on 3G', -helpText: +id:'load-fast-enough-for-pwa', +title:'Page load is fast enough on 3G', +failureTitle:'Page load is not fast enough on 3G', +description: 'A fast page load over a 3G network ensures a good mobile user experience. '+ '[Learn more](https://developers.google.com/web/tools/lighthouse/audits/fast-3g).', requiredArtifacts:['traces','devtoolsLogs']}; @@ -5617,7 +5269,7 @@ module.exports=LoadFastEnough4Pwa; -},{"../config/constants":8,"./audit":2,"lodash.isequal":144}],"../audits/mainthread-work-breakdown":[function(require,module,exports){ +},{"../config/constants":8,"./audit":2,"lodash.isequal":126}],"../audits/mainthread-work-breakdown":[function(require,module,exports){ @@ -5632,8 +5284,7 @@ const Audit=require('./audit'); const Util=require('../report/html/renderer/util'); - -const{taskToGroup}=require('../lib/task-groups'); +const{taskGroups}=require('../lib/task-groups'); class MainThreadWorkBreakdown extends Audit{ @@ -5641,11 +5292,11 @@ static get meta(){ return{ -name:'mainthread-work-breakdown', -description:'Minimizes main thread work', -failureDescription:'Has significant main thread work', +id:'mainthread-work-breakdown', +title:'Minimizes main thread work', +failureTitle:'Has significant main thread work', scoreDisplayMode:Audit.SCORING_MODES.NUMERIC, -helpText:'Consider reducing the time spent parsing, compiling and executing JS. '+ +description:'Consider reducing the time spent parsing, compiling and executing JS. '+ 'You may find delivering smaller JS payloads helps with this.', requiredArtifacts:['traces']}; @@ -5666,12 +5317,14 @@ -static getExecutionTimingsByCategory(timelineModel){ -const bottomUpByName=timelineModel.bottomUpGroupBy('EventName'); +static getExecutionTimingsByGroup(tasks){ const result=new Map(); -bottomUpByName.children.forEach((event,eventName)=> -result.set(eventName,event.selfTime)); + +for(const task of tasks){ +const originalTime=result.get(task.group.id)||0; +result.set(task.group.id,originalTime+task.selfTime); +} return result; } @@ -5685,40 +5338,34 @@ const settings=context.settings||{}; const trace=artifacts.traces[MainThreadWorkBreakdown.DEFAULT_PASS]; -const devtoolsTimelineModel=await artifacts.requestDevtoolsTimelineModel(trace); -const executionTimings=MainThreadWorkBreakdown.getExecutionTimingsByCategory( -devtoolsTimelineModel); - -let totalExecutionTime=0; - +const tasks=await artifacts.requestMainThreadTasks(trace); const multiplier=settings.throttlingMethod==='simulate'? settings.throttling.cpuSlowdownMultiplier:1; -const extendedInfo={}; -const categoryTotals={}; -const results=Array.from(executionTimings).map(([eventName,duration])=>{ -duration*=multiplier; -totalExecutionTime+=duration; -extendedInfo[eventName]=duration; -const groupName=taskToGroup[eventName]; +const executionTimings=MainThreadWorkBreakdown.getExecutionTimingsByGroup(tasks); -const categoryTotal=categoryTotals[groupName]||0; -categoryTotals[groupName]=categoryTotal+duration; +let totalExecutionTime=0; +const categoryTotals={}; +const results=Array.from(executionTimings).map(([groupId,rawDuration])=>{ +const duration=rawDuration*multiplier; +totalExecutionTime+=duration; + +const categoryTotal=categoryTotals[groupId]||0; +categoryTotals[groupId]=categoryTotal+duration; return{ -category:eventName, -group:groupName, +group:groupId, +groupLabel:taskGroups[groupId].label, duration:duration}; }); const headings=[ -{key:'group',itemType:'text',text:'Category'}, -{key:'category',itemType:'text',text:'Work'}, -{key:'duration',itemType:'ms',granularity:1,text:'Time spent'}]; +{key:'groupLabel',itemType:'text',text:'Category'}, +{key:'duration',itemType:'ms',granularity:1,text:'Time Spent'}]; -results.stableSort((a,b)=>categoryTotals[b.group]-categoryTotals[a.group]); +results.sort((a,b)=>categoryTotals[b.group]-categoryTotals[a.group]); const tableDetails=MainThreadWorkBreakdown.makeTableDetails(headings,results); const score=Audit.computeLogNormalScore( @@ -5731,17 +5378,14 @@ score, rawValue:totalExecutionTime, displayValue:[Util.MS_DISPLAY_VALUE,totalExecutionTime], -details:tableDetails, -extendedInfo:{ -value:extendedInfo}}; - +details:tableDetails}; }} module.exports=MainThreadWorkBreakdown; -},{"../lib/task-groups":42,"../report/html/renderer/util":48,"./audit":2}],"../audits/manifest-short-name-length":[function(require,module,exports){ +},{"../lib/task-groups":43,"../report/html/renderer/util":48,"./audit":2}],"../audits/manifest-short-name-length":[function(require,module,exports){ @@ -5757,11 +5401,10 @@ static get meta(){ return{ -name:'manifest-short-name-length', -description:'Manifest\'s `short_name` won\'t be truncated when displayed on homescreen', -failureDescription:'Manifest\'s `short_name` will be truncated when displayed '+ -'on homescreen', -helpText:'Make your app\'s `short_name` fewer than 12 characters to '+ +id:'manifest-short-name-length', +title:'The `short_name` won\'t be truncated on the homescreen', +failureTitle:'The `short_name` will be truncated on the homescreen', +description:'Make your app\'s `short_name` fewer than 12 characters to '+ 'ensure that it\'s not truncated on homescreens. [Learn '+ 'more](https://developers.google.com/web/tools/lighthouse/audits/manifest-short_name-is-not-truncated).', requiredArtifacts:['Manifest']}; @@ -5772,27 +5415,37 @@ -static audit(artifacts){ -return artifacts.requestManifestValues(artifacts.Manifest).then(manifestValues=>{ +static async audit(artifacts){ +const manifestValues=await artifacts.requestManifestValues(artifacts.Manifest); + if(manifestValues.isParseFailure){ return{ -rawValue:false}; +rawValue:true, +notApplicable:true}; } -const hasShortName=manifestValues.allChecks.find(i=>i.id==='hasShortName'); -if(!hasShortName||!hasShortName.passing){ +const shortNameCheck=manifestValues.allChecks.find(i=>i.id==='hasShortName'); +const shortNameLengthCheck=manifestValues.allChecks.find(i=>i.id==='shortNameLength'); + + +if(shortNameCheck&&!shortNameCheck.passing){ +return{ +rawValue:true, +notApplicable:true}; + +} + +if(shortNameLengthCheck&&!shortNameLengthCheck.passing){ return{ rawValue:false, -explanation:'No short_name found in manifest.'}; +explanation:`Failure: ${shortNameLengthCheck.failureText}.`}; } -const isShortEnough=manifestValues.allChecks.find(i=>i.id==='shortNameLength'); return{ -rawValue:!!isShortEnough&&isShortEnough.passing}; +rawValue:true}; -}); }} @@ -5819,10 +5472,10 @@ static get meta(){ return Object.assign({ -name:'pwa-cross-browser', -helpText:'To reach the most number of users, sites should work across '+ +id:'pwa-cross-browser', +description:'To reach the most number of users, sites should work across '+ 'every major browser. [Learn more](https://developers.google.com/web/progressive-web-apps/checklist#site-works-cross-browser).', -description:'Site works cross-browser'}, +title:'Site works cross-browser'}, super.partialMeta); }} @@ -5849,10 +5502,10 @@ static get meta(){ return Object.assign({ -name:'pwa-each-page-has-url', -helpText:'Ensure individual pages are deep linkable via the URLs and that URLs are '+ +id:'pwa-each-page-has-url', +description:'Ensure individual pages are deep linkable via the URLs and that URLs are '+ 'unique for the purpose of shareability on social media. [Learn more](https://developers.google.com/web/progressive-web-apps/checklist#each-page-has-a-url).', -description:'Each page has a URL'}, +title:'Each page has a URL'}, super.partialMeta); }} @@ -5879,17 +5532,430 @@ static get meta(){ return Object.assign({ -name:'pwa-page-transitions', -helpText:'Transitions should feel snappy as you tap around, even on a slow network, a key '+ -'to perceived performance. [Learn more](https://developers.google.com/web/progressive-web-apps/checklist#page-transitions-dont-feel-like-they-block-on-the-network).', -description:'Page transitions don\'t feel like they block on the network'}, +id:'pwa-page-transitions', +description:'Transitions should feel snappy as you tap around, even on a slow network, a '+ +'key to perceived performance. [Learn more](https://developers.google.com/web/progressive-web-apps/checklist#page-transitions-dont-feel-like-they-block-on-the-network).', +title:'Page transitions don\'t feel like they block on the network'}, super.partialMeta); }} module.exports=PWAPageTransitions; -},{"./manual-audit":4}],"../audits/metrics":[function(require,module,exports){ +},{"./manual-audit":4}],"../audits/metrics/estimated-input-latency":[function(require,module,exports){ + + + + + +'use strict'; + +const Audit=require('../audit'); + +class EstimatedInputLatency extends Audit{ + + + +static get meta(){ +return{ +id:'estimated-input-latency', +title:'Estimated Input Latency', +description:'The score above is an estimate of how long your app takes to respond to user '+ +'input, in milliseconds, during the busiest 5s window of page load. If your '+ +'latency is higher than 50 ms, users may perceive your app as laggy. '+ +'[Learn more](https://developers.google.com/web/tools/lighthouse/audits/estimated-input-latency).', +scoreDisplayMode:Audit.SCORING_MODES.NUMERIC, +requiredArtifacts:['traces']}; + +} + + + + +static get defaultOptions(){ +return{ + +scorePODR:50, +scoreMedian:100}; + +} + + + + + + + + + +static async audit(artifacts,context){ +const trace=artifacts.traces[Audit.DEFAULT_PASS]; +const devtoolsLog=artifacts.devtoolsLogs[Audit.DEFAULT_PASS]; +const metricComputationData={trace,devtoolsLog,settings:context.settings}; +const metricResult=await artifacts.requestEstimatedInputLatency(metricComputationData); + +return{ +score:Audit.computeLogNormalScore( +metricResult.timing, +context.options.scorePODR, +context.options.scoreMedian), + +rawValue:metricResult.timing, +displayValue:['%d\xa0ms',metricResult.timing]}; + +}} + + +module.exports=EstimatedInputLatency; + +},{"../audit":2}],"../audits/metrics/first-contentful-paint":[function(require,module,exports){ + + + + + +'use strict'; + +const Audit=require('../audit'); +const Util=require('../../report/html/renderer/util.js'); + +class FirstContentfulPaint extends Audit{ + + + +static get meta(){ +return{ +id:'first-contentful-paint', +title:'First Contentful Paint', +description:'First contentful paint marks the time at which the first text/image is '+ +`painted. [Learn more](https://developers.google.com/web/fundamentals/performance/user-centric-performance-metrics#first_paint_and_first_contentful_paint).`, +scoreDisplayMode:Audit.SCORING_MODES.NUMERIC, +requiredArtifacts:['traces','devtoolsLogs']}; + +} + + + + +static get defaultOptions(){ +return{ + + + +scorePODR:2000, +scoreMedian:4000}; + +} + + + + + + +static async audit(artifacts,context){ +const trace=artifacts.traces[Audit.DEFAULT_PASS]; +const devtoolsLog=artifacts.devtoolsLogs[Audit.DEFAULT_PASS]; +const metricComputationData={trace,devtoolsLog,settings:context.settings}; +const metricResult=await artifacts.requestFirstContentfulPaint(metricComputationData); + +return{ +score:Audit.computeLogNormalScore( +metricResult.timing, +context.options.scorePODR, +context.options.scoreMedian), + +rawValue:metricResult.timing, +displayValue:[Util.MS_DISPLAY_VALUE,metricResult.timing]}; + +}} + + +module.exports=FirstContentfulPaint; + +},{"../../report/html/renderer/util.js":48,"../audit":2}],"../audits/metrics/first-cpu-idle":[function(require,module,exports){ + + + + + +'use strict'; + +const Audit=require('../audit'); +const Util=require('../../report/html/renderer/util.js'); + +class FirstCPUIdle extends Audit{ + + + +static get meta(){ +return{ +id:'first-cpu-idle', +title:'First CPU Idle', +description:'First CPU Idle marks the first time at which the page\'s main thread is '+ +'quiet enough to handle input. '+ +'[Learn more](https://developers.google.com/web/tools/lighthouse/audits/first-interactive).', +scoreDisplayMode:Audit.SCORING_MODES.NUMERIC, +requiredArtifacts:['traces']}; + +} + + + + +static get defaultOptions(){ +return{ + + + +scorePODR:2900, +scoreMedian:6500}; + +} + + + + + + + + + +static async audit(artifacts,context){ +const trace=artifacts.traces[Audit.DEFAULT_PASS]; +const devtoolsLog=artifacts.devtoolsLogs[Audit.DEFAULT_PASS]; +const metricComputationData={trace,devtoolsLog,settings:context.settings}; +const metricResult=await artifacts.requestFirstCPUIdle(metricComputationData); + +return{ +score:Audit.computeLogNormalScore( +metricResult.timing, +context.options.scorePODR, +context.options.scoreMedian), + +rawValue:metricResult.timing, +displayValue:[Util.MS_DISPLAY_VALUE,metricResult.timing]}; + +}} + + +module.exports=FirstCPUIdle; + +},{"../../report/html/renderer/util.js":48,"../audit":2}],"../audits/metrics/first-meaningful-paint":[function(require,module,exports){ + + + + + +'use strict'; + +const Audit=require('../audit'); +const Util=require('../../report/html/renderer/util'); + +class FirstMeaningfulPaint extends Audit{ + + + +static get meta(){ +return{ +id:'first-meaningful-paint', +title:'First Meaningful Paint', +description:'First Meaningful Paint measures when the primary content of a page is '+ +'visible. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/first-meaningful-paint).', +scoreDisplayMode:Audit.SCORING_MODES.NUMERIC, +requiredArtifacts:['traces']}; + +} + + + + +static get defaultOptions(){ +return{ + + + +scorePODR:2000, +scoreMedian:4000}; + +} + + + + + + + + + +static async audit(artifacts,context){ +const trace=artifacts.traces[Audit.DEFAULT_PASS]; +const devtoolsLog=artifacts.devtoolsLogs[Audit.DEFAULT_PASS]; +const metricComputationData={trace,devtoolsLog,settings:context.settings}; +const metricResult=await artifacts.requestFirstMeaningfulPaint(metricComputationData); + +return{ +score:Audit.computeLogNormalScore( +metricResult.timing, +context.options.scorePODR, +context.options.scoreMedian), + +rawValue:metricResult.timing, +displayValue:[Util.MS_DISPLAY_VALUE,metricResult.timing]}; + +}} + + +module.exports=FirstMeaningfulPaint; + +},{"../../report/html/renderer/util":48,"../audit":2}],"../audits/metrics/interactive":[function(require,module,exports){ + + + + + +'use strict'; + +const Audit=require('../audit'); +const Util=require('../../report/html/renderer/util'); + + + + + + + +class InteractiveMetric extends Audit{ + + + +static get meta(){ +return{ +id:'interactive', +title:'Time to Interactive', +description:'Interactive marks the time at which the page is fully interactive. '+ +'[Learn more](https://developers.google.com/web/tools/lighthouse/audits/consistently-interactive).', +scoreDisplayMode:Audit.SCORING_MODES.NUMERIC, +requiredArtifacts:['traces','devtoolsLogs']}; + +} + + + + +static get defaultOptions(){ +return{ + + + +scorePODR:2900, +scoreMedian:7300}; + +} + + + + + + +static async audit(artifacts,context){ +const trace=artifacts.traces[Audit.DEFAULT_PASS]; +const devtoolsLog=artifacts.devtoolsLogs[Audit.DEFAULT_PASS]; +const metricComputationData={trace,devtoolsLog,settings:context.settings}; +const metricResult=await artifacts.requestInteractive(metricComputationData); +const timeInMs=metricResult.timing; +const extendedInfo={ +timeInMs, +timestamp:metricResult.timestamp, + +optimistic:metricResult.optimisticEstimate&&metricResult.optimisticEstimate.timeInMs, + +pessimistic:metricResult.pessimisticEstimate&&metricResult.pessimisticEstimate.timeInMs}; + + +return{ +score:Audit.computeLogNormalScore( +timeInMs, +context.options.scorePODR, +context.options.scoreMedian), + +rawValue:timeInMs, +displayValue:[Util.MS_DISPLAY_VALUE,timeInMs], +extendedInfo:{ +value:extendedInfo}}; + + +}} + + +module.exports=InteractiveMetric; + +},{"../../report/html/renderer/util":48,"../audit":2}],"../audits/metrics/speed-index":[function(require,module,exports){ + + + + + +'use strict'; + +const Audit=require('../audit'); +const Util=require('../../report/html/renderer/util'); + +class SpeedIndex extends Audit{ + + + +static get meta(){ +return{ +id:'speed-index', +title:'Speed Index', +description:'Speed Index shows how quickly the contents of a page are visibly populated. '+ +'[Learn more](https://developers.google.com/web/tools/lighthouse/audits/speed-index).', +scoreDisplayMode:Audit.SCORING_MODES.NUMERIC, +requiredArtifacts:['traces','devtoolsLogs']}; + +} + + + + +static get defaultOptions(){ +return{ + + + +scorePODR:2900, +scoreMedian:5800}; + +} + + + + + + + + +static async audit(artifacts,context){ +const trace=artifacts.traces[Audit.DEFAULT_PASS]; +const devtoolsLog=artifacts.devtoolsLogs[Audit.DEFAULT_PASS]; +const metricComputationData={trace,devtoolsLog,settings:context.settings}; +const metricResult=await artifacts.requestSpeedIndex(metricComputationData); + +return{ +score:Audit.computeLogNormalScore( +metricResult.timing, +context.options.scorePODR, +context.options.scoreMedian), + +rawValue:metricResult.timing, +displayValue:[Util.MS_DISPLAY_VALUE,metricResult.timing]}; + +}} + + +module.exports=SpeedIndex; + +},{"../../report/html/renderer/util":48,"../audit":2}],"../audits/metrics":[function(require,module,exports){ @@ -5905,10 +5971,10 @@ static get meta(){ return{ -name:'metrics', +id:'metrics', scoreDisplayMode:Audit.SCORING_MODES.INFORMATIVE, -description:'Metrics', -helpText:'Collects all available metrics.', +title:'Metrics', +description:'Collects all available metrics.', requiredArtifacts:['traces','devtoolsLogs']}; } @@ -6037,7 +6103,6 @@ - 'use strict'; const Audit=require('./audit'); @@ -6056,10 +6121,10 @@ static get meta(){ return{ -name:'mixed-content', -description:'All resources loaded are secure', -failureDescription:'Some insecure resources can be upgraded to HTTPS', -helpText:`Mixed content warnings can prevent you from upgrading to HTTPS. +id:'mixed-content', +title:'All resources loaded are secure', +failureTitle:'Some insecure resources can be upgraded to HTTPS', +description:`Mixed content warnings can prevent you from upgrading to HTTPS. This audit shows which insecure resources this page uses that can be upgraded to HTTPS. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/mixed-content)`, requiredArtifacts:['devtoolsLogs','MixedContent']}; @@ -6072,18 +6137,6 @@ - - -static isSecureRecord(record){ -return record.securityState()==='secure'||record.protocol==='data'; -} - - - - - - - static upgradeURL(url){ const parsedURL=new URL(url); parsedURL.protocol='https:'; @@ -6109,7 +6162,7 @@ -static displayURL(url){ +static displayURL(url=''){ const displayOptions={ numPathParts:4, preserveQuery:false, @@ -6134,15 +6187,15 @@ return Promise.all(computedArtifacts).then(([defaultRecords,upgradedRecords])=>{ const insecureRecords=defaultRecords.filter( -record=>!MixedContent.isSecureRecord(record)); +record=>!record.isSecure); const secureRecords=defaultRecords.filter( -record=>MixedContent.isSecureRecord(record)); +record=>record.isSecure); const upgradePassHosts=new Set(); const upgradePassSecureHosts=new Set(); upgradedRecords.forEach(record=>{ upgradePassHosts.add(new URL(record.url).hostname); -if(MixedContent.isSecureRecord(record)&&record.finished&&!record.failed){ +if(record.isSecure&&record.finished&&!record.failed){ upgradePassSecureHosts.add(new URL(record.url).hostname); } }); @@ -6161,7 +6214,7 @@ const resource={ host:new URL(record.url).hostname, fullUrl:record.url, -referrerDocUrl:this.displayURL(record._documentURL)}; +referrerDocUrl:this.displayURL(record.documentURL)}; if(!upgradePassSecureHosts.has(resource.host))continue; @@ -6211,10 +6264,10 @@ static get meta(){ return{ -name:'network-requests', +id:'network-requests', scoreDisplayMode:Audit.SCORING_MODES.INFORMATIVE, -description:'Network Requests', -helpText:'Lists the network requests that were made during page load.', +title:'Network Requests', +description:'Lists the network requests that were made during page load.', requiredArtifacts:['devtoolsLogs']}; } @@ -6231,15 +6284,19 @@ Infinity); + +const timeToMs=time=>time<earliestStartTime||!Number.isFinite(time)? +undefined:(time-earliestStartTime)*1000; + const results=records.map(record=>{ return{ url:URL.elideDataURI(record.url), -startTime:(record.startTime-earliestStartTime)*1000, -endTime:(record.endTime-earliestStartTime)*1000, +startTime:timeToMs(record.startTime), +endTime:timeToMs(record.endTime), transferSize:record.transferSize, statusCode:record.statusCode, -mimeType:record._mimeType, -resourceType:record._resourceType&&record._resourceType._name}; +mimeType:record.mimeType, +resourceType:record.resourceType}; }); @@ -6264,7 +6321,6 @@ return{ score:1, rawValue:results.length, -extendedInfo:{value:results}, details:tableDetails}; }); @@ -6295,9 +6351,9 @@ static get meta(){ return{ -name:'predictive-perf', -description:'Predicted Performance (beta)', -helpText: +id:'predictive-perf', +title:'Predicted Performance (beta)', +description: 'Predicted performance evaluates how your site will perform under '+ 'a 3G connection on a mobile device.', scoreDisplayMode:Audit.SCORING_MODES.NUMERIC, @@ -6381,11 +6437,11 @@ static get meta(){ return{ -name:'redirects-http', -description:'Redirects HTTP traffic to HTTPS', -failureDescription:'Does not redirect HTTP traffic to HTTPS', -helpText:'If you\'ve already set up HTTPS, make sure that you redirect all HTTP traffic '+ -'to HTTPS. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/http-redirects-to-https).', +id:'redirects-http', +title:'Redirects HTTP traffic to HTTPS', +failureTitle:'Does not redirect HTTP traffic to HTTPS', +description:'If you\'ve already set up HTTPS, make sure that you redirect all HTTP '+ +'traffic to HTTPS. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/http-redirects-to-https).', requiredArtifacts:['HTTPRedirect']}; } @@ -6420,10 +6476,10 @@ static get meta(){ return{ -name:'redirects', -description:'Avoid multiple page redirects', +id:'redirects', +title:'Avoid multiple page redirects', scoreDisplayMode:Audit.SCORING_MODES.NUMERIC, -helpText:'Redirects introduce additional delays before the page can be loaded. [Learn more](https://developers.google.com/speed/docs/insights/AvoidRedirects).', +description:'Redirects introduce additional delays before the page can be loaded. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/redirects).', requiredArtifacts:['URL','devtoolsLogs','traces']}; } @@ -6481,7 +6537,6 @@ throw new Error('Could not find redirects in graph'); } - const wastedMs=redirectedTiming.startTime-initialTiming.startTime; totalWastedMs+=wastedMs; @@ -6538,10 +6593,10 @@ static get meta(){ return{ -name:'screenshot-thumbnails', +id:'screenshot-thumbnails', scoreDisplayMode:Audit.SCORING_MODES.INFORMATIVE, -description:'Screenshot Thumbnails', -helpText:'This is what the load of your site looked like.', +title:'Screenshot Thumbnails', +description:'This is what the load of your site looked like.', requiredArtifacts:['traces','devtoolsLogs']}; } @@ -6661,7 +6716,7 @@ module.exports=ScreenshotThumbnails; -},{"../lib/errors":33,"./audit":2,"jpeg-js":140}],"../audits/seo/canonical":[function(require,module,exports){ +},{"../lib/errors":33,"./audit":2,"jpeg-js":122}],"../audits/seo/canonical":[function(require,module,exports){ @@ -6724,10 +6779,10 @@ static get meta(){ return{ -name:'canonical', -description:'Document has a valid `rel=canonical`', -failureDescription:'Document does not have a valid `rel=canonical`', -helpText:'Canonical links suggest which URL to show in search results. '+ +id:'canonical', +title:'Document has a valid `rel=canonical`', +failureTitle:'Document does not have a valid `rel=canonical`', +description:'Canonical links suggest which URL to show in search results. '+ '[Learn more](https://developers.google.com/web/tools/lighthouse/audits/canonical).', requiredArtifacts:['Canonical','Hreflang','URL']}; @@ -6748,7 +6803,7 @@ let hreflangs=[]; -mainResource._responseHeaders&&mainResource._responseHeaders. +mainResource.responseHeaders&&mainResource.responseHeaders. filter(h=>h.name.toLowerCase()===LINK_HEADER). forEach(h=>{ canonicals=canonicals.concat(getCanonicalLinksFromHeader(h.value)); @@ -6838,7 +6893,7 @@ module.exports=Canonical; -},{"../../lib/url-shim":"url","../audit":2,"http-link-header":137}],"../audits/seo/font-size":[function(require,module,exports){ +},{"../../lib/url-shim":"url","../audit":2,"http-link-header":119}],"../audits/seo/font-size":[function(require,module,exports){ @@ -7025,10 +7080,10 @@ static get meta(){ return{ -name:'font-size', -description:'Document uses legible font sizes', -failureDescription:'Document doesn\'t use legible font sizes', -helpText:'Font sizes less than 12px are too small to be legible and require mobile '+ +id:'font-size', +title:'Document uses legible font sizes', +failureTitle:'Document doesn\'t use legible font sizes', +description:'Font sizes less than 12px are too small to be legible and require mobile '+ 'visitors to “pinch to zoom” in order to read. Strive to have >60% of page text ≥12px. '+ '[Learn more](https://developers.google.com/web/tools/lighthouse/audits/font-sizes).', requiredArtifacts:['FontSize','URL','Viewport']}; @@ -7206,10 +7261,10 @@ static get meta(){ return{ -name:'hreflang', -description:'Document has a valid `hreflang`', -failureDescription:'Document doesn\'t have a valid `hreflang`', -helpText:'hreflang links tell search engines what version of a page they should '+ +id:'hreflang', +title:'Document has a valid `hreflang`', +failureTitle:'Document doesn\'t have a valid `hreflang`', +description:'hreflang links tell search engines what version of a page they should '+ 'list in search results for a given language or region. [Learn more]'+ '(https://developers.google.com/web/tools/lighthouse/audits/hreflang).', requiredArtifacts:['Hreflang','URL']}; @@ -7242,7 +7297,7 @@ }); } -mainResource._responseHeaders&&mainResource._responseHeaders. +mainResource.responseHeaders&&mainResource.responseHeaders. filter(h=>h.name.toLowerCase()===LINK_HEADER&&!headerHasValidHreflangs(h.value)). forEach(h=>invalidHreflangs.push({source:`${h.name}: ${h.value}`})); @@ -7262,7 +7317,7 @@ module.exports=Hreflang; }).call(this,typeof global!=="undefined"?global:typeof self!=="undefined"?self:typeof window!=="undefined"?window:{}); -},{"../audit":2,"axe-core/lib/commons/utils/valid-langs.js":98,"http-link-header":137}],"../audits/seo/http-status-code":[function(require,module,exports){ +},{"../audit":2,"axe-core/lib/commons/utils/valid-langs.js":98,"http-link-header":119}],"../audits/seo/http-status-code":[function(require,module,exports){ @@ -7280,10 +7335,10 @@ static get meta(){ return{ -name:'http-status-code', -description:'Page has successful HTTP status code', -failureDescription:'Page has unsuccessful HTTP status code', -helpText:'Pages with unsuccessful HTTP status codes may not be indexed properly. '+ +id:'http-status-code', +title:'Page has successful HTTP status code', +failureTitle:'Page has unsuccessful HTTP status code', +description:'Pages with unsuccessful HTTP status codes may not be indexed properly. '+ '[Learn more]'+ '(https://developers.google.com/web/tools/lighthouse/audits/successful-http-code).', requiredArtifacts:['devtoolsLogs','URL']}; @@ -7384,10 +7439,10 @@ static get meta(){ return{ -name:'is-crawlable', -description:'Page isn’t blocked from indexing', -failureDescription:'Page is blocked from indexing', -helpText:'Search engines are unable to include your pages in search results '+ +id:'is-crawlable', +title:'Page isn’t blocked from indexing', +failureTitle:'Page is blocked from indexing', +description:'Search engines are unable to include your pages in search results '+ 'if they don\'t have permission to crawl them. [Learn '+ 'more](https://developers.google.com/web/tools/lighthouse/audits/indexing).', requiredArtifacts:['MetaRobots','RobotsTxt','URL']}; @@ -7419,7 +7474,7 @@ } } -mainResource._responseHeaders&&mainResource._responseHeaders. +mainResource.responseHeaders&&mainResource.responseHeaders. filter(h=>h.name.toLowerCase()===ROBOTS_HEADER&&!hasUserAgent(h.value)&& hasBlockingDirective(h.value)). forEach(h=>blockingDirectives.push({source:`${h.name}: ${h.value}`})); @@ -7453,7 +7508,7 @@ module.exports=IsCrawlable; -},{"../../lib/url-shim":"url","../audit":2,"robots-parser":149}],"../audits/seo/link-text":[function(require,module,exports){ +},{"../../lib/url-shim":"url","../audit":2,"robots-parser":131}],"../audits/seo/link-text":[function(require,module,exports){ @@ -7481,10 +7536,10 @@ static get meta(){ return{ -name:'link-text', -description:'Links have descriptive text', -failureDescription:'Links do not have descriptive text', -helpText:'Descriptive link text helps search engines understand your content. '+ +id:'link-text', +title:'Links have descriptive text', +failureTitle:'Links do not have descriptive text', +description:'Descriptive link text helps search engines understand your content. '+ '[Learn more](https://developers.google.com/web/tools/lighthouse/audits/descriptive-link-text).', requiredArtifacts:['URL','CrawlableLinks']}; @@ -7550,9 +7605,9 @@ static get meta(){ return Object.assign({ -name:'mobile-friendly', -helpText:'Take the [Mobile-Friendly Test](https://search.google.com/test/mobile-friendly) to check for audits not covered by Lighthouse, like sizing tap targets appropriately. [Learn more](https://developers.google.com/search/mobile-sites/).', -description:'Page is mobile friendly'}, +id:'mobile-friendly', +description:'Take the [Mobile-Friendly Test](https://search.google.com/test/mobile-friendly) to check for audits not covered by Lighthouse, like sizing tap targets appropriately. [Learn more](https://developers.google.com/search/mobile-sites/).', +title:'Page is mobile friendly'}, super.partialMeta); }} @@ -7579,9 +7634,9 @@ static get meta(){ return Object.assign({ -name:'structured-data', -helpText:'Run the [Structured Data Testing Tool](https://search.google.com/structured-data/testing-tool/) and the [Structured Data Linter](http://linter.structured-data.org/) to validate structured data. [Learn more](https://developers.google.com/search/docs/guides/mark-up-content).', -description:'Structured data is valid'}, +id:'structured-data', +description:'Run the [Structured Data Testing Tool](https://search.google.com/structured-data/testing-tool/) and the [Structured Data Linter](http://linter.structured-data.org/) to validate structured data. [Learn more](https://developers.google.com/search/docs/guides/mark-up-content).', +title:'Structured data is valid'}, super.partialMeta); }} @@ -7604,10 +7659,10 @@ static get meta(){ return{ -name:'meta-description', -description:'Document has a meta description', -failureDescription:'Document does not have a meta description', -helpText:'Meta descriptions may be included in search results to concisely summarize '+ +id:'meta-description', +title:'Document has a meta description', +failureTitle:'Document does not have a meta description', +description:'Meta descriptions may be included in search results to concisely summarize '+ 'page content. '+ '[Learn more](https://developers.google.com/web/tools/lighthouse/audits/description).', requiredArtifacts:['MetaDescription']}; @@ -7715,10 +7770,10 @@ static get meta(){ return{ -name:'plugins', -description:'Document avoids plugins', -failureDescription:'Document uses plugins', -helpText:'Search engines can\'t index plugin content, and '+ +id:'plugins', +title:'Document avoids plugins', +failureTitle:'Document uses plugins', +description:'Search engines can\'t index plugin content, and '+ 'many devices restrict plugins or don\'t support them. '+ '[Learn more](https://developers.google.com/web/tools/lighthouse/audits/plugins).', requiredArtifacts:['EmbeddedContent']}; @@ -7961,10 +8016,10 @@ static get meta(){ return{ -name:'robots-txt', -description:'robots.txt is valid', -failureDescription:'robots.txt is not valid', -helpText:'If your robots.txt file is malformed, crawlers may not be able to understand '+ +id:'robots-txt', +title:'robots.txt is valid', +failureTitle:'robots.txt is not valid', +description:'If your robots.txt file is malformed, crawlers may not be able to understand '+ 'how you want your website to be crawled or indexed.', requiredArtifacts:['RobotsTxt']}; @@ -8047,10 +8102,10 @@ static get meta(){ return{ -name:'service-worker', -description:'Registers a service worker', -failureDescription:'Does not register a service worker', -helpText:'The service worker is the technology that enables your app to use many '+ +id:'service-worker', +title:'Registers a service worker', +failureTitle:'Does not register a service worker', +description:'The service worker is the technology that enables your app to use many '+ 'Progressive Web App features, such as offline, add to homescreen, and push '+ 'notifications. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/registered-service-worker).', requiredArtifacts:['URL','ServiceWorker']}; @@ -8079,73 +8134,7 @@ module.exports=ServiceWorker; -},{"../lib/url-shim":"url","./audit":2}],"../audits/speed-index":[function(require,module,exports){ - - - - - -'use strict'; - -const Audit=require('./audit'); -const Util=require('../report/html/renderer/util'); - -class SpeedIndex extends Audit{ - - - -static get meta(){ -return{ -name:'speed-index', -description:'Speed Index', -helpText:'Speed Index shows how quickly the contents of a page are visibly populated. '+ -'[Learn more](https://developers.google.com/web/tools/lighthouse/audits/speed-index).', -scoreDisplayMode:Audit.SCORING_MODES.NUMERIC, -requiredArtifacts:['traces','devtoolsLogs']}; - -} - - - - -static get defaultOptions(){ -return{ - - - -scorePODR:2900, -scoreMedian:5800}; - -} - - - - - - - - -static async audit(artifacts,context){ -const trace=artifacts.traces[Audit.DEFAULT_PASS]; -const devtoolsLog=artifacts.devtoolsLogs[Audit.DEFAULT_PASS]; -const metricComputationData={trace,devtoolsLog,settings:context.settings}; -const metricResult=await artifacts.requestSpeedIndex(metricComputationData); - -return{ -score:Audit.computeLogNormalScore( -metricResult.timing, -context.options.scorePODR, -context.options.scoreMedian), - -rawValue:metricResult.timing, -displayValue:[Util.MS_DISPLAY_VALUE,metricResult.timing]}; - -}} - - -module.exports=SpeedIndex; - -},{"../report/html/renderer/util":48,"./audit":2}],"../audits/splash-screen":[function(require,module,exports){ +},{"../lib/url-shim":"url","./audit":2}],"../audits/splash-screen":[function(require,module,exports){ @@ -8174,10 +8163,10 @@ static get meta(){ return{ -name:'splash-screen', -description:'Configured for a custom splash screen', -failureDescription:'Is not configured for a custom splash screen', -helpText:'A themed splash screen ensures a high-quality experience when '+ +id:'splash-screen', +title:'Configured for a custom splash screen', +failureTitle:'Is not configured for a custom splash screen', +description:'A themed splash screen ensures a high-quality experience when '+ 'users launch your app from their homescreens. [Learn '+ 'more](https://developers.google.com/web/tools/lighthouse/audits/custom-splash-screen).', requiredArtifacts:['Manifest']}; @@ -8258,10 +8247,10 @@ static get meta(){ return{ -name:'themed-omnibox', -description:'Address bar matches brand colors', -failureDescription:'Address bar does not match brand colors', -helpText:'The browser address bar can be themed to match your site. '+ +id:'themed-omnibox', +title:'Address bar matches brand colors', +failureTitle:'Address bar does not match brand colors', +description:'The browser address bar can be themed to match your site. '+ '[Learn more](https://developers.google.com/web/tools/lighthouse/audits/address-bar).', requiredArtifacts:['Manifest','ThemeColor']}; @@ -8327,7 +8316,6 @@ 'use strict'; const Audit=require('./audit'); -const Util=require('../report/html/renderer/util'); const TTFB_THRESHOLD=600; @@ -8337,10 +8325,10 @@ static get meta(){ return{ -name:'time-to-first-byte', -description:'Keep server response times low (TTFB)', -helpText:'Time To First Byte identifies the time at which your server sends a response.'+ -' [Learn more](https://developers.google.com/web/tools/chrome-devtools/network-performance/issues).', +id:'time-to-first-byte', +title:'Keep server response times low (TTFB)', +description:'Time To First Byte identifies the time at which your server sends a response.'+ +' [Learn more](https://developers.google.com/web/tools/lighthouse/audits/ttfb).', requiredArtifacts:['devtoolsLogs','URL']}; } @@ -8349,9 +8337,8 @@ static caclulateTTFB(record){ -const timing=record._timing; - -return timing.receiveHeadersEnd-timing.sendEnd; +const timing=record.timing; +return timing?timing.receiveHeadersEnd-timing.sendEnd:0; } @@ -8363,10 +8350,11 @@ return artifacts.requestNetworkRecords(devtoolsLogs). then(networkRecords=>{ + let displayValue=''; const finalUrl=artifacts.URL.finalUrl; -const finalUrlRequest=networkRecords.find(record=>record._url===finalUrl); +const finalUrlRequest=networkRecords.find(record=>record.url===finalUrl); if(!finalUrlRequest){ throw new Error(`finalUrl '${finalUrl} not found in network records.`); } @@ -8374,18 +8362,22 @@ const passed=ttfb<TTFB_THRESHOLD; if(!passed){ -displayValue=`Root document took ${Util.formatMilliseconds(ttfb,1)} `; +displayValue=['Root document took %10d',ttfb]; } + +const details={ +type:'opportunity', +overallSavingsMs:ttfb-TTFB_THRESHOLD, +headings:[], +items:[]}; + + return{ rawValue:ttfb, score:Number(passed), displayValue, -details:{ -summary:{ -wastedMs:ttfb-TTFB_THRESHOLD}}, - - +details, extendedInfo:{ value:{ wastedMs:ttfb-TTFB_THRESHOLD}}}; @@ -8398,7 +8390,7 @@ module.exports=TTFBMetric; -},{"../report/html/renderer/util":48,"./audit":2}],"../audits/user-timings":[function(require,module,exports){ +},{"./audit":2}],"../audits/user-timings":[function(require,module,exports){ @@ -8417,10 +8409,10 @@ static get meta(){ return{ -name:'user-timings', +id:'user-timings', scoreDisplayMode:Audit.SCORING_MODES.INFORMATIVE, -description:'User Timing marks and measures', -helpText:'Consider instrumenting your app with the User Timing API to create custom, '+ +title:'User Timing marks and measures', +description:'Consider instrumenting your app with the User Timing API to create custom, '+ 'real-world measurements of key user experiences. '+ '[Learn more](https://developers.google.com/web/tools/lighthouse/audits/user-timing).', requiredArtifacts:['traces']}; @@ -8516,16 +8508,16 @@ return artifacts.requestTraceOfTab(trace).then(tabTrace=>{ const userTimings=this.filterTrace(tabTrace).filter(UserTimings.excludeBlacklisted); const tableRows=userTimings.map(item=>{ -const time=item.isMark?item.startTime:item.duration; return{ name:item.name, -timingType:item.isMark?'Mark':'Measure', -time}; +startTime:item.startTime, +duration:item.isMark?undefined:item.duration, +timingType:item.isMark?'Mark':'Measure'}; }).sort((itemA,itemB)=>{ if(itemA.timingType===itemB.timingType){ -return itemA.time-itemB.time; +return itemA.startTime-itemB.startTime; }else if(itemA.timingType==='Measure'){ return-1; @@ -8537,16 +8529,26 @@ const headings=[ {key:'name',itemType:'text',text:'Name'}, {key:'timingType',itemType:'text',text:'Type'}, -{key:'time',itemType:'ms',granularity:0.01,text:'Time'}]; +{key:'startTime',itemType:'ms',granularity:0.01,text:'Start Time'}, +{key:'duration',itemType:'ms',granularity:0.01,text:'Duration'}]; const details=Audit.makeTableDetails(headings,tableRows); + +let displayValue; +if(userTimings.length){ +displayValue=[ +userTimings.length===1?'%d user timing':'%d user timings', +userTimings.length]; + +} + return{ rawValue:userTimings.length===0, notApplicable:userTimings.length===0, -displayValue:userTimings.length?`${userTimings.length}`:'', +displayValue, extendedInfo:{ value:userTimings}, @@ -8583,9 +8585,9 @@ static get meta(){ return{ -name:'uses-rel-preconnect', -description:'Avoid multiple, costly round trips to any origin', -helpText: +id:'uses-rel-preconnect', +title:'Avoid multiple, costly round trips to any origin', +description: 'Consider adding preconnect or dns-prefetch resource hints to establish early '+ `connections to important third-party origins. [Learn more](https://developers.google.com/web/fundamentals/performance/resource-prioritization#preconnect).`, requiredArtifacts:['devtoolsLogs','URL'], @@ -8599,7 +8601,7 @@ static hasValidTiming(record){ -return record._timing&&record._timing.connectEnd>0&&record._timing.connectStart>0; +return!!record.timing&&record.timing.connectEnd>0&&record.timing.connectStart>0; } @@ -8609,8 +8611,9 @@ static hasAlreadyConnectedToOrigin(record){ return( -record._timing.dnsEnd-record._timing.dnsStart===0&& -record._timing.connectEnd-record._timing.connectStart===0); +!!record.timing&& +record.timing.dnsEnd-record.timing.dnsStart===0&& +record.timing.connectEnd-record.timing.connectStart===0); } @@ -8651,11 +8654,11 @@ !UsesRelPreconnectAudit.hasValidTiming(record)|| -record.initiatorRequest()===mainResource|| +record.initiator.url===mainResource.url|| -!record.parsedURL||!record.parsedURL.securityOrigin()|| +!record.parsedURL||!record.parsedURL.securityOrigin|| -mainResource.parsedURL.securityOrigin()===record.parsedURL.securityOrigin()|| +mainResource.parsedURL.securityOrigin===record.parsedURL.securityOrigin|| UsesRelPreconnectAudit.hasAlreadyConnectedToOrigin(record)|| @@ -8664,7 +8667,7 @@ return; } -const securityOrigin=record.parsedURL.securityOrigin(); +const securityOrigin=record.parsedURL.securityOrigin; const records=origins.get(securityOrigin)||[]; records.push(record); origins.set(securityOrigin,records); @@ -8679,7 +8682,10 @@ return record.startTime<firstRecord.startTime?record:firstRecord; }); -const securityOrigin=firstRecordOfOrigin.parsedURL.securityOrigin(); + +if(!firstRecordOfOrigin.timing)return; + +const securityOrigin=firstRecordOfOrigin.parsedURL.securityOrigin; @@ -8692,7 +8698,7 @@ const timeBetweenMainResourceAndDnsStart= firstRecordOfOrigin.startTime*1000- mainResource.endTime*1000+ -firstRecordOfOrigin._timing.dnsStart; +firstRecordOfOrigin.timing.dnsStart; const wastedMs=Math.min(connectionTime,timeBetweenMainResourceAndDnsStart); if(wastedMs<IGNORE_THRESHOLD_IN_MS)return; @@ -8707,12 +8713,12 @@ results=results. sort((a,b)=>b.wastedMs-a.wastedMs); -const headings=[ -{key:'url',itemType:'url',text:'Origin'}, -{key:'wastedMs',itemType:'ms',text:'Potential Savings'}]; -const summary={wastedMs:maxWasted}; -const details=Audit.makeTableDetails(headings,results,summary); +const headings=[ +{key:'url',valueType:'url',label:'Origin'}, +{key:'wastedMs',valueType:'timespanMs',label:'Potential Savings'}]; + +const details=Audit.makeOpportunityDetails(headings,results,maxWasted); return{ score:UnusedBytes.scoreForWastedMs(maxWasted), @@ -8736,6 +8742,7 @@ 'use strict'; +const URL=require('../lib/url-shim'); const Audit=require('./audit'); const UnusedBytes=require('./byte-efficiency/byte-efficiency-audit'); const THRESHOLD_IN_MS=100; @@ -8746,10 +8753,10 @@ static get meta(){ return{ -name:'uses-rel-preload', -description:'Preload key requests', -helpText:'Consider using <link rel=preload> to prioritize fetching late-discovered '+ -'resources sooner. [Learn more](https://developers.google.com/web/updates/2016/03/link-rel-preload).', +id:'uses-rel-preload', +title:'Preload key requests', +description:'Consider using <link rel=preload> to prioritize fetching late-discovered '+ +'resources sooner. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/preload).', requiredArtifacts:['devtoolsLogs','traces','URL'], scoreDisplayMode:Audit.SCORING_MODES.NUMERIC}; @@ -8794,6 +8801,20 @@ +static shouldPreload(request,mainResource){ +if(request.isLinkPreload||URL.NON_NETWORK_PROTOCOLS.includes(request.protocol)){ +return false; +} + +return URL.rootDomainsMatch(request.url,mainResource.url); +} + + + + + + + static computeWasteWithGraph(urls,graph,simulator){ if(!urls.size){ @@ -8843,6 +8864,7 @@ const originalNode=originalNodesByRecord.get(node.record); const timingAfter=simulationAfterChanges.nodeTimings.get(node); const timingBefore=simulationBeforeChanges.nodeTimings.get(originalNode); +if(!timingBefore||!timingAfter)throw new Error('Missing preload node'); const wastedMs=Math.round(timingBefore.endTime-timingAfter.endTime); if(wastedMs<THRESHOLD_IN_MS)continue; @@ -8889,8 +8911,8 @@ const urls=new Set(); for(const networkRecord of criticalRequests){ -if(!networkRecord._isLinkPreload&&networkRecord.protocol!=='data'){ -urls.add(networkRecord._url); +if(UsesRelPreloadAudit.shouldPreload(networkRecord,mainResource)){ +urls.add(networkRecord.url); } } @@ -8898,12 +8920,12 @@ results.sort((a,b)=>b.wastedMs-a.wastedMs); -const headings=[ -{key:'url',itemType:'url',text:'URL'}, -{key:'wastedMs',itemType:'ms',text:'Potential Savings',granularity:10}]; -const summary={wastedMs}; -const details=Audit.makeTableDetails(headings,results,summary); +const headings=[ +{key:'url',valueType:'url',label:'URL'}, +{key:'wastedMs',valueType:'timespanMs',label:'Potential Savings'}]; + +const details=Audit.makeOpportunityDetails(headings,results,wastedMs); return{ score:UnusedBytes.scoreForWastedMs(wastedMs), @@ -8919,7 +8941,7 @@ module.exports=UsesRelPreloadAudit; -},{"./audit":2,"./byte-efficiency/byte-efficiency-audit":3}],"../audits/viewport":[function(require,module,exports){ +},{"../lib/url-shim":"url","./audit":2,"./byte-efficiency/byte-efficiency-audit":3}],"../audits/viewport":[function(require,module,exports){ @@ -8936,11 +8958,11 @@ static get meta(){ return{ -name:'viewport', -description:'Has a `<meta name="viewport">` tag with `width` or `initial-scale`', -failureDescription:'Does not have a `<meta name="viewport">` tag with `width` '+ +id:'viewport', +title:'Has a `<meta name="viewport">` tag with `width` or `initial-scale`', +failureTitle:'Does not have a `<meta name="viewport">` tag with `width` '+ 'or `initial-scale`', -helpText:'Add a viewport meta tag to optimize your app for mobile screens. '+ +description:'Add a viewport meta tag to optimize your app for mobile screens. '+ '[Learn more](https://developers.google.com/web/tools/lighthouse/audits/has-viewport-meta-tag).', requiredArtifacts:['Viewport']}; @@ -8980,7 +9002,7 @@ module.exports=Viewport; -},{"./audit":2,"metaviewport-parser":145}],"../audits/webapp-install-banner":[function(require,module,exports){ +},{"./audit":2,"metaviewport-parser":127}],"../audits/webapp-install-banner":[function(require,module,exports){ @@ -9017,10 +9039,10 @@ static get meta(){ return{ -name:'webapp-install-banner', -description:'User can be prompted to Install the Web App', -failureDescription:'User will not be prompted to Install the Web App', -helpText:'Browsers can proactively prompt users to add your app to their homescreen, '+ +id:'webapp-install-banner', +title:'User can be prompted to Install the Web App', +failureTitle:'User will not be prompted to Install the Web App', +description:'Browsers can proactively prompt users to add your app to their homescreen, '+ 'which can lead to higher engagement. '+ '[Learn more](https://developers.google.com/web/tools/lighthouse/audits/install-prompt).', requiredArtifacts:['URL','ServiceWorker','Manifest','StartUrl']}; @@ -9040,6 +9062,12 @@ const failures=[]; const bannerCheckIds=[ 'hasName', + + + + + + 'hasShortName', 'hasStartUrl', 'hasPWADisplayValue', @@ -9144,11 +9172,11 @@ static get meta(){ return{ -name:'without-javascript', -description:'Contains some content when JavaScript is not available', -failureDescription:'Does not provide fallback content when JavaScript is not available', -helpText:'Your app should display some content when JavaScript is disabled, even if it\'s '+ -'just a warning to the user that JavaScript is required to use the app. '+ +id:'without-javascript', +title:'Contains some content when JavaScript is not available', +failureTitle:'Does not provide fallback content when JavaScript is not available', +description:'Your app should display some content when JavaScript is disabled, even if '+ +'it\'s just a warning to the user that JavaScript is required to use the app. '+ '[Learn more](https://developers.google.com/web/tools/lighthouse/audits/no-js).', requiredArtifacts:['HTMLWithoutJavaScript']}; @@ -9161,7 +9189,8 @@ static audit(artifacts){ const artifact=artifacts.HTMLWithoutJavaScript; -if(artifact.value.trim()===''){ + +if(artifact.bodyText.trim()===''&&!artifact.hasNoScript){ return{ rawValue:false, explanation:'The page body should render some content if its scripts are not available.'}; @@ -9193,11 +9222,11 @@ static get meta(){ return{ -name:'works-offline', -description:'Responds with a 200 when offline', -failureDescription:'Does not respond with a 200 when offline', -helpText:'If you\'re building a Progressive Web App, consider using a service worker so '+ -'that your app can work offline. '+ +id:'works-offline', +title:'Responds with a 200 when offline', +failureTitle:'Does not respond with a 200 when offline', +description:'If you\'re building a Progressive Web App, consider using a service worker '+ +'so that your app can work offline. '+ '[Learn more](https://developers.google.com/web/tools/lighthouse/audits/http-200-when-offline).', requiredArtifacts:['Offline','URL']}; @@ -9531,156 +9560,7 @@ module.exports=CSSUsage; -},{"./gatherer":19}],"../gather/gatherers/dobetterweb/all-event-listeners":[function(require,module,exports){ - - - - - - - - - - -'use strict'; - -const Gatherer=require('../gatherer'); -const Driver=require('../../driver.js'); -const Element=require('../../../lib/element.js'); - -class EventListeners extends Gatherer{ - - - -async listenForScriptParsedEvents(driver){ - -const parsedScripts=new Map(); - -const scriptListener=script=>{ -parsedScripts.set(script.scriptId,script); -}; - - -driver.on('Debugger.scriptParsed',scriptListener); -await driver.sendCommand('Debugger.enable'); -await driver.sendCommand('Debugger.disable'); -driver.off('Debugger.scriptParsed',scriptListener); - -return parsedScripts; -} - - - - - - - - -_listEventListeners(driver,nodeIdOrObject){ -let promise; - -if(typeof nodeIdOrObject==='string'){ -promise=driver.sendCommand('Runtime.evaluate',{ -expression:nodeIdOrObject, -objectGroup:'event-listeners-gatherer'}). -then(result=>result.result); -}else{ -promise=driver.sendCommand('DOM.resolveNode',{ -nodeId:nodeIdOrObject, -objectGroup:'event-listeners-gatherer'}). -then(result=>result.object); -} - -return promise.then(obj=>{ -const objectId=obj.objectId; -const description=obj.description; -if(!objectId||!description){ -return{listeners:[],tagName:''}; -} - -return driver.sendCommand('DOMDebugger.getEventListeners',{ -objectId}). -then(results=>{ -return{listeners:results.listeners,tagName:description}; -}); -}); -} - - - - - - - - - - - -getEventListeners(driver,parsedScripts,nodeId){ - -const matchedListeners=[]; - -return this._listEventListeners(driver,nodeId).then(results=>{ -results.listeners.forEach(listener=>{ - - -const script=parsedScripts.get(listener.scriptId); -if(script){ - - - -matchedListeners.push({ -url:script.url, -type:listener.type, -handler:listener.handler, -objectName:results.tagName, - - -line:listener.lineNumber+1, -col:listener.columnNumber+1}); - -} -}); - -return matchedListeners; -}); -} - - - - - - - - - -collectListeners(driver,parsedScripts,nodeIds){ - -return Promise.all(nodeIds.map(node=>this.getEventListeners(driver,parsedScripts,node))). -then(nestedListeners=>nestedListeners.reduce((prev,curr)=>prev.concat(curr))); -} - - - - - -async afterPass(passContext){ -const driver=passContext.driver; -await passContext.driver.sendCommand('DOM.enable'); -const parsedScripts=await this.listenForScriptParsedEvents(driver); - -const elements=await passContext.driver.getElementsInDocument(); -const elementIds=[...elements.map(el=>el.getNodeId()),'document','window']; - -const listeners=await this.collectListeners(driver,parsedScripts,elementIds); -await passContext.driver.sendCommand('DOM.disable'); -return listeners; -}} - - -module.exports=EventListeners; - -},{"../../../lib/element.js":31,"../../driver.js":17,"../gatherer":19}],"../gather/gatherers/dobetterweb/anchors-with-no-rel-noopener":[function(require,module,exports){ +},{"./gatherer":19}],"../gather/gatherers/dobetterweb/anchors-with-no-rel-noopener":[function(require,module,exports){ @@ -9741,6 +9621,44 @@ module.exports=AppCacheManifest; +},{"../gatherer":19}],"../gather/gatherers/dobetterweb/doctype":[function(require,module,exports){ + + + + + +'use strict'; + +const Gatherer=require('../gatherer'); + + + + + + +function getDoctype(){ + +if(!document.doctype){ +return null; +} + +const{name,publicId,systemId}=document.doctype; +return{name,publicId,systemId}; +} + +class Doctype extends Gatherer{ + + + + +afterPass(passContext){ +const driver=passContext.driver; +return driver.evaluateAsync(`(${getDoctype.toString()}())`); +}} + + +module.exports=Doctype; + },{"../gatherer":19}],"../gather/gatherers/dobetterweb/domstats":[function(require,module,exports){ @@ -9990,6 +9908,7 @@ const Gatherer=require('../gatherer'); const URL=require('../../../lib/url-shim'); +const NetworkRequest=require('../../../lib/network-request'); const Sentry=require('../../../lib/sentry'); const Driver=require('../../driver.js'); @@ -10058,25 +9977,24 @@ const seenUrls=new Set(); return networkRecords.reduce((prev,record)=>{ -if(seenUrls.has(record._url)||!record.finished){ +if(seenUrls.has(record.url)||!record.finished){ return prev; } -seenUrls.add(record._url); -const isOptimizableImage=record._resourceType&& -record._resourceType._name==='image'&& -/image\/(png|bmp|jpeg)/.test(record._mimeType); -const isSameOrigin=URL.originsMatch(pageUrl,record._url); -const isBase64DataUri=/^data:.{2,40}base64\s*,/.test(record._url); +seenUrls.add(record.url); +const isOptimizableImage=record.resourceType===NetworkRequest.TYPES.Image&& +/image\/(png|bmp|jpeg)/.test(record.mimeType); +const isSameOrigin=URL.originsMatch(pageUrl,record.url); +const isBase64DataUri=/^data:.{2,40}base64\s*,/.test(record.url); -const actualResourceSize=Math.min(record._resourceSize||0,record._transferSize||0); +const actualResourceSize=Math.min(record.resourceSize||0,record.transferSize||0); if(isOptimizableImage&&actualResourceSize>MINIMUM_IMAGE_SIZE){ prev.push({ isSameOrigin, isBase64DataUri, -requestId:record._requestId, -url:record._url, -mimeType:record._mimeType, +requestId:record.requestId, +url:record.url, +mimeType:record.mimeType, resourceSize:actualResourceSize}); } @@ -10092,6 +10010,8 @@ _getEncodedResponse(driver,requestId,encoding){ +requestId=NetworkRequest.getRequestIdForBackend(requestId); + const quality=encoding==='jpeg'?JPEG_QUALITY:WEBP_QUALITY; const params={requestId,encoding,quality,sizeOnly:true}; return driver.sendCommand('Audits.getEncodedResponse',params); @@ -10163,8 +10083,7 @@ } - -const image=Object.assign({failed:false},stats,record); +const image={failed:false,...stats,...record}; results.push(image); }catch(err){ @@ -10177,8 +10096,7 @@ - -const imageError=Object.assign({failed:true,errMsg:err.message},record); +const imageError={failed:true,errMsg:err.message,...record}; results.push(imageError); } } @@ -10210,7 +10128,7 @@ module.exports=OptimizedImages; -},{"../../../lib/sentry":39,"../../../lib/url-shim":"url","../../driver.js":17,"../gatherer":19}],"../gather/gatherers/dobetterweb/password-inputs-with-prevented-paste":[function(require,module,exports){ +},{"../../../lib/network-request":38,"../../../lib/sentry":40,"../../../lib/url-shim":"url","../../driver.js":17,"../gatherer":19}],"../gather/gatherers/dobetterweb/password-inputs-with-prevented-paste":[function(require,module,exports){ @@ -10281,12 +10199,23 @@ 'use strict'; const Gatherer=require('../gatherer'); +const URL=require('../../../lib/url-shim'); +const Sentry=require('../../../lib/sentry'); +const NetworkRequest=require('../../../lib/network-request'); const gzip=require('zlib').gzip; +const CHROME_EXTENSION_PROTOCOL='chrome-extension:'; const compressionHeaders=['content-encoding','x-original-content-encoding']; const compressionTypes=['gzip','br','deflate']; const binaryMimeTypes=['image','audio','video']; -const CHROME_EXTENSION_PROTOCOL='chrome-extension:'; +const textResourceTypes=[ +NetworkRequest.TYPES.Document, +NetworkRequest.TYPES.Script, +NetworkRequest.TYPES.Stylesheet, +NetworkRequest.TYPES.XHR, +NetworkRequest.TYPES.Fetch, +NetworkRequest.TYPES.EventSource]; + class ResponseCompression extends Gatherer{ @@ -10298,20 +10227,20 @@ const unoptimizedResponses=[]; networkRecords.forEach(record=>{ -const mimeType=record._mimeType; -const resourceType=record._resourceType; -const resourceSize=record._resourceSize; +const mimeType=record.mimeType; +const resourceType=record.resourceType||NetworkRequest.TYPES.Other; +const resourceSize=record.resourceSize; const isBinaryResource=mimeType&&binaryMimeTypes.some(type=>mimeType.startsWith(type)); -const isTextBasedResource=!isBinaryResource&&resourceType&&resourceType.isTextType(); +const isTextResource=!isBinaryResource&&textResourceTypes.includes(resourceType); const isChromeExtensionResource=record.url.startsWith(CHROME_EXTENSION_PROTOCOL); -if(!isTextBasedResource||!resourceSize||!record.finished|| +if(!isTextResource||!resourceSize||!record.finished|| isChromeExtensionResource||!record.transferSize||record.statusCode===304){ return; } -const isContentEncoded=(record._responseHeaders||[]).find(header=> +const isContentEncoded=(record.responseHeaders||[]).find(header=> compressionHeaders.includes(header.name.toLowerCase())&& compressionTypes.includes(header.value)); @@ -10360,6 +10289,16 @@ resolve(record); }); }); +}).catch(err=>{ + +Sentry.captureException(err,{ +tags:{gatherer:'ResponseCompression'}, +extra:{url:URL.elideDataURI(record.url)}, +level:'warning'}); + + +record.gzipSize=undefined; +return record; }); })); }} @@ -10368,7 +10307,7 @@ module.exports=ResponseCompression; }).call(this,require("buffer").Buffer); -},{"../gatherer":19,"buffer":60,"zlib":57}],"../gather/gatherers/dobetterweb/tags-blocking-first-paint":[function(require,module,exports){ +},{"../../../lib/network-request":38,"../../../lib/sentry":40,"../../../lib/url-shim":"url","../gatherer":19,"buffer":60,"zlib":57}],"../gather/gatherers/dobetterweb/tags-blocking-first-paint":[function(require,module,exports){ @@ -10475,21 +10414,21 @@ return prev; } -const isParserGenerated=record._initiator.type==='parser'; +const isParserGenerated=record.initiator.type==='parser'; -const isParserScriptOrStyle=/(css|script)/.test(record._mimeType)&&isParserGenerated; +const isParserScriptOrStyle=/(css|script)/.test(record.mimeType)&&isParserGenerated; const isFailedRequest=record._failed; -const isHtml=record._mimeType&&record._mimeType.includes('html'); +const isHtml=record.mimeType&&record.mimeType.includes('html'); if(isHtml||isParserScriptOrStyle||isFailedRequest&&isParserGenerated){ -prev[record._url]={ -isLinkPreload:record.isLinkPreload, -transferSize:record._transferSize, -startTime:record._startTime, -endTime:record._endTime}; +prev[record.url]={ +isLinkPreload:!!record.isLinkPreload, +transferSize:record.transferSize, +startTime:record.startTime, +endTime:record.endTime}; } @@ -10504,7 +10443,7 @@ static findBlockingTags(driver,networkRecords){ const scriptSrc=`(${collectTagsThatBlockFirstPaint.toString()}())`; const firstRequestEndTime=networkRecords.reduce( -(min,record)=>Math.min(min,record._endTime), +(min,record)=>Math.min(min,record.endTime), Infinity); return driver.evaluateAsync(scriptSrc).then(tags=>{ @@ -10545,7 +10484,7 @@ beforePass(passContext){ -return passContext.driver.evaluteScriptOnNewDocument(`(${installMediaListener.toString()})()`); +return passContext.driver.evaluateScriptOnNewDocument(`(${installMediaListener.toString()})()`); } @@ -10741,11 +10680,24 @@ return new Promise(resolve=>{ newNode.addEventListener('load',function onload(){ newNode.removeEventListener('load',onload); -resolve(getFontFaceFromStylesheets()); +try{ +const stylesheet=Array.from(document.styleSheets).find(s=>s.ownerNode===newNode); +if(stylesheet){ +const cssStylesheet=stylesheet; +resolve(getSheetsFontFaces(cssStylesheet)); +}else{ +resolve([{err:{message:'Could not load stylesheet with CORS'}}]); +} +}catch(err){ +resolve([{err:{message:err.message,stack:err.stack}}]); +} }); newNode.crossOrigin='anonymous'; oldNode.parentNode&&oldNode.parentNode.insertBefore(newNode,oldNode); oldNode.remove(); + + +setTimeout(()=>resolve([{err:{message:'Could not load stylesheet (timeout)'}}]),5000); }); } @@ -10755,21 +10707,30 @@ const corsDataPromises=[]; for(const stylesheet of Array.from(document.styleSheets)){ -try{ const cssStylesheet=stylesheet; -if(cssStylesheet.cssRules===null&&cssStylesheet.href&&cssStylesheet.ownerNode&& +try{ -!cssStylesheet.ownerNode.crossOrigin){ +if(!cssStylesheet.cssRules){ +throw new Error('Failed to read cssRules'); +} + +data.push(...getSheetsFontFaces(cssStylesheet)); +}catch(err){ +const failedToReadRules=/Failed to read.*cssRules/.test(err.message); + +const alreadyCORS=!cssStylesheet.ownerNode||!!cssStylesheet.ownerNode.crossOrigin; + +if(failedToReadRules&&!alreadyCORS&&cssStylesheet.href){ + const ownerLinkEl=cssStylesheet.ownerNode; corsDataPromises.push(loadStylesheetWithCORS(ownerLinkEl)); }else{ -data.push(...getSheetsFontFaces(cssStylesheet)); -} -}catch(err){ + data.push({err:{message:err.message,stack:err.stack}}); } } +} return Promise.all(corsDataPromises).then(corsFontFaces=>data.concat(...corsFontFaces)); } @@ -10811,7 +10772,7 @@ const dataError=fontOrError; if(dataError.err){ const err=new Error(dataError.err.message); -err.stack=dataError.err.stack; +err.stack=dataError.err.stack||err.stack; Sentry.captureException(err,{tags:{gatherer:'Fonts'},level:'warning'}); return false; @@ -10831,7 +10792,7 @@ module.exports=Fonts; -},{"../../lib/sentry":39,"./gatherer":19}],"../gather/gatherers/html-without-javascript":[function(require,module,exports){ +},{"../../lib/sentry":40,"./gatherer":19}],"../gather/gatherers/html-without-javascript":[function(require,module,exports){ @@ -10852,7 +10813,10 @@ function getBodyText(){ const body=document.querySelector('body'); -return Promise.resolve(body?body.innerText:''); +return Promise.resolve({ +bodyText:body?body.innerText:'', +hasNoScript:!!document.querySelector('noscript')}); + } class HTMLWithoutJavaScript extends Gatherer{ @@ -10871,13 +10835,15 @@ passContext.disableJavaScript=false; -const bodyText=await passContext.driver.evaluateAsync(`(${getBodyText.toString()}())`); +const expression=`(${getBodyText.toString()}())`; +const{bodyText,hasNoScript}=await passContext.driver.evaluateAsync(expression); if(typeof bodyText!=='string'){ throw new Error('document body innerText returned by protocol was not a string'); } return{ -value:bodyText}; +bodyText, +hasNoScript}; }} @@ -11080,14 +11046,14 @@ async afterPass(passContext,loadData){ const driver=passContext.driver; const indexedNetworkRecords=loadData.networkRecords.reduce((map,record)=>{ -if(/^image/.test(record._mimeType)&&record.finished){ -map[record._url]={ +if(/^image/.test(record.mimeType)&&record.finished){ +map[record.url]={ url:record.url, -resourceSize:Math.min(record._resourceSize||0,record.transferSize), +resourceSize:Math.min(record.resourceSize||0,record.transferSize), startTime:record.startTime, endTime:record.endTime, -responseReceivedTime:record._responseReceivedTime, -mimeType:record._mimeType}; +responseReceivedTime:record.responseReceivedTime, +mimeType:record.mimeType}; } @@ -11361,8 +11327,8 @@ afterPass(passContext,loadData){ const navigationRecord=loadData.networkRecords.filter(record=>{ -return URL.equalWithExcludedFragments(record._url,passContext.url)&& -record._fetchedViaServiceWorker; +return URL.equalWithExcludedFragments(record.url,passContext.url)&& +record.fetchedViaServiceWorker; }).pop(); return passContext.driver.goOnline(passContext). @@ -11431,7 +11397,7 @@ 'use strict'; const Gatherer=require('./gatherer'); -const WebInspector=require('../../lib/web-inspector'); +const NetworkRequest=require('../../lib/network-request'); @@ -11448,7 +11414,7 @@ const scriptContentMap={}; const scriptRecords=loadData.networkRecords. -filter(record=>record._resourceType===WebInspector.resourceTypes.Script); +filter(record=>record.resourceType===NetworkRequest.TYPES.Script); for(const record of scriptRecords){ try{ @@ -11465,7 +11431,7 @@ module.exports=Scripts; -},{"../../lib/web-inspector":47,"./gatherer":19}],"../gather/gatherers/seo/canonical":[function(require,module,exports){ +},{"../../lib/network-request":38,"./gatherer":19}],"../gather/gatherers/seo/canonical":[function(require,module,exports){ @@ -11823,7 +11789,7 @@ }).call(this,typeof global!=="undefined"?global:typeof self!=="undefined"?self:typeof window!=="undefined"?window:{}); -},{"../../../lib/sentry.js":39,"../../../lib/web-inspector":47,"../../driver.js":17,"../gatherer":19}],"../gather/gatherers/seo/hreflang":[function(require,module,exports){ +},{"../../../lib/sentry.js":40,"../../../lib/web-inspector":47,"../../driver.js":17,"../gatherer":19}],"../gather/gatherers/seo/hreflang":[function(require,module,exports){ @@ -12196,7 +12162,7 @@ 'use strict'; const ComputedArtifact=require('./computed-artifact'); -const WebInspector=require('../../lib/web-inspector'); +const NetworkRequest=require('../../lib/network-request'); const assert=require('assert'); class CriticalRequestChains extends ComputedArtifact{ @@ -12216,29 +12182,29 @@ assert.ok(mainResource,'mainResource not provided'); -if(request._isLinkPreload){ +if(request.isLinkPreload){ return false; } -const resourceTypeCategory=request._resourceType&&request._resourceType._category; - -const isIframe=request._resourceType===WebInspector.resourceTypes.Document&& -request._frameId!==mainResource._frameId; +const isIframe=request.resourceType===NetworkRequest.TYPES.Document&& +request.frameId!==mainResource.frameId; const nonCriticalResourceTypes=[ -WebInspector.resourceTypes.Image._category, -WebInspector.resourceTypes.XHR._category]; +NetworkRequest.TYPES.Image, +NetworkRequest.TYPES.XHR, +NetworkRequest.TYPES.Fetch, +NetworkRequest.TYPES.EventSource]; -if(nonCriticalResourceTypes.includes(resourceTypeCategory)|| +if(nonCriticalResourceTypes.includes(request.resourceType||'Other')|| isIframe|| -request._mimeType&&request._mimeType.startsWith('image/')){ +request.mimeType&&request.mimeType.startsWith('image/')){ return false; } -return['VeryHigh','High','Medium'].includes(request.priority()); +return['VeryHigh','High','Medium'].includes(request.priority); } @@ -12270,7 +12236,7 @@ const ancestors=[]; -let ancestorRequest=request.initiatorRequest(); +let ancestorRequest=request.initiatorRequest; let node=criticalRequestChains; while(ancestorRequest){ @@ -12288,7 +12254,7 @@ break; } ancestors.push(ancestorRequest.requestId); -ancestorRequest=ancestorRequest.initiatorRequest(); +ancestorRequest=ancestorRequest.initiatorRequest; } @@ -12349,34 +12315,7 @@ module.exports=CriticalRequestChains; -},{"../../lib/web-inspector":47,"./computed-artifact":11,"assert":53}],"./gather/computed/dtm-model":[function(require,module,exports){ - - - - - -'use strict'; - -const ComputedArtifact=require('./computed-artifact'); -const DTM=require('../../lib/traces/devtools-timeline-model'); - -class DevtoolsTimelineModel extends ComputedArtifact{ -get name(){ -return'DevtoolsTimelineModel'; -} - - - - - -async compute_(trace){ -return new DTM(trace); -}} - - -module.exports=DevtoolsTimelineModel; - -},{"../../lib/traces/devtools-timeline-model":43,"./computed-artifact":11}],"./gather/computed/load-simulator":[function(require,module,exports){ +},{"../../lib/network-request":38,"./computed-artifact":11,"assert":53}],"./gather/computed/load-simulator":[function(require,module,exports){ @@ -12453,6 +12392,7 @@ 'use strict'; const ComputedArtifact=require('./computed-artifact'); +const URL=require('../../lib/url-shim'); @@ -12468,24 +12408,267 @@ -compute_(data,artifacts){ -const{URL,devtoolsLog}=data; -return artifacts.requestNetworkRecords(devtoolsLog). -then(requests=>{ -const mainResource=requests.find(request=>request.url===URL.finalUrl); +async compute_(data,artifacts){ +const finalUrl=data.URL.finalUrl; +const requests=await artifacts.requestNetworkRecords(data.devtoolsLog); + +const mainResource=requests.find(request=>finalUrl.startsWith(request.url)&& +URL.equalWithExcludedFragments(request.url,finalUrl)); if(!mainResource){ throw new Error('Unable to identify the main resource'); } return mainResource; -}); }} module.exports=MainResource; -},{"./computed-artifact":11}],"./gather/computed/manifest-values":[function(require,module,exports){ +},{"../../lib/url-shim":"url","./computed-artifact":11}],"./gather/computed/main-thread-tasks":[function(require,module,exports){ + + + + + +'use strict'; + +const ComputedArtifact=require('./computed-artifact'); +const{taskGroups,taskNameToGroup}=require('../../lib/task-groups'); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +class MainThreadTasks extends ComputedArtifact{ +get name(){ +return'MainThreadTasks'; +} + + + + + + +static _createNewTaskNode(event,parent){ +const newTask={ +event, +startTime:event.ts, +endTime:event.ph==='X'?event.ts+Number(event.dur||0):NaN, +parent:parent, +children:[], + + +attributableURLs:[], +group:taskGroups.other, +duration:NaN, +selfTime:NaN}; + + +if(parent){ +parent.children.push(newTask); +} + +return newTask; +} + + + + + +static _createTasksFromEvents(mainThreadEvents){ + +const tasks=[]; + +let currentTask; + +for(const event of mainThreadEvents){ + +if(event.ph!=='X'&&event.ph!=='B'&&event.ph!=='E')continue; + + + +while( +currentTask&& +Number.isFinite(currentTask.endTime)&& +currentTask.endTime<=event.ts) +{ +currentTask=currentTask.parent; +} + + +if(!currentTask){ + +if(event.ph==='E'){ +throw new Error('Fatal trace logic error'); +} + +currentTask=MainThreadTasks._createNewTaskNode(event); +tasks.push(currentTask); + +continue; +} + +if(event.ph==='X'||event.ph==='B'){ + +const newTask=MainThreadTasks._createNewTaskNode(event,currentTask); +tasks.push(newTask); +currentTask=newTask; +}else{ +if(currentTask.event.ph!=='B'){ +throw new Error('Fatal trace logic error'); +} + + +currentTask.endTime=event.ts; +currentTask=currentTask.parent; +} +} + +return tasks; +} + + + + + +static _computeRecursiveSelfTime(task){ +const childTime=task.children. +map(MainThreadTasks._computeRecursiveSelfTime). +reduce((sum,child)=>sum+child,0); +task.duration=task.endTime-task.startTime; +task.selfTime=task.duration-childTime; +return task.duration; +} + + + + + +static _computeRecursiveAttributableURLs(task,parentURLs){ +const argsData=task.event.args.data||{}; +const stackFrameURLs=(argsData.stackTrace||[]).map(entry=>entry.url); + +let taskURLs=[]; +switch(task.event.name){ + + + + + +case'v8.compile': +case'EvaluateScript': +case'FunctionCall': +taskURLs=[argsData.url].concat(stackFrameURLs); +break; +case'v8.compileModule': +taskURLs=[task.event.args.fileName].concat(stackFrameURLs); +break; +default: +taskURLs=stackFrameURLs; +break;} + + + +const attributableURLs=Array.from(parentURLs); +for(const url of taskURLs){ + +if(!url)continue; + +if(attributableURLs[attributableURLs.length-1]===url)continue; +attributableURLs.push(url); +} + +task.attributableURLs=attributableURLs; +task.children.forEach(child=> +MainThreadTasks._computeRecursiveAttributableURLs(child,attributableURLs)); +} + + + + + +static _computeRecursiveTaskGroup(task,parentGroup){ +const group=taskNameToGroup[task.event.name]; +task.group=group||parentGroup||taskGroups.other; +task.children.forEach(child=>MainThreadTasks._computeRecursiveTaskGroup(child,task.group)); +} + + + + + +static getMainThreadTasks(traceEvents){ +const tasks=MainThreadTasks._createTasksFromEvents(traceEvents); + + +for(const task of tasks){ +if(task.parent)continue; + +MainThreadTasks._computeRecursiveSelfTime(task); +MainThreadTasks._computeRecursiveAttributableURLs(task,[]); +MainThreadTasks._computeRecursiveTaskGroup(task); +} + + +const firstTs=(tasks[0]||{startTime:0}).startTime; +for(const task of tasks){ +task.startTime=(task.startTime-firstTs)/1000; +task.endTime=(task.endTime-firstTs)/1000; +task.duration/=1000; +task.selfTime/=1000; + + +if(!Number.isFinite(task.selfTime)){ +throw new Error('Invalid task timing data'); +} +} + +return tasks; +} + + + + + + +async compute_(trace,artifacts){ +const{mainThreadEvents}=await artifacts.requestTraceOfTab(trace); +return MainThreadTasks.getMainThreadTasks(mainThreadEvents); +}} + + +module.exports=MainThreadTasks; + +},{"../../lib/task-groups":43,"./computed-artifact":11}],"./gather/computed/manifest-values":[function(require,module,exports){ @@ -12557,7 +12740,9 @@ { id:'shortNameLength', -failureText:'Manifest `short_name` will be truncated when displayed on the homescreen', +failureText:`Manifest's \`short_name\` is too long (>${SUGGESTED_SHORTNAME_LENGTH} `+ +`characters) to be displayed on a homescreen without truncation`, + validate:manifestValue=>!!manifestValue.short_name.value&& manifestValue.short_name.value.length<=SUGGESTED_SHORTNAME_LENGTH}, @@ -12613,7 +12798,7 @@ module.exports=ManifestValues; -},{"../../lib/icons":35,"./computed-artifact":11}],"./gather/computed/metrics/estimated-input-latency":[function(require,module,exports){ +},{"../../lib/icons":34,"./computed-artifact":11}],"./gather/computed/metrics/estimated-input-latency":[function(require,module,exports){ @@ -13156,9 +13341,11 @@ 'use strict'; const LanternMetricArtifact=require('./lantern-metric'); -const Node=require('../../../lib/dependency-graph/node'); +const BaseNode=require('../../../lib/dependency-graph/base-node'); const EstimatedInputLatency=require('./estimated-input-latency'); + + class LanternEstimatedInputLatency extends LanternMetricArtifact{ get name(){ return'LanternEstimatedInputLatency'; @@ -13232,14 +13419,13 @@ const events=[]; for(const[node,timing]of nodeTimings.entries()){ -if(node.type!==Node.TYPES.CPU)continue; -if(!timing.endTime||!timing.startTime)continue; +if(node.type!==BaseNode.TYPES.CPU)continue; if(timing.endTime<fmpTimeInMs)continue; events.push({ start:timing.startTime, end:timing.endTime, -duration:timing.endTime-timing.startTime}); +duration:timing.duration}); } @@ -13249,7 +13435,7 @@ module.exports=LanternEstimatedInputLatency; -},{"../../../lib/dependency-graph/node":25,"./estimated-input-latency":"./gather/computed/metrics/estimated-input-latency","./lantern-metric":12}],"./gather/computed/metrics/lantern-first-contentful-paint":[function(require,module,exports){ +},{"../../../lib/dependency-graph/base-node":22,"./estimated-input-latency":"./gather/computed/metrics/estimated-input-latency","./lantern-metric":12}],"./gather/computed/metrics/lantern-first-contentful-paint":[function(require,module,exports){ @@ -13258,9 +13444,9 @@ 'use strict'; const MetricArtifact=require('./lantern-metric'); -const Node=require('../../../lib/dependency-graph/node'); -const CPUNode=require('../../../lib/dependency-graph/cpu-node'); -const NetworkNode=require('../../../lib/dependency-graph/network-node'); +const BaseNode=require('../../../lib/dependency-graph/base-node'); + + class FirstContentfulPaint extends MetricArtifact{ get name(){ @@ -13272,8 +13458,8 @@ get COEFFICIENTS(){ return{ -intercept:600, -optimistic:0.6, +intercept:0, +optimistic:0.5, pessimistic:0.5}; } @@ -13294,13 +13480,12 @@ return dependencyGraph.cloneWithRelationships(node=>{ if(node.endTime>fcp&&!node.isMainDocument())return false; -if(node.type===Node.TYPES.CPU){ +if(node.type===BaseNode.TYPES.CPU){ return node.isEvaluateScriptFor(blockingScriptUrls); } -const asNetworkNode=node; -return asNetworkNode.hasRenderBlockingPriority()&&asNetworkNode.initiatorType!=='script'; +return node.hasRenderBlockingPriority()&&node.initiatorType!=='script'; }); } @@ -13318,7 +13503,7 @@ return dependencyGraph.cloneWithRelationships(node=>{ if(node.endTime>fcp&&!node.isMainDocument())return false; -if(node.type===Node.TYPES.CPU){ +if(node.type===BaseNode.TYPES.CPU){ return node.isEvaluateScriptFor(blockingScriptUrls); } @@ -13330,7 +13515,7 @@ module.exports=FirstContentfulPaint; -},{"../../../lib/dependency-graph/cpu-node":23,"../../../lib/dependency-graph/network-node":24,"../../../lib/dependency-graph/node":25,"./lantern-metric":12}],"./gather/computed/metrics/lantern-first-cpu-idle":[function(require,module,exports){ +},{"../../../lib/dependency-graph/base-node":22,"./lantern-metric":12}],"./gather/computed/metrics/lantern-first-cpu-idle":[function(require,module,exports){ @@ -13338,10 +13523,7 @@ 'use strict'; -const Node=require('../../../lib/dependency-graph/node'); -const CPUNode=require('../../../lib/dependency-graph/cpu-node'); -const NetworkNode=require('../../../lib/dependency-graph/network-node'); - +const BaseNode=require('../../../lib/dependency-graph/base-node'); const FirstCPUIdle=require('./first-cpu-idle'); const LanternInteractive=require('./lantern-interactive'); @@ -13353,6 +13535,18 @@ +get COEFFICIENTS(){ +return{ +intercept:0, +optimistic:1, +pessimistic:0}; + +} + + + + + getEstimateFromSimulation(simulation,extras){ @@ -13375,9 +13569,8 @@ const longTasks=[]; for(const[node,timing]of nodeTimings.entries()){ -if(node.type!==Node.TYPES.CPU)continue; -if(!timing.endTime||!timing.startTime)continue; -if(timing.endTime-timing.startTime<longTaskLength)continue; +if(node.type!==BaseNode.TYPES.CPU)continue; +if(timing.duration<longTaskLength)continue; longTasks.push({start:timing.startTime,end:timing.endTime}); } @@ -13387,7 +13580,7 @@ module.exports=LanternFirstCPUIdle; -},{"../../../lib/dependency-graph/cpu-node":23,"../../../lib/dependency-graph/network-node":24,"../../../lib/dependency-graph/node":25,"./first-cpu-idle":"./gather/computed/metrics/first-cpu-idle","./lantern-interactive":"./gather/computed/metrics/lantern-interactive"}],"./gather/computed/metrics/lantern-first-meaningful-paint":[function(require,module,exports){ +},{"../../../lib/dependency-graph/base-node":22,"./first-cpu-idle":"./gather/computed/metrics/first-cpu-idle","./lantern-interactive":"./gather/computed/metrics/lantern-interactive"}],"./gather/computed/metrics/lantern-first-meaningful-paint":[function(require,module,exports){ @@ -13396,9 +13589,9 @@ 'use strict'; const MetricArtifact=require('./lantern-metric'); -const Node=require('../../../lib/dependency-graph/node'); -const CPUNode=require('../../../lib/dependency-graph/cpu-node'); -const NetworkNode=require('../../../lib/dependency-graph/network-node'); +const BaseNode=require('../../../lib/dependency-graph/base-node'); + + class FirstMeaningfulPaint extends MetricArtifact{ get name(){ @@ -13410,9 +13603,9 @@ get COEFFICIENTS(){ return{ -intercept:900, -optimistic:0.45, -pessimistic:0.6}; +intercept:0, +optimistic:0.5, +pessimistic:0.5}; } @@ -13432,13 +13625,12 @@ return dependencyGraph.cloneWithRelationships(node=>{ if(node.endTime>fmp&&!node.isMainDocument())return false; -if(node.type===Node.TYPES.CPU){ +if(node.type===BaseNode.TYPES.CPU){ return node.isEvaluateScriptFor(blockingScriptUrls); } -const asNetworkNode=node; -return asNetworkNode.hasRenderBlockingPriority()&&asNetworkNode.initiatorType!=='script'; +return node.hasRenderBlockingPriority()&&node.initiatorType!=='script'; }); } @@ -13457,9 +13649,8 @@ if(node.endTime>fmp&&!node.isMainDocument())return false; -if(node.type===Node.TYPES.CPU){ -const asCpuNode=node; -return asCpuNode.didPerformLayout()||asCpuNode.isEvaluateScriptFor(requiredScriptUrls); +if(node.type===BaseNode.TYPES.CPU){ +return node.didPerformLayout()||node.isEvaluateScriptFor(requiredScriptUrls); } @@ -13482,7 +13673,7 @@ module.exports=FirstMeaningfulPaint; -},{"../../../lib/dependency-graph/cpu-node":23,"../../../lib/dependency-graph/network-node":24,"../../../lib/dependency-graph/node":25,"./lantern-metric":12}],"./gather/computed/metrics/lantern-interactive":[function(require,module,exports){ +},{"../../../lib/dependency-graph/base-node":22,"./lantern-metric":12}],"./gather/computed/metrics/lantern-interactive":[function(require,module,exports){ @@ -13491,10 +13682,10 @@ 'use strict'; const MetricArtifact=require('./lantern-metric'); -const Node=require('../../../lib/dependency-graph/node'); -const CPUNode=require('../../../lib/dependency-graph/cpu-node'); -const NetworkNode=require('../../../lib/dependency-graph/network-node'); -const WebInspector=require('../../../lib/web-inspector'); +const BaseNode=require('../../../lib/dependency-graph/base-node'); +const NetworkRequest=require('../../../lib/network-request'); + + const CRITICAL_LONG_TASK_THRESHOLD=20; @@ -13509,9 +13700,9 @@ get COEFFICIENTS(){ return{ -intercept:1600, -optimistic:0.6, -pessimistic:0.45}; +intercept:0, +optimistic:0.5, +pessimistic:0.5}; } @@ -13525,19 +13716,18 @@ return dependencyGraph.cloneWithRelationships(node=>{ -if(node.type===Node.TYPES.CPU){ +if(node.type===BaseNode.TYPES.CPU){ return node.event.dur>minimumCpuTaskDuration; } -const asNetworkNode=node; -const isImage=asNetworkNode.record._resourceType===WebInspector.resourceTypes.Image; -const isScript=asNetworkNode.record._resourceType===WebInspector.resourceTypes.Script; +const isImage=node.record.resourceType===NetworkRequest.TYPES.Image; +const isScript=node.record.resourceType===NetworkRequest.TYPES.Script; return( !isImage&&( isScript|| -asNetworkNode.record.priority()==='High'|| -asNetworkNode.record.priority()==='VeryHigh')); +node.record.priority==='High'|| +node.record.priority==='VeryHigh')); }); } @@ -13586,9 +13776,8 @@ return Array.from(nodeTimings.entries()). filter(([node,timing])=>{ -if(node.type!==Node.TYPES.CPU)return false; -if(!timing.endTime||!timing.startTime)return false; -return timing.endTime-timing.startTime>duration; +if(node.type!==BaseNode.TYPES.CPU)return false; +return timing.duration>duration; }). map(([_,timing])=>timing.endTime). reduce((max,x)=>Math.max(max||0,x||0),0); @@ -13597,7 +13786,7 @@ module.exports=Interactive; -},{"../../../lib/dependency-graph/cpu-node":23,"../../../lib/dependency-graph/network-node":24,"../../../lib/dependency-graph/node":25,"../../../lib/web-inspector":47,"./lantern-metric":12}],"./gather/computed/metrics/lantern-speed-index":[function(require,module,exports){ +},{"../../../lib/dependency-graph/base-node":22,"../../../lib/network-request":38,"./lantern-metric":12}],"./gather/computed/metrics/lantern-speed-index":[function(require,module,exports){ @@ -13606,8 +13795,9 @@ 'use strict'; const MetricArtifact=require('./lantern-metric'); -const Node=require('../../../lib/dependency-graph/node'); -const CPUNode=require('../../../lib/dependency-graph/cpu-node'); +const BaseNode=require('../../../lib/dependency-graph/base-node'); + + class SpeedIndex extends MetricArtifact{ get name(){ @@ -13695,11 +13885,9 @@ const layoutWeights=[]; for(const[node,timing]of nodeTimings.entries()){ -if(node.type!==Node.TYPES.CPU)continue; -if(!timing.startTime||!timing.endTime)continue; +if(node.type!==BaseNode.TYPES.CPU)continue; -const cpuNode=node; -if(cpuNode.childEvents.some(x=>x.name==='Layout')){ +if(node.childEvents.some(x=>x.name==='Layout')){ const timingWeight=Math.max(Math.log2(timing.endTime-timing.startTime),0); layoutWeights.push({time:timing.endTime,weight:timingWeight}); } @@ -13719,7 +13907,7 @@ module.exports=SpeedIndex; -},{"../../../lib/dependency-graph/cpu-node":23,"../../../lib/dependency-graph/node":25,"./lantern-metric":12}],"./gather/computed/metrics/speed-index":[function(require,module,exports){ +},{"../../../lib/dependency-graph/base-node":22,"./lantern-metric":12}],"./gather/computed/metrics/speed-index":[function(require,module,exports){ @@ -13876,7 +14064,7 @@ } totalBytes+=record.transferSize; -boundaries.push({time:record._responseReceivedTime,isStart:true}); +boundaries.push({time:record.responseReceivedTime,isStart:true}); boundaries.push({time:record.endTime,isStart:false}); return boundaries; },[]).sort((a,b)=>a.time-b.time); @@ -13932,9 +14120,9 @@ const CPUNode=require('../../lib/dependency-graph/cpu-node'); const NetworkAnalyzer=require('../../lib/dependency-graph/simulator/network-analyzer'); const TracingProcessor=require('../../lib/traces/tracing-processor'); -const WebInspector=require('../../lib/web-inspector'); +const NetworkRequest=require('../../lib/network-request'); -const Node=require('../../lib/dependency-graph/node.js'); + const MINIMUM_TASK_DURATION_OF_INTEREST=10; @@ -13952,10 +14140,10 @@ static getNetworkInitiators(record){ -if(!record._initiator)return[]; -if(record._initiator.url)return[record._initiator.url]; -if(record._initiator.type==='script'&&record._initiator.stack){ -const frames=record._initiator.stack.callFrames; +if(!record.initiator)return[]; +if(record.initiator.url)return[record.initiator.url]; +if(record.initiator.type==='script'&&record.initiator.stack){ +const frames=record.initiator.stack.callFrames; return Array.from(new Set(frames.map(frame=>frame.url))).filter(Boolean); } @@ -13973,12 +14161,14 @@ const urlToNodeMap=new Map(); networkRecords.forEach(record=>{ -if(IGNORED_MIME_TYPES_REGEX.test(record._mimeType))return; +if(IGNORED_MIME_TYPES_REGEX.test(record.mimeType))return; + + while(idToNodeMap.has(record.requestId)){ -record._requestId+=':duplicate'; +record.requestId+=':duplicate'; } const node=new NetworkNode(record); @@ -14077,7 +14267,7 @@ const networkNode=networkNodeOutput.idToNodeMap.get(reqId); if(!networkNode|| -networkNode.record._resourceType!==WebInspector.resourceTypes.XHR|| +networkNode.record.resourceType!==NetworkRequest.TYPES.XHR|| networkNode.startTime<=cpuNode.startTime)return; @@ -14236,7 +14426,7 @@ const bar=padRight('',offset)+padRight('',length,'='); -const displayName=node.record?node.record._url:node.type; +const displayName=node.record?node.record.url:node.type; console.log(padRight(bar,widthInCharacters),`| ${displayName.slice(0,30)}`); }); @@ -14268,7 +14458,7 @@ -},{"../../lib/dependency-graph/cpu-node":23,"../../lib/dependency-graph/network-node":24,"../../lib/dependency-graph/node.js":25,"../../lib/dependency-graph/simulator/network-analyzer":27,"../../lib/traces/tracing-processor":46,"../../lib/web-inspector":47,"./computed-artifact":11}],"./gather/computed/pushed-requests":[function(require,module,exports){ +},{"../../lib/dependency-graph/cpu-node":23,"../../lib/dependency-graph/network-node":24,"../../lib/dependency-graph/simulator/network-analyzer":27,"../../lib/network-request":38,"../../lib/traces/tracing-processor":46,"./computed-artifact":11}],"./gather/computed/pushed-requests":[function(require,module,exports){ @@ -14291,7 +14481,7 @@ compute_(devtoolsLog,artifacts){ return artifacts.requestNetworkRecords(devtoolsLog).then(records=>{ -const pushedRecords=records.filter(r=>r._timing&&!!r._timing.pushStart); +const pushedRecords=records.filter(r=>r.timing&&!!r.timing.pushStart); return pushedRecords; }); }} @@ -14309,6 +14499,8 @@ const ComputedArtifact=require('./computed-artifact'); +const SCREENSHOT_TRACE_NAME='Screenshot'; + class ScreenshotFilmstrip extends ComputedArtifact{ get name(){ return'Screenshots'; @@ -14318,29 +14510,14 @@ -fetchScreenshot(frame){ -return frame. -imageDataPromise(). -then(data=>'data:image/jpg;base64,'+data); -} +async compute_(trace){ +return trace.traceEvents. +filter(evt=>evt.name===SCREENSHOT_TRACE_NAME). +map(evt=>{ +return{ +timestamp:evt.ts/1000, +datauri:`data:image/jpg;base64,${evt.args.snapshot}`}; - - - - - -compute_(trace,computedArtifacts){ -return computedArtifacts.requestDevtoolsTimelineModel(trace).then(model=>{ -const filmStripFrames=model.filmStripModel().frames(); -const frameFetches=filmStripFrames.map(frame=>this.fetchScreenshot(frame)); - -return Promise.all(frameFetches).then(images=>{ -const result=filmStripFrames.map((frame,i)=>({ -timestamp:frame.timestamp, -datauri:images[i]})); - -return result; -}); }); }} @@ -14406,7 +14583,7 @@ module.exports=Speedline; -},{"../../lib/errors":33,"./computed-artifact":11,"speedline":152}],"./gather/computed/trace-of-tab":[function(require,module,exports){ +},{"../../lib/errors":33,"./computed-artifact":11,"speedline":134}],"./gather/computed/trace-of-tab":[function(require,module,exports){ @@ -14427,13 +14604,14 @@ const ComputedArtifact=require('./computed-artifact'); const log=require('lighthouse-logger'); +const TracingProcessor=require('../../lib/traces/tracing-processor'); const LHError=require('../../lib/errors'); const Sentry=require('../../lib/sentry'); -const WebInspector=require('../../lib/web-inspector'); +const NetworkRequest=require('../../lib/network-request'); class TraceOfTab extends ComputedArtifact{ get name(){ @@ -14444,28 +14622,50 @@ +static filteredStableSort(traceEvents,filter){ + +const indices=[]; +for(let srcIndex=0;srcIndex<traceEvents.length;srcIndex++){ +if(filter(traceEvents[srcIndex])){ +indices.push(srcIndex); +} +} + + +indices.sort((indexA,indexB)=>{ +const result=traceEvents[indexA].ts-traceEvents[indexB].ts; +return result?result:indexA-indexB; +}); + + +const sorted=[]; +for(let i=0;i<indices.length;i++){ +sorted.push(traceEvents[indices[i]]); +} + +return sorted; +} + + + + + + async compute_(trace){ - -const keyEvents=trace.traceEvents. -filter(e=>{ +const keyEvents=TraceOfTab.filteredStableSort(trace.traceEvents,e=>{ return e.cat.includes('blink.user_timing')|| e.cat.includes('loading')|| e.cat.includes('devtools.timeline')|| -e.name==='TracingStartedInPage'; -}). - -stableSort((event0,event1)=>event0.ts-event1.ts); +e.cat==='__metadata'; +}); +const{startedInPageEvt,frameId}=TracingProcessor.findTracingStartedEvt(keyEvents); -const startedInPageEvt=keyEvents.find(e=>e.name==='TracingStartedInPage'); -if(!startedInPageEvt)throw new LHError(LHError.errors.NO_TRACING_STARTED); - -const frameId=startedInPageEvt.args.data.page; const frameEvents=keyEvents.filter(e=>e.args.frame===frameId); @@ -14513,11 +14713,8 @@ - -const processEvents=trace.traceEvents. -filter(e=>e.pid===startedInPageEvt.pid). - -stableSort((event0,event1)=>event0.ts-event1.ts); +const processEvents=TraceOfTab. +filteredStableSort(trace.traceEvents,e=>e.pid===startedInPageEvt.pid); const mainThreadEvents=processEvents. filter(e=>e.tid===startedInPageEvt.tid); @@ -14566,7 +14763,7 @@ module.exports=TraceOfTab; -},{"../../lib/errors":33,"../../lib/sentry":39,"../../lib/web-inspector":47,"./computed-artifact":11,"lighthouse-logger":143}],1:[function(require,module,exports){ +},{"../../lib/errors":33,"../../lib/network-request":38,"../../lib/sentry":40,"../../lib/traces/tracing-processor":46,"./computed-artifact":11,"lighthouse-logger":125}],1:[function(require,module,exports){ @@ -14592,7 +14789,7 @@ const notApplicables=artifacts.Accessibility.notApplicable||[]; -const isNotApplicable=notApplicables.find(result=>result.id===this.meta.name); +const isNotApplicable=notApplicables.find(result=>result.id===this.meta.id); if(isNotApplicable){ return{ rawValue:true, @@ -14601,7 +14798,9 @@ } const violations=artifacts.Accessibility.violations||[]; -const rule=violations.find(result=>result.id===this.meta.name); +const rule=violations.find(result=>result.id===this.meta.id); +const impact=rule&&rule.impact; +const tags=rule&&rule.tags; let items=[]; @@ -14611,7 +14810,8 @@ type:'node', selector:Array.isArray(node.target)?node.target.join(' '):'', path:node.path, -snippet:node.snippet}})); +snippet:node.html||node.snippet, +explanation:node.failureSummary}})); } @@ -14625,7 +14825,7 @@ extendedInfo:{ value:rule}, -details:Audit.makeTableDetails(headings,items)}; +details:{...Audit.makeTableDetails(headings,items),impact,tags}}; }} @@ -14765,13 +14965,30 @@ + + +static makeOpportunityDetails(headings,items,overallSavingsMs,overallSavingsBytes){ +return{ +type:'opportunity', +headings:items.length===0?[]:headings, +items, +overallSavingsMs, +overallSavingsBytes}; + +} + + + + + + static _normalizeAuditScore(audit,result){ let score=result.score===undefined?Number(result.rawValue):result.score; if(!Number.isFinite(score))throw new Error(`Invalid score: ${score}`); -if(score>1)throw new Error(`Audit score for ${audit.meta.name} is > 1`); -if(score<0)throw new Error(`Audit score for ${audit.meta.name} is < 0`); +if(score>1)throw new Error(`Audit score for ${audit.meta.id} is > 1`); +if(score<0)throw new Error(`Audit score for ${audit.meta.id} is < 0`); score=clampTo2Decimals(score); @@ -14806,10 +15023,10 @@ scoreDisplayMode=Audit.SCORING_MODES.ERROR; } -let auditDescription=audit.meta.description; -if(audit.meta.failureDescription){ +let auditTitle=audit.meta.title; +if(audit.meta.failureTitle){ if(Number(score)<Util.PASS_THRESHOLD){ -auditDescription=audit.meta.failureDescription; +auditTitle=audit.meta.failureTitle; } } @@ -14819,9 +15036,9 @@ } return{ -id:audit.meta.name, -title:auditDescription, -description:audit.meta.helpText, +id:audit.meta.id, +title:auditTitle, +description:audit.meta.description, score, scoreDisplayMode, @@ -14839,7 +15056,7 @@ module.exports=Audit; -},{"../lib/statistics":40,"../report/html/renderer/util":48}],3:[function(require,module,exports){ +},{"../lib/statistics":41,"../report/html/renderer/util":48}],3:[function(require,module,exports){ @@ -14848,16 +15065,26 @@ 'use strict'; const Audit=require('../audit'); +const linearInterpolation=require('../../lib/statistics').linearInterpolation; const Interactive=require('../../gather/computed/metrics/lantern-interactive'); -const Simulator=require('../../lib/dependency-graph/simulator/simulator'); -const Node=require('../../lib/dependency-graph/node.js'); -const NetworkNode=require('../../lib/dependency-graph/network-node.js'); + + const KB_IN_BYTES=1024; const WASTED_MS_FOR_AVERAGE=300; const WASTED_MS_FOR_POOR=750; +const WASTED_MS_FOR_SCORE_OF_ZERO=5000; + + + + + + + + + @@ -14868,11 +15095,21 @@ + + static scoreForWastedMs(wastedMs){ -if(wastedMs===0)return 1;else -if(wastedMs<WASTED_MS_FOR_AVERAGE)return 0.9;else -if(wastedMs<WASTED_MS_FOR_POOR)return 0.65;else -return 0; +if(wastedMs===0){ +return 1; +}else if(wastedMs<WASTED_MS_FOR_AVERAGE){ +return linearInterpolation(0,1,WASTED_MS_FOR_AVERAGE,0.75,wastedMs); +}else if(wastedMs<WASTED_MS_FOR_POOR){ +return linearInterpolation(WASTED_MS_FOR_AVERAGE,0.75,WASTED_MS_FOR_POOR,0.5,wastedMs); +}else{ +return Math.max( +0, +linearInterpolation(WASTED_MS_FOR_POOR,0.5,WASTED_MS_FOR_SCORE_OF_ZERO,0,wastedMs)); + +} } @@ -14901,14 +15138,14 @@ return Math.round(totalBytes*compressionRatio); -}else if(networkRecord._resourceType&&networkRecord._resourceType._name===resourceType){ +}else if(networkRecord.resourceType===resourceType){ -return networkRecord._transferSize||0; +return networkRecord.transferSize||0; }else{ -const transferSize=networkRecord._transferSize||0; -const resourceSize=networkRecord._resourceSize; +const transferSize=networkRecord.transferSize||0; +const resourceSize=networkRecord.resourceSize; const compressionRatio=resourceSize!==undefined?transferSize/resourceSize:1; return Math.round(totalBytes*compressionRatio); } @@ -14953,9 +15190,11 @@ static computeWasteWithTTIGraph(results,graph,simulator,options){ -options=Object.assign({includeLoad:true},options); +options=Object.assign({includeLoad:true,label:this.meta.id},options); +const beforeLabel=`${options.label}-before`; +const afterLabel=`${options.label}-after`; -const simulationBeforeChanges=simulator.simulate(graph); +const simulationBeforeChanges=simulator.simulate(graph,{label:beforeLabel}); const resultsByUrl=new Map(); for(const result of results){ @@ -14967,26 +15206,24 @@ const originalTransferSizes=new Map(); graph.traverse(node=>{ if(node.type!=='network')return; -const networkNode=node; -const result=resultsByUrl.get(networkNode.record.url); +const result=resultsByUrl.get(node.record.url); if(!result)return; -const original=networkNode.record.transferSize; -originalTransferSizes.set(networkNode.record.requestId,original); +const original=node.record.transferSize; +originalTransferSizes.set(node.record.requestId,original); const wastedBytes=result.wastedBytes; -networkNode.record._transferSize=Math.max(original-wastedBytes,0); +node.record.transferSize=Math.max(original-wastedBytes,0); }); -const simulationAfterChanges=simulator.simulate(graph); +const simulationAfterChanges=simulator.simulate(graph,{label:afterLabel}); graph.traverse(node=>{ if(node.type!=='network')return; -const networkNode=node; -const originalTransferSize=originalTransferSizes.get(networkNode.record.requestId); +const originalTransferSize=originalTransferSizes.get(node.record.requestId); if(originalTransferSize===undefined)return; -networkNode.record._transferSize=originalTransferSize; +node.record.transferSize=originalTransferSize; }); const savingsOnOverallLoad=simulationBeforeChanges.timeInMs-simulationAfterChanges.timeInMs; @@ -15007,7 +15244,7 @@ static createAuditProduct(result,graph,simulator){ -const results=result.results.sort((itemA,itemB)=>itemB.wastedBytes-itemA.wastedBytes); +const results=result.items.sort((itemA,itemB)=>itemB.wastedBytes-itemA.wastedBytes); const wastedBytes=results.reduce((sum,item)=>sum+item.wastedBytes,0); const wastedKb=Math.round(wastedBytes/KB_IN_BYTES); @@ -15019,13 +15256,7 @@ displayValue=['Potential savings of %d\xa0KB',wastedKb]; } -const summary={ -wastedMs, -wastedBytes}; - - - -const details=Audit.makeTableDetails(result.headings,results,summary); +const details=Audit.makeOpportunityDetails(result.headings,results,wastedMs,wastedBytes); return{ explanation:result.explanation, @@ -15061,7 +15292,7 @@ module.exports=UnusedBytes; -},{"../../gather/computed/metrics/lantern-interactive":"./gather/computed/metrics/lantern-interactive","../../lib/dependency-graph/network-node.js":24,"../../lib/dependency-graph/node.js":25,"../../lib/dependency-graph/simulator/simulator":28,"../audit":2}],4:[function(require,module,exports){ +},{"../../gather/computed/metrics/lantern-interactive":"./gather/computed/metrics/lantern-interactive","../../lib/statistics":41,"../audit":2}],4:[function(require,module,exports){ @@ -15128,23 +15359,36 @@ static createAuditProduct(result){ -const extendedInfo={ -value:result}; +const detailsItem={ +...result, +...result.manifestValues, +manifestValues:undefined, +warnings:undefined, +allChecks:undefined}; + + +if(result.manifestValues&&result.manifestValues.allChecks){ +result.manifestValues.allChecks.forEach(check=>{ +detailsItem[check.id]=check.passing; +}); +} + +const details={items:[detailsItem]}; if(result.failures.length>0){ return{ rawValue:false, explanation:`Failures: ${result.failures.join(',\n')}.`, -extendedInfo}; +details}; } return{ rawValue:true, -extendedInfo, +details, warnings:result.warnings}; } @@ -15206,7 +15450,6 @@ - 'use strict'; const defaultConfigPath='./default-config.js'; @@ -15217,21 +15460,29 @@ const isDeepEqual=require('lodash.isequal'); const log=require('lighthouse-logger'); const path=require('path'); -const Audit=require('../audits/audit'); -const Runner=require('../runner'); +const Audit=require('../audits/audit.js'); +const Runner=require('../runner.js'); + + + + + + + function validatePasses(passes,audits){ if(!Array.isArray(passes)){ return; } + const requiredGatherers=Config.getGatherersNeededByAudits(audits); passes.forEach(pass=>{ pass.gatherers.forEach(gathererDefn=>{ -const gatherer=gathererDefn.instance||gathererDefn.implementation; +const gatherer=gathererDefn.instance; const isGatherRequiredByAudits=requiredGatherers.has(gatherer.name); -if(isGatherRequiredByAudits===false){ +if(!isGatherRequiredByAudits){ const msg=`${gatherer.name} gatherer requested, however no audit requires it.`; log.warn('config',msg); } @@ -15249,6 +15500,11 @@ }); } + + + + + function validateCategories(categories,audits,groups){ if(!categories){ return; @@ -15260,7 +15516,7 @@ throw new Error(`missing an audit id at ${categoryId}[${index}]`); } -const audit=audits.find(a=>a.implementation.meta.name===auditRef.id); +const audit=audits&&audits.find(a=>a.implementation.meta.id===auditRef.id); if(!audit){ throw new Error(`could not find ${auditRef.id} audit for category ${categoryId}`); } @@ -15275,44 +15531,48 @@ throw new Error(`${auditRef.id} is manual but has a positive weight`); } -if(auditRef.group&&!groups[auditRef.group]){ +if(auditRef.group&&(!groups||!groups[auditRef.group])){ throw new Error(`${auditRef.id} references unknown group ${auditRef.group}`); } }); }); } + + + + function assertValidAudit(auditDefinition,auditPath){ const auditName=auditPath|| -auditDefinition&&auditDefinition.meta&&auditDefinition.meta.name; +auditDefinition&&auditDefinition.meta&&auditDefinition.meta.id; if(typeof auditDefinition.audit!=='function'||auditDefinition.audit===Audit.audit){ throw new Error(`${auditName} has no audit() method.`); } -if(typeof auditDefinition.meta.name!=='string'){ -throw new Error(`${auditName} has no meta.name property, or the property is not a string.`); +if(typeof auditDefinition.meta.id!=='string'){ +throw new Error(`${auditName} has no meta.id property, or the property is not a string.`); +} + +if(typeof auditDefinition.meta.title!=='string'){ +throw new Error( +`${auditName} has no meta.title property, or the property is not a string.`); + +} + + +if(typeof auditDefinition.meta.failureTitle!=='string'&& +auditDefinition.meta.scoreDisplayMode===Audit.SCORING_MODES.BINARY){ +throw new Error(`${auditName} has no failureTitle and should.`); } if(typeof auditDefinition.meta.description!=='string'){ throw new Error( `${auditName} has no meta.description property, or the property is not a string.`); -} - - -if(typeof auditDefinition.meta.failureDescription!=='string'&& -auditDefinition.meta.scoreDisplayMode===Audit.SCORING_MODES.BINARY){ -throw new Error(`${auditName} has no failureDescription and should.`); -} - -if(typeof auditDefinition.meta.helpText!=='string'){ +}else if(auditDefinition.meta.description===''){ throw new Error( -`${auditName} has no meta.helpText property, or the property is not a string.`); - -}else if(auditDefinition.meta.helpText===''){ -throw new Error( -`${auditName} has an empty meta.helpText string. Please add a description for the UI.`); +`${auditName} has an empty meta.description string. Please add a description for the UI.`); } @@ -15323,6 +15583,10 @@ } } + + + + function assertValidGatherer(gathererInstance,gathererName){ gathererName=gathererName||gathererInstance.name||'gatherer'; @@ -15347,10 +15611,14 @@ function cleanFlagsForSettings(flags={}){ + const settings={}; + for(const key of Object.keys(flags)){ + if(typeof constants.defaultSettings[key]!=='undefined'){ -settings[key]=flags[key]; +const safekey=key; +settings[safekey]=flags[safekey]; } } @@ -15358,7 +15626,13 @@ } -function merge(base,extension,overwriteArrays=false){ + + + + + + +function _merge(base,extension,overwriteArrays=false){ if(typeof base==='undefined'||base===null){ return extension; @@ -15375,10 +15649,11 @@ return merged; }else if(typeof extension==='object'){ if(typeof base!=='object')throw new TypeError(`Expected object but got ${typeof base}`); +if(Array.isArray(base))throw new TypeError('Expected object but got Array'); Object.keys(extension).forEach(key=>{ const localOverwriteArrays=overwriteArrays|| key==='settings'&&typeof base[key]==='object'; -base[key]=merge(base[key],extension[key],localOverwriteArrays); +base[key]=_merge(base[key],extension[key],localOverwriteArrays); }); return base; } @@ -15386,21 +15661,61 @@ return extension; } + + + + + +const merge=_merge; + + + + + + function cloneArrayWithPluginSafety(array){ return array.map(item=>{ -return typeof item==='object'?Object.assign({},item):item; +if(typeof item==='object'){ + +return Object.assign( +Object.create( +Object.getPrototypeOf(item)), + +item); + +} + +return item; }); } + + + + + + + function deepClone(json){ -const cloned=JSON.parse(JSON.stringify(json)); +return JSON.parse(JSON.stringify(json)); +} -if(Array.isArray(json.passes)){ -cloned.passes.forEach((pass,i)=>{ + + + + +function deepCloneConfigJson(json){ +const cloned=deepClone(json); + + + +if(Array.isArray(cloned.passes)&&Array.isArray(json.passes)){ +for(let i=0;i<cloned.passes.length;i++){ +const pass=cloned.passes[i]; pass.gatherers=cloneArrayWithPluginSafety(json.passes[i].gatherers||[]); -}); +} } if(Array.isArray(json.audits)){ @@ -15410,12 +15725,35 @@ return cloned; } + + + + + +const mergeOptionsOfItems=function(items){ + +const mergedItems=[]; + +for(const item of items){ +const existingItem=item.path&&mergedItems.find(candidate=>candidate.path===item.path); +if(!existingItem){ +mergedItems.push(item); +continue; +} + +existingItem.options=Object.assign({},existingItem.options,item.options); +} + +return mergedItems; +}; + class Config{ + constructor(configJSON,flags){ let configPath=flags&&flags.configPath; @@ -15429,52 +15767,46 @@ } -configJSON=deepClone(configJSON); +configJSON=deepCloneConfigJson(configJSON); if(configJSON.extends==='lighthouse:full'){ -const explodedFullConfig=Config.extendConfigJSON(deepClone(defaultConfig), -deepClone(fullConfig)); +const explodedFullConfig=Config.extendConfigJSON(deepCloneConfigJson(defaultConfig), +deepCloneConfigJson(fullConfig)); configJSON=Config.extendConfigJSON(explodedFullConfig,configJSON); }else if(configJSON.extends){ -configJSON=Config.extendConfigJSON(deepClone(defaultConfig),configJSON); +configJSON=Config.extendConfigJSON(deepCloneConfigJson(defaultConfig),configJSON); } -configJSON=Config.augmentWithDefaults(configJSON); +const configDir=configPath?path.dirname(configPath):undefined; + +const settings=Config.initSettings(configJSON.settings,flags); -configJSON.audits=Config.expandAuditShorthandAndMergeOptions(configJSON.audits); -configJSON.passes=Config.expandGathererShorthandAndMergeOptions(configJSON.passes); +const passesWithDefaults=Config.augmentPassesWithDefaults(configJSON.passes); +Config.adjustDefaultPassForThrottling(settings,passesWithDefaults); +const passes=Config.requireGatherers(passesWithDefaults,configDir); -configJSON.settings=merge(configJSON.settings||{},cleanFlagsForSettings(flags),true); +this.settings=settings; + +this.passes=passes; + +this.audits=Config.requireAudits(configJSON.audits,configDir); + +this.categories=configJSON.categories||null; + +this.groups=configJSON.groups||null; + +Config.filterConfigIfNeeded(this); + +validatePasses(this.passes,this.audits); +validateCategories(this.categories,this.audits,this.groups); -if(Array.isArray(configJSON.settings.onlyCategories)|| -Array.isArray(configJSON.settings.onlyAudits)|| -Array.isArray(configJSON.settings.skipAudits)){ -const categoryIds=configJSON.settings.onlyCategories; -const auditIds=configJSON.settings.onlyAudits; -const skipAuditIds=configJSON.settings.skipAudits; -configJSON=Config.generateNewFilteredConfig(configJSON,categoryIds,auditIds, -skipAuditIds); -} -Config.adjustDefaultPassForThrottling(configJSON); - - -this._configDir=configPath?path.dirname(configPath):undefined; - -this._passes=Config.requireGatherers(configJSON.passes,this._configDir); -this._audits=Config.requireAudits(configJSON.audits,this._configDir); -this._categories=configJSON.categories; -this._groups=configJSON.groups; -this._settings=configJSON.settings||{}; - - -validatePasses(configJSON.passes,this._audits); -validateCategories(configJSON.categories,this._audits,this._groups); +const configJson=this; } @@ -15483,8 +15815,8 @@ static extendConfigJSON(baseJSON,extendJSON){ -if(extendJSON.passes){ -extendJSON.passes.forEach(pass=>{ +if(extendJSON.passes&&baseJSON.passes){ +for(const pass of extendJSON.passes){ const passName=pass.passName||constants.defaultPassConfig.passName; const basePass=baseJSON.passes.find(candidate=>candidate.passName===passName); @@ -15494,7 +15826,7 @@ }else{ merge(basePass,pass); } -}); +} delete extendJSON.passes; } @@ -15506,14 +15838,13 @@ -static augmentWithDefaults(config){ -const{defaultSettings,defaultPassConfig}=constants; -config.settings=merge(deepClone(defaultSettings),config.settings,true); -if(config.passes){ -config.passes=config.passes.map(pass=>merge(deepClone(defaultPassConfig),pass)); +static augmentPassesWithDefaults(passes){ +if(!passes){ +return null; } -return config; +const{defaultPassConfig}=constants; +return passes.map(pass=>merge(deepClone(defaultPassConfig),pass)); } @@ -15521,23 +15852,46 @@ +static initSettings(settings={},flags){ -static expandAuditShorthandAndMergeOptions(audits){ +const{defaultSettings}=constants; +const settingWithDefaults=merge(deepClone(defaultSettings),settings,true); + + +const settingsWithFlags=merge(settingWithDefaults||{},cleanFlagsForSettings(flags),true); + +return settingsWithFlags; +} + + + + + + +static expandAuditShorthand(audits){ if(!audits){ -return audits; +return null; } const newAudits=audits.map(audit=>{ if(typeof audit==='string'){ + return{path:audit,options:{}}; -}else if(audit&&typeof audit.audit==='function'){ +}else if('implementation'in audit&&typeof audit.implementation.audit==='function'){ + +return audit; +}else if('path'in audit&&typeof audit.path==='string'){ + +return audit; +}else if('audit'in audit&&typeof audit.audit==='function'){ + return{implementation:audit,options:{}}; }else{ -return audit; +throw new Error('Invalid Audit type '+JSON.stringify(audit)); } }); -return Config._mergeOptionsOfItems(newAudits); +return newAudits; } @@ -15551,48 +15905,32 @@ -static expandGathererShorthandAndMergeOptions(passes){ -if(!passes){ -return passes; -} - -passes.forEach(pass=>{ -pass.gatherers=pass.gatherers.map(gatherer=>{ +static expandGathererShorthand(gatherers){ +const expanded=gatherers.map(gatherer=>{ if(typeof gatherer==='string'){ + return{path:gatherer,options:{}}; +}else if('implementation'in gatherer||'instance'in gatherer){ + +return gatherer; +}else if('path'in gatherer){ + +if(typeof gatherer.path!=='string'){ +throw new Error('Invalid Gatherer type '+JSON.stringify(gatherer)); +} +return gatherer; }else if(typeof gatherer==='function'){ + return{implementation:gatherer,options:{}}; }else if(gatherer&&typeof gatherer.beforePass==='function'){ + return{instance:gatherer,options:{}}; }else{ -return gatherer; +throw new Error('Invalid Gatherer type '+JSON.stringify(gatherer)); } }); -pass.gatherers=Config._mergeOptionsOfItems(pass.gatherers); -}); - -return passes; -} - - - - - -static _mergeOptionsOfItems(items){ -const mergedItems=[]; - -for(const item of items){ -const existingItem=item.path&&mergedItems.find(candidate=>candidate.path===item.path); -if(!existingItem){ -mergedItems.push(item); -continue; -} - -existingItem.options=Object.assign({},existingItem.options,item.options); -} - -return mergedItems; +return expanded; } @@ -15601,15 +15939,14 @@ -static adjustDefaultPassForThrottling(config){ -if(config.settings.throttlingMethod!=='devtools'&& -config.settings.throttlingMethod!=='provided'){ +static adjustDefaultPassForThrottling(settings,passes){ +if(!passes|| +settings.throttlingMethod!=='devtools'&&settings.throttlingMethod!=='provided'){ return; } -const defaultPass=config.passes.find(pass=>pass.passName==='defaultPass'); +const defaultPass=passes.find(pass=>pass.passName==='defaultPass'); if(!defaultPass)return; - const overrides=constants.nonSimulatedPassConfigOverrides; defaultPass.pauseAfterLoadMs= Math.max(overrides.pauseAfterLoadMs,defaultPass.pauseAfterLoadMs); @@ -15623,42 +15960,29 @@ +static filterConfigIfNeeded(config){ +const settings=config.settings; +if(!settings.onlyCategories&&!settings.onlyAudits&&!settings.skipAudits){ +return; +} +const{categories,requestedAuditNames}=Config.filterCategoriesAndAudits(config.categories, +settings); -static generateNewFilteredConfig(oldConfig,categoryIds,auditIds,skipAuditIds){ - -const config=deepClone(oldConfig); -config.audits=Config.expandAuditShorthandAndMergeOptions(config.audits); -config.passes=Config.expandGathererShorthandAndMergeOptions(config.passes); -config.passes=Config.requireGatherers(config.passes); +const audits=config.audits&&config.audits.filter(auditDefn=> +requestedAuditNames.has(auditDefn.implementation.meta.id)); -const{categories,audits:requestedAuditNames}=Config.filterCategoriesAndAudits( -config.categories, -categoryIds, -auditIds, -skipAuditIds); +const requiredGathererIds=Config.getGatherersNeededByAudits(audits); +const passes=Config.generatePassesNeededByGatherers(config.passes,requiredGathererIds); + config.categories=categories; - - -const auditPathToNameMap=Config.getMapOfAuditPathToName(config); -const getAuditName=auditDefn=>auditDefn.implementation? -auditDefn.implementation.meta.name: -auditPathToNameMap.get(auditDefn.path); -config.audits=config.audits.filter(auditDefn=> -requestedAuditNames.has(getAuditName(auditDefn))); - - -const auditObjectsSelected=Config.requireAudits(config.audits); -const requiredGatherers=Config.getGatherersNeededByAudits(auditObjectsSelected); - - -config.passes=Config.generatePassesNeededByGatherers(config.passes,requiredGatherers); -return config; +config.audits=audits; +config.passes=passes; } @@ -15667,19 +15991,22 @@ +static filterCategoriesAndAudits(oldCategories,settings){ +if(!oldCategories){ +return{categories:null,requestedAuditNames:new Set()}; +} - -static filterCategoriesAndAudits(oldCategories,categoryIds,auditIds,skipAuditIds){ -if(auditIds&&skipAuditIds){ +if(settings.onlyAudits&&settings.skipAudits){ throw new Error('Cannot set both skipAudits and onlyAudits'); } + const categories={}; -const filterByIncludedCategory=!!categoryIds; -const filterByIncludedAudit=!!auditIds; -categoryIds=categoryIds||[]; -auditIds=auditIds||[]; -skipAuditIds=skipAuditIds||[]; +const filterByIncludedCategory=!!settings.onlyCategories; +const filterByIncludedAudit=!!settings.onlyAudits; +const categoryIds=settings.onlyCategories||[]; +const auditIds=settings.onlyAudits||[]; +const skipAuditIds=settings.skipAudits||[]; categoryIds.forEach(categoryId=>{ @@ -15693,15 +16020,13 @@ for(const auditId of auditsToValidate){ const foundCategory=Object.keys(oldCategories).find(categoryId=>{ const auditRefs=oldCategories[categoryId].auditRefs; -return auditRefs.find(candidate=>candidate.id===auditId); +return!!auditRefs.find(candidate=>candidate.id===auditId); }); if(!foundCategory){ const parentKeyName=skipAuditIds.includes(auditId)?'skipAudits':'onlyAudits'; log.warn('config',`unrecognized audit in '${parentKeyName}': ${auditId}`); -} - -if(auditIds.includes(auditId)&&categoryIds.includes(foundCategory)){ +}else if(auditIds.includes(auditId)&&categoryIds.includes(foundCategory)){ log.warn('config',`${auditId} in 'onlyAudits' is already included by `+ `${foundCategory} in 'onlyCategories'`); } @@ -15736,7 +16061,7 @@ } }); -return{categories,audits:includedAudits}; +return{categories,requestedAuditNames:includedAudits}; } @@ -15744,26 +16069,15 @@ static getCategories(config){ -return Object.keys(config.categories).map(id=>{ -const title=config.categories[id].title; -return{id,title}; -}); +const categories=config.categories; +if(!categories){ +return[]; } - - - - - -static getMapOfAuditPathToName(config){ -const auditObjectsAll=Config.requireAudits(config.audits); -const auditPathToName=new Map(auditObjectsAll.map((auditDefn,index)=>{ -const AuditClass=auditDefn.implementation; -const auditPath=config.audits[index]; -const auditName=AuditClass.meta.name; -return[auditPath,auditName]; -})); -return auditPathToName; +return Object.keys(categories).map(id=>{ +const title=categories[id].title; +return{id,title}; +}); } @@ -15791,11 +16105,15 @@ static generatePassesNeededByGatherers(passes,requiredGatherers){ +if(!passes){ +return null; +} + const auditsNeedTrace=requiredGatherers.has('traces'); const filteredPasses=passes.map(pass=>{ pass.gatherers=pass.gatherers.filter(gathererDefn=>{ -const gatherer=gathererDefn.instance||gathererDefn.implementation; +const gatherer=gathererDefn.instance; return requiredGatherers.has(gatherer.name); }); @@ -15826,28 +16144,38 @@ static requireAudits(audits,configPath){ -if(!audits){ +const expandedAudits=Config.expandAuditShorthand(audits); +if(!expandedAudits){ return null; } const coreList=Runner.getAuditList(); -return audits.map(auditDefn=>{ -if(!auditDefn.implementation){ -const path=auditDefn.path; +const auditDefns=expandedAudits.map(audit=>{ +let implementation; +if('implementation'in audit){ +implementation=audit.implementation; +}else{ -const coreAudit=coreList.find(a=>a===`${path}.js`); -let requirePath=`../audits/${path}`; +const auditPathJs=`${audit.path}.js`; +const coreAudit=coreList.find(a=>a===auditPathJs); +let requirePath=`../audits/${audit.path}`; if(!coreAudit){ -requirePath=Runner.resolvePlugin(path,configPath,'audit'); +requirePath=Runner.resolvePlugin(audit.path,configPath,'audit'); +} +implementation=require(requirePath); } -auditDefn.implementation=require(requirePath); -} +return{ +implementation, +path:audit.path, +options:audit.options||{}}; -assertValidAudit(auditDefn.implementation,auditDefn.path); -return auditDefn; }); + +const mergedAuditDefns=mergeOptionsOfItems(auditDefns); +mergedAuditDefns.forEach(audit=>assertValidAudit(audit.implementation,audit.path)); +return mergedAuditDefns; } @@ -15856,91 +16184,80 @@ + +static requireGathererFromPath(path,options,coreAuditList,configPath){ +const coreGatherer=coreAuditList.find(a=>a===`${path}.js`); + +let requirePath=`../gather/gatherers/${path}`; +if(!coreGatherer){ + +requirePath=Runner.resolvePlugin(path,configPath,'gatherer'); +} + +const GathererClass=require(requirePath); + +return{ +instance:new GathererClass(), +implementation:GathererClass, +path, +options:options||{}}; + +} + + + + + + + + + static requireGatherers(passes,configPath){ if(!passes){ return null; } const coreList=Runner.getGathererList(); -passes.forEach(pass=>{ -pass.gatherers.forEach(gathererDefn=>{ -if(!gathererDefn.instance){ -let GathererClass=gathererDefn.implementation; -if(!GathererClass){ +const fullPasses=passes.map(pass=>{ +const gathererDefns=Config.expandGathererShorthand(pass.gatherers).map(gathererDefn=>{ +if(gathererDefn.instance){ +return{ +instance:gathererDefn.instance, +implementation:gathererDefn.implementation, +path:gathererDefn.path, +options:gathererDefn.options||{}}; -const name=gathererDefn.path; -const coreGatherer=coreList.find(a=>a===`${name}.js`); +}else if(gathererDefn.implementation){ +const GathererClass=gathererDefn.implementation; +return{ +instance:new GathererClass(), +implementation:gathererDefn.implementation, +path:gathererDefn.path, +options:gathererDefn.options||{}}; -let requirePath=`../gather/gatherers/${name}`; -if(!coreGatherer){ - -requirePath=Runner.resolvePlugin(name,configPath,'gatherer'); +}else if(gathererDefn.path){ +const path=gathererDefn.path; +const options=gathererDefn.options; +return Config.requireGathererFromPath(path,options,coreList,configPath); +}else{ +throw new Error('Invalid expanded Gatherer: '+JSON.stringify(gathererDefn)); } - -GathererClass=require(requirePath); -} - -gathererDefn.implementation=GathererClass; -gathererDefn.instance=new GathererClass(); -} - -assertValidGatherer(gathererDefn.instance,gathererDefn.path); -}); }); -return passes; -} +const mergedDefns=mergeOptionsOfItems(gathererDefns); +mergedDefns.forEach(gatherer=>assertValidGatherer(gatherer.instance,gatherer.path)); +return Object.assign(pass,{gatherers:mergedDefns}); +}); -get configDir(){ -return this._configDir; -} - - -get passes(){ -return this._passes; -} - - -get audits(){ -return this._audits; -} - - -get categories(){ -return this._categories; -} - - -get groups(){ -return this._groups; -} - - -get settings(){ -return this._settings; +return fullPasses; }} - - - - - - - - - - - - - - - module.exports=Config; }).call(this,"/../lighthouse-core/config"); -},{"../audits/audit":2,"../runner":50,"./constants":8,"./default-config.js":9,"./full-config.js":10,"lighthouse-logger":143,"lodash.isequal":144,"path":75}],8:[function(require,module,exports){ +},{"../audits/audit.js":2,"../runner.js":50,"./constants":8,"./default-config.js":9,"./full-config.js":10,"lighthouse-logger":125,"lodash.isequal":126,"path":75}],8:[function(require,module,exports){ @@ -15990,6 +16307,7 @@ skipAudits:null}; + const defaultPassConfig={ passName:'defaultPass', recordTrace:false, @@ -16048,9 +16366,9 @@ 'chrome-console-messages', 'image-usage', 'accessibility', -'dobetterweb/all-event-listeners', 'dobetterweb/anchors-with-no-rel-noopener', 'dobetterweb/appcache', +'dobetterweb/doctype', 'dobetterweb/domstats', 'dobetterweb/js-libraries', 'dobetterweb/optimized-images', @@ -16093,16 +16411,16 @@ 'works-offline', 'viewport', 'without-javascript', -'first-contentful-paint', -'first-meaningful-paint', +'metrics/first-contentful-paint', +'metrics/first-meaningful-paint', 'load-fast-enough-for-pwa', -'speed-index', +'metrics/speed-index', 'screenshot-thumbnails', -'estimated-input-latency', +'metrics/estimated-input-latency', 'errors-in-console', 'time-to-first-byte', -'first-cpu-idle', -'interactive', +'metrics/first-cpu-idle', +'metrics/interactive', 'user-timings', 'critical-request-chains', 'redirects', @@ -16181,11 +16499,11 @@ 'byte-efficiency/uses-responsive-images', 'byte-efficiency/efficient-animated-content', 'dobetterweb/appcache-manifest', +'dobetterweb/doctype', 'dobetterweb/dom-size', 'dobetterweb/external-anchors-use-rel-noopener', 'dobetterweb/geolocation-on-start', 'dobetterweb/no-document-write', -'dobetterweb/no-mutation-events', 'dobetterweb/no-vulnerable-libraries', 'dobetterweb/no-websql', 'dobetterweb/notification-on-start', @@ -16308,17 +16626,22 @@ '[PWA Checklist](https://developers.google.com/web/progressive-web-apps/checklist) but are '+ 'not automatically checked by Lighthouse. They do not affect your score but it\'s important that you verify them manually.', auditRefs:[ + +{id:'load-fast-enough-for-pwa',weight:7}, +{id:'works-offline',weight:5}, + +{id:'webapp-install-banner',weight:3}, + +{id:'is-on-https',weight:2}, +{id:'redirects-http',weight:2}, +{id:'viewport',weight:2}, + {id:'service-worker',weight:1}, -{id:'works-offline',weight:1}, {id:'without-javascript',weight:1}, -{id:'is-on-https',weight:1}, -{id:'redirects-http',weight:1}, -{id:'load-fast-enough-for-pwa',weight:1}, -{id:'webapp-install-banner',weight:1}, {id:'splash-screen',weight:1}, {id:'themed-omnibox',weight:1}, -{id:'viewport',weight:1}, {id:'content-width',weight:1}, +{id:'manifest-short-name-length',weight:0}, {id:'pwa-cross-browser',weight:0}, {id:'pwa-page-transitions',weight:0}, @@ -16386,14 +16709,13 @@ {id:'is-on-https',weight:1}, {id:'uses-http2',weight:1}, {id:'uses-passive-event-listeners',weight:1}, -{id:'no-mutation-events',weight:1}, {id:'no-document-write',weight:1}, {id:'external-anchors-use-rel-noopener',weight:1}, {id:'geolocation-on-start',weight:1}, +{id:'doctype',weight:1}, {id:'no-vulnerable-libraries',weight:1}, {id:'notification-on-start',weight:1}, {id:'deprecations',weight:1}, -{id:'manifest-short-name-length',weight:1}, {id:'password-inputs-can-be-pasted-into',weight:1}, {id:'errors-in-console',weight:1}, {id:'image-aspect-ratio',weight:1}]}, @@ -16539,10 +16861,12 @@ 'use strict'; const ComputedArtifact=require('../computed-artifact'); -const Node=require('../../../lib/dependency-graph/node'); -const NetworkNode=require('../../../lib/dependency-graph/network-node'); -const Simulator=require('../../../lib/dependency-graph/simulator/simulator'); -const WebInspector=require('../../../lib/web-inspector'); +const BaseNode=require('../../../lib/dependency-graph/base-node'); +const NetworkRequest=require('../../../lib/network-request'); + + + + class LanternMetricArtifact extends ComputedArtifact{ @@ -16551,14 +16875,14 @@ static getScriptUrls(dependencyGraph,condition){ + const scriptUrls=new Set(); dependencyGraph.traverse(node=>{ -if(node.type===Node.TYPES.CPU)return; -const asNetworkNode=node; -if(asNetworkNode.record._resourceType!==WebInspector.resourceTypes.Script)return; -if(condition&&!condition(asNetworkNode))return; -scriptUrls.add(asNetworkNode.record.url); +if(node.type===BaseNode.TYPES.CPU)return; +if(node.record.resourceType!==NetworkRequest.TYPES.Script)return; +if(condition&&!condition(node))return; +scriptUrls.add(node.record.url); }); return scriptUrls; @@ -16606,6 +16930,7 @@ async computeMetricWithGraphs(data,artifacts,extras){ const{trace,devtoolsLog,settings}=data; +const metricName=this.name.replace('Lantern',''); const graph=await artifacts.requestPageDependencyGraph({trace,devtoolsLog}); const traceOfTab=await artifacts.requestTraceOfTab(trace); @@ -16615,9 +16940,14 @@ const optimisticGraph=this.getOptimisticGraph(graph,traceOfTab); const pessimisticGraph=this.getPessimisticGraph(graph,traceOfTab); -const optimisticSimulation=simulator.simulate(optimisticGraph); -const optimisticFlexSimulation=simulator.simulate(optimisticGraph,{flexibleOrdering:true}); -const pessimisticSimulation=simulator.simulate(pessimisticGraph); +let simulateOptions={label:`optimistic${metricName}`}; +const optimisticSimulation=simulator.simulate(optimisticGraph,simulateOptions); + +simulateOptions={label:`optimisticFlex${metricName}`,flexibleOrdering:true}; +const optimisticFlexSimulation=simulator.simulate(optimisticGraph,simulateOptions); + +simulateOptions={label:`pessimistic${metricName}`}; +const pessimisticSimulation=simulator.simulate(pessimisticGraph,simulateOptions); const optimisticEstimate=this.getEstimateFromSimulation( optimisticSimulation.timeInMs<optimisticFlexSimulation.timeInMs? @@ -16659,7 +16989,7 @@ module.exports=LanternMetricArtifact; -},{"../../../lib/dependency-graph/network-node":24,"../../../lib/dependency-graph/node":25,"../../../lib/dependency-graph/simulator/simulator":28,"../../../lib/web-inspector":47,"../computed-artifact":11}],13:[function(require,module,exports){ +},{"../../../lib/dependency-graph/base-node":22,"../../../lib/network-request":38,"../computed-artifact":11}],13:[function(require,module,exports){ @@ -16903,7 +17233,7 @@ module.exports=Connection; -},{"../../lib/errors":33,"events":62,"lighthouse-logger":143}],15:[function(require,module,exports){ +},{"../../lib/errors":33,"events":62,"lighthouse-logger":125}],15:[function(require,module,exports){ @@ -16918,21 +17248,10 @@ -class Port{ -on(eventName,cb){} - - - - -send(message){} - -close(){}} - - class RawConnection extends Connection{ @@ -17043,6 +17362,7 @@ const emulation=require('../lib/emulation'); const Element=require('../lib/element'); const LHError=require('../lib/errors'); +const NetworkRequest=require('../lib/network-request'); const EventEmitter=require('events').EventEmitter; const URL=require('../lib/url-shim'); const TraceParser=require('../lib/traces/trace-parser'); @@ -17207,7 +17527,7 @@ -evaluteScriptOnNewDocument(scriptSource){ +evaluateScriptOnNewDocument(scriptSource){ return this.sendCommand('Page.addScriptToEvaluateOnLoad',{ scriptSource}); @@ -17620,7 +17940,7 @@ _beginNetworkStatusMonitoring(startingUrl){ -this._networkStatusMonitor=new NetworkRecorder([]); +this._networkStatusMonitor=new NetworkRecorder(); this._monitoredUrl=startingUrl; @@ -17761,6 +18081,8 @@ getRequestContent(requestId,timeout=1000){ +requestId=NetworkRequest.getRequestIdForBackend(requestId); + return new Promise((resolve,reject)=>{ @@ -18098,7 +18420,7 @@ async cacheNatives(){ -await this.evaluteScriptOnNewDocument(`window.__nativePromise = Promise; +await this.evaluateScriptOnNewDocument(`window.__nativePromise = Promise; window.__nativeError = Error;`); } @@ -18108,39 +18430,7 @@ async registerPerformanceObserver(){ const scriptStr=`(${pageFunctions.registerPerformanceObserverInPage.toString()})()`; -await this.evaluteScriptOnNewDocument(scriptStr); -} - - - - - - - - -captureFunctionCallSites(funcName){ -const globalVarToPopulate=`window['__${funcName}StackTraces']`; -const collectUsage=()=>{ -return this.evaluateAsync( -`Array.from(${globalVarToPopulate}).map(item => JSON.parse(item))`). -then(result=>{ -if(!Array.isArray(result)){ -throw new Error( -'Driver failure: Expected evaluateAsync results to be an array '+ -`but got "${JSON.stringify(result)}" instead.`); -} - -return result.filter(item=>!item.isExtension); -}); -}; - -const funcBody=pageFunctions.captureJSCallUsage.toString(); - -this.evaluteScriptOnNewDocument(` - ${globalVarToPopulate} = new Set(); - (${funcName} = ${funcBody}(${funcName}, ${globalVarToPopulate}))`); - -return collectUsage; +await this.evaluateScriptOnNewDocument(scriptStr); } @@ -18252,7 +18542,7 @@ module.exports=Driver; -},{"../config/constants":8,"../lib/element":31,"../lib/emulation":32,"../lib/errors":33,"../lib/network-recorder":37,"../lib/page-functions.js":38,"../lib/traces/trace-parser":45,"../lib/url-shim":"url","./connections/connection.js":14,"./devtools-log":16,"events":62,"lighthouse-logger":143}],18:[function(require,module,exports){ +},{"../config/constants":8,"../lib/element":31,"../lib/emulation":32,"../lib/errors":33,"../lib/network-recorder":37,"../lib/network-request":38,"../lib/page-functions.js":39,"../lib/traces/trace-parser":45,"../lib/url-shim":"url","./connections/connection.js":14,"./devtools-log":16,"events":62,"lighthouse-logger":125}],18:[function(require,module,exports){ @@ -18325,12 +18615,13 @@ -static loadBlank( +static async loadBlank( driver, url=constants.defaultPassConfig.blankPage, duration=constants.defaultPassConfig.blankDuration) { -return driver.gotoURL(url).then(_=>new Promise(resolve=>setTimeout(resolve,duration))); +await driver.gotoURL(url); +await new Promise(resolve=>setTimeout(resolve,duration)); } @@ -18342,13 +18633,12 @@ -static loadPage(driver,passContext){ -return driver.gotoURL(passContext.url,{ +static async loadPage(driver,passContext){ +const finalUrl=await driver.gotoURL(passContext.url,{ waitForLoad:true, -passContext}). -then(finalUrl=>{ +passContext}); + passContext.url=finalUrl; -}); } @@ -18356,25 +18646,17 @@ - -static setupDriver(driver,gathererResults,options){ +static async setupDriver(driver,options){ log.log('status','Initializing…'); const resetStorage=!options.settings.disableStorageReset; -return driver.assertNoSameOriginServiceWorkerClients(options.requestedUrl). -then(_=>driver.getUserAgent()). -then(userAgent=>{ -gathererResults.UserAgent=[userAgent]; -GatherRunner.warnOnHeadless(userAgent,gathererResults); -}). -then(_=>driver.beginEmulation(options.settings)). -then(_=>driver.enableRuntimeEvents()). -then(_=>driver.cacheNatives()). -then(_=>driver.registerPerformanceObserver()). -then(_=>driver.dismissJavaScriptDialogs()). -then(_=>{ -if(resetStorage)return driver.clearDataForOrigin(options.requestedUrl); -}); +await driver.assertNoSameOriginServiceWorkerClients(options.requestedUrl); +await driver.beginEmulation(options.settings); +await driver.enableRuntimeEvents(); +await driver.cacheNatives(); +await driver.registerPerformanceObserver(); +await driver.dismissJavaScriptDialogs(); +if(resetStorage)await driver.clearDataForOrigin(options.requestedUrl); } @@ -18439,48 +18721,28 @@ -static warnOnHeadless(userAgent,gathererResults){ -const chromeVersion=userAgent.split(/HeadlessChrome\/(.*) /)[1]; -const minVersion='63.0.3239.0'; -if(chromeVersion&&chromeVersion<minVersion){ -gathererResults.LighthouseRunWarnings.push('Your site\'s mobile performance may be '+ -'worse than the numbers presented in this report. Lighthouse could not test on a '+ -'mobile connection because Headless Chrome does not support network throttling '+ -'prior to version '+minVersion+'. The version used was '+chromeVersion); -} -} - - - - - - - - -static beforePass(passContext,gathererResults){ +static async beforePass(passContext,gathererResults){ const blockedUrls=(passContext.passConfig.blockedUrlPatterns||[]). concat(passContext.settings.blockedUrlPatterns||[]); const blankPage=passContext.passConfig.blankPage; const blankDuration=passContext.passConfig.blankDuration; -const pass=GatherRunner.loadBlank(passContext.driver,blankPage,blankDuration). +await GatherRunner.loadBlank(passContext.driver,blankPage,blankDuration); -then(()=>passContext.driver.blockUrlPatterns(blockedUrls)). -then(()=>passContext.driver.setExtraHTTPHeaders(passContext.settings.extraHeaders)); +await passContext.driver.blockUrlPatterns(blockedUrls); +await passContext.driver.setExtraHTTPHeaders(passContext.settings.extraHeaders); -return passContext.passConfig.gatherers.reduce((chain,gathererDefn)=>{ -return chain.then(_=>{ +for(const gathererDefn of passContext.passConfig.gatherers){ const gatherer=gathererDefn.instance; passContext.options=gathererDefn.options||{}; const artifactPromise=Promise.resolve().then(_=>gatherer.beforePass(passContext)); gathererResults[gatherer.name]=[artifactPromise]; -return GatherRunner.recoverOrThrow(artifactPromise); -}); -},pass); +await GatherRunner.recoverOrThrow(artifactPromise); +} } @@ -18490,7 +18752,7 @@ -static pass(passContext,gathererResults){ +static async pass(passContext,gathererResults){ const driver=passContext.driver; const config=passContext.passConfig; const settings=passContext.settings; @@ -18503,31 +18765,28 @@ const status='Loading page & waiting for onload'; log.log('status',status,gatherernames); -const pass=Promise.resolve(). -then(_=>{ -if(isPerfRun)driver.cleanBrowserCaches(); -}). +if(isPerfRun)await driver.cleanBrowserCaches(); -then(_=>driver.beginDevtoolsLog()). +await driver.beginDevtoolsLog(); -then(_=>{ -if(recordTrace)driver.beginTrace(settings); -}). +if(recordTrace)await driver.beginTrace(settings); -then(_=>GatherRunner.loadPage(driver,passContext)). -then(_=>log.log('statusEnd',status)); -return gatherers.reduce((chain,gathererDefn)=>{ -return chain.then(_=>{ +await GatherRunner.loadPage(driver,passContext); +log.log('statusEnd',status); + +for(const gathererDefn of gatherers){ const gatherer=gathererDefn.instance; passContext.options=gathererDefn.options||{}; const artifactPromise=Promise.resolve().then(_=>gatherer.pass(passContext)); -gathererResults[gatherer.name].push(artifactPromise); -return GatherRunner.recoverOrThrow(artifactPromise); -}); -},pass); + +const gathererResult=gathererResults[gatherer.name]||[]; +gathererResult.push(artifactPromise); +gathererResults[gatherer.name]=gathererResult; +await GatherRunner.recoverOrThrow(artifactPromise); +} } @@ -18561,7 +18820,7 @@ if(!driver.online)pageLoadError=undefined; if(pageLoadError){ -gathererResults.LighthouseRunWarnings.push('Lighthouse was unable to reliably load the '+ +passContext.LighthouseRunWarnings.push('Lighthouse was unable to reliably load the '+ 'page you requested. Make sure you are testing the correct URL and that the server is '+ 'properly responding to all requests.'); } @@ -18590,7 +18849,10 @@ Promise.reject(pageLoadError): Promise.resolve().then(_=>gatherer.afterPass(passContext,passData)); -gathererResults[gatherer.name].push(artifactPromise); + +const gathererResult=gathererResults[gatherer.name]||[]; +gathererResult.push(artifactPromise); +gathererResults[gatherer.name]=gathererResult; await GatherRunner.recoverOrThrow(artifactPromise); log.verbose('statusEnd',status); } @@ -18608,48 +18870,61 @@ +static async collectArtifacts(gathererResults,baseArtifacts){ -static async collectArtifacts(gathererResults,tracingData,settings){ - - - -const artifacts={ -traces:tracingData.traces, -devtoolsLogs:tracingData.devtoolsLogs, -settings, - -LighthouseRunWarnings:Array.from(new Set(gathererResults.LighthouseRunWarnings))}; - +const gathererArtifacts={}; const pageLoadFailures=[]; -for(const[gathererName,phaseResultsPromises]of Object.entries(gathererResults)){ -if(artifacts[gathererName]!==undefined)continue; +const resultsEntries=Object.entries(gathererResults); +for(const[gathererName,phaseResultsPromises]of resultsEntries){ +if(gathererArtifacts[gathererName]!==undefined)continue; try{ const phaseResults=await Promise.all(phaseResultsPromises); const definedResults=phaseResults.filter(element=>element!==undefined); const artifact=definedResults[definedResults.length-1]; -artifacts[gathererName]=artifact; + +gathererArtifacts[gathererName]=artifact; }catch(err){ -artifacts[gathererName]=err; +gathererArtifacts[gathererName]=err; if(LHError.isPageLoadError(err))pageLoadFailures.push(err); } -if(artifacts[gathererName]===undefined){ +if(gathererArtifacts[gathererName]===undefined){ throw new Error(`${gathererName} failed to provide an artifact.`); } -if(pageLoadFailures.length>Object.keys(artifacts).length*0.5){ +if(pageLoadFailures.length>Object.keys(gathererArtifacts).length*0.5){ throw pageLoadFailures[0]; } } -return artifacts; + +baseArtifacts.LighthouseRunWarnings=Array.from(new Set(baseArtifacts.LighthouseRunWarnings)); + + +return{...baseArtifacts,...gathererArtifacts}; +} + + + + + +static async getBaseArtifacts(options){ +return{ +fetchTime:new Date().toJSON(), +LighthouseRunWarnings:[], +UserAgent:await options.driver.getUserAgent(), +traces:{}, +devtoolsLogs:{}, +settings:options.settings, +URL:{requestedUrl:options.requestedUrl,finalUrl:''}}; + } @@ -18657,71 +18932,63 @@ -static run(passes,options){ +static async run(passes,options){ const driver=options.driver; -const tracingData={ -traces:{}, -devtoolsLogs:{}}; + +const gathererResults={}; + +try{ +await driver.connect(); +const baseArtifacts=await GatherRunner.getBaseArtifacts(options); +await GatherRunner.loadBlank(driver); +await GatherRunner.setupDriver(driver,options); - -const gathererResults={ -LighthouseRunWarnings:[], -fetchTime:[new Date().toJSON()], -URL:[{requestedUrl:options.requestedUrl,finalUrl:''}]}; - - -return driver.connect(). -then(_=>GatherRunner.loadBlank(driver)). -then(_=>GatherRunner.setupDriver(driver,gathererResults,options)). - - -then(_=>{ -return passes.reduce((chain,passConfig,passIndex)=>{ +let firstPass=true; +for(const passConfig of passes){ const passContext={ driver:options.driver, url:options.requestedUrl, settings:options.settings, -passConfig}; +passConfig, + +LighthouseRunWarnings:baseArtifacts.LighthouseRunWarnings}; -return chain. -then(_=>driver.setThrottling(options.settings,passConfig)). -then(_=>GatherRunner.beforePass(passContext,gathererResults)). -then(_=>GatherRunner.pass(passContext,gathererResults)). -then(_=>GatherRunner.afterPass(passContext,gathererResults)). -then(passData=>{ +await driver.setThrottling(options.settings,passConfig); +await GatherRunner.beforePass(passContext,gathererResults); +await GatherRunner.pass(passContext,gathererResults); +const passData=await GatherRunner.afterPass(passContext,gathererResults); -tracingData.devtoolsLogs[passConfig.passName]=passData.devtoolsLog; + +baseArtifacts.devtoolsLogs[passConfig.passName]=passData.devtoolsLog; if(passData.trace){ -tracingData.traces[passConfig.passName]=passData.trace; +baseArtifacts.traces[passConfig.passName]=passData.trace; } -if(passIndex===0){ +if(firstPass){ -gathererResults.URL[0].finalUrl=passContext.url; +baseArtifacts.URL.finalUrl=passContext.url; +firstPass=false; } -}); -},Promise.resolve()); -}). -then(_=>GatherRunner.disposeDriver(driver)). -then(_=>GatherRunner.collectArtifacts(gathererResults,tracingData,options.settings)). +} +await GatherRunner.disposeDriver(driver); +return GatherRunner.collectArtifacts(gathererResults,baseArtifacts); +}catch(err){ -catch(err=>{ GatherRunner.disposeDriver(driver); - throw err; -}); +} }} module.exports=GatherRunner; -},{"../config/constants":8,"../gather/driver.js":17,"../lib/errors":33,"../lib/network-recorder.js":37,"../lib/url-shim":"url","lighthouse-logger":143}],19:[function(require,module,exports){ +},{"../config/constants":8,"../gather/driver.js":17,"../lib/errors":33,"../lib/network-recorder.js":37,"../lib/url-shim":"url","lighthouse-logger":125}],19:[function(require,module,exports){ @@ -18740,11 +19007,14 @@ + + class Gatherer{ get name(){ + return this.constructor.name; } @@ -18862,7 +19132,8 @@ }}; -},{"lodash.isequal":144}],21:[function(require,module,exports){ +},{"lodash.isequal":126}],21:[function(require,module,exports){ +(function(process){ @@ -18874,6 +19145,8 @@ const path=require('path'); const log=require('lighthouse-logger'); const stream=require('stream'); +const Simulator=require('./dependency-graph/simulator/simulator'); +const lanternTraceSaver=require('./lantern-trace-saver'); const Metrics=require('./traces/pwmetrics-events'); const TraceParser=require('./traces/trace-parser'); const rimraf=require('rimraf'); @@ -19064,7 +19337,9 @@ + function*traceJsonGenerator(traceData){ +const EVENTS_PER_ITERATION=500; const keys=Object.keys(traceData); yield'{\n'; @@ -19076,10 +19351,20 @@ const firstEvent=eventsIterator.next().value; yield` ${JSON.stringify(firstEvent)}`; + +let eventsRemaining=EVENTS_PER_ITERATION; +let eventsJSON=''; for(const event of eventsIterator){ -yield`,\n ${JSON.stringify(event)}`; +eventsJSON+=`,\n ${JSON.stringify(event)}`; +eventsRemaining--; +if(eventsRemaining===0){ +yield eventsJSON; +eventsRemaining=EVENTS_PER_ITERATION; +eventsJSON=''; } } +yield eventsJSON; +} yield'\n]'; @@ -19124,6 +19409,22 @@ +async function saveLanternDebugTraces(pathWithBasename){ +if(!process.env.LANTERN_DEBUG)return; + +for(const[label,nodeTimings]of Simulator.ALL_NODE_TIMINGS){ +if(lanternTraceSaver.simulationNamesToIgnore.includes(label))continue; + +const traceFilename=`${pathWithBasename}-${label}${traceSuffix}`; +await saveTrace(lanternTraceSaver.convertNodeTimingsToTrace(nodeTimings),traceFilename); +log.log('saveAssets',`${label} lantern trace file streamed to disk: ${traceFilename}`); +} +} + + + + + @@ -19149,6 +19450,7 @@ }); await Promise.all(saveAll); +await saveLanternDebugTraces(pathWithBasename); } @@ -19179,234 +19481,8 @@ logAssets}; -},{"../runner.js":50,"./traces/pwmetrics-events":44,"./traces/trace-parser":45,"lighthouse-logger":143,"mkdirp":58,"path":75,"rimraf":58,"stream":92}],22:[function(require,module,exports){ - - - - - -'use strict'; - - - -const log=require('lighthouse-logger'); - - -let _logs=[]; - -class ConsoleQuieter{ - -static mute(opts){ -_logs=_logs||[]; - - -console.log=function(...args){ -_logs.push({type:'log',args,prefix:opts.prefix}); -}; - -console.warn=function(...args){ -_logs.push({type:'warn',args,prefix:opts.prefix}); -}; - -console.error=function(...args){ -_logs.push({type:'error',args,prefix:opts.prefix}); -}; -} - -static unmuteAndFlush(){ -console.log=ConsoleQuieter._consolelog; -console.warn=ConsoleQuieter._consolewarn; -console.error=ConsoleQuieter._consoleerror; - -_logs.forEach(entry=>{ -log.verbose(`${entry.prefix}-${entry.type}`,...entry.args); -}); -_logs=[]; -}} - - -ConsoleQuieter._consolelog=console.log.bind(console); -ConsoleQuieter._consolewarn=console.warn.bind(console); -ConsoleQuieter._consoleerror=console.error.bind(console); - -module.exports=ConsoleQuieter; - -},{"lighthouse-logger":143}],23:[function(require,module,exports){ - - - - - -'use strict'; - -const Node=require('./node'); - -class CPUNode extends Node{ - - - - -constructor(parentEvent,childEvents=[]){ -const nodeId=`${parentEvent.tid}.${parentEvent.ts}`; -super(nodeId); - -this._event=parentEvent; -this._childEvents=childEvents; -} - - - - -get type(){ -return Node.TYPES.CPU; -} - - - - -get startTime(){ -return this._event.ts; -} - - - - -get endTime(){ -return this._event.ts+this._event.dur; -} - - - - -get event(){ -return this._event; -} - - - - -get childEvents(){ -return this._childEvents; -} - - - - - -didPerformLayout(){ -return this._childEvents.some(evt=>evt.name==='Layout'); -} - - - - - - -isEvaluateScriptFor(urls){ -return this._childEvents.some(evt=>{ -return evt.name==='EvaluateScript'&& -!!evt.args.data&&!!evt.args.data.url&& -urls.has(evt.args.data.url); -}); -} - - - - -cloneWithoutRelationships(){ -return new CPUNode(this._event,this._childEvents); -}} - - -module.exports=CPUNode; - -},{"./node":25}],24:[function(require,module,exports){ - - - - - -'use strict'; - -const Node=require('./node'); -const WebInspector=require('../web-inspector'); - -class NetworkNode extends Node{ - - - -constructor(networkRecord){ -super(networkRecord.requestId); -this._record=networkRecord; -} - - - - -get type(){ -return Node.TYPES.NETWORK; -} - - - - -get startTime(){ -return this._record.startTime*1000*1000; -} - - - - -get endTime(){ -return this._record.endTime*1000*1000; -} - - - - -get record(){ -return this._record; -} - - - - -get initiatorType(){ -return this._record._initiator&&this._record._initiator.type; -} - - - - -get fromDiskCache(){ -return!!this._record._fromDiskCache; -} - - - - -hasRenderBlockingPriority(){ -const priority=this._record.priority(); -const isScript=this._record._resourceType===WebInspector.resourceTypes.Script; -const isDocument=this._record._resourceType===WebInspector.resourceTypes.Document; -const isBlockingScript=priority==='High'&&isScript; -const isBlockingHtmlImport=priority==='High'&&isDocument; -return priority==='VeryHigh'||isBlockingScript||isBlockingHtmlImport; -} - - - - -cloneWithoutRelationships(){ -const node=new NetworkNode(this._record); -node.setIsMainDocument(this._isMainDocument); -return node; -}} - - -module.exports=NetworkNode; - -},{"../web-inspector":47,"./node":25}],25:[function(require,module,exports){ +}).call(this,require('_process')); +},{"../runner.js":50,"./dependency-graph/simulator/simulator":28,"./lantern-trace-saver":35,"./traces/pwmetrics-events":44,"./traces/trace-parser":45,"_process":77,"lighthouse-logger":125,"mkdirp":58,"path":75,"rimraf":58,"stream":92}],22:[function(require,module,exports){ @@ -19426,7 +19502,13 @@ -class Node{ + + + + + + +class BaseNode{ @@ -19506,7 +19588,6 @@ getRootNode(){ - let rootNode=this; while(rootNode._dependencies.length){ rootNode=rootNode._dependencies[0]; @@ -19549,7 +19630,8 @@ return; } -node._dependents.splice(node._dependents.indexOf(this),1); +const thisIndex=node._dependents.indexOf(this); +node._dependents.splice(thisIndex,1); this._dependencies.splice(this._dependencies.indexOf(node),1); } @@ -19564,7 +19646,7 @@ cloneWithoutRelationships(){ -const node=new Node(this.id); +const node=new BaseNode(this.id); node.setIsMainDocument(this._isMainDocument); return node; } @@ -19623,7 +19705,6 @@ _traversePaths(iterator,getNext){ - const stack=[[this]]; while(stack.length){ @@ -19673,7 +19754,7 @@ static hasCycle(node,direction='both'){ if(direction==='both'){ -return Node.hasCycle(node,'dependents')||Node.hasCycle(node,'dependencies'); +return BaseNode.hasCycle(node,'dependents')||BaseNode.hasCycle(node,'dependencies'); } const visited=new Set(); @@ -19717,14 +19798,184 @@ }} -Node.TYPES={ +BaseNode.TYPES={ NETWORK:'network', CPU:'cpu'}; -module.exports=Node; +module.exports=BaseNode; -},{}],26:[function(require,module,exports){ +},{}],23:[function(require,module,exports){ + + + + + +'use strict'; + +const BaseNode=require('./base-node'); + +class CPUNode extends BaseNode{ + + + + +constructor(parentEvent,childEvents=[]){ +const nodeId=`${parentEvent.tid}.${parentEvent.ts}`; +super(nodeId); + +this._event=parentEvent; +this._childEvents=childEvents; +} + +get type(){ +return BaseNode.TYPES.CPU; +} + + + + +get startTime(){ +return this._event.ts; +} + + + + +get endTime(){ +return this._event.ts+this._event.dur; +} + + + + +get event(){ +return this._event; +} + + + + +get childEvents(){ +return this._childEvents; +} + + + + + +didPerformLayout(){ +return this._childEvents.some(evt=>evt.name==='Layout'); +} + + + + + + +isEvaluateScriptFor(urls){ +return this._childEvents.some(evt=>{ +return evt.name==='EvaluateScript'&& +!!evt.args.data&&!!evt.args.data.url&& +urls.has(evt.args.data.url); +}); +} + + + + +cloneWithoutRelationships(){ +return new CPUNode(this._event,this._childEvents); +}} + + +module.exports=CPUNode; + +},{"./base-node":22}],24:[function(require,module,exports){ + + + + + +'use strict'; + +const BaseNode=require('./base-node'); +const NetworkRequest=require('../network-request'); + +class NetworkNode extends BaseNode{ + + + +constructor(networkRecord){ +super(networkRecord.requestId); + +this._record=networkRecord; +} + +get type(){ +return BaseNode.TYPES.NETWORK; +} + + + + +get startTime(){ +return this._record.startTime*1000*1000; +} + + + + +get endTime(){ +return this._record.endTime*1000*1000; +} + + + + +get record(){ +return this._record; +} + + + + +get initiatorType(){ +return this._record.initiator&&this._record.initiator.type; +} + + + + +get fromDiskCache(){ +return!!this._record.fromDiskCache; +} + + + + +hasRenderBlockingPriority(){ +const priority=this._record.priority; +const isScript=this._record.resourceType===NetworkRequest.TYPES.Script; +const isDocument=this._record.resourceType===NetworkRequest.TYPES.Document; +const isBlockingScript=priority==='High'&&isScript; +const isBlockingHtmlImport=priority==='High'&&isDocument; +return priority==='VeryHigh'||isBlockingScript||isBlockingHtmlImport; +} + + + + +cloneWithoutRelationships(){ +const node=new NetworkNode(this._record); +node.setIsMainDocument(this._isMainDocument); +return node; +}} + + +module.exports=NetworkNode; + +},{"../network-request":38,"./base-node":22}],25:[function(require,module,exports){ @@ -19838,7 +20089,7 @@ return this._connectionsByRecord.get(record); } -const origin=String(record.parsedURL.securityOrigin()); +const origin=String(record.parsedURL.securityOrigin); const connections=this._connectionsByOrigin.get(origin)||[]; @@ -19873,7 +20124,92 @@ }}; -},{"./network-analyzer":27,"./tcp-connection":29}],27:[function(require,module,exports){ +},{"./network-analyzer":27,"./tcp-connection":29}],26:[function(require,module,exports){ + + + + + +'use strict'; + + + + + + +const DNS_RESOLUTION_RTT_MULTIPLIER=2; + +class DNSCache{ + + + +constructor(options){ +this._options=Object.assign( +{ +rtt:undefined}, + +options); + + +if(!this._options.rtt){ +throw new Error('Cannot create DNS cache with no rtt'); +} + +this._rtt=this._options.rtt; + +this._resolvedDomainNames=new Map(); +} + + + + + + +getTimeUntilResolution(request,options){ +const{requestedAt=0,shouldUpdateCache=false}=options||{}; + +const domain=request.parsedURL.host; +const cacheEntry=this._resolvedDomainNames.get(domain); +let timeUntilResolved=this._rtt*DNSCache.RTT_MULTIPLIER; +if(cacheEntry){ +const timeUntilCachedIsResolved=Math.max(cacheEntry.resolvedAt-requestedAt,0); +timeUntilResolved=Math.min(timeUntilCachedIsResolved,timeUntilResolved); +} + +const resolvedAt=requestedAt+timeUntilResolved; +if(shouldUpdateCache)this._updateCacheResolvedAtIfNeeded(request,resolvedAt); + +return timeUntilResolved; +} + + + + + +_updateCacheResolvedAtIfNeeded(request,resolvedAt){ +const domain=request.parsedURL.host; +const cacheEntry=this._resolvedDomainNames.get(domain)||{resolvedAt}; +cacheEntry.resolvedAt=Math.min(cacheEntry.resolvedAt,resolvedAt); +this._resolvedDomainNames.set(domain,cacheEntry); +} + + + + + + + + +setResolvedAt(domain,resolvedAt){ +this._resolvedDomainNames.set(domain,{resolvedAt}); +}} + + +DNSCache.RTT_MULTIPLIER=DNS_RESOLUTION_RTT_MULTIPLIER; + +module.exports=DNSCache; + +},{}],27:[function(require,module,exports){ @@ -19882,7 +20218,7 @@ 'use strict'; const INITIAL_CWD=14*1024; -const WebInspector=require('../../web-inspector'); +const NetworkRequest=require('../../network-request'); class NetworkAnalyzer{ @@ -19899,7 +20235,7 @@ static groupByOrigin(records){ const grouped=new Map(); records.forEach(item=>{ -const key=item.parsedURL.securityOrigin(); +const key=item.parsedURL.securityOrigin; const group=grouped.get(key)||[]; group.push(item); grouped.set(key,group); @@ -19953,7 +20289,7 @@ let originEstimates=[]; for(const record of originRecords){ -const timing=record._timing; +const timing=record.timing; if(!timing)continue; const value=iteratee({ @@ -20056,7 +20392,7 @@ if(!Number.isFinite(timing.sendEnd)||timing.sendEnd<0)return; const ttfb=timing.receiveHeadersEnd-timing.sendEnd; -const origin=record.parsedURL.securityOrigin(); +const origin=record.parsedURL.securityOrigin; const rtt=rttByOrigin.get(origin)||rttByOrigin.get(NetworkAnalyzer.SUMMARY)||0; return Math.max(ttfb-rtt,0); }); @@ -20203,8 +20539,8 @@ static findMainDocument(records){ -const documentRequests=records.filter(record=>record._resourceType=== -WebInspector.resourceTypes.Document); +const documentRequests=records.filter(record=>record.resourceType=== +NetworkRequest.TYPES.Document); return documentRequests.sort((a,b)=>a.startTime-b.startTime)[0]; }} @@ -20219,7 +20555,7 @@ -},{"../../web-inspector":47}],28:[function(require,module,exports){ +},{"../../network-request":38}],28:[function(require,module,exports){ @@ -20227,14 +20563,17 @@ 'use strict'; -const Node=require('../node'); -const NetworkNode=require('../network-node'); -const CpuNode=require('../cpu-node'); +const BaseNode=require('../base-node'); const TcpConnection=require('./tcp-connection'); const ConnectionPool=require('./connection-pool'); +const DNSCache=require('./dns-cache'); const mobile3G=require('../../../config/constants').throttling.mobile3G; + + + + const DEFAULT_MAXIMUM_CONCURRENT_REQUESTS=10; const DEFAULT_LAYOUT_TASK_MULTIPLIER=0.5; @@ -20248,6 +20587,9 @@ Complete:3}; + +const ALL_SIMULATION_NODE_TIMINGS=new Map(); + class Simulator{ @@ -20278,9 +20620,12 @@ this._flexibleOrdering=false; + this._nodeTimings=new Map(); + this._numberInProgressByType=new Map(); this._nodes={}; +this._dns=new DNSCache({rtt:this._rtt}); this._connectionPool=null; } @@ -20292,7 +20637,7 @@ const records=[]; graph.getRootNode().traverse(node=>{ -if(node.type===Node.TYPES.NETWORK){ +if(node.type===BaseNode.TYPES.NETWORK){ records.push(node.record); } }); @@ -20335,6 +20680,16 @@ +_getTimingData(node){ +const timingData=this._nodeTimings.get(node); +if(!timingData)throw new Error(`Unable to get timing data for node ${node.id}`); +return timingData; +} + + + + + _markNodeAsReadyToStart(node,queuedTime){ this._nodes[NodeState.ReadyToStart].add(node); this._nodes[NodeState.NotReadyToStart].delete(node); @@ -20388,7 +20743,7 @@ _startNodeIfPossible(node,totalElapsedTime){ -if(node.type===Node.TYPES.CPU){ +if(node.type===BaseNode.TYPES.CPU){ if(this._numberInProgress(node.type)===0){ this._markNodeAsInProgress(node,totalElapsedTime); @@ -20398,15 +20753,14 @@ return; } -if(node.type!==Node.TYPES.NETWORK)throw new Error('Unsupported'); +if(node.type!==BaseNode.TYPES.NETWORK)throw new Error('Unsupported'); -const networkNode=node; -if(!networkNode.fromDiskCache){ +if(!node.fromDiskCache){ const numberOfActiveRequests=this._numberInProgress(node.type); if(numberOfActiveRequests>=this._maximumConcurrentRequests)return; -const connection=this._acquireConnection(networkNode.record); +const connection=this._acquireConnection(node.record); if(!connection)return; } @@ -20434,9 +20788,9 @@ _estimateTimeRemaining(node){ -if(node.type===Node.TYPES.CPU){ +if(node.type===BaseNode.TYPES.CPU){ return this._estimateCPUTimeRemaining(node); -}else if(node.type===Node.TYPES.NETWORK){ +}else if(node.type===BaseNode.TYPES.NETWORK){ return this._estimateNetworkTimeRemaining(node); }else{ throw new Error('Unsupported'); @@ -20448,7 +20802,7 @@ _estimateCPUTimeRemaining(cpuNode){ -const timingData=this._nodeTimings.get(cpuNode); +const timingData=this._getTimingData(cpuNode); const multiplier=cpuNode.didPerformLayout()? this._layoutTaskMultiplier: this._cpuSlowdownMultiplier; @@ -20466,20 +20820,26 @@ _estimateNetworkTimeRemaining(networkNode){ -const timingData=this._nodeTimings.get(networkNode); +const record=networkNode.record; +const timingData=this._getTimingData(networkNode); let timeElapsed=0; if(networkNode.fromDiskCache){ -const sizeInMb=(networkNode.record._resourceSize||0)/1024/1024; +const sizeInMb=(record.resourceSize||0)/1024/1024; timeElapsed=8+20*sizeInMb-timingData.timeElapsed; }else{ -const connection=this._acquireConnection(networkNode.record); +const connection=this._acquireConnection(record); +const dnsResolutionTime=this._dns.getTimeUntilResolution(record,{ +requestedAt:timingData.startTime, +shouldUpdateCache:true}); + +const timeAlreadyElapsed=timingData.timeElapsed; const calculation=connection.simulateDownloadUntil( -networkNode.record.transferSize-timingData.bytesDownloaded, -{timeAlreadyElapsed:timingData.timeElapsed,maximumTimeToElapse:Infinity}); +record.transferSize-timingData.bytesDownloaded, +{timeAlreadyElapsed,dnsResolutionTime,maximumTimeToElapse:Infinity}); timeElapsed=calculation.timeElapsed; @@ -20510,25 +20870,28 @@ _updateProgressMadeInTimePeriod(node,timePeriodLength,totalElapsedTime){ -const timingData=this._nodeTimings.get(node); +const timingData=this._getTimingData(node); const isFinished=timingData.estimatedTimeElapsed===timePeriodLength; -const networkNode=node; - -if(node.type===Node.TYPES.CPU||networkNode.fromDiskCache){ +if(node.type===BaseNode.TYPES.CPU||node.fromDiskCache){ return isFinished? this._markNodeAsComplete(node,totalElapsedTime): timingData.timeElapsed+=timePeriodLength; } -if(node.type!==Node.TYPES.NETWORK)throw new Error('Unsupported'); +if(node.type!==BaseNode.TYPES.NETWORK)throw new Error('Unsupported'); -const record=networkNode.record; +const record=node.record; const connection=this._acquireConnection(record); +const dnsResolutionTime=this._dns.getTimeUntilResolution(record,{ +requestedAt:timingData.startTime, +shouldUpdateCache:true}); + const calculation=connection.simulateDownloadUntil( record.transferSize-timingData.bytesDownloaded, { +dnsResolutionTime, timeAlreadyElapsed:timingData.timeElapsed, maximumTimeToElapse:timePeriodLength-timingData.timeElapsedOvershoot}); @@ -20548,6 +20911,20 @@ } } +_computeFinalNodeTimings(){ + +const nodeTimings=new Map(); +for(const[node,timing]of this._nodeTimings){ +nodeTimings.set(node,{ +startTime:timing.startTime, +endTime:timing.endTime, +duration:timing.endTime-timing.startTime}); + +} + +return nodeTimings; +} + @@ -20569,13 +20946,18 @@ simulate(graph,options){ -if(Node.hasCycle(graph)){ +if(BaseNode.hasCycle(graph)){ throw new Error('Cannot simulate graph with cycle'); } -options=Object.assign({flexibleOrdering:false},options); +options=Object.assign({ +label:undefined, +flexibleOrdering:false}, +options); + this._flexibleOrdering=!!options.flexibleOrdering; +this._dns=new DNSCache({rtt:this._rtt}); this._initializeConnectionPool(graph); this._initializeAuxiliaryData(); @@ -20625,16 +21007,35 @@ } } +const nodeTimings=this._computeFinalNodeTimings(); +ALL_SIMULATION_NODE_TIMINGS.set(options.label||'unlabeled',nodeTimings); + return{ timeInMs:totalElapsedTime, -nodeTimings:this._nodeTimings}; +nodeTimings}; +} + + +static get ALL_NODE_TIMINGS(){ +return ALL_SIMULATION_NODE_TIMINGS; }} module.exports=Simulator; -},{"../../../config/constants":8,"../cpu-node":23,"../network-node":24,"../node":25,"./connection-pool":26,"./tcp-connection":29}],29:[function(require,module,exports){ + + + + + + + + + + + +},{"../../../config/constants":8,"../base-node":22,"./connection-pool":25,"./dns-cache":26,"./tcp-connection":29}],29:[function(require,module,exports){ @@ -20758,7 +21159,8 @@ simulateDownloadUntil(bytesToDownload,options){ -const{timeAlreadyElapsed=0,maximumTimeToElapse=Infinity}=options||{}; +const{timeAlreadyElapsed=0,maximumTimeToElapse=Infinity,dnsResolutionTime=0}= +options||{}; if(this._warmed&&this._h2){ bytesToDownload-=this._h2OverflowBytesDownloaded; @@ -20771,6 +21173,8 @@ if(!this._warmed){ handshakeAndRequest= +dnsResolutionTime+ + oneWayLatency+ oneWayLatency+ @@ -20838,6 +21242,7 @@ + },{}],30:[function(require,module,exports){ @@ -20952,7 +21357,8 @@ return null; } return this.driver.getObjectProperty(resp.object.objectId,propName); -}); +}). +catch(()=>null); }} @@ -21025,10 +21431,7 @@ driver.sendCommand('Network.enable'), driver.sendCommand('Network.setUserAgentOverride',NEXUS5X_USERAGENT), -driver.sendCommand('Emulation.setEmitTouchEventsForMouse',{ -enabled:true, -configuration:'mobile'})]); - +driver.sendCommand('Emulation.setTouchEmulationEnabled',{enabled:true})]); } @@ -21203,74 +21606,7 @@ module.exports=LighthouseError; -},{"./strings":41}],34:[function(require,module,exports){ - - - - - -'use strict'; - - - - - - - - - - - - - - -function addFormattedCodeSnippet(listener){ -const handler=listener.handler?listener.handler.description:'...'; -const objectName=listener.objectName.toLowerCase().replace('#document','document'); -return Object.assign({ -pre:`${objectName}.addEventListener('${listener.type}', ${handler})`}, -listener); -} - - - - - - - - - - - - - - - - -function groupCodeSnippetsByLocation(listeners){ - -const locToListenerMap=new Map(); - - -listeners.forEach(loc=>{ -const accPre=loc.pre.trim()+'\n\n'; -const simplifiedLoc={line:loc.line,col:loc.col,url:loc.url,type:loc.type,pre:''}; - -const key=JSON.stringify(simplifiedLoc); -const accListener=locToListenerMap.get(key)||simplifiedLoc; -accListener.pre+=accPre; -locToListenerMap.set(key,accListener); -}); - -return[...locToListenerMap.values()]; -} - -module.exports={ -addFormattedCodeSnippet, -groupCodeSnippetsByLocation}; - - -},{}],35:[function(require,module,exports){ +},{"./strings":42}],34:[function(require,module,exports){ @@ -21330,6 +21666,214 @@ sizeAtLeast}; +},{}],35:[function(require,module,exports){ + + + + + +'use strict'; + + + + + +function convertNodeTimingsToTrace(nodeTimings){ + +const traceEvents=[]; +const baseTs=1e9; +const baseEvent={pid:1,tid:1,cat:'devtools.timeline'}; +const frame='A00001'; + +const toMicroseconds=ms=>baseTs+ms*1000; + +traceEvents.push(createFakeTracingStartedEvent()); +traceEvents.push({...createFakeTracingStartedEvent(),name:'TracingStartedInBrowser'}); + + +let requestId=1; +let lastEventEndTime=0; +for(const[node,timing]of nodeTimings.entries()){ +lastEventEndTime=Math.max(lastEventEndTime,timing.endTime); +if(node.type==='cpu'){ + +const cpuNode=node; +traceEvents.push(...createFakeTaskEvents(cpuNode,timing)); +}else{ +const networkNode=node; + +if(/^data/.test(networkNode.record.url))continue; +traceEvents.push(...createFakeNetworkEvents(networkNode.record,timing)); +} +} + + +traceEvents.push( +...createFakeTaskEvents( + +{childEvents:[],event:{}}, +{ +startTime:lastEventEndTime+1000, +endTime:lastEventEndTime+1001})); + + + + +return{traceEvents}; + + + + +function createFakeTracingStartedEvent(){ +const argsData={ +frameTreeNodeId:1, +sessionId:'1.1', +page:frame, +persistentIds:true, +frames:[{frame,url:'about:blank',name:'',processId:1}]}; + + +return{ +...baseEvent, +ts:baseTs-1e5, +ph:'I', +s:'t', +cat:'disabled-by-default-devtools.timeline', +name:'TracingStartedInPage', +args:{data:argsData}, +dur:0}; + +} + + + + + + +function createFakeTaskEvents(cpuNode,timing){ +const argsData={ +url:'', +frame, +lineNumber:0, +columnNumber:0}; + + +const eventTs=toMicroseconds(timing.startTime); + + +const events=[ +{ +...baseEvent, +ph:'X', +name:'Task', +ts:eventTs, +dur:(timing.endTime-timing.startTime)*1000, +args:{data:argsData}}]; + + + +const nestedBaseTs=cpuNode.event.ts||0; +const multiplier=(timing.endTime-timing.startTime)*1000/cpuNode.event.dur; + +const netReqEvents=new Set(['ResourceSendRequest','ResourceFinish', +'ResourceReceiveResponse','ResourceReceivedData']); +for(const event of cpuNode.childEvents){ +if(netReqEvents.has(event.name))continue; +const ts=eventTs+(event.ts-nestedBaseTs)*multiplier; +const newEvent={...event,...{pid:baseEvent.pid,tid:baseEvent.tid},ts}; +if(event.dur)newEvent.dur=event.dur*multiplier; +events.push(newEvent); +} + +return events; +} + + + + + + +function createFakeNetworkEvents(record,timing){ +requestId++; + + + +let{startTime,endTime}=timing; +if(startTime===endTime)endTime+=0.3; + +const requestData={requestId:requestId.toString(),frame}; + +const baseRequestEvent={...baseEvent,ph:'I',s:'t',dur:0}; + +const sendRequestData={ +...requestData, +requestMethod:record.requestMethod, +url:record.url, +priority:record.priority}; + + +const receiveResponseData={ +...requestData, +statusCode:record.statusCode, +mimeType:record.mimeType, +encodedDataLength:record.transferSize, +fromCache:record.fromDiskCache, +fromServiceWorker:record.fetchedViaServiceWorker}; + + +const resourceFinishData={ +...requestData, +decodedBodyLength:record.resourceSize, +didFail:!!record.failed, +finishTime:endTime}; + + + +const events=[ +{ +...baseRequestEvent, +name:'ResourceSendRequest', +ts:toMicroseconds(startTime), +args:{data:sendRequestData}}, + +{ +...baseRequestEvent, +name:'ResourceFinish', +ts:toMicroseconds(endTime), +args:{data:resourceFinishData}}]; + + + +if(!record.failed){ +events.push({ +...baseRequestEvent, +name:'ResourceReceiveResponse', +ts:toMicroseconds((startTime+endTime)/2), +args:{data:receiveResponseData}}); + +} + +return events; +} +} + +module.exports={ +simulationNamesToIgnore:[ +'unlabeled', + +'optimisticFirstCPUIdle', +'optimisticFlexFirstCPUIdle', +'pessimisticFirstCPUIdle', +'optimisticSpeedIndex', +'optimisticFlexSpeedIndex', +'pessimisticSpeedIndex', +'optimisticEstimatedInputLatency', +'optimisticFlexEstimatedInputLatency', +'pessimisticEstimatedInputLatency'], + +convertNodeTimingsToTrace}; + + },{}],36:[function(require,module,exports){ @@ -21795,6 +22339,7 @@ 'use strict'; const NetworkManager=require('./web-inspector').NetworkManager; +const NetworkRequest=require('./network-request'); const EventEmitter=require('events').EventEmitter; const log=require('lighthouse-logger'); @@ -21806,21 +22351,17 @@ - -constructor(recordArray){ +constructor(){ super(); -this._records=recordArray; -this.networkManager=NetworkManager.createWithFakeTarget(); -this.networkManager.addEventListener( -this.EventTypes.RequestStarted, -this.onRequestStarted.bind(this)); +this._records=[]; -this.networkManager.addEventListener( -this.EventTypes.RequestFinished, -this.onRequestFinished.bind(this)); +this._recordsById=new Map(); +} +getRecords(){ +return Array.from(this._records); } @@ -21885,9 +22426,9 @@ static _isQUICAndFinished(record){ -const isQUIC=record._responseHeaders&&record._responseHeaders. +const isQUIC=record.responseHeaders&&record.responseHeaders. some(header=>header.name.toLowerCase()==='alt-svc'&&/quic/.test(header.value)); -const receivedHeaders=record._timing&&record._timing.receiveHeadersEnd>0; +const receivedHeaders=record.timing&&record.timing.receiveHeadersEnd>0; return!!(isQUIC&&receivedHeaders&&record.endTime); } @@ -21955,7 +22496,9 @@ onRequestStarted(request){ -this._records.push(request.data); +this._records.push(request); +this._recordsById.set(request.requestId,request); + this._emitNetworkStatus(); } @@ -21966,7 +22509,7 @@ onRequestFinished(request){ -this.emit('requestloaded',request.data); +this.emit('requestloaded',request); this._emitNetworkStatus(); } @@ -21976,66 +22519,97 @@ - onRequestWillBeSent(data){ +const originalRequest=this._findRealRequest(data.requestId); -this.networkManager._dispatcher.requestWillBeSent(data.requestId, -data.frameId,data.loaderId,data.documentURL,data.request, -data.timestamp,data.wallTime,data.initiator,data.redirectResponse, -data.type); +if(!originalRequest){ +const request=new NetworkRequest(); +request.onRequestWillBeSent(data); +this.onRequestStarted(request); +return; +} + + +if(!data.redirectResponse){ +return; +} + + + +const modifiedData={ +...data, + + +initiator:originalRequest.initiator, +requestId:`${originalRequest.requestId}:redirect`}; + +const redirectedRequest=new NetworkRequest(); + +redirectedRequest.onRequestWillBeSent(modifiedData); +originalRequest.onRedirectResponse(data); + +originalRequest.redirectDestination=redirectedRequest; +redirectedRequest.redirectSource=originalRequest; + + +this.onRequestStarted(redirectedRequest); +this.onRequestFinished(originalRequest); } onRequestServedFromCache(data){ -this.networkManager._dispatcher.requestServedFromCache(data.requestId); +const request=this._findRealRequest(data.requestId); +if(!request)return; +request.onRequestServedFromCache(); } onResponseReceived(data){ - -this.networkManager._dispatcher.responseReceived(data.requestId, -data.frameId,data.loaderId,data.timestamp,data.type,data.response); +const request=this._findRealRequest(data.requestId); +if(!request)return; +request.onResponseReceived(data); } onDataReceived(data){ - -this.networkManager._dispatcher.dataReceived(data.requestId,data.timestamp, -data.dataLength,data.encodedDataLength); +const request=this._findRealRequest(data.requestId); +if(!request)return; +request.onDataReceived(data); } onLoadingFinished(data){ - -this.networkManager._dispatcher.loadingFinished(data.requestId, -data.timestamp,data.encodedDataLength); +const request=this._findRealRequest(data.requestId); +if(!request)return; +request.onLoadingFinished(data); +this.onRequestFinished(request); } onLoadingFailed(data){ - - -this.networkManager._dispatcher.loadingFailed(data.requestId, -data.timestamp,data.type,data.errorText,data.canceled, -data.blockedReason); +const request=this._findRealRequest(data.requestId); +if(!request)return; +request.onLoadingFailed(data); +this.onRequestFinished(request); } onResourceChangedPriority(data){ -this.networkManager._dispatcher.resourceChangedPriority(data.requestId, -data.newPriority,data.timestamp); +const request=this._findRealRequest(data.requestId); +if(!request)return; +request.onResourceChangedPriority(data); } @@ -22064,21 +22638,73 @@ -static recordsFromLogs(devtoolsLog){ -const records=[]; -const nr=new NetworkRecorder(records); -devtoolsLog.forEach(message=>{ -nr.dispatch(message); -}); + + + +_findRealRequest(requestId){ +let request=this._recordsById.get(requestId); +if(!request)return undefined; + +while(request.redirectDestination){ +request=request.redirectDestination; +} + +return request; +} + + + + + + +static recordsFromLogs(devtoolsLog){ +const networkRecorder=new NetworkRecorder(); + +devtoolsLog.forEach(message=>networkRecorder.dispatch(message)); + + +const records=networkRecorder.getRecords(); + + +const recordsByURL=new Map(); +for(const record of records){ +if(recordsByURL.has(record.url))continue; +recordsByURL.set(record.url,record); +} + + +for(const record of records){ +const stackFrames=record.initiator.stack&&record.initiator.stack.callFrames||[]; +const initiatorURL=record.initiator.url||stackFrames[0]&&stackFrames[0].url; +const initiator=recordsByURL.get(initiatorURL)||record.redirectSource; +if(initiator){ +record.setInitiatorRequest(initiator); +} + +let finalRecord=record; +while(finalRecord.redirectDestination)finalRecord=finalRecord.redirectDestination; +if(finalRecord===record||finalRecord.redirects)continue; + +const redirects=[]; +for( +let redirect=finalRecord.redirectSource; +redirect; +redirect=redirect.redirectSource) +{ +redirects.unshift(redirect); +} + +finalRecord.redirects=redirects; +} + return records; }} module.exports=NetworkRecorder; -},{"./web-inspector":47,"events":62,"lighthouse-logger":143}],38:[function(require,module,exports){ - +},{"./network-request":38,"./web-inspector":47,"events":62,"lighthouse-logger":125}],38:[function(require,module,exports){ @@ -22092,67 +22718,235 @@ +const URL=require('./url-shim'); + +const SECURE_SCHEMES=['data','https','wss','blob','chrome','chrome-extension','about']; -function captureJSCallUsage(funcRef,set){ - -const __nativeError=window.__nativeError||Error; -const originalFunc=funcRef; -const originalPrepareStackTrace=__nativeError.prepareStackTrace; - -return function(...args){ - - - -__nativeError.prepareStackTrace=function(error,structStackTrace){ - - -const callFrame=structStackTrace[1]; -let url=callFrame.getFileName()||callFrame.getEvalOrigin(); -const line=callFrame.getLineNumber(); -const col=callFrame.getColumnNumber(); -const isEval=callFrame.isEval(); -let isExtension=false; -const stackTrace=structStackTrace.slice(1).map(callsite=>callsite.toString()); -if(isEval){ -url=stackTrace[1]; + + + + +const RESOURCE_TYPES={ +XHR:'XHR', +Fetch:'Fetch', +EventSource:'EventSource', +Script:'Script', +Stylesheet:'Stylesheet', +Image:'Image', +Media:'Media', +Font:'Font', +Document:'Document', +TextTrack:'TextTrack', +WebSocket:'WebSocket', +Other:'Other', +Manifest:'Manifest'}; + + +module.exports=class NetworkRequest{ +constructor(){ +this.requestId=''; + +this.connectionId='0'; +this.connectionReused=false; + +this.url=''; +this.protocol=''; +this.isSecure=false; +this.parsedURL={scheme:''}; +this.documentURL=''; + +this.startTime=-1; + +this.endTime=-1; + +this.responseReceivedTime=-1; + +this.transferSize=0; +this.resourceSize=0; +this.fromDiskCache=false; +this.fromMemoryCache=false; + +this.finished=false; +this.requestMethod=''; +this.statusCode=-1; + +this.redirectSource=undefined; + +this.redirectDestination=undefined; + +this.redirects=undefined; +this.failed=false; +this.localizedFailDescription=''; + +this.initiator={type:'other'}; + +this.timing=undefined; + +this.resourceType=undefined; +this.mimeType=''; + +this.priority='Low'; + +this.initiatorRequest=undefined; + +this.responseHeaders=[]; + +this.fetchedViaServiceWorker=false; + +this.frameId=''; +this.isLinkPreload=false; } -if(stackTrace[0].startsWith('<anonymous>')){ + +setInitiatorRequest(initiator){ +this.initiatorRequest=initiator; +} -url=stackTrace[0]; -isExtension=true; + +onRequestWillBeSent(data){ +this.requestId=data.requestId; + +const url=new URL(data.request.url); +this.url=data.request.url; +this.documentURL=data.documentURL; +this.parsedURL={ +scheme:url.protocol.split(':')[0], + +host:url.hostname, +securityOrigin:url.origin}; + +this.isSecure=SECURE_SCHEMES.includes(this.parsedURL.scheme); + +this.startTime=data.timestamp; + +this.requestMethod=data.request.method; + +this.initiator=data.initiator; +this.resourceType=data.type&&RESOURCE_TYPES[data.type]; +this.priority=data.request.initialPriority; + +this.frameId=data.frameId; +this.isLinkPreload=data.initiator.type==='preload'||!!data.request.isLinkPreload; +} + +onRequestServedFromCache(){ +this.fromMemoryCache=true; +} + + + + +onResponseReceived(data){ +this._onResponse(data.response,data.timestamp,data.type); +this.frameId=data.frameId; +} + + + + +onDataReceived(data){ +this.resourceSize+=data.dataLength; +if(data.encodedDataLength!==-1){ +this.transferSize+=data.encodedDataLength; +} +} + + + + +onLoadingFinished(data){ + +if(this.finished)return; + +this.finished=true; +this.endTime=data.timestamp; +if(data.encodedDataLength>=0){ +this.transferSize=data.encodedDataLength; +} + +this._updateResponseReceivedTimeIfNecessary(); +} + + + + +onLoadingFailed(data){ + +if(this.finished)return; + +this.finished=true; +this.endTime=data.timestamp; + +this.failed=true; +this.resourceType=data.type&&RESOURCE_TYPES[data.type]; +this.localizedFailDescription=data.errorText; + +this._updateResponseReceivedTimeIfNecessary(); +} + + + + +onResourceChangedPriority(data){ +this.priority=data.newPriority; +} + + + + +onRedirectResponse(data){ +if(!data.redirectResponse)throw new Error('Missing redirectResponse data'); +this._onResponse(data.redirectResponse,data.timestamp,data.type); +this.resourceType=undefined; +this.finished=true; +this.endTime=data.timestamp; + +this._updateResponseReceivedTimeIfNecessary(); } -return{url,args,line,col,isEval,isExtension}; -}; -const e=new __nativeError(`__called ${funcRef.name}__`); -set.add(JSON.stringify(e.stack)); +_onResponse(response,timestamp,resourceType){ +this.url=response.url; +this.connectionId=String(response.connectionId); +this.connectionReused=response.connectionReused; -__nativeError.prepareStackTrace=originalPrepareStackTrace; +if(response.protocol)this.protocol=response.protocol; +this.responseReceivedTime=timestamp; -return originalFunc.apply(this,args); -}; +this.transferSize=response.encodedDataLength; +if(typeof response.fromDiskCache==='boolean')this.fromDiskCache=response.fromDiskCache; + +this.statusCode=response.status; + +this.timing=response.timing; +if(resourceType)this.resourceType=RESOURCE_TYPES[resourceType]; +this.mimeType=response.mimeType; +this.responseHeaders=NetworkRequest._headersDictToHeadersArray(response.headers); + +this.fetchedViaServiceWorker=!!response.fromServiceWorker; + +if(this.fromMemoryCache)this.timing=undefined; +if(this.timing)this._recomputeTimesWithResourceTiming(this.timing); } @@ -22160,6 +22954,80 @@ +_recomputeTimesWithResourceTiming(timing){ + + +this.startTime=timing.requestTime; +const headersReceivedTime=timing.requestTime+timing.receiveHeadersEnd/1000; +if(!this.responseReceivedTime||this.responseReceivedTime<0){ +this.responseReceivedTime=headersReceivedTime; +} + +this.responseReceivedTime=Math.min(this.responseReceivedTime,headersReceivedTime); +this.responseReceivedTime=Math.max(this.responseReceivedTime,this.startTime); +this.endTime=Math.max(this.endTime,this.responseReceivedTime); +} + + + + + +_updateResponseReceivedTimeIfNecessary(){ +this.responseReceivedTime=Math.min(this.endTime,this.responseReceivedTime); +} + + + + + + + +static getRequestIdForBackend(requestId){ +return requestId.replace(/(:redirect)+$/,''); +} + + + + + + + +static _headersDictToHeadersArray(headersDict){ +const result=[]; +for(const name of Object.keys(headersDict)){ +const values=headersDict[name].split('\n'); +for(let i=0;i<values.length;++i){ +result.push({name:name,value:values[i]}); +} +} +return result; +} + +static get TYPES(){ +return RESOURCE_TYPES; +}}; + + +},{"./url-shim":"url"}],39:[function(require,module,exports){ + + + + + + +'use strict'; + + + + + + + + + + + + function wrapRuntimeEvalErrorInBrowser(err){ @@ -22222,13 +23090,12 @@ } module.exports={ -captureJSCallUsage, wrapRuntimeEvalErrorInBrowser, registerPerformanceObserverInPage, checkTimeSinceLastLongTask}; -},{}],39:[function(require,module,exports){ +},{}],40:[function(require,module,exports){ @@ -22257,6 +23124,7 @@ {pattern:/(IDLE_PERIOD|FMP_TOO_LATE)/,rate:0.1}, {pattern:/^NO_.*/,rate:0.1}, +{pattern:/Could not load stylesheet/,rate:0.01}, {pattern:/Failed to decode/,rate:0.1}, {pattern:/All image optimizations failed/,rate:0.1}, {pattern:/No.*resource with given/,rate:0.01}, @@ -22325,7 +23193,7 @@ module.exports=sentryDelegate; -},{"lighthouse-logger":143,"raven":58}],40:[function(require,module,exports){ +},{"lighthouse-logger":125,"raven":58}],41:[function(require,module,exports){ @@ -22387,11 +23255,26 @@ } + + + + + + + + + +function linearInterpolation(x0,y0,x1,y1,x){ +const slope=(y1-y0)/(x1-x0); +return y0+(x-x0)*slope; +} + module.exports={ +linearInterpolation, getLogNormalDistribution}; -},{}],41:[function(require,module,exports){ +},{}],42:[function(require,module,exports){ @@ -22409,102 +23292,6 @@ requestContentTimeout:'Fetching resource content has exceeded the allotted time'}; -},{}],42:[function(require,module,exports){ - - - - - -'use strict'; - -const groupIdToName={ -loading:'Network request loading', -parseHTML:'Parsing HTML & CSS', -styleLayout:'Style & Layout', -compositing:'Compositing', -painting:'Paint', -gpu:'GPU', -scripting:'Script Evaluation', -scriptParseCompile:'Script Parsing & Compile', -scriptGC:'Garbage collection', -other:'Other', -images:'Images'}; - - -const taskToGroup={ -'Animation':groupIdToName.painting, -'Async Task':groupIdToName.other, -'Frame Start':groupIdToName.painting, -'Frame Start (main thread)':groupIdToName.painting, -'Cancel Animation Frame':groupIdToName.scripting, -'Cancel Idle Callback':groupIdToName.scripting, -'Compile Script':groupIdToName.scriptParseCompile, -'Composite Layers':groupIdToName.compositing, -'Console Time':groupIdToName.scripting, -'Image Decode':groupIdToName.images, -'Draw Frame':groupIdToName.painting, -'Embedder Callback':groupIdToName.scripting, -'Evaluate Script':groupIdToName.scripting, -'Event':groupIdToName.scripting, -'Animation Frame Fired':groupIdToName.scripting, -'Fire Idle Callback':groupIdToName.scripting, -'Function Call':groupIdToName.scripting, -'DOM GC':groupIdToName.scriptGC, -'GC Event':groupIdToName.scriptGC, -'GPU':groupIdToName.gpu, -'Hit Test':groupIdToName.compositing, -'Invalidate Layout':groupIdToName.styleLayout, -'JS Frame':groupIdToName.scripting, -'Input Latency':groupIdToName.scripting, -'Layout':groupIdToName.styleLayout, -'Major GC':groupIdToName.scriptGC, -'DOMContentLoaded event':groupIdToName.scripting, -'First paint':groupIdToName.painting, -'FMP':groupIdToName.painting, -'FMP candidate':groupIdToName.painting, -'Load event':groupIdToName.scripting, -'Minor GC':groupIdToName.scriptGC, -'Paint':groupIdToName.painting, -'Paint Image':groupIdToName.images, -'Paint Setup':groupIdToName.painting, -'Parse Stylesheet':groupIdToName.parseHTML, -'Parse HTML':groupIdToName.parseHTML, -'Parse Script':groupIdToName.scriptParseCompile, -'Other':groupIdToName.other, -'Rasterize Paint':groupIdToName.painting, -'Recalculate Style':groupIdToName.styleLayout, -'Request Animation Frame':groupIdToName.scripting, -'Request Idle Callback':groupIdToName.scripting, -'Request Main Thread Frame':groupIdToName.painting, -'Image Resize':groupIdToName.images, -'Finish Loading':groupIdToName.loading, -'Receive Data':groupIdToName.loading, -'Receive Response':groupIdToName.loading, -'Send Request':groupIdToName.loading, -'Run Microtasks':groupIdToName.scripting, -'Schedule Style Recalculation':groupIdToName.styleLayout, -'Scroll':groupIdToName.compositing, -'Task':groupIdToName.other, -'Timer Fired':groupIdToName.scripting, -'Install Timer':groupIdToName.scripting, -'Remove Timer':groupIdToName.scripting, -'Timestamp':groupIdToName.scripting, -'Update Layer':groupIdToName.compositing, -'Update Layer Tree':groupIdToName.compositing, -'User Timing':groupIdToName.scripting, -'Create WebSocket':groupIdToName.scripting, -'Destroy WebSocket':groupIdToName.scripting, -'Receive WebSocket Handshake':groupIdToName.scripting, -'Send WebSocket Handshake':groupIdToName.scripting, -'XHR Load':groupIdToName.scripting, -'XHR Ready State Change':groupIdToName.scripting}; - - -module.exports={ -groupIdToName, -taskToGroup}; - - },{}],43:[function(require,module,exports){ @@ -22513,132 +23300,113 @@ 'use strict'; -const WebInspector=require('../web-inspector'); -const ConsoleQuieter=require('../console-quieter'); -const TimelineModelTreeView= -require('devtools-timeline-model/lib/timeline-model-treeview.js')(WebInspector); -class TimelineModel{ -constructor(events){ -this.init(events); + + + + + + + +const taskGroups={ +parseHTML:{ +id:'', +label:'Parse HTML & CSS', +traceEventNames:['ParseHTML','ParseAuthorStyleSheet']}, + +styleLayout:{ +id:'', +label:'Style & Layout', +traceEventNames:[ +'ScheduleStyleRecalculation', +'RecalculateStyles', +'UpdateLayoutTree', +'InvalidateLayout', +'Layout']}, + + +paintCompositeRender:{ +id:'', +label:'Rendering', +traceEventNames:[ +'Animation', +'RequestMainThreadFrame', +'ActivateLayerTree', +'DrawFrame', +'HitTest', +'PaintSetup', +'Paint', +'PaintImage', +'Rasterize', +'RasterTask', +'ScrollLayer', +'UpdateLayer', +'UpdateLayerTree', +'CompositeLayers']}, + + +scriptParseCompile:{ +id:'', +label:'Script Parsing & Compilation', +traceEventNames:['v8.compile','v8.compileModule','v8.parseOnBackground']}, + +scriptEvaluation:{ +id:'', +label:'Script Evaluation', +traceEventNames:[ +'EventDispatch', +'EvaluateScript', +'v8.evaluateModule', +'FunctionCall', +'TimerFire', +'FireIdleCallback', +'FireAnimationFrame', +'RunMicrotasks', +'V8.Execute']}, + + +garbageCollection:{ +id:'', +label:'Garbage Collection', +traceEventNames:[ +'GCEvent', +'MinorGC', +'MajorGC', +'ThreadState::performIdleLazySweep', +'ThreadState::completeSweep', +'BlinkGCMarking']}, + + +other:{ +id:'', +label:'Other', +traceEventNames:[ +'MessageLoop::RunTask', +'TaskQueueManager::ProcessTaskFromWorkQueue', +'ThreadControllerImpl::DoWork']}}; + + + + + +const taskNameToGroup={}; +for(const[groupId,group]of Object.entries(taskGroups)){ +group.id=groupId; +for(const traceEventName of group.traceEventNames){ +taskNameToGroup[traceEventName]=group; +} } - -init(trace){ - -this._tracingModel= -new WebInspector.TracingModel(new WebInspector.TempFileBackingStorage('tracing')); - -this._timelineModel= -new WebInspector.TimelineModel(WebInspector.TimelineUIUtils.visibleEventsFilter()); - -let events; -if(Array.isArray(trace)){ -events=trace; -} -if(typeof trace==='string'){ -events=JSON.parse(trace); -} -if(trace.hasOwnProperty('traceEvents')){ -events=trace.traceEvents; -} +module.exports={ +taskGroups, +taskNameToGroup}; -this._tracingModel.reset(); - -try{ -ConsoleQuieter.mute({prefix:'timelineModel'}); -this._tracingModel.addEvents(events); -this._tracingModel.tracingComplete(); -this._timelineModel.setEvents(this._tracingModel); -}finally{ -ConsoleQuieter.unmuteAndFlush(); -} - -return this; -} - -_createAggregator(){ -return WebInspector.AggregatedTimelineTreeView.prototype._createAggregator(); -} - -timelineModel(){ -return this._timelineModel; -} - -tracingModel(){ -return this._tracingModel; -} - -topDown(){ -const filters=[]; -filters.push(WebInspector.TimelineUIUtils.visibleEventsFilter()); -filters.push(new WebInspector.ExcludeTopLevelFilter()); -const nonessentialEvents=[ -WebInspector.TimelineModel.RecordType.EventDispatch, -WebInspector.TimelineModel.RecordType.FunctionCall, -WebInspector.TimelineModel.RecordType.TimerFire]; - -filters.push(new WebInspector.ExclusiveNameFilter(nonessentialEvents)); - -const topDown=WebInspector.TimelineProfileTree.buildTopDown( -this._timelineModel.mainThreadEvents(), -filters,0,Infinity, -WebInspector.TimelineAggregator.eventId); -return topDown; -} - -bottomUp(){ -const topDown=this.topDown(); -const noGrouping=WebInspector.TimelineAggregator.GroupBy.None; -const noGroupAggregator=this._createAggregator().groupFunction(noGrouping); -return WebInspector.TimelineProfileTree.buildBottomUp(topDown,noGroupAggregator); -} - - - - -bottomUpGroupBy(grouping){ -const topDown=this.topDown(); - -const groupSetting=WebInspector.TimelineAggregator.GroupBy[grouping]; -const groupingAggregator=this._createAggregator().groupFunction(groupSetting); -const bottomUpGrouped= -WebInspector.TimelineProfileTree.buildBottomUp(topDown,groupingAggregator); - - -new TimelineModelTreeView(bottomUpGrouped).sortingChanged('self','desc'); -return bottomUpGrouped; -} - -frameModel(){ - -const mapper=event=>WebInspector.TimelineUIUtils.eventStyle(event).category.name; -const frameModel=new WebInspector.TimelineFrameModel(mapper); -frameModel.addTraceEvents({}, -this._timelineModel.inspectedTargetEvents(),this._timelineModel.sessionId()||''); -return frameModel; -} - - -filmStripModel(){ -return new WebInspector.FilmStripModel(this._tracingModel); -} - -interactionModel(){ -const irModel=new WebInspector.TimelineIRModel(); -irModel.populate(this._timelineModel); -return irModel; -}} - - -module.exports=TimelineModel; - -},{"../console-quieter":22,"../web-inspector":47,"devtools-timeline-model/lib/timeline-model-treeview.js":135}],44:[function(require,module,exports){ +},{}],44:[function(require,module,exports){ @@ -22839,7 +23607,7 @@ module.exports=Metrics; -},{"lighthouse-logger":143}],45:[function(require,module,exports){ +},{"lighthouse-logger":125}],45:[function(require,module,exports){ @@ -22928,6 +23696,7 @@ const BASE_RESPONSE_LATENCY=16; const SCHEDULABLE_TASK_TITLE='TaskQueueManager::ProcessTaskFromWorkQueue'; const SCHEDULABLE_TASK_TITLE_ALT='ThreadControllerImpl::DoWork'; +const LHError=require('../errors'); class TraceProcessor{ @@ -23101,6 +23870,43 @@ +static findTracingStartedEvt(events){ + +let startedInPageEvt; + + +const startedInBrowserEvt=events.find(e=>e.name==='TracingStartedInBrowser'); +if(startedInBrowserEvt&&startedInBrowserEvt.args.data&& +startedInBrowserEvt.args.data.frames){ +const mainFrame=startedInBrowserEvt.args.data.frames.find(frame=>!frame.parent); +const pid=mainFrame&&mainFrame.processId; +const threadNameEvt=events.find(e=>e.pid===pid&&e.ph==='M'&& +e.cat==='__metadata'&&e.name==='thread_name'&&e.args.name==='CrRendererMain'); +startedInPageEvt=mainFrame&&threadNameEvt? +Object.assign({},startedInBrowserEvt,{ +pid,tid:threadNameEvt.tid,name:'TracingStartedInPage', +args:{data:{page:mainFrame.frame}}}): +undefined; +} + + +if(!startedInPageEvt){ + + +startedInPageEvt=events.find(e=>e.name==='TracingStartedInPage'); +} + +if(!startedInPageEvt)throw new LHError(LHError.errors.NO_TRACING_STARTED); + + +const frameId=startedInPageEvt.args.data.page; +return{startedInPageEvt,frameId}; +} + + + + + static isScheduleableTask(evt){ return evt.name===SCHEDULABLE_TASK_TITLE||evt.name===SCHEDULABLE_TASK_TITLE_ALT; }} @@ -23115,7 +23921,7 @@ module.exports=TraceProcessor; -},{}],47:[function(require,module,exports){ +},{"../errors":33}],47:[function(require,module,exports){ (function(global){ @@ -23220,66 +24026,13 @@ }; -global.NetworkAgent={ -RequestMixedContentType:{ -Blockable:'blockable', -OptionallyBlockable:'optionally-blockable', -None:'none'}, - -BlockedReason:{ -CSP:'csp', -MixedContent:'mixed-content', -Origin:'origin', -Inspector:'inspector', -Other:'other'}, - -InitiatorType:{ -Other:'other', -Parser:'parser', -Redirect:'redirect', -Script:'script'}}; - - - - -global.SecurityAgent={ -SecurityState:{ -Unknown:'unknown', -Neutral:'neutral', -Insecure:'insecure', -Warning:'warning', -Secure:'secure', -Info:'info'}}; - - - -global.PageAgent={ -ResourceType:{ -Document:'document', -Stylesheet:'stylesheet', -Image:'image', -Media:'media', -Font:'font', -Script:'script', -TextTrack:'texttrack', -XHR:'xhr', -Fetch:'fetch', -EventSource:'eventsource', -WebSocket:'websocket', -Manifest:'manifest', -Other:'other'}}; - - - require('chrome-devtools-frontend/front_end/common/Object.js'); require('chrome-devtools-frontend/front_end/common/ParsedURL.js'); -require('chrome-devtools-frontend/front_end/common/ResourceType.js'); require('chrome-devtools-frontend/front_end/common/UIString.js'); +require('chrome-devtools-frontend/front_end/common/SegmentedRange.js'); require('chrome-devtools-frontend/front_end/platform/utilities.js'); require('chrome-devtools-frontend/front_end/sdk/Target.js'); require('chrome-devtools-frontend/front_end/sdk/TargetManager.js'); -require('chrome-devtools-frontend/front_end/sdk/NetworkManager.js'); -require('chrome-devtools-frontend/front_end/sdk/NetworkRequest.js'); WebInspector.targetManager={ @@ -23305,34 +24058,10 @@ WebInspector.ViewportDataGridNode=function(){}; global.WorkerRuntime.Worker=function(){}; -require('chrome-devtools-frontend/front_end/common/SegmentedRange.js'); -require('chrome-devtools-frontend/front_end/bindings/TempFile.js'); -require('chrome-devtools-frontend/front_end/sdk/TracingModel.js'); -require('chrome-devtools-frontend/front_end/sdk/ProfileTreeModel.js'); -require('chrome-devtools-frontend/front_end/timeline/TimelineUIUtils.js'); -require('chrome-devtools-frontend/front_end/timeline_model/TimelineJSProfile.js'); -require('chrome-devtools-frontend/front_end/sdk/CPUProfileDataModel.js'); -require('chrome-devtools-frontend/front_end/timeline_model/LayerTreeModel.js'); -require('chrome-devtools-frontend/front_end/timeline_model/TimelineModel.js'); -require('chrome-devtools-frontend/front_end/ui_lazy/SortableDataGrid.js'); -require('chrome-devtools-frontend/front_end/timeline/TimelineTreeView.js'); - require('chrome-devtools-frontend/front_end/common/TextUtils.js'); require('chrome-devtools-frontend/front_end/timeline/TimelineLoader.js'); -require('chrome-devtools-frontend/front_end/timeline_model/TimelineProfileTree.js'); -require('chrome-devtools-frontend/front_end/components_lazy/FilmStripModel.js'); -require('chrome-devtools-frontend/front_end/timeline_model/TimelineIRModel.js'); -require('chrome-devtools-frontend/front_end/timeline_model/TimelineFrameModel.js'); - - -WebInspector.DeferredTempFile=function(){}; -WebInspector.DeferredTempFile.prototype={ -write:function(){}, -finishWriting:function(){}}; - - WebInspector.ConsoleMessage=function(){}; WebInspector.ConsoleMessage.MessageSource={ @@ -23346,78 +24075,9 @@ -WebInspector.NetworkLog=function(target){ -this._requests=new Map(); -target.networkManager.addEventListener( -WebInspector.NetworkManager.Events.RequestStarted,this._onRequestStarted,this); -}; - -WebInspector.NetworkLog.prototype={ -requestForURL:function(url){ -return this._requests.get(url)||null; -}, - -_onRequestStarted:function(event){ -const request=event.data; -if(this._requests.has(request.url)){ -return; -} -this._requests.set(request.url,request); -}}; - - - require('chrome-devtools-frontend/front_end/common/Color.js'); - -const Dispatcher=WebInspector.NetworkDispatcher; -const origUpdateRequest=Dispatcher.prototype._updateNetworkRequestWithRequest; -Dispatcher.prototype._updateNetworkRequestWithRequest=function(netRecord,request){ -origUpdateRequest.apply(this,arguments); -netRecord.isLinkPreload=Boolean(request.isLinkPreload); -netRecord._isLinkPreload=Boolean(request.isLinkPreload); -}; - - - - - -WebInspector.NetworkManager.createWithFakeTarget=function(){ - -const fakeNetworkAgent={ -enable(){}, -getResponseBody(){ -throw new Error('Use driver.getRequestContent() for network request content'); -}}; - -const fakeConsoleModel={ -addMessage(){}, -target(){}}; - -const fakeTarget={ -_modelByConstructor:new Map(), -get consoleModel(){ -return fakeConsoleModel; -}, -networkAgent(){ -return fakeNetworkAgent; -}, -registerNetworkDispatcher(){}, -model(){}}; - - -fakeTarget.networkManager=new WebInspector.NetworkManager(fakeTarget); -fakeTarget.networkLog=new WebInspector.NetworkLog(fakeTarget); - -WebInspector.NetworkLog.fromTarget=()=>{ -return fakeTarget.networkLog; -}; - -return fakeTarget.networkManager; -}; - - require('chrome-devtools-frontend/front_end/common/TextRange.js'); require('chrome-devtools-frontend/front_end/sdk/CSSMatchedStyles.js'); require('chrome-devtools-frontend/front_end/sdk/CSSMedia.js'); @@ -23440,7 +24100,7 @@ }(); }).call(this,typeof global!=="undefined"?global:typeof self!=="undefined"?self:typeof window!=="undefined"?window:{}); -},{"chrome-devtools-frontend/front_end/bindings/TempFile.js":99,"chrome-devtools-frontend/front_end/common/Color.js":100,"chrome-devtools-frontend/front_end/common/Object.js":101,"chrome-devtools-frontend/front_end/common/ParsedURL.js":102,"chrome-devtools-frontend/front_end/common/ResourceType.js":103,"chrome-devtools-frontend/front_end/common/SegmentedRange.js":104,"chrome-devtools-frontend/front_end/common/TextRange.js":105,"chrome-devtools-frontend/front_end/common/TextUtils.js":106,"chrome-devtools-frontend/front_end/common/UIString.js":107,"chrome-devtools-frontend/front_end/components_lazy/FilmStripModel.js":108,"chrome-devtools-frontend/front_end/platform/utilities.js":109,"chrome-devtools-frontend/front_end/sdk/CPUProfileDataModel.js":110,"chrome-devtools-frontend/front_end/sdk/CSSMatchedStyles.js":111,"chrome-devtools-frontend/front_end/sdk/CSSMedia.js":112,"chrome-devtools-frontend/front_end/sdk/CSSMetadata.js":113,"chrome-devtools-frontend/front_end/sdk/CSSProperty.js":114,"chrome-devtools-frontend/front_end/sdk/CSSRule.js":115,"chrome-devtools-frontend/front_end/sdk/CSSStyleDeclaration.js":116,"chrome-devtools-frontend/front_end/sdk/NetworkManager.js":117,"chrome-devtools-frontend/front_end/sdk/NetworkRequest.js":118,"chrome-devtools-frontend/front_end/sdk/ProfileTreeModel.js":119,"chrome-devtools-frontend/front_end/sdk/Target.js":120,"chrome-devtools-frontend/front_end/sdk/TargetManager.js":121,"chrome-devtools-frontend/front_end/sdk/TracingModel.js":122,"chrome-devtools-frontend/front_end/timeline/TimelineLoader.js":123,"chrome-devtools-frontend/front_end/timeline/TimelineTreeView.js":124,"chrome-devtools-frontend/front_end/timeline/TimelineUIUtils.js":125,"chrome-devtools-frontend/front_end/timeline_model/LayerTreeModel.js":126,"chrome-devtools-frontend/front_end/timeline_model/TimelineFrameModel.js":127,"chrome-devtools-frontend/front_end/timeline_model/TimelineIRModel.js":128,"chrome-devtools-frontend/front_end/timeline_model/TimelineJSProfile.js":129,"chrome-devtools-frontend/front_end/timeline_model/TimelineModel.js":130,"chrome-devtools-frontend/front_end/timeline_model/TimelineProfileTree.js":131,"chrome-devtools-frontend/front_end/ui_lazy/SortableDataGrid.js":132}],48:[function(require,module,exports){ +},{"chrome-devtools-frontend/front_end/common/Color.js":99,"chrome-devtools-frontend/front_end/common/Object.js":100,"chrome-devtools-frontend/front_end/common/ParsedURL.js":101,"chrome-devtools-frontend/front_end/common/SegmentedRange.js":102,"chrome-devtools-frontend/front_end/common/TextRange.js":103,"chrome-devtools-frontend/front_end/common/TextUtils.js":104,"chrome-devtools-frontend/front_end/common/UIString.js":105,"chrome-devtools-frontend/front_end/platform/utilities.js":106,"chrome-devtools-frontend/front_end/sdk/CSSMatchedStyles.js":107,"chrome-devtools-frontend/front_end/sdk/CSSMedia.js":108,"chrome-devtools-frontend/front_end/sdk/CSSMetadata.js":109,"chrome-devtools-frontend/front_end/sdk/CSSProperty.js":110,"chrome-devtools-frontend/front_end/sdk/CSSRule.js":111,"chrome-devtools-frontend/front_end/sdk/CSSStyleDeclaration.js":112,"chrome-devtools-frontend/front_end/sdk/Target.js":113,"chrome-devtools-frontend/front_end/sdk/TargetManager.js":114,"chrome-devtools-frontend/front_end/timeline/TimelineLoader.js":115}],48:[function(require,module,exports){ @@ -23970,7 +24630,8 @@ const Sentry=require('./lib/sentry'); const generateReport=require('./report/report-generator').generateReport; -const Connection=require('./gather/connections/connection.js'); + + class Runner{ @@ -23989,19 +24650,6 @@ const lighthouseRunWarnings=[]; - -const rawRequestedUrl=opts.url; -if(typeof rawRequestedUrl!=='string'||rawRequestedUrl.length===0){ -throw new Error(`You must provide a url to the runner. '${rawRequestedUrl}' provided.`); -} - -let parsedURL; -try{ -parsedURL=new URL(opts.url); -}catch(e){ -throw new Error('The url provided should have a proper protocol and hostname.'); -} - const sentryContext=Sentry.getContext(); Sentry.captureBreadcrumb({ @@ -24012,26 +24660,37 @@ -if(parsedURL.protocol!=='https:'&&parsedURL.hostname!=='localhost'){ -log.warn('Lighthouse','The URL provided should be on HTTPS'); -log.warn('Lighthouse','Performance stats will be skewed redirecting from HTTP to HTTPS.'); -} - - -const requestedUrl=parsedURL.href; - - let artifacts; +let requestedUrl; if(settings.auditMode&&!settings.gatherMode){ const path=Runner._getArtifactsPath(settings); artifacts=await assetSaver.loadArtifacts(path); +requestedUrl=artifacts.URL.requestedUrl; + +if(!requestedUrl){ +throw new Error('Cannot run audit mode on empty URL'); +} +if(opts.url&&opts.url!==requestedUrl){ +throw new Error('Cannot run audit mode on different URL'); +} }else{ +if(typeof opts.url!=='string'||opts.url.length===0){ +throw new Error(`You must provide a url to the runner. '${opts.url}' provided.`); +} + +try{ + +requestedUrl=new URL(opts.url).href; +}catch(e){ +throw new Error('The url provided should have a proper protocol and hostname.'); +} + artifacts=await Runner._gatherArtifactsFromBrowser(requestedUrl,opts,connection); if(settings.gatherMode){ @@ -24083,7 +24742,7 @@ audits:resultsById, configSettings:settings, categories, -categoryGroups:opts.config.groups, +categoryGroups:opts.config.groups||undefined, timing:{total:Date.now()-startTime}}; @@ -24161,7 +24820,7 @@ static async _runAudit(auditDefn,artifacts,settings){ const audit=auditDefn.implementation; -const status=`Evaluating: ${audit.meta.description}`; +const status=`Evaluating: ${audit.meta.title}`; log.log('status',status); let auditResult; @@ -24176,7 +24835,7 @@ if(noArtifact||noTrace){ log.warn('Runner', -`${artifactName} gatherer, required by audit ${audit.meta.name}, did not run.`); +`${artifactName} gatherer, required by audit ${audit.meta.id}, did not run.`); throw new Error(`Required ${artifactName} gatherer did not run.`); } @@ -24193,7 +24852,7 @@ level:'error'}); -log.warn('Runner',`${artifactName} gatherer, required by audit ${audit.meta.name},`+ +log.warn('Runner',`${artifactName} gatherer, required by audit ${audit.meta.id},`+ ` encountered an error: ${artifactError.message}`); @@ -24210,13 +24869,13 @@ const product=await audit.audit(artifacts,{options:auditOptions,settings:settings}); auditResult=Audit.generateAuditResult(audit,product); }catch(err){ -log.warn(audit.meta.name,`Caught exception: ${err.message}`); +log.warn(audit.meta.id,`Caught exception: ${err.message}`); if(err.fatal){ throw err; } -Sentry.captureException(err,{tags:{audit:audit.meta.name},level:'error'}); +Sentry.captureException(err,{tags:{audit:audit.meta.id},level:'error'}); const errorMessage=err.friendlyMessage? `${err.friendlyMessage} (${err.message})`: @@ -24243,8 +24902,9 @@ const fileList=[ -...["accessibility","audit.js","bootup-time.js","byte-efficiency","content-width.js","critical-request-chains.js","deprecations.js","dobetterweb","errors-in-console.js","estimated-input-latency.js","first-contentful-paint.js","first-cpu-idle.js","first-meaningful-paint.js","font-display.js","image-aspect-ratio.js","interactive.js","is-on-https.js","load-fast-enough-for-pwa.js","mainthread-work-breakdown.js","manifest-short-name-length.js","manual","metrics.js","mixed-content.js","multi-check-audit.js","network-requests.js","predictive-perf.js","redirects-http.js","redirects.js","screenshot-thumbnails.js","seo","service-worker.js","speed-index.js","splash-screen.js","themed-omnibox.js","time-to-first-byte.js","user-timings.js","uses-rel-preconnect.js","uses-rel-preload.js","viewport.js","violation-audit.js","webapp-install-banner.js","without-javascript.js","works-offline.js"], -...["appcache-manifest.js","dom-size.js","external-anchors-use-rel-noopener.js","geolocation-on-start.js","no-document-write.js","no-mutation-events.js","no-vulnerable-libraries.js","no-websql.js","notification-on-start.js","password-inputs-can-be-pasted-into.js","uses-http2.js","uses-passive-event-listeners.js"].map(f=>`dobetterweb/${f}`), +...["accessibility","audit.js","bootup-time.js","byte-efficiency","content-width.js","critical-request-chains.js","deprecations.js","dobetterweb","errors-in-console.js","font-display.js","image-aspect-ratio.js","is-on-https.js","load-fast-enough-for-pwa.js","mainthread-work-breakdown.js","manifest-short-name-length.js","manual","metrics","metrics.js","mixed-content.js","multi-check-audit.js","network-requests.js","predictive-perf.js","redirects-http.js","redirects.js","screenshot-thumbnails.js","seo","service-worker.js","splash-screen.js","themed-omnibox.js","time-to-first-byte.js","user-timings.js","uses-rel-preconnect.js","uses-rel-preload.js","viewport.js","violation-audit.js","webapp-install-banner.js","without-javascript.js","works-offline.js"], +...["appcache-manifest.js","doctype.js","dom-size.js","external-anchors-use-rel-noopener.js","geolocation-on-start.js","no-document-write.js","no-vulnerable-libraries.js","no-websql.js","notification-on-start.js","password-inputs-can-be-pasted-into.js","uses-http2.js","uses-passive-event-listeners.js"].map(f=>`dobetterweb/${f}`), +...["estimated-input-latency.js","first-contentful-paint.js","first-cpu-idle.js","first-meaningful-paint.js","interactive.js","speed-index.js"].map(f=>`metrics/${f}`), ...["canonical.js","font-size.js","hreflang.js","http-status-code.js","is-crawlable.js","link-text.js","manual","meta-description.js","plugins.js","robots-txt.js"].map(f=>`seo/${f}`), ...["mobile-friendly.js","structured-data.js"].map(f=>`seo/manual/${f}`), ...["accesskeys.js","aria-allowed-attr.js","aria-required-attr.js","aria-required-children.js","aria-required-parent.js","aria-roles.js","aria-valid-attr-value.js","aria-valid-attr.js","audio-caption.js","axe-audit.js","button-name.js","bypass.js","color-contrast.js","definition-list.js","dlitem.js","document-title.js","duplicate-id.js","frame-title.js","html-has-lang.js","html-lang-valid.js","image-alt.js","input-image-alt.js","label.js","layout-table.js","link-name.js","list.js","listitem.js","manual","meta-refresh.js","meta-viewport.js","object-alt.js","tabindex.js","td-headers-attr.js","th-has-data-cells.js","valid-lang.js","video-caption.js","video-description.js"]. @@ -24268,7 +24928,7 @@ const fileList=[ ...["accessibility.js","cache-contents.js","chrome-console-messages.js","css-usage.js","dobetterweb","fonts.js","gatherer.js","html-without-javascript.js","http-redirect.js","image-usage.js","js-usage.js","manifest.js","mixed-content.js","offline.js","runtime-exceptions.js","scripts.js","seo","service-worker.js","start-url.js","theme-color.js","viewport-dimensions.js","viewport.js"], ...["canonical.js","crawlable-links.js","embedded-content.js","font-size.js","hreflang.js","meta-description.js","meta-robots.js","robots-txt.js"].map(f=>`seo/${f}`), -...["all-event-listeners.js","anchors-with-no-rel-noopener.js","appcache.js","domstats.js","js-libraries.js","optimized-images.js","password-inputs-with-prevented-paste.js","response-compression.js","tags-blocking-first-paint.js","websql.js"]. +...["anchors-with-no-rel-noopener.js","appcache.js","doctype.js","domstats.js","js-libraries.js","optimized-images.js","password-inputs-with-prevented-paste.js","response-compression.js","tags-blocking-first-paint.js","websql.js"]. map(f=>`dobetterweb/${f}`)]; return fileList.filter(f=>/\.js$/.test(f)&&f!=='gatherer.js').sort(); @@ -24287,7 +24947,7 @@ const fileList=[ -...["computed-artifact.js","critical-request-chains.js","dtm-model.js","load-simulator.js","main-resource.js","manifest-values.js","metrics","network-analysis.js","network-records.js","network-throughput.js","page-dependency-graph.js","pushed-requests.js","screenshots.js","speedline.js","trace-of-tab.js"], +...["computed-artifact.js","critical-request-chains.js","load-simulator.js","main-resource.js","main-thread-tasks.js","manifest-values.js","metrics","network-analysis.js","network-records.js","network-throughput.js","page-dependency-graph.js","pushed-requests.js","screenshots.js","speedline.js","trace-of-tab.js"], ...["estimated-input-latency.js","first-contentful-paint.js","first-cpu-idle.js","first-meaningful-paint.js","interactive.js","lantern-estimated-input-latency.js","lantern-first-contentful-paint.js","lantern-first-cpu-idle.js","lantern-first-meaningful-paint.js","lantern-interactive.js","lantern-metric.js","lantern-speed-index.js","metric.js","speed-index.js"].map(f=>`metrics/${f}`)]; @@ -24377,7 +25037,7 @@ module.exports=Runner; }).call(this,require('_process'),"/../lighthouse-core"); -},{"../package.json":154,"./audits/audit":2,"./gather/connections/connection.js":14,"./gather/driver.js":17,"./gather/gather-runner":18,"./lib/asset-saver":21,"./lib/sentry":39,"./lib/url-shim":"url","./report/report-generator":49,"./scoring":51,"_process":77,"lighthouse-logger":143,"lodash.isequal":144,"path":75}],51:[function(require,module,exports){ +},{"../package.json":136,"./audits/audit":2,"./gather/driver.js":17,"./gather/gather-runner":18,"./lib/asset-saver":21,"./lib/sentry":40,"./lib/url-shim":"url","./report/report-generator":49,"./scoring":51,"_process":77,"lighthouse-logger":125,"lodash.isequal":126,"path":75}],51:[function(require,module,exports){ @@ -24492,7 +25152,10 @@ -window.runLighthouseForConnection=function( + + + +function runLighthouseForConnection( connection,url,options,categoryIDs, updateBadgeFn=function(){}){ const config=new Config({ @@ -24513,7 +25176,7 @@ updateBadgeFn(); throw err; }); -}; +} @@ -24522,26 +25185,45 @@ -window.runLighthouseInWorker=function(port,url,options,categoryIDs){ +function runLighthouseInWorker(port,url,options,categoryIDs){ log.setLevel('info'); const connection=new RawProtocol(port); -return window.runLighthouseForConnection(connection,url,options,categoryIDs); -}; +return runLighthouseForConnection(connection,url,options,categoryIDs); +} -window.getDefaultCategories=function(){ +function getDefaultCategories(){ return Config.getCategories(defaultConfig); -}; +} -window.listenForStatus=function(listenCallback){ + +function listenForStatus(listenCallback){ log.events.addListener('status',listenCallback); -}; +} -},{"../../../lighthouse-core/config/config":7,"../../../lighthouse-core/config/default-config.js":9,"../../../lighthouse-core/gather/connections/raw":15,"../../../lighthouse-core/runner":50,"lighthouse-logger":143}],53:[function(require,module,exports){ +if(typeof module!=='undefined'&&module.exports){ + +module.exports={ +runLighthouseForConnection, +runLighthouseInWorker, +getDefaultCategories, +listenForStatus}; + +} + +if(typeof window!=='undefined'){ + + +window.runLighthouseInWorker=runLighthouseInWorker; + +window.listenForStatus=listenForStatus; +} + +},{"../../../lighthouse-core/config/config":7,"../../../lighthouse-core/config/default-config.js":9,"../../../lighthouse-core/gather/connections/raw":15,"../../../lighthouse-core/runner":50,"lighthouse-logger":125}],53:[function(require,module,exports){ (function(global){ 'use strict'; @@ -44032,600 +44714,6 @@ -window.requestFileSystem=window.requestFileSystem||window.webkitRequestFileSystem; - - - - -WebInspector.TempFile=function() -{ -this._fileEntry=null; -this._writer=null; -}; - - - - - - -WebInspector.TempFile.create=function(dirPath,name) -{ -var file=new WebInspector.TempFile(); - -function requestTempFileSystem() -{ -return new Promise(window.requestFileSystem.bind(window,window.TEMPORARY,10)); -} - - - - -function getDirectoryEntry(fs) -{ -return new Promise(fs.root.getDirectory.bind(fs.root,dirPath,{create:true})); -} - - - - -function getFileEntry(dir) -{ -return new Promise(dir.getFile.bind(dir,name,{create:true})); -} - - - - -function createFileWriter(fileEntry) -{ -file._fileEntry=fileEntry; -return new Promise(fileEntry.createWriter.bind(fileEntry)); -} - - - - -function truncateFile(writer) -{ -if(!writer.length){ -file._writer=writer; -return Promise.resolve(file); -} - - - - - -function truncate(fulfill,reject) -{ -writer.onwriteend=fulfill; -writer.onerror=reject; -writer.truncate(0); -} - -function didTruncate() -{ -file._writer=writer; -writer.onwriteend=null; -writer.onerror=null; -return Promise.resolve(file); -} - -function onTruncateError(e) -{ -writer.onwriteend=null; -writer.onerror=null; -throw e; -} - -return new Promise(truncate).then(didTruncate,onTruncateError); -} - -return WebInspector.TempFile.ensureTempStorageCleared(). -then(requestTempFileSystem). -then(getDirectoryEntry). -then(getFileEntry). -then(createFileWriter). -then(truncateFile); -}; - -WebInspector.TempFile.prototype={ - - - - -write:function(strings,callback) -{ -var blob=new Blob(strings,{type:"text/plain"}); -this._writer.onerror=function(e) -{ -WebInspector.console.error("Failed to write into a temp file: "+e.target.error.message); -callback(-1); -}; -this._writer.onwriteend=function(e) -{ -callback(e.target.length); -}; -this._writer.write(blob); -}, - -finishWriting:function() -{ -this._writer=null; -}, - - - - -read:function(callback) -{ -this.readRange(undefined,undefined,callback); -}, - - - - - - -readRange:function(startOffset,endOffset,callback) -{ - - - -function didGetFile(file) -{ -var reader=new FileReader(); - -if(typeof startOffset==="number"||typeof endOffset==="number") -file=file.slice(startOffset,endOffset); - - - -reader.onloadend=function(e) -{ -callback(this.result); -}; -reader.onerror=function(error) -{ -WebInspector.console.error("Failed to read from temp file: "+error.message); -}; -reader.readAsText(file); -} -function didFailToGetFile(error) -{ -WebInspector.console.error("Failed to load temp file: "+error.message); -callback(null); -} -this._fileEntry.file(didGetFile,didFailToGetFile); -}, - - - - - -copyToOutputStream:function(outputStream,delegate) -{ - - - -function didGetFile(file) -{ -var reader=new WebInspector.ChunkedFileReader(file,10*1000*1000,delegate); -reader.start(outputStream); -} - -function didFailToGetFile(error) -{ -WebInspector.console.error("Failed to load temp file: "+error.message); -outputStream.close(); -} - -this._fileEntry.file(didGetFile,didFailToGetFile); -}, - -remove:function() -{ -if(this._fileEntry) -this._fileEntry.remove(function(){}); -}}; - - - - - - - -WebInspector.DeferredTempFile=function(dirPath,name) -{ - -this._chunks=[]; -this._tempFile=null; -this._isWriting=false; -this._finishCallback=null; -this._finishedWriting=false; -this._callsPendingOpen=[]; -this._pendingReads=[]; -WebInspector.TempFile.create(dirPath,name). -then(this._didCreateTempFile.bind(this),this._failedToCreateTempFile.bind(this)); -}; - -WebInspector.DeferredTempFile.prototype={ - - - - -write:function(strings,callback) -{ -if(this._finishCallback) -throw new Error("No writes are allowed after close."); -this._chunks.push({strings:strings,callback:callback||null}); -if(this._tempFile&&!this._isWriting) -this._writeNextChunk(); -}, - - - - -finishWriting:function(callback) -{ -this._finishCallback=callback; -if(this._finishedWriting) -callback(this._tempFile);else -if(!this._isWriting&&!this._chunks.length) -this._notifyFinished(); -}, - - - - -_failedToCreateTempFile:function(e) -{ -WebInspector.console.error("Failed to create temp file "+e.code+" : "+e.message); -this._notifyFinished(); -}, - - - - -_didCreateTempFile:function(tempFile) -{ -this._tempFile=tempFile; -var callsPendingOpen=this._callsPendingOpen; -this._callsPendingOpen=null; -for(var i=0;i<callsPendingOpen.length;++i) -callsPendingOpen[i](); -if(this._chunks.length) -this._writeNextChunk(); -}, - -_writeNextChunk:function() -{ - -if(!this._tempFile) -return; -var chunk=this._chunks.shift(); -this._isWriting=true; -this._tempFile.write(chunk.strings,this._didWriteChunk.bind(this,chunk.callback)); -}, - - - - - -_didWriteChunk:function(callback,size) -{ -this._isWriting=false; -if(size===-1){ -this._tempFile=null; -this._notifyFinished(); -return; -} -if(callback) -callback(size); -if(this._chunks.length) -this._writeNextChunk();else -if(this._finishCallback) -this._notifyFinished(); -}, - -_notifyFinished:function() -{ -this._finishedWriting=true; -if(this._tempFile) -this._tempFile.finishWriting(); -var chunks=this._chunks; -this._chunks=[]; -for(var i=0;i<chunks.length;++i){ -if(chunks[i].callback) -chunks[i].callback(-1); -} -if(this._finishCallback) -this._finishCallback(this._tempFile); -var pendingReads=this._pendingReads; -this._pendingReads=[]; -for(var i=0;i<pendingReads.length;++i) -pendingReads[i](); -}, - - - - - - -readRange:function(startOffset,endOffset,callback) -{ -if(!this._finishedWriting){ -this._pendingReads.push(this.readRange.bind(this,startOffset,endOffset,callback)); -return; -} -if(!this._tempFile){ -callback(null); -return; -} -this._tempFile.readRange(startOffset,endOffset,callback); -}, - - - - - -copyToOutputStream:function(outputStream,delegate) -{ -if(!this._finishedWriting){ -this._pendingReads.push(this.copyToOutputStream.bind(this,outputStream,delegate)); -return; -} -if(this._tempFile) -this._tempFile.copyToOutputStream(outputStream,delegate); -}, - -remove:function() -{ -if(this._callsPendingOpen){ -this._callsPendingOpen.push(this.remove.bind(this)); -return; -} -if(this._tempFile) -this._tempFile.remove(); -this._tempFile=null; -}}; - - - - - - -WebInspector.TempFile._clearTempStorage=function(fulfill,reject) -{ - - - -function handleError(event) -{ -WebInspector.console.error(WebInspector.UIString("Failed to clear temp storage: %s",event.data)); -reject(event.data); -} - - - - -function handleMessage(event) -{ -if(event.data.type==="tempStorageCleared"){ -if(event.data.error) -WebInspector.console.error(event.data.error);else - -fulfill(undefined); -return; -} -reject(event.data); -} - -try{ -var worker=new WebInspector.Worker("temp_storage_shared_worker","TempStorageCleaner"); -worker.onerror=handleError; -worker.onmessage=handleMessage; -}catch(e){ -if(e.name==="URLMismatchError") -console.log("Shared worker wasn't started due to url difference. "+e);else - -throw e; -} -}; - - - - -WebInspector.TempFile.ensureTempStorageCleared=function() -{ -if(!WebInspector.TempFile._storageCleanerPromise) -WebInspector.TempFile._storageCleanerPromise=new Promise(WebInspector.TempFile._clearTempStorage); -return WebInspector.TempFile._storageCleanerPromise; -}; - - - - - - -WebInspector.TempFileBackingStorage=function(dirName) -{ -this._dirName=dirName; -this.reset(); -}; - - - - - - - - -WebInspector.TempFileBackingStorage.Chunk; - -WebInspector.TempFileBackingStorage.prototype={ - - - - -appendString:function(string) -{ -this._strings.push(string); -this._stringsLength+=string.length; -var flushStringLength=10*1024*1024; -if(this._stringsLength>flushStringLength) -this._flush(false); -}, - - - - - - -appendAccessibleString:function(string) -{ -this._flush(false); -this._strings.push(string); -var chunk=this._flush(true); - - - - - - -function readString(chunk,file) -{ -if(chunk.string) -return Promise.resolve(chunk.string); - -console.assert(chunk.endOffset); -if(!chunk.endOffset) -return Promise.reject("Nor string nor offset to the string in the file were found."); - - - - - -function readRange(fulfill,reject) -{ - -file.readRange(chunk.startOffset,chunk.endOffset,fulfill); -} - -return new Promise(readRange); -} - -return readString.bind(null,chunk,this._file); -}, - - - - - -_flush:function(createChunk) -{ -if(!this._strings.length) -return null; - -var chunk=null; -if(createChunk){ -console.assert(this._strings.length===1); -chunk={ -string:this._strings[0], -startOffset:0, -endOffset:0}; - -} - - - - - - -function didWrite(chunk,fileSize) -{ -if(fileSize===-1) -return; -if(chunk){ -chunk.startOffset=this._fileSize; -chunk.endOffset=fileSize; -chunk.string=null; -} -this._fileSize=fileSize; -} - -this._file.write(this._strings,didWrite.bind(this,chunk)); -this._strings=[]; -this._stringsLength=0; -return chunk; -}, - - - - -finishWriting:function() -{ -this._flush(false); -this._file.finishWriting(function(){}); -}, - - - - -reset:function() -{ -if(this._file) -this._file.remove(); -this._file=new WebInspector.DeferredTempFile(this._dirName,String(Date.now())); - - - -this._strings=[]; -this._stringsLength=0; -this._fileSize=0; -}, - - - - - -writeToStream:function(outputStream,delegate) -{ -this._file.copyToOutputStream(outputStream,delegate); -}}; - - -},{}],100:[function(require,module,exports){ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -45422,7 +45510,7 @@ return format; }; -},{}],101:[function(require,module,exports){ +},{}],100:[function(require,module,exports){ @@ -45643,7 +45731,7 @@ this.method=method; }; -},{}],102:[function(require,module,exports){ +},{}],101:[function(require,module,exports){ @@ -46006,317 +46094,7 @@ return null; }; -},{}],103:[function(require,module,exports){ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -WebInspector.ResourceType=function(name,title,category,isTextType) -{ -this._name=name; -this._title=title; -this._category=category; -this._isTextType=isTextType; -}; - -WebInspector.ResourceType.prototype={ - - - -name:function() -{ -return this._name; -}, - - - - -title:function() -{ -return this._title; -}, - - - - -category:function() -{ -return this._category; -}, - - - - -isTextType:function() -{ -return this._isTextType; -}, - - - - -isScript:function() -{ -return this._name==="script"||this._name==="sm-script"; -}, - - - - -hasScripts:function() -{ -return this.isScript()||this.isDocument(); -}, - - - - -isStyleSheet:function() -{ -return this._name==="stylesheet"||this._name==="sm-stylesheet"; -}, - - - - -isDocument:function() -{ -return this._name==="document"; -}, - - - - -isDocumentOrScriptOrStyleSheet:function() -{ -return this.isDocument()||this.isScript()||this.isStyleSheet(); -}, - - - - -isFromSourceMap:function() -{ -return this._name.startsWith("sm-"); -}, - - - - - -toString:function() -{ -return this._name; -}, - - - - -canonicalMimeType:function() -{ -if(this.isDocument()) -return"text/html"; -if(this.isScript()) -return"text/javascript"; -if(this.isStyleSheet()) -return"text/css"; -return""; -}}; - - - - - - - -WebInspector.ResourceCategory=function(title,shortTitle) -{ -this.title=title; -this.shortTitle=shortTitle; -}; - -WebInspector.resourceCategories={ -XHR:new WebInspector.ResourceCategory("XHR and Fetch","XHR"), -Script:new WebInspector.ResourceCategory("Scripts","JS"), -Stylesheet:new WebInspector.ResourceCategory("Stylesheets","CSS"), -Image:new WebInspector.ResourceCategory("Images","Img"), -Media:new WebInspector.ResourceCategory("Media","Media"), -Font:new WebInspector.ResourceCategory("Fonts","Font"), -Document:new WebInspector.ResourceCategory("Documents","Doc"), -WebSocket:new WebInspector.ResourceCategory("WebSockets","WS"), -Manifest:new WebInspector.ResourceCategory("Manifest","Manifest"), -Other:new WebInspector.ResourceCategory("Other","Other")}; - - - - - - -WebInspector.resourceTypes={ -XHR:new WebInspector.ResourceType("xhr","XHR",WebInspector.resourceCategories.XHR,true), -Fetch:new WebInspector.ResourceType("fetch","Fetch",WebInspector.resourceCategories.XHR,true), -EventSource:new WebInspector.ResourceType("eventsource","EventSource",WebInspector.resourceCategories.XHR,true), -Script:new WebInspector.ResourceType("script","Script",WebInspector.resourceCategories.Script,true), -Stylesheet:new WebInspector.ResourceType("stylesheet","Stylesheet",WebInspector.resourceCategories.Stylesheet,true), -Image:new WebInspector.ResourceType("image","Image",WebInspector.resourceCategories.Image,false), -Media:new WebInspector.ResourceType("media","Media",WebInspector.resourceCategories.Media,false), -Font:new WebInspector.ResourceType("font","Font",WebInspector.resourceCategories.Font,false), -Document:new WebInspector.ResourceType("document","Document",WebInspector.resourceCategories.Document,true), -TextTrack:new WebInspector.ResourceType("texttrack","TextTrack",WebInspector.resourceCategories.Other,true), -WebSocket:new WebInspector.ResourceType("websocket","WebSocket",WebInspector.resourceCategories.WebSocket,false), -Other:new WebInspector.ResourceType("other","Other",WebInspector.resourceCategories.Other,false), -SourceMapScript:new WebInspector.ResourceType("sm-script","Script",WebInspector.resourceCategories.Script,false), -SourceMapStyleSheet:new WebInspector.ResourceType("sm-stylesheet","Stylesheet",WebInspector.resourceCategories.Stylesheet,false), -Manifest:new WebInspector.ResourceType("manifest","Manifest",WebInspector.resourceCategories.Manifest,true)}; - - - - - - -WebInspector.ResourceType.mimeFromURL=function(url) -{ -var name=WebInspector.ParsedURL.extractName(url); -if(WebInspector.ResourceType._mimeTypeByName.has(name)){ -return WebInspector.ResourceType._mimeTypeByName.get(name); -} -var ext=WebInspector.ParsedURL.extractExtension(url).toLowerCase(); -return WebInspector.ResourceType._mimeTypeByExtension.get(ext); -}; - -WebInspector.ResourceType._mimeTypeByName=new Map([ - -["Cakefile","text/x-coffeescript"]]); - - -WebInspector.ResourceType._mimeTypeByExtension=new Map([ - -["js","text/javascript"], -["css","text/css"], -["html","text/html"], -["htm","text/html"], -["xml","application/xml"], -["xsl","application/xml"], - - -["asp","application/x-aspx"], -["aspx","application/x-aspx"], -["jsp","application/x-jsp"], - - -["c","text/x-c++src"], -["cc","text/x-c++src"], -["cpp","text/x-c++src"], -["h","text/x-c++src"], -["m","text/x-c++src"], -["mm","text/x-c++src"], - - -["coffee","text/x-coffeescript"], - - -["dart","text/javascript"], - - -["ts","text/typescript"], -["tsx","text/typescript"], - - -["json","application/json"], -["gyp","application/json"], -["gypi","application/json"], - - -["cs","text/x-csharp"], - - -["java","text/x-java"], - - -["less","text/x-less"], - - -["php","text/x-php"], -["phtml","application/x-httpd-php"], - - -["py","text/x-python"], - - -["sh","text/x-sh"], - - -["scss","text/x-scss"], - - -["vtt","text/vtt"], - - -["ls","text/x-livescript"], - - -["cljs","text/x-clojure"], -["cljc","text/x-clojure"], -["cljx","text/x-clojure"], - - -["styl","text/x-styl"], - - -["jsx","text/jsx"], - - -["jpeg","image/jpeg"], -["jpg","image/jpeg"], -["svg","image/svg"], -["gif","image/gif"], -["webp","image/webp"], -["png","image/png"], -["ico","image/ico"], -["tiff","image/tiff"], -["tif","image/tif"], -["bmp","image/bmp"], - - -["ttf","font/opentype"], -["otf","font/opentype"], -["ttc","font/opentype"], -["woff","application/font-woff"]]); - - -},{}],104:[function(require,module,exports){ +},{}],102:[function(require,module,exports){ @@ -46430,7 +46208,7 @@ }}; -},{}],105:[function(require,module,exports){ +},{}],103:[function(require,module,exports){ @@ -46777,7 +46555,7 @@ return WebInspector.TextRange.comparator(edit1.oldRange,edit2.oldRange); }; -},{}],106:[function(require,module,exports){ +},{}],104:[function(require,module,exports){ @@ -47087,7 +46865,7 @@ createTokenizer:function(mimeType){}}; -},{}],107:[function(require,module,exports){ +},{}],105:[function(require,module,exports){ @@ -47199,163 +46977,7 @@ }}; -},{}],108:[function(require,module,exports){ - - - - - - - - - - - -WebInspector.FilmStripModel=function(tracingModel,zeroTime) -{ -this.reset(tracingModel,zeroTime); -}; - -WebInspector.FilmStripModel._category="disabled-by-default-devtools.screenshot"; - -WebInspector.FilmStripModel.TraceEvents={ -CaptureFrame:"CaptureFrame", -Screenshot:"Screenshot"}; - - -WebInspector.FilmStripModel.prototype={ - - - - -reset:function(tracingModel,zeroTime) -{ -this._zeroTime=zeroTime||tracingModel.minimumRecordTime(); -this._spanTime=tracingModel.maximumRecordTime()-this._zeroTime; - - -this._frames=[]; -var browserMain=WebInspector.TracingModel.browserMainThread(tracingModel); -if(!browserMain) -return; - -var events=browserMain.events(); -for(var i=0;i<events.length;++i){ -var event=events[i]; -if(event.startTime<this._zeroTime) -continue; -if(!event.hasCategory(WebInspector.FilmStripModel._category)) -continue; -if(event.name===WebInspector.FilmStripModel.TraceEvents.CaptureFrame){ -var data=event.args["data"]; -if(data) -this._frames.push(WebInspector.FilmStripModel.Frame._fromEvent(this,event,this._frames.length)); -}else if(event.name===WebInspector.FilmStripModel.TraceEvents.Screenshot){ -this._frames.push(WebInspector.FilmStripModel.Frame._fromSnapshot(this,event,this._frames.length)); -} -} -}, - - - - -frames:function() -{ -return this._frames; -}, - - - - -zeroTime:function() -{ -return this._zeroTime; -}, - - - - -spanTime:function() -{ -return this._spanTime; -}, - - - - - -frameByTimestamp:function(timestamp) -{ -var index=this._frames.upperBound(timestamp,(timestamp,frame)=>timestamp-frame.timestamp)-1; -return index>=0?this._frames[index]:null; -}}; - - - - - - - - -WebInspector.FilmStripModel.Frame=function(model,timestamp,index) -{ -this._model=model; -this.timestamp=timestamp; -this.index=index; - -this._imageData=null; - -this._snapshot=null; -}; - - - - - - - -WebInspector.FilmStripModel.Frame._fromEvent=function(model,event,index) -{ -var frame=new WebInspector.FilmStripModel.Frame(model,event.startTime,index); -frame._imageData=event.args["data"]; -return frame; -}; - - - - - - - -WebInspector.FilmStripModel.Frame._fromSnapshot=function(model,snapshot,index) -{ -var frame=new WebInspector.FilmStripModel.Frame(model,snapshot.startTime,index); -frame._snapshot=snapshot; -return frame; -}; - -WebInspector.FilmStripModel.Frame.prototype={ - - - -model:function() -{ -return this._model; -}, - - - - -imageDataPromise:function() -{ -if(this._imageData||!this._snapshot) -return Promise.resolve(this._imageData); - -return this._snapshot.objectPromise(); -}}; - - -},{}],109:[function(require,module,exports){ +},{}],106:[function(require,module,exports){ @@ -48897,386 +48519,7 @@ }; -},{}],110:[function(require,module,exports){ - - - - - - - - - - -WebInspector.CPUProfileNode=function(node,sampleTime) -{ -var callFrame=node.callFrame||{ - -functionName:node["functionName"], -scriptId:node["scriptId"], -url:node["url"], -lineNumber:node["lineNumber"]-1, -columnNumber:node["columnNumber"]-1}; - -WebInspector.ProfileNode.call(this,callFrame); -this.id=node.id; -this.self=node.hitCount*sampleTime; -this.positionTicks=node.positionTicks; - -this.deoptReason=node.deoptReason&&node.deoptReason!=="no reason"?node.deoptReason:null; -}; - -WebInspector.CPUProfileNode.prototype={ -__proto__:WebInspector.ProfileNode.prototype}; - - - - - - - -WebInspector.CPUProfileDataModel=function(profile) -{ -var isLegacyFormat=!!profile["head"]; -if(isLegacyFormat){ - -this.profileStartTime=profile.startTime*1000; -this.profileEndTime=profile.endTime*1000; -this.timestamps=profile.timestamps; -this._compatibilityConversionHeadToNodes(profile); -}else{ - -this.profileStartTime=profile.startTime/1000; -this.profileEndTime=profile.endTime/1000; -this.timestamps=this._convertTimeDeltas(profile); -} -this.samples=profile.samples; -this.totalHitCount=0; -this.profileHead=this._translateProfileTree(profile.nodes); -WebInspector.ProfileTreeModel.call(this,this.profileHead); -this._extractMetaNodes(); -if(this.samples){ -this._buildIdToNodeMap(); -this._sortSamples(); -this._normalizeTimestamps(); -} -}; - -WebInspector.CPUProfileDataModel.prototype={ - - - -_compatibilityConversionHeadToNodes:function(profile) -{ -if(!profile.head||profile.nodes) -return; - -var nodes=[]; -convertNodesTree(profile.head); -profile.nodes=nodes; -delete profile.head; - - - - -function convertNodesTree(node) -{ -nodes.push(node); -node.children=node.children.map(convertNodesTree); -return node.id; -} -}, - - - - - -_convertTimeDeltas:function(profile) -{ -if(!profile.timeDeltas) -return null; -var lastTimeUsec=profile.startTime; -var timestamps=new Array(profile.timeDeltas.length); -for(var i=0;i<timestamps.length;++i){ -lastTimeUsec+=profile.timeDeltas[i]; -timestamps[i]=lastTimeUsec; -} -return timestamps; -}, - - - - - -_translateProfileTree:function(nodes) -{ - - - - -function isNativeNode(node) -{ -if(node.callFrame) -return!!node.callFrame.url&&node.callFrame.url.startsWith("native "); -return!!node.url&&node.url.startsWith("native "); -} - - - -function buildChildrenFromParents(nodes) -{ -if(nodes[0].children) -return; -nodes[0].children=[]; -for(var i=1;i<nodes.length;++i){ -var node=nodes[i]; -var parentNode=nodeByIdMap.get(node.parent); -if(parentNode.children) -parentNode.children.push(node.id);else - -parentNode.children=[node.id]; -} -} - -var nodeByIdMap=new Map(); -for(var i=0;i<nodes.length;++i){ -var node=nodes[i]; -nodeByIdMap.set(node.id,node); -} -buildChildrenFromParents(nodes); -this.totalHitCount=nodes.reduce((acc,node)=>acc+node.hitCount,0); -var sampleTime=(this.profileEndTime-this.profileStartTime)/this.totalHitCount; -var keepNatives=!!WebInspector.moduleSetting("showNativeFunctionsInJSProfile").get(); -var root=nodes[0]; - -var idMap=new Map([[root.id,root.id]]); -var resultRoot=new WebInspector.CPUProfileNode(root,sampleTime); -var parentNodeStack=root.children.map(()=>resultRoot); -var sourceNodeStack=root.children.map(id=>nodeByIdMap.get(id)); -while(sourceNodeStack.length){ -var parentNode=parentNodeStack.pop(); -var sourceNode=sourceNodeStack.pop(); -if(!sourceNode.children) -sourceNode.children=[]; -var targetNode=new WebInspector.CPUProfileNode(sourceNode,sampleTime); -if(keepNatives||!isNativeNode(sourceNode)){ -parentNode.children.push(targetNode); -parentNode=targetNode; -}else{ -parentNode.self+=targetNode.self; -} -idMap.set(sourceNode.id,parentNode.id); -parentNodeStack.push.apply(parentNodeStack,sourceNode.children.map(()=>parentNode)); -sourceNodeStack.push.apply(sourceNodeStack,sourceNode.children.map(id=>nodeByIdMap.get(id))); -} -if(this.samples) -this.samples=this.samples.map(id=>idMap.get(id)); -return resultRoot; -}, - -_sortSamples:function() -{ -var timestamps=this.timestamps; -if(!timestamps) -return; -var samples=this.samples; -var indices=timestamps.map((x,index)=>index); -indices.sort((a,b)=>timestamps[a]-timestamps[b]); -for(var i=0;i<timestamps.length;++i){ -var index=indices[i]; -if(index===i) -continue; - -var savedTimestamp=timestamps[i]; -var savedSample=samples[i]; -var currentIndex=i; -while(index!==i){ -samples[currentIndex]=samples[index]; -timestamps[currentIndex]=timestamps[index]; -currentIndex=index; -index=indices[index]; -indices[currentIndex]=currentIndex; -} -samples[currentIndex]=savedSample; -timestamps[currentIndex]=savedTimestamp; -} -}, - -_normalizeTimestamps:function() -{ -var timestamps=this.timestamps; -if(!timestamps){ - - -var profileStartTime=this.profileStartTime; -var interval=(this.profileEndTime-profileStartTime)/this.samples.length; -timestamps=new Float64Array(this.samples.length+1); -for(var i=0;i<timestamps.length;++i) -timestamps[i]=profileStartTime+i*interval; -this.timestamps=timestamps; -return; -} - - -for(var i=0;i<timestamps.length;++i) -timestamps[i]/=1000; -var averageSample=(timestamps.peekLast()-timestamps[0])/(timestamps.length-1); - -this.timestamps.push(timestamps.peekLast()+averageSample); -this.profileStartTime=timestamps[0]; -this.profileEndTime=timestamps.peekLast(); -}, - -_buildIdToNodeMap:function() -{ - -this._idToNode=new Map(); -var idToNode=this._idToNode; -var stack=[this.profileHead]; -while(stack.length){ -var node=stack.pop(); -idToNode.set(node.id,node); -stack.push.apply(stack,node.children); -} -}, - -_extractMetaNodes:function() -{ -var topLevelNodes=this.profileHead.children; -for(var i=0;i<topLevelNodes.length&&!(this.gcNode&&this.programNode&&this.idleNode);i++){ -var node=topLevelNodes[i]; -if(node.functionName==="(garbage collector)") -this.gcNode=node;else -if(node.functionName==="(program)") -this.programNode=node;else -if(node.functionName==="(idle)") -this.idleNode=node; -} -}, - - - - - - - -forEachFrame:function(openFrameCallback,closeFrameCallback,startTime,stopTime) -{ -if(!this.profileHead||!this.samples) -return; - -startTime=startTime||0; -stopTime=stopTime||Infinity; -var samples=this.samples; -var timestamps=this.timestamps; -var idToNode=this._idToNode; -var gcNode=this.gcNode; -var samplesCount=samples.length; -var startIndex=timestamps.lowerBound(startTime); -var stackTop=0; -var stackNodes=[]; -var prevId=this.profileHead.id; -var sampleTime=timestamps[samplesCount]; -var gcParentNode=null; - -if(!this._stackStartTimes) -this._stackStartTimes=new Float64Array(this.maxDepth+2); -var stackStartTimes=this._stackStartTimes; -if(!this._stackChildrenDuration) -this._stackChildrenDuration=new Float64Array(this.maxDepth+2); -var stackChildrenDuration=this._stackChildrenDuration; - -for(var sampleIndex=startIndex;sampleIndex<samplesCount;sampleIndex++){ -sampleTime=timestamps[sampleIndex]; -if(sampleTime>=stopTime) -break; -var id=samples[sampleIndex]; -if(id===prevId) -continue; -var node=idToNode.get(id); -var prevNode=idToNode.get(prevId); - -if(node===gcNode){ - -gcParentNode=prevNode; -openFrameCallback(gcParentNode.depth+1,gcNode,sampleTime); -stackStartTimes[++stackTop]=sampleTime; -stackChildrenDuration[stackTop]=0; -prevId=id; -continue; -} -if(prevNode===gcNode){ - -var start=stackStartTimes[stackTop]; -var duration=sampleTime-start; -stackChildrenDuration[stackTop-1]+=duration; -closeFrameCallback(gcParentNode.depth+1,gcNode,start,duration,duration-stackChildrenDuration[stackTop]); ---stackTop; -prevNode=gcParentNode; -prevId=prevNode.id; -gcParentNode=null; -} - -while(node.depth>prevNode.depth){ -stackNodes.push(node); -node=node.parent; -} - - -while(prevNode!==node){ -var start=stackStartTimes[stackTop]; -var duration=sampleTime-start; -stackChildrenDuration[stackTop-1]+=duration; -closeFrameCallback(prevNode.depth,prevNode,start,duration,duration-stackChildrenDuration[stackTop]); ---stackTop; -if(node.depth===prevNode.depth){ -stackNodes.push(node); -node=node.parent; -} -prevNode=prevNode.parent; -} - - -while(stackNodes.length){ -node=stackNodes.pop(); -openFrameCallback(node.depth,node,sampleTime); -stackStartTimes[++stackTop]=sampleTime; -stackChildrenDuration[stackTop]=0; -} - -prevId=id; -} - -if(idToNode.get(prevId)===gcNode){ -var start=stackStartTimes[stackTop]; -var duration=sampleTime-start; -stackChildrenDuration[stackTop-1]+=duration; -closeFrameCallback(gcParentNode.depth+1,node,start,duration,duration-stackChildrenDuration[stackTop]); ---stackTop; -} - -for(var node=idToNode.get(prevId);node.parent;node=node.parent){ -var start=stackStartTimes[stackTop]; -var duration=sampleTime-start; -stackChildrenDuration[stackTop-1]+=duration; -closeFrameCallback(node.depth,node,start,duration,duration-stackChildrenDuration[stackTop]); ---stackTop; -} -}, - - - - - -nodeByIndex:function(index) -{ -return this._idToNode.get(this.samples[index])||null; -}, - -__proto__:WebInspector.ProfileTreeModel.prototype}; - - -},{}],111:[function(require,module,exports){ +},{}],107:[function(require,module,exports){ @@ -49722,7 +48965,7 @@ Overloaded:"Overloaded"}; -},{}],112:[function(require,module,exports){ +},{}],108:[function(require,module,exports){ @@ -49976,7 +49219,7 @@ }}; -},{}],113:[function(require,module,exports){ +},{}],109:[function(require,module,exports){ @@ -50947,7 +50190,7 @@ "zoom":200}; -},{}],114:[function(require,module,exports){ +},{}],110:[function(require,module,exports){ @@ -51249,7 +50492,7 @@ }}; -},{}],115:[function(require,module,exports){ +},{}],111:[function(require,module,exports){ @@ -51575,7 +50818,7 @@ __proto__:WebInspector.CSSRule.prototype}; -},{}],116:[function(require,module,exports){ +},{}],112:[function(require,module,exports){ @@ -51890,2337 +51133,7 @@ }}; -},{}],117:[function(require,module,exports){ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -WebInspector.NetworkManager=function(target) -{ -WebInspector.SDKModel.call(this,WebInspector.NetworkManager,target); -this._dispatcher=new WebInspector.NetworkDispatcher(this); -this._target=target; -this._networkAgent=target.networkAgent(); -target.registerNetworkDispatcher(this._dispatcher); -if(WebInspector.moduleSetting("cacheDisabled").get()) -this._networkAgent.setCacheDisabled(true); -if(WebInspector.moduleSetting("monitoringXHREnabled").get()) -this._networkAgent.setMonitoringXHREnabled(true); - - -if(Runtime.queryParam("remoteFrontend")||Runtime.queryParam("ws")) -this._networkAgent.enable(10000000,5000000);else - -this._networkAgent.enable(); - -this._bypassServiceWorkerSetting=WebInspector.settings.createSetting("bypassServiceWorker",false); -if(this._bypassServiceWorkerSetting.get()) -this._bypassServiceWorkerChanged(); -this._bypassServiceWorkerSetting.addChangeListener(this._bypassServiceWorkerChanged,this); - -WebInspector.moduleSetting("cacheDisabled").addChangeListener(this._cacheDisabledSettingChanged,this); -}; - - -WebInspector.NetworkManager.Events={ -RequestStarted:Symbol("RequestStarted"), -RequestUpdated:Symbol("RequestUpdated"), -RequestFinished:Symbol("RequestFinished"), -RequestUpdateDropped:Symbol("RequestUpdateDropped"), -ResponseReceived:Symbol("ResponseReceived")}; - - -WebInspector.NetworkManager._MIMETypes={ -"text/html":{"document":true}, -"text/xml":{"document":true}, -"text/plain":{"document":true}, -"application/xhtml+xml":{"document":true}, -"image/svg+xml":{"document":true}, -"text/css":{"stylesheet":true}, -"text/xsl":{"stylesheet":true}, -"text/vtt":{"texttrack":true}}; - - - - - - -WebInspector.NetworkManager.fromTarget=function(target) -{ -return target.model(WebInspector.NetworkManager); -}; - - -WebInspector.NetworkManager.Conditions; - -WebInspector.NetworkManager.NoThrottlingConditions={title:WebInspector.UIString("No throttling"),download:-1,upload:-1,latency:0}; - -WebInspector.NetworkManager.OfflineConditions={title:WebInspector.UIString("Offline"),download:0,upload:0,latency:0}; - - - - - - -WebInspector.NetworkManager._connectionType=function(conditions) -{ -if(!conditions.download&&!conditions.upload) -return NetworkAgent.ConnectionType.None; -var types=WebInspector.NetworkManager._connectionTypes; -if(!types){ -WebInspector.NetworkManager._connectionTypes=[]; -types=WebInspector.NetworkManager._connectionTypes; -types.push(["2g",NetworkAgent.ConnectionType.Cellular2g]); -types.push(["3g",NetworkAgent.ConnectionType.Cellular3g]); -types.push(["4g",NetworkAgent.ConnectionType.Cellular4g]); -types.push(["bluetooth",NetworkAgent.ConnectionType.Bluetooth]); -types.push(["wifi",NetworkAgent.ConnectionType.Wifi]); -types.push(["wimax",NetworkAgent.ConnectionType.Wimax]); -} -for(var type of types){ -if(conditions.title.toLowerCase().indexOf(type[0])!==-1) -return type[1]; -} -return NetworkAgent.ConnectionType.Other; -}; - -WebInspector.NetworkManager.prototype={ - - - - -inflightRequestForURL:function(url) -{ -return this._dispatcher._inflightRequestsByURL[url]; -}, - - - - -_cacheDisabledSettingChanged:function(event) -{ -var enabled=event.data; -this._networkAgent.setCacheDisabled(enabled); -}, - -dispose:function() -{ -WebInspector.moduleSetting("cacheDisabled").removeChangeListener(this._cacheDisabledSettingChanged,this); -}, - - - - -bypassServiceWorkerSetting:function() -{ -return this._bypassServiceWorkerSetting; -}, - -_bypassServiceWorkerChanged:function() -{ -this._networkAgent.setBypassServiceWorker(this._bypassServiceWorkerSetting.get()); -}, - -__proto__:WebInspector.SDKModel.prototype}; - - - - - - -WebInspector.NetworkDispatcher=function(manager) -{ -this._manager=manager; -this._inflightRequestsById={}; -this._inflightRequestsByURL={}; -}; - -WebInspector.NetworkDispatcher.prototype={ - - - - -_headersMapToHeadersArray:function(headersMap) -{ -var result=[]; -for(var name in headersMap){ -var values=headersMap[name].split("\n"); -for(var i=0;i<values.length;++i) -result.push({name:name,value:values[i]}); -} -return result; -}, - - - - - -_updateNetworkRequestWithRequest:function(networkRequest,request) -{ -networkRequest.requestMethod=request.method; -networkRequest.setRequestHeaders(this._headersMapToHeadersArray(request.headers)); -networkRequest.requestFormData=request.postData; -networkRequest.setInitialPriority(request.initialPriority); -networkRequest.mixedContentType=request.mixedContentType||NetworkAgent.RequestMixedContentType.None; -}, - - - - - -_updateNetworkRequestWithResponse:function(networkRequest,response) -{ -if(response.url&&networkRequest.url!==response.url) -networkRequest.url=response.url; -networkRequest.mimeType=response.mimeType; -networkRequest.statusCode=response.status; -networkRequest.statusText=response.statusText; -networkRequest.responseHeaders=this._headersMapToHeadersArray(response.headers); -if(response.encodedDataLength>=0) -networkRequest.setTransferSize(response.encodedDataLength); -if(response.headersText) -networkRequest.responseHeadersText=response.headersText; -if(response.requestHeaders){ -networkRequest.setRequestHeaders(this._headersMapToHeadersArray(response.requestHeaders)); -networkRequest.setRequestHeadersText(response.requestHeadersText||""); -} - -networkRequest.connectionReused=response.connectionReused; -networkRequest.connectionId=String(response.connectionId); -if(response.remoteIPAddress) -networkRequest.setRemoteAddress(response.remoteIPAddress,response.remotePort||-1); - -if(response.fromServiceWorker) -networkRequest.fetchedViaServiceWorker=true; - -if(response.fromDiskCache) -networkRequest.setFromDiskCache(); -networkRequest.timing=response.timing; - -networkRequest.protocol=response.protocol; - -networkRequest.setSecurityState(response.securityState); - -if(!this._mimeTypeIsConsistentWithType(networkRequest)){ -var consoleModel=this._manager._target.consoleModel; -consoleModel.addMessage(new WebInspector.ConsoleMessage(consoleModel.target(),WebInspector.ConsoleMessage.MessageSource.Network, -WebInspector.ConsoleMessage.MessageLevel.Log, -WebInspector.UIString("Resource interpreted as %s but transferred with MIME type %s: \"%s\".",networkRequest.resourceType().title(),networkRequest.mimeType,networkRequest.url), -WebInspector.ConsoleMessage.MessageType.Log, -"", -0, -0, -networkRequest.requestId)); -} - -if(response.securityDetails) -networkRequest.setSecurityDetails(response.securityDetails); -}, - - - - - -_mimeTypeIsConsistentWithType:function(networkRequest) -{ - - - - - - -if(networkRequest.hasErrorStatusCode()||networkRequest.statusCode===304||networkRequest.statusCode===204) -return true; - -var resourceType=networkRequest.resourceType(); -if(resourceType!==WebInspector.resourceTypes.Stylesheet&& -resourceType!==WebInspector.resourceTypes.Document&& -resourceType!==WebInspector.resourceTypes.TextTrack){ -return true; -} - -if(!networkRequest.mimeType) -return true; - -if(networkRequest.mimeType in WebInspector.NetworkManager._MIMETypes) -return resourceType.name()in WebInspector.NetworkManager._MIMETypes[networkRequest.mimeType]; - -return false; -}, - - - - - - - -resourceChangedPriority:function(requestId,newPriority,timestamp) -{ -var networkRequest=this._inflightRequestsById[requestId]; -if(networkRequest) -networkRequest.setPriority(newPriority); -}, - - - - - - - - - - - - - - -requestWillBeSent:function(requestId,frameId,loaderId,documentURL,request,time,wallTime,initiator,redirectResponse,resourceType) -{ -var networkRequest=this._inflightRequestsById[requestId]; -if(networkRequest){ - -if(!redirectResponse) -return; -this.responseReceived(requestId,frameId,loaderId,time,PageAgent.ResourceType.Other,redirectResponse); -networkRequest=this._appendRedirect(requestId,time,request.url); -}else -networkRequest=this._createNetworkRequest(requestId,frameId,loaderId,request.url,documentURL,initiator); -networkRequest.hasNetworkData=true; -this._updateNetworkRequestWithRequest(networkRequest,request); -networkRequest.setIssueTime(time,wallTime); -networkRequest.setResourceType(WebInspector.resourceTypes[resourceType]); - -this._startNetworkRequest(networkRequest); -}, - - - - - -requestServedFromCache:function(requestId) -{ -var networkRequest=this._inflightRequestsById[requestId]; -if(!networkRequest) -return; - -networkRequest.setFromMemoryCache(); -}, - - - - - - - - - - -responseReceived:function(requestId,frameId,loaderId,time,resourceType,response) -{ -var networkRequest=this._inflightRequestsById[requestId]; -if(!networkRequest){ - -var eventData={}; -eventData.url=response.url; -eventData.frameId=frameId; -eventData.loaderId=loaderId; -eventData.resourceType=resourceType; -eventData.mimeType=response.mimeType; -this._manager.dispatchEventToListeners(WebInspector.NetworkManager.Events.RequestUpdateDropped,eventData); -return; -} - -networkRequest.responseReceivedTime=time; -networkRequest.setResourceType(WebInspector.resourceTypes[resourceType]); - -this._updateNetworkRequestWithResponse(networkRequest,response); - -this._updateNetworkRequest(networkRequest); -this._manager.dispatchEventToListeners(WebInspector.NetworkManager.Events.ResponseReceived,networkRequest); -}, - - - - - - - - -dataReceived:function(requestId,time,dataLength,encodedDataLength) -{ -var networkRequest=this._inflightRequestsById[requestId]; -if(!networkRequest) -return; - -networkRequest.resourceSize+=dataLength; -if(encodedDataLength!==-1) -networkRequest.increaseTransferSize(encodedDataLength); -networkRequest.endTime=time; - -this._updateNetworkRequest(networkRequest); -}, - - - - - - - -loadingFinished:function(requestId,finishTime,encodedDataLength) -{ -var networkRequest=this._inflightRequestsById[requestId]; -if(!networkRequest) -return; -this._finishNetworkRequest(networkRequest,finishTime,encodedDataLength); -}, - - - - - - - - - - -loadingFailed:function(requestId,time,resourceType,localizedDescription,canceled,blockedReason) -{ -var networkRequest=this._inflightRequestsById[requestId]; -if(!networkRequest) -return; - -networkRequest.failed=true; -networkRequest.setResourceType(WebInspector.resourceTypes[resourceType]); -networkRequest.canceled=canceled; -if(blockedReason){ -networkRequest.setBlockedReason(blockedReason); -if(blockedReason===NetworkAgent.BlockedReason.Inspector){ -var consoleModel=this._manager._target.consoleModel; -consoleModel.addMessage(new WebInspector.ConsoleMessage(consoleModel.target(),WebInspector.ConsoleMessage.MessageSource.Network, -WebInspector.ConsoleMessage.MessageLevel.Warning, -WebInspector.UIString("Request was blocked by DevTools: \"%s\".",networkRequest.url), -WebInspector.ConsoleMessage.MessageType.Log, -"", -0, -0, -networkRequest.requestId)); -} -} -networkRequest.localizedFailDescription=localizedDescription; -this._finishNetworkRequest(networkRequest,time,-1); -}, - - - - - - - -webSocketCreated:function(requestId,requestURL,initiator) -{ -var networkRequest=new WebInspector.NetworkRequest(this._manager._target,requestId,requestURL,"","","",initiator||null); -networkRequest.setResourceType(WebInspector.resourceTypes.WebSocket); -this._startNetworkRequest(networkRequest); -}, - - - - - - - - -webSocketWillSendHandshakeRequest:function(requestId,time,wallTime,request) -{ -var networkRequest=this._inflightRequestsById[requestId]; -if(!networkRequest) -return; - -networkRequest.requestMethod="GET"; -networkRequest.setRequestHeaders(this._headersMapToHeadersArray(request.headers)); -networkRequest.setIssueTime(time,wallTime); - -this._updateNetworkRequest(networkRequest); -}, - - - - - - - -webSocketHandshakeResponseReceived:function(requestId,time,response) -{ -var networkRequest=this._inflightRequestsById[requestId]; -if(!networkRequest) -return; - -networkRequest.statusCode=response.status; -networkRequest.statusText=response.statusText; -networkRequest.responseHeaders=this._headersMapToHeadersArray(response.headers); -networkRequest.responseHeadersText=response.headersText; -if(response.requestHeaders) -networkRequest.setRequestHeaders(this._headersMapToHeadersArray(response.requestHeaders)); -if(response.requestHeadersText) -networkRequest.setRequestHeadersText(response.requestHeadersText); -networkRequest.responseReceivedTime=time; -networkRequest.protocol="websocket"; - -this._updateNetworkRequest(networkRequest); -}, - - - - - - - -webSocketFrameReceived:function(requestId,time,response) -{ -var networkRequest=this._inflightRequestsById[requestId]; -if(!networkRequest) -return; - -networkRequest.addFrame(response,time); -networkRequest.responseReceivedTime=time; - -this._updateNetworkRequest(networkRequest); -}, - - - - - - - -webSocketFrameSent:function(requestId,time,response) -{ -var networkRequest=this._inflightRequestsById[requestId]; -if(!networkRequest) -return; - -networkRequest.addFrame(response,time,true); -networkRequest.responseReceivedTime=time; - -this._updateNetworkRequest(networkRequest); -}, - - - - - - - -webSocketFrameError:function(requestId,time,errorMessage) -{ -var networkRequest=this._inflightRequestsById[requestId]; -if(!networkRequest) -return; - -networkRequest.addFrameError(errorMessage,time); -networkRequest.responseReceivedTime=time; - -this._updateNetworkRequest(networkRequest); -}, - - - - - - -webSocketClosed:function(requestId,time) -{ -var networkRequest=this._inflightRequestsById[requestId]; -if(!networkRequest) -return; -this._finishNetworkRequest(networkRequest,time,-1); -}, - - - - - - - - - -eventSourceMessageReceived:function(requestId,time,eventName,eventId,data) -{ -var networkRequest=this._inflightRequestsById[requestId]; -if(!networkRequest) -return; -networkRequest.addEventSourceMessage(time,eventName,eventId,data); -}, - - - - - - - -_appendRedirect:function(requestId,time,redirectURL) -{ -var originalNetworkRequest=this._inflightRequestsById[requestId]; -var previousRedirects=originalNetworkRequest.redirects||[]; -originalNetworkRequest.requestId=requestId+":redirected."+previousRedirects.length; -delete originalNetworkRequest.redirects; -if(previousRedirects.length>0) -originalNetworkRequest.redirectSource=previousRedirects[previousRedirects.length-1]; -this._finishNetworkRequest(originalNetworkRequest,time,-1); -var newNetworkRequest=this._createNetworkRequest(requestId,originalNetworkRequest.frameId,originalNetworkRequest.loaderId, -redirectURL,originalNetworkRequest.documentURL,originalNetworkRequest.initiator()); -newNetworkRequest.redirects=previousRedirects.concat(originalNetworkRequest); -return newNetworkRequest; -}, - - - - -_startNetworkRequest:function(networkRequest) -{ -this._inflightRequestsById[networkRequest.requestId]=networkRequest; -this._inflightRequestsByURL[networkRequest.url]=networkRequest; -this._dispatchEventToListeners(WebInspector.NetworkManager.Events.RequestStarted,networkRequest); -}, - - - - -_updateNetworkRequest:function(networkRequest) -{ -this._dispatchEventToListeners(WebInspector.NetworkManager.Events.RequestUpdated,networkRequest); -}, - - - - - - -_finishNetworkRequest:function(networkRequest,finishTime,encodedDataLength) -{ -networkRequest.endTime=finishTime; -networkRequest.finished=true; -if(encodedDataLength>=0) -networkRequest.setTransferSize(encodedDataLength); -this._dispatchEventToListeners(WebInspector.NetworkManager.Events.RequestFinished,networkRequest); -delete this._inflightRequestsById[networkRequest.requestId]; -delete this._inflightRequestsByURL[networkRequest.url]; -}, - - - - - -_dispatchEventToListeners:function(eventType,networkRequest) -{ -this._manager.dispatchEventToListeners(eventType,networkRequest); -}, - - - - - - - - - -_createNetworkRequest:function(requestId,frameId,loaderId,url,documentURL,initiator) -{ -return new WebInspector.NetworkRequest(this._manager._target,requestId,url,documentURL,frameId,loaderId,initiator); -}}; - - - - - - - - -WebInspector.MultitargetNetworkManager=function() -{ -WebInspector.Object.call(this); -WebInspector.targetManager.observeTargets(this); - - -this._blockedURLs=new Set(); -this._blockedSetting=WebInspector.moduleSetting("blockedURLs"); -this._blockedSetting.addChangeListener(this._updateBlockedURLs,this); -this._blockedSetting.set([]); -this._updateBlockedURLs(); - -this._userAgentOverride=""; - -this._agents=new Set(); - -this._networkConditions=WebInspector.NetworkManager.NoThrottlingConditions; -}; - - -WebInspector.MultitargetNetworkManager.Events={ -ConditionsChanged:Symbol("ConditionsChanged"), -UserAgentChanged:Symbol("UserAgentChanged")}; - - - - - - -WebInspector.MultitargetNetworkManager.patchUserAgentWithChromeVersion=function(uaString) -{ - -var chromeRegex=new RegExp("(?:^|\\W)Chrome/(\\S+)"); -var chromeMatch=navigator.userAgent.match(chromeRegex); -if(chromeMatch&&chromeMatch.length>1) -return String.sprintf(uaString,chromeMatch[1]); -return uaString; -}; - -WebInspector.MultitargetNetworkManager.prototype={ - - - - -targetAdded:function(target) -{ -var networkAgent=target.networkAgent(); -if(this._extraHeaders) -networkAgent.setExtraHTTPHeaders(this._extraHeaders); -if(this._currentUserAgent()) -networkAgent.setUserAgentOverride(this._currentUserAgent()); -for(var url of this._blockedURLs) -networkAgent.addBlockedURL(url); -this._agents.add(networkAgent); -if(this.isThrottling()) -this._updateNetworkConditions(networkAgent); -}, - - - - - -targetRemoved:function(target) -{ -this._agents.delete(target.networkAgent()); -}, - - - - -isThrottling:function() -{ -return this._networkConditions.download>=0||this._networkConditions.upload>=0||this._networkConditions.latency>0; -}, - - - - -isOffline:function() -{ -return!this._networkConditions.download&&!this._networkConditions.upload; -}, - - - - -setNetworkConditions:function(conditions) -{ -this._networkConditions=conditions; -for(var agent of this._agents) -this._updateNetworkConditions(agent); -this.dispatchEventToListeners(WebInspector.MultitargetNetworkManager.Events.ConditionsChanged); -}, - - - - -networkConditions:function() -{ -return this._networkConditions; -}, - - - - -_updateNetworkConditions:function(networkAgent) -{ -var conditions=this._networkConditions; -if(!this.isThrottling()){ -networkAgent.emulateNetworkConditions(false,0,0,0); -}else{ -networkAgent.emulateNetworkConditions(this.isOffline(),conditions.latency,conditions.download<0?0:conditions.download,conditions.upload<0?0:conditions.upload,WebInspector.NetworkManager._connectionType(conditions)); -} -}, - - - - -setExtraHTTPHeaders:function(headers) -{ -this._extraHeaders=headers; -for(var target of WebInspector.targetManager.targets()) -target.networkAgent().setExtraHTTPHeaders(this._extraHeaders); -}, - - - - -_currentUserAgent:function() -{ -return this._customUserAgent?this._customUserAgent:this._userAgentOverride; -}, - -_updateUserAgentOverride:function() -{ -var userAgent=this._currentUserAgent(); -WebInspector.ResourceLoader.targetUserAgent=userAgent; -for(var target of WebInspector.targetManager.targets()) -target.networkAgent().setUserAgentOverride(userAgent); -}, - - - - -setUserAgentOverride:function(userAgent) -{ -if(this._userAgentOverride===userAgent) -return; -this._userAgentOverride=userAgent; -if(!this._customUserAgent) -this._updateUserAgentOverride(); -this.dispatchEventToListeners(WebInspector.MultitargetNetworkManager.Events.UserAgentChanged); -}, - - - - -userAgentOverride:function() -{ -return this._userAgentOverride; -}, - - - - -setCustomUserAgentOverride:function(userAgent) -{ -this._customUserAgent=userAgent; -this._updateUserAgentOverride(); -}, - -_updateBlockedURLs:function() -{ -var blocked=this._blockedSetting.get(); -for(var url of blocked){ -if(!this._blockedURLs.has(url)) -this._addBlockedURL(url); -} -for(var url of this._blockedURLs){ -if(blocked.indexOf(url)===-1) -this._removeBlockedURL(url); -} -}, - - - - -_addBlockedURL:function(url) -{ -this._blockedURLs.add(url); -for(var target of WebInspector.targetManager.targets()) -target.networkAgent().addBlockedURL(url); -}, - - - - -_removeBlockedURL:function(url) -{ -this._blockedURLs.delete(url); -for(var target of WebInspector.targetManager.targets()) -target.networkAgent().removeBlockedURL(url); -}, - -clearBrowserCache:function() -{ -for(var target of WebInspector.targetManager.targets()) -target.networkAgent().clearBrowserCache(); -}, - -clearBrowserCookies:function() -{ -for(var target of WebInspector.targetManager.targets()) -target.networkAgent().clearBrowserCookies(); -}, - - - - - -getCertificate:function(origin,callback) -{ -var target=WebInspector.targetManager.mainTarget(); -target.networkAgent().getCertificate(origin,mycallback); - - - - - -function mycallback(error,certificate) -{ -callback(error?[]:certificate); -} -}, - - - - - -loadResource:function(url,callback) -{ -var headers={}; - -var currentUserAgent=this._currentUserAgent(); -if(currentUserAgent) -headers["User-Agent"]=currentUserAgent; - -if(WebInspector.moduleSetting("cacheDisabled").get()) -headers["Cache-Control"]="no-cache"; - -WebInspector.ResourceLoader.load(url,headers,callback); -}, - -__proto__:WebInspector.Object.prototype}; - - - - - -WebInspector.multitargetNetworkManager; - -},{}],118:[function(require,module,exports){ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -WebInspector.NetworkRequest=function(target,requestId,url,documentURL,frameId,loaderId,initiator) -{ -WebInspector.SDKObject.call(this,target); - -this._networkLog=WebInspector.NetworkLog.fromTarget(target); -this._networkManager=WebInspector.NetworkManager.fromTarget(target); -this._requestId=requestId; -this.url=url; -this._documentURL=documentURL; -this._frameId=frameId; -this._loaderId=loaderId; - -this._initiator=initiator; -this._issueTime=-1; -this._startTime=-1; -this._endTime=-1; - -this._blockedReason=undefined; - -this.statusCode=0; -this.statusText=""; -this.requestMethod=""; -this.requestTime=0; -this.protocol=""; - -this.mixedContentType=NetworkAgent.RequestMixedContentType.None; - - -this._initialPriority=null; - -this._currentPriority=null; - - -this._resourceType=WebInspector.resourceTypes.Other; -this._contentEncoded=false; -this._pendingContentCallbacks=[]; - -this._frames=[]; - -this._eventSourceMessages=[]; - -this._responseHeaderValues={}; - -this._remoteAddress=""; - - -this._securityState=SecurityAgent.SecurityState.Unknown; - -this._securityDetails=null; - - -this.connectionId="0"; -}; - - -WebInspector.NetworkRequest.Events={ -FinishedLoading:Symbol("FinishedLoading"), -TimingChanged:Symbol("TimingChanged"), -RemoteAddressChanged:Symbol("RemoteAddressChanged"), -RequestHeadersChanged:Symbol("RequestHeadersChanged"), -ResponseHeadersChanged:Symbol("ResponseHeadersChanged"), -WebsocketFrameAdded:Symbol("WebsocketFrameAdded"), -EventSourceMessageAdded:Symbol("EventSourceMessageAdded")}; - - - -WebInspector.NetworkRequest.InitiatorType={ -Other:"other", -Parser:"parser", -Redirect:"redirect", -Script:"script"}; - - - -WebInspector.NetworkRequest.NameValue; - - -WebInspector.NetworkRequest.WebSocketFrameType={ -Send:"send", -Receive:"receive", -Error:"error"}; - - - -WebInspector.NetworkRequest.WebSocketFrame; - - -WebInspector.NetworkRequest.EventSourceMessage; - -WebInspector.NetworkRequest.prototype={ - - - - -indentityCompare:function(other) -{ -if(this._requestId>other._requestId) -return 1; -if(this._requestId<other._requestId) -return-1; -return 0; -}, - - - - -get requestId() -{ -return this._requestId; -}, - -set requestId(requestId) -{ -this._requestId=requestId; -}, - - - - -get url() -{ -return this._url; -}, - -set url(x) -{ -if(this._url===x) -return; - -this._url=x; -this._parsedURL=new WebInspector.ParsedURL(x); -delete this._queryString; -delete this._parsedQueryParameters; -delete this._name; -delete this._path; -}, - - - - -get documentURL() -{ -return this._documentURL; -}, - -get parsedURL() -{ -return this._parsedURL; -}, - - - - -get frameId() -{ -return this._frameId; -}, - - - - -get loaderId() -{ -return this._loaderId; -}, - - - - - -setRemoteAddress:function(ip,port) -{ -this._remoteAddress=ip+":"+port; -this.dispatchEventToListeners(WebInspector.NetworkRequest.Events.RemoteAddressChanged,this); -}, - - - - -remoteAddress:function() -{ -return this._remoteAddress; -}, - - - - -securityState:function() -{ -return this._securityState; -}, - - - - -setSecurityState:function(securityState) -{ -this._securityState=securityState; -}, - - - - -securityDetails:function() -{ -return this._securityDetails; -}, - - - - -setSecurityDetails:function(securityDetails) -{ -this._securityDetails=securityDetails; -}, - - - - -get startTime() -{ -return this._startTime||-1; -}, - - - - - -setIssueTime:function(monotonicTime,wallTime) -{ -this._issueTime=monotonicTime; -this._wallIssueTime=wallTime; -this._startTime=monotonicTime; -}, - - - - -issueTime:function() -{ -return this._issueTime; -}, - - - - - -pseudoWallTime:function(monotonicTime) -{ -return this._wallIssueTime?this._wallIssueTime-this._issueTime+monotonicTime:monotonicTime; -}, - - - - -get responseReceivedTime() -{ -return this._responseReceivedTime||-1; -}, - -set responseReceivedTime(x) -{ -this._responseReceivedTime=x; -}, - - - - -get endTime() -{ -return this._endTime||-1; -}, - -set endTime(x) -{ -if(this.timing&&this.timing.requestTime){ - -this._endTime=Math.max(x,this.responseReceivedTime); -}else{ - -this._endTime=x; -if(this._responseReceivedTime>x) -this._responseReceivedTime=x; -} -this.dispatchEventToListeners(WebInspector.NetworkRequest.Events.TimingChanged,this); -}, - - - - -get duration() -{ -if(this._endTime===-1||this._startTime===-1) -return-1; -return this._endTime-this._startTime; -}, - - - - -get latency() -{ -if(this._responseReceivedTime===-1||this._startTime===-1) -return-1; -return this._responseReceivedTime-this._startTime; -}, - - - - -get resourceSize() -{ -return this._resourceSize||0; -}, - -set resourceSize(x) -{ -this._resourceSize=x; -}, - - - - -get transferSize() -{ -return this._transferSize||0; -}, - - - - -increaseTransferSize:function(x) -{ -this._transferSize=(this._transferSize||0)+x; -}, - - - - -setTransferSize:function(x) -{ -this._transferSize=x; -}, - - - - -get finished() -{ -return this._finished; -}, - -set finished(x) -{ -if(this._finished===x) -return; - -this._finished=x; - -if(x){ -this.dispatchEventToListeners(WebInspector.NetworkRequest.Events.FinishedLoading,this); -if(this._pendingContentCallbacks.length) -this._innerRequestContent(); -} -}, - - - - -get failed() -{ -return this._failed; -}, - -set failed(x) -{ -this._failed=x; -}, - - - - -get canceled() -{ -return this._canceled; -}, - -set canceled(x) -{ -this._canceled=x; -}, - - - - -blockedReason:function() -{ -return this._blockedReason; -}, - - - - -setBlockedReason:function(reason) -{ -this._blockedReason=reason; -}, - - - - -wasBlocked:function() -{ -return!!this._blockedReason; -}, - - - - -cached:function() -{ -return(!!this._fromMemoryCache||!!this._fromDiskCache)&&!this._transferSize; -}, - - - - -cachedInMemory:function() -{ -return!!this._fromMemoryCache&&!this._transferSize; -}, - -setFromMemoryCache:function() -{ -this._fromMemoryCache=true; -delete this._timing; -}, - -setFromDiskCache:function() -{ -this._fromDiskCache=true; -}, - - - - -get fetchedViaServiceWorker() -{ -return this._fetchedViaServiceWorker; -}, - -set fetchedViaServiceWorker(x) -{ -this._fetchedViaServiceWorker=x; -}, - - - - -get timing() -{ -return this._timing; -}, - -set timing(x) -{ -if(x&&!this._fromMemoryCache){ - - -this._startTime=x.requestTime; -this._responseReceivedTime=x.requestTime+x.receiveHeadersEnd/1000.0; - -this._timing=x; -this.dispatchEventToListeners(WebInspector.NetworkRequest.Events.TimingChanged,this); -} -}, - - - - -get mimeType() -{ -return this._mimeType; -}, - -set mimeType(x) -{ -this._mimeType=x; -}, - - - - -get displayName() -{ -return this._parsedURL.displayName; -}, - - - - -name:function() -{ -if(this._name) -return this._name; -this._parseNameAndPathFromURL(); -return this._name; -}, - - - - -path:function() -{ -if(this._path) -return this._path; -this._parseNameAndPathFromURL(); -return this._path; -}, - -_parseNameAndPathFromURL:function() -{ -if(this._parsedURL.isDataURL()){ -this._name=this._parsedURL.dataURLDisplayName(); -this._path=""; -}else if(this._parsedURL.isAboutBlank()){ -this._name=this._parsedURL.url; -this._path=""; -}else{ -this._path=this._parsedURL.host+this._parsedURL.folderPathComponents; - -var inspectedURL=this.target().inspectedURL().asParsedURL(); -this._path=this._path.trimURL(inspectedURL?inspectedURL.host:""); -if(this._parsedURL.lastPathComponent||this._parsedURL.queryParams) -this._name=this._parsedURL.lastPathComponent+(this._parsedURL.queryParams?"?"+this._parsedURL.queryParams:"");else -if(this._parsedURL.folderPathComponents){ -this._name=this._parsedURL.folderPathComponents.substring(this._parsedURL.folderPathComponents.lastIndexOf("/")+1)+"/"; -this._path=this._path.substring(0,this._path.lastIndexOf("/")); -}else{ -this._name=this._parsedURL.host; -this._path=""; -} -} -}, - - - - -get folder() -{ -var path=this._parsedURL.path; -var indexOfQuery=path.indexOf("?"); -if(indexOfQuery!==-1) -path=path.substring(0,indexOfQuery); -var lastSlashIndex=path.lastIndexOf("/"); -return lastSlashIndex!==-1?path.substring(0,lastSlashIndex):""; -}, - - - - -resourceType:function() -{ -return this._resourceType; -}, - - - - -setResourceType:function(resourceType) -{ -this._resourceType=resourceType; -}, - - - - -get domain() -{ -return this._parsedURL.host; -}, - - - - -get scheme() -{ -return this._parsedURL.scheme; -}, - - - - -get redirectSource() -{ -if(this.redirects&&this.redirects.length>0) -return this.redirects[this.redirects.length-1]; -return this._redirectSource; -}, - -set redirectSource(x) -{ -this._redirectSource=x; -delete this._initiatorInfo; -}, - - - - -requestHeaders:function() -{ -return this._requestHeaders||[]; -}, - - - - -setRequestHeaders:function(headers) -{ -this._requestHeaders=headers; -delete this._requestCookies; - -this.dispatchEventToListeners(WebInspector.NetworkRequest.Events.RequestHeadersChanged); -}, - - - - -requestHeadersText:function() -{ -return this._requestHeadersText; -}, - - - - -setRequestHeadersText:function(text) -{ -this._requestHeadersText=text; - -this.dispatchEventToListeners(WebInspector.NetworkRequest.Events.RequestHeadersChanged); -}, - - - - - -requestHeaderValue:function(headerName) -{ -return this._headerValue(this.requestHeaders(),headerName); -}, - - - - -get requestCookies() -{ -if(!this._requestCookies) -this._requestCookies=WebInspector.CookieParser.parseCookie(this.target(),this.requestHeaderValue("Cookie")); -return this._requestCookies; -}, - - - - -get requestFormData() -{ -return this._requestFormData; -}, - -set requestFormData(x) -{ -this._requestFormData=x; -delete this._parsedFormParameters; -}, - - - - -requestHttpVersion:function() -{ -var headersText=this.requestHeadersText(); -if(!headersText) -return this.requestHeaderValue("version")||this.requestHeaderValue(":version")||"unknown"; -var firstLine=headersText.split(/\r\n/)[0]; -var match=firstLine.match(/(HTTP\/\d+\.\d+)$/); -return match?match[1]:"HTTP/0.9"; -}, - - - - -get responseHeaders() -{ -return this._responseHeaders||[]; -}, - -set responseHeaders(x) -{ -this._responseHeaders=x; -delete this._sortedResponseHeaders; -delete this._serverTimings; -delete this._responseCookies; -this._responseHeaderValues={}; - -this.dispatchEventToListeners(WebInspector.NetworkRequest.Events.ResponseHeadersChanged); -}, - - - - -get responseHeadersText() -{ -return this._responseHeadersText; -}, - -set responseHeadersText(x) -{ -this._responseHeadersText=x; - -this.dispatchEventToListeners(WebInspector.NetworkRequest.Events.ResponseHeadersChanged); -}, - - - - -get sortedResponseHeaders() -{ -if(this._sortedResponseHeaders!==undefined) -return this._sortedResponseHeaders; - -this._sortedResponseHeaders=this.responseHeaders.slice(); -this._sortedResponseHeaders.sort(function(a,b){return a.name.toLowerCase().compareTo(b.name.toLowerCase());}); -return this._sortedResponseHeaders; -}, - - - - - -responseHeaderValue:function(headerName) -{ -var value=this._responseHeaderValues[headerName]; -if(value===undefined){ -value=this._headerValue(this.responseHeaders,headerName); -this._responseHeaderValues[headerName]=value!==undefined?value:null; -} -return value!==null?value:undefined; -}, - - - - -get responseCookies() -{ -if(!this._responseCookies) -this._responseCookies=WebInspector.CookieParser.parseSetCookie(this.target(),this.responseHeaderValue("Set-Cookie")); -return this._responseCookies; -}, - - - - -get serverTimings() -{ -if(typeof this._serverTimings==="undefined") -this._serverTimings=WebInspector.ServerTiming.parseHeaders(this.responseHeaders); -return this._serverTimings; -}, - - - - -queryString:function() -{ -if(this._queryString!==undefined) -return this._queryString; - -var queryString=null; -var url=this.url; -var questionMarkPosition=url.indexOf("?"); -if(questionMarkPosition!==-1){ -queryString=url.substring(questionMarkPosition+1); -var hashSignPosition=queryString.indexOf("#"); -if(hashSignPosition!==-1) -queryString=queryString.substring(0,hashSignPosition); -} -this._queryString=queryString; -return this._queryString; -}, - - - - -get queryParameters() -{ -if(this._parsedQueryParameters) -return this._parsedQueryParameters; -var queryString=this.queryString(); -if(!queryString) -return null; -this._parsedQueryParameters=this._parseParameters(queryString); -return this._parsedQueryParameters; -}, - - - - -get formParameters() -{ -if(this._parsedFormParameters) -return this._parsedFormParameters; -if(!this.requestFormData) -return null; -var requestContentType=this.requestContentType(); -if(!requestContentType||!requestContentType.match(/^application\/x-www-form-urlencoded\s*(;.*)?$/i)) -return null; -this._parsedFormParameters=this._parseParameters(this.requestFormData); -return this._parsedFormParameters; -}, - - - - -responseHttpVersion:function() -{ -var headersText=this._responseHeadersText; -if(!headersText) -return this.responseHeaderValue("version")||this.responseHeaderValue(":version")||"unknown"; -var firstLine=headersText.split(/\r\n/)[0]; -var match=firstLine.match(/^(HTTP\/\d+\.\d+)/); -return match?match[1]:"HTTP/0.9"; -}, - - - - - -_parseParameters:function(queryString) -{ -function parseNameValue(pair) -{ -var position=pair.indexOf("="); -if(position===-1) -return{name:pair,value:""};else - -return{name:pair.substring(0,position),value:pair.substring(position+1)}; -} -return queryString.split("&").map(parseNameValue); -}, - - - - - - -_headerValue:function(headers,headerName) -{ -headerName=headerName.toLowerCase(); - -var values=[]; -for(var i=0;i<headers.length;++i){ -if(headers[i].name.toLowerCase()===headerName) -values.push(headers[i].value); -} -if(!values.length) -return undefined; - -if(headerName==="set-cookie") -return values.join("\n"); -return values.join(", "); -}, - - - - -get content() -{ -return this._content; -}, - - - - -contentError:function() -{ -return this._contentError; -}, - - - - -get contentEncoded() -{ -return this._contentEncoded; -}, - - - - - -contentURL:function() -{ -return this._url; -}, - - - - - -contentType:function() -{ -return this._resourceType; -}, - - - - - -requestContent:function() -{ - - - -if(this._resourceType===WebInspector.resourceTypes.WebSocket) -return Promise.resolve(null); -if(typeof this._content!=="undefined") -return Promise.resolve(this.content||null); -var callback; -var promise=new Promise(fulfill=>callback=fulfill); -this._pendingContentCallbacks.push(callback); -if(this.finished) -this._innerRequestContent(); -return promise; -}, - - - - - - - - -searchInContent:function(query,caseSensitive,isRegex,callback) -{ -callback([]); -}, - - - - -isHttpFamily:function() -{ -return!!this.url.match(/^https?:/i); -}, - - - - -requestContentType:function() -{ -return this.requestHeaderValue("Content-Type"); -}, - - - - -hasErrorStatusCode:function() -{ -return this.statusCode>=400; -}, - - - - -setInitialPriority:function(priority) -{ -this._initialPriority=priority; -}, - - - - -initialPriority:function() -{ -return this._initialPriority; -}, - - - - -setPriority:function(priority) -{ -this._currentPriority=priority; -}, - - - - -priority:function() -{ -return this._currentPriority||this._initialPriority||null; -}, - - - - -populateImageSource:function(image) -{ - - - - -function onResourceContent(content) -{ -var imageSrc=WebInspector.ContentProvider.contentAsDataURL(content,this._mimeType,true); -if(imageSrc===null) -imageSrc=this._url; -image.src=imageSrc; -} - -this.requestContent().then(onResourceContent.bind(this)); -}, - - - - -asDataURL:function() -{ -var content=this._content; -var charset=null; -if(!this._contentEncoded){ -content=content.toBase64(); -charset="utf-8"; -} -return WebInspector.ContentProvider.contentAsDataURL(content,this.mimeType,true,charset); -}, - -_innerRequestContent:function() -{ -if(this._contentRequested) -return; -this._contentRequested=true; - - - - - - - -function onResourceContent(error,content,contentEncoded) -{ -this._content=error?null:content; -this._contentError=error; -this._contentEncoded=contentEncoded; -var callbacks=this._pendingContentCallbacks.slice(); -for(var i=0;i<callbacks.length;++i) -callbacks[i](this._content); -this._pendingContentCallbacks.length=0; -delete this._contentRequested; -} -this.target().networkAgent().getResponseBody(this._requestId,onResourceContent.bind(this)); -}, - - - - -initiator:function() -{ -return this._initiator; -}, - - - - -initiatorInfo:function() -{ -if(this._initiatorInfo) -return this._initiatorInfo; - -var type=WebInspector.NetworkRequest.InitiatorType.Other; -var url=""; -var lineNumber=-Infinity; -var columnNumber=-Infinity; -var scriptId=null; -var initiator=this._initiator; - -if(this.redirectSource){ -type=WebInspector.NetworkRequest.InitiatorType.Redirect; -url=this.redirectSource.url; -}else if(initiator){ -if(initiator.type===NetworkAgent.InitiatorType.Parser){ -type=WebInspector.NetworkRequest.InitiatorType.Parser; -url=initiator.url?initiator.url:url; -lineNumber=initiator.lineNumber?initiator.lineNumber:lineNumber; -}else if(initiator.type===NetworkAgent.InitiatorType.Script){ -for(var stack=initiator.stack;stack;stack=stack.parent){ -var topFrame=stack.callFrames.length?stack.callFrames[0]:null; -if(!topFrame) -continue; -type=WebInspector.NetworkRequest.InitiatorType.Script; -url=topFrame.url||WebInspector.UIString("<anonymous>"); -lineNumber=topFrame.lineNumber; -columnNumber=topFrame.columnNumber; -scriptId=topFrame.scriptId; -break; -} -} -} - -this._initiatorInfo={type:type,url:url,lineNumber:lineNumber,columnNumber:columnNumber,scriptId:scriptId}; -return this._initiatorInfo; -}, - - - - -initiatorRequest:function() -{ -if(this._initiatorRequest===undefined) -this._initiatorRequest=this._networkLog.requestForURL(this.initiatorInfo().url); -return this._initiatorRequest; -}, - - - - -initiatorChain:function() -{ -if(this._initiatorChain) -return this._initiatorChain; -this._initiatorChain=new Set(); -var request=this; -while(request){ -this._initiatorChain.add(request); -request=request.initiatorRequest(); -} -return this._initiatorChain; -}, - - - - -frames:function() -{ -return this._frames; -}, - - - - - -addFrameError:function(errorMessage,time) -{ -this._addFrame({type:WebInspector.NetworkRequest.WebSocketFrameType.Error,text:errorMessage,time:this.pseudoWallTime(time),opCode:-1,mask:false}); -}, - - - - - - -addFrame:function(response,time,sent) -{ -var type=sent?WebInspector.NetworkRequest.WebSocketFrameType.Send:WebInspector.NetworkRequest.WebSocketFrameType.Receive; -this._addFrame({type:type,text:response.payloadData,time:this.pseudoWallTime(time),opCode:response.opcode,mask:response.mask}); -}, - - - - -_addFrame:function(frame) -{ -this._frames.push(frame); -this.dispatchEventToListeners(WebInspector.NetworkRequest.Events.WebsocketFrameAdded,frame); -}, - - - - -eventSourceMessages:function() -{ -return this._eventSourceMessages; -}, - - - - - - - -addEventSourceMessage:function(time,eventName,eventId,data) -{ -var message={time:this.pseudoWallTime(time),eventName:eventName,eventId:eventId,data:data}; -this._eventSourceMessages.push(message); -this.dispatchEventToListeners(WebInspector.NetworkRequest.Events.EventSourceMessageAdded,message); -}, - -replayXHR:function() -{ -this.target().networkAgent().replayXHR(this.requestId); -}, - - - - -networkLog:function() -{ -return this._networkLog; -}, - - - - -networkManager:function() -{ -return this._networkManager; -}, - -__proto__:WebInspector.SDKObject.prototype}; - - -},{}],119:[function(require,module,exports){ - - - - - - - - -WebInspector.ProfileNode=function(callFrame) -{ - -this.callFrame=callFrame; - -this.callUID=`${this.callFrame.functionName}@${this.callFrame.scriptId}:${this.callFrame.lineNumber}`; - -this.self=0; - -this.total=0; - -this.id=0; - -this.parent=null; - -this.children=[]; -}; - -WebInspector.ProfileNode.prototype={ - - - -get functionName() -{ -return this.callFrame.functionName; -}, - - - - -get scriptId() -{ -return this.callFrame.scriptId; -}, - - - - -get url() -{ -return this.callFrame.url; -}, - - - - -get lineNumber() -{ -return this.callFrame.lineNumber; -}, - - - - -get columnNumber() -{ -return this.callFrame.columnNumber; -}}; - - - - - - -WebInspector.ProfileTreeModel=function(root) -{ -this.root=root; -this._assignDepthsAndParents(); -this.total=this._calculateTotals(this.root); -}; - -WebInspector.ProfileTreeModel.prototype={ -_assignDepthsAndParents:function() -{ -var root=this.root; -root.depth=-1; -root.parent=null; -this.maxDepth=0; -var nodesToTraverse=[root]; -while(nodesToTraverse.length){ -var parent=nodesToTraverse.pop(); -var depth=parent.depth+1; -if(depth>this.maxDepth) -this.maxDepth=depth; -var children=parent.children; -var length=children.length; -for(var i=0;i<length;++i){ -var child=children[i]; -child.depth=depth; -child.parent=parent; -if(child.children.length) -nodesToTraverse.push(child); -} -} -}, - - - - - -_calculateTotals:function(root) -{ -var nodesToTraverse=[root]; -var dfsList=[]; -while(nodesToTraverse.length){ -var node=nodesToTraverse.pop(); -node.total=node.self; -dfsList.push(node); -nodesToTraverse.push(...node.children); -} -while(dfsList.length>1){ -var node=dfsList.pop(); -node.parent.total+=node.total; -} -return root.total; -}}; - - -},{}],120:[function(require,module,exports){ +},{}],113:[function(require,module,exports){ @@ -54513,7 +51426,7 @@ __proto__:WebInspector.SDKObject.prototype}; -},{}],121:[function(require,module,exports){ +},{}],114:[function(require,module,exports){ @@ -54904,994 +51817,7 @@ WebInspector.targetManager=new WebInspector.TargetManager(); -},{}],122:[function(require,module,exports){ - - - - - - - - - - -WebInspector.TracingModel=function(backingStorage) -{ -this._backingStorage=backingStorage; - -this._firstWritePending=true; -this.reset(); -}; - - - - -WebInspector.TracingModel.Phase={ -Begin:"B", -End:"E", -Complete:"X", -Instant:"I", -AsyncBegin:"S", -AsyncStepInto:"T", -AsyncStepPast:"p", -AsyncEnd:"F", -NestableAsyncBegin:"b", -NestableAsyncEnd:"e", -NestableAsyncInstant:"n", -FlowBegin:"s", -FlowStep:"t", -FlowEnd:"f", -Metadata:"M", -Counter:"C", -Sample:"P", -CreateObject:"N", -SnapshotObject:"O", -DeleteObject:"D"}; - - -WebInspector.TracingModel.MetadataEvent={ -ProcessSortIndex:"process_sort_index", -ProcessName:"process_name", -ThreadSortIndex:"thread_sort_index", -ThreadName:"thread_name"}; - - -WebInspector.TracingModel.TopLevelEventCategory="toplevel"; -WebInspector.TracingModel.DevToolsMetadataEventCategory="disabled-by-default-devtools.timeline"; -WebInspector.TracingModel.DevToolsTimelineEventCategory="disabled-by-default-devtools.timeline"; - -WebInspector.TracingModel.FrameLifecycleEventCategory="cc,devtools"; - - - - - -WebInspector.TracingModel.isNestableAsyncPhase=function(phase) -{ -return phase==="b"||phase==="e"||phase==="n"; -}; - - - - - -WebInspector.TracingModel.isAsyncBeginPhase=function(phase) -{ -return phase==="S"||phase==="b"; -}; - - - - - -WebInspector.TracingModel.isAsyncPhase=function(phase) -{ -return WebInspector.TracingModel.isNestableAsyncPhase(phase)||phase==="S"||phase==="T"||phase==="F"||phase==="p"; -}; - - - - - -WebInspector.TracingModel.isFlowPhase=function(phase) -{ -return phase==="s"||phase==="t"||phase==="f"; -}; - - - - - -WebInspector.TracingModel.isTopLevelEvent=function(event) -{ -return event.hasCategory(WebInspector.TracingModel.TopLevelEventCategory)|| -event.hasCategory(WebInspector.TracingModel.DevToolsMetadataEventCategory)&&event.name==="Program"; -}; - - - - - -WebInspector.TracingModel._extractId=function(payload) -{ -var scope=payload.scope||""; -if(typeof payload.id2==="undefined") -return scope&&payload.id?`${scope}@${payload.id}`:payload.id; -var id2=payload.id2; -if(typeof id2==="object"&&"global"in id2!=="local"in id2) -return typeof id2["global"]!=="undefined"?`:${scope}:${id2["global"]}`:`:${scope}:${payload.pid}:${id2["local"]}`; -console.error(`Unexpected id2 field at ${payload.ts/1000}, one and only one of 'local' and 'global' should be present.`); -}; - - - - - - - - - -WebInspector.TracingModel.browserMainThread=function(tracingModel) -{ -var processes=tracingModel.sortedProcesses(); -var browserProcesses=[]; -var crRendererMainThreads=[]; -for(var process of processes){ -if(process.name().toLowerCase().endsWith("browser")) -browserProcesses.push(process); -crRendererMainThreads.push(...process.sortedThreads().filter(t=>t.name()==="CrBrowserMain")); -} -if(crRendererMainThreads.length===1) -return crRendererMainThreads[0]; -if(browserProcesses.length===1) -return browserProcesses[0].threadByName("CrBrowserMain"); -var tracingStartedInBrowser=tracingModel.devToolsMetadataEvents().filter(e=>e.name==="TracingStartedInBrowser"); -if(tracingStartedInBrowser.length===1) -return tracingStartedInBrowser[0].thread; -WebInspector.console.error("Failed to find browser main thread in trace, some timeline features may be unavailable"); -return null; -}; - - - - -WebInspector.BackingStorage=function() -{ -}; - -WebInspector.BackingStorage.prototype={ - - - -appendString:function(string){}, - - - - - -appendAccessibleString:function(string){}, - -finishWriting:function(){}, - -reset:function(){}}; - - - -WebInspector.TracingModel.prototype={ - - - -devToolsMetadataEvents:function() -{ -return this._devToolsMetadataEvents; -}, - - - - -setEventsForTest:function(events) -{ -this.reset(); -this.addEvents(events); -this.tracingComplete(); -}, - - - - -addEvents:function(events) -{ -for(var i=0;i<events.length;++i) -this._addEvent(events[i]); -}, - -tracingComplete:function() -{ -this._processPendingAsyncEvents(); -this._backingStorage.appendString(this._firstWritePending?"[]":"]"); -this._backingStorage.finishWriting(); -this._firstWritePending=false; -for(var process of this._processById.values()){ -for(var thread of process._threads.values()) -thread.tracingComplete(); -} -}, - -reset:function() -{ - -this._processById=new Map(); -this._processByName=new Map(); -this._minimumRecordTime=0; -this._maximumRecordTime=0; -this._devToolsMetadataEvents=[]; -if(!this._firstWritePending) -this._backingStorage.reset(); - -this._firstWritePending=true; - -this._asyncEvents=[]; - -this._openAsyncEvents=new Map(); - -this._openNestableAsyncEvents=new Map(); - -this._parsedCategories=new Map(); -}, - - - - -_addEvent:function(payload) -{ -var process=this._processById.get(payload.pid); -if(!process){ -process=new WebInspector.TracingModel.Process(this,payload.pid); -this._processById.set(payload.pid,process); -} - -var eventsDelimiter=",\n"; -this._backingStorage.appendString(this._firstWritePending?"[":eventsDelimiter); -this._firstWritePending=false; -var stringPayload=JSON.stringify(payload); -var isAccessible=payload.ph===WebInspector.TracingModel.Phase.SnapshotObject; -var backingStorage=null; -var keepStringsLessThan=10000; -if(isAccessible&&stringPayload.length>keepStringsLessThan) -backingStorage=this._backingStorage.appendAccessibleString(stringPayload);else - -this._backingStorage.appendString(stringPayload); - -var timestamp=payload.ts/1000; - - -if(timestamp&&(!this._minimumRecordTime||timestamp<this._minimumRecordTime)) -this._minimumRecordTime=timestamp; -var endTimeStamp=(payload.ts+(payload.dur||0))/1000; -this._maximumRecordTime=Math.max(this._maximumRecordTime,endTimeStamp); -var event=process._addEvent(payload); -if(!event) -return; - - - -if(WebInspector.TracingModel.isAsyncPhase(payload.ph)) -this._asyncEvents.push(event); -event._setBackingStorage(backingStorage); -if(event.hasCategory(WebInspector.TracingModel.DevToolsMetadataEventCategory)) -this._devToolsMetadataEvents.push(event); - -if(payload.ph!==WebInspector.TracingModel.Phase.Metadata) -return; - -switch(payload.name){ -case WebInspector.TracingModel.MetadataEvent.ProcessSortIndex: -process._setSortIndex(payload.args["sort_index"]); -break; -case WebInspector.TracingModel.MetadataEvent.ProcessName: -var processName=payload.args["name"]; -process._setName(processName); -this._processByName.set(processName,process); -break; -case WebInspector.TracingModel.MetadataEvent.ThreadSortIndex: -process.threadById(payload.tid)._setSortIndex(payload.args["sort_index"]); -break; -case WebInspector.TracingModel.MetadataEvent.ThreadName: -process.threadById(payload.tid)._setName(payload.args["name"]); -break;} - -}, - - - - -minimumRecordTime:function() -{ -return this._minimumRecordTime; -}, - - - - -maximumRecordTime:function() -{ -return this._maximumRecordTime; -}, - - - - -sortedProcesses:function() -{ -return WebInspector.TracingModel.NamedObject._sort(this._processById.valuesArray()); -}, - - - - - -processByName:function(name) -{ -return this._processByName.get(name); -}, - - - - - - -threadByName:function(processName,threadName) -{ -var process=this.processByName(processName); -return process&&process.threadByName(threadName); -}, - -_processPendingAsyncEvents:function() -{ -this._asyncEvents.stableSort(WebInspector.TracingModel.Event.compareStartTime); -for(var i=0;i<this._asyncEvents.length;++i){ -var event=this._asyncEvents[i]; -if(WebInspector.TracingModel.isNestableAsyncPhase(event.phase)) -this._addNestableAsyncEvent(event);else - -this._addAsyncEvent(event); -} -this._asyncEvents=[]; -this._closeOpenAsyncEvents(); -}, - -_closeOpenAsyncEvents:function() -{ -for(var event of this._openAsyncEvents.values()){ -event.setEndTime(this._maximumRecordTime); - - -event.steps[0].setEndTime(this._maximumRecordTime); -} -this._openAsyncEvents.clear(); - -for(var eventStack of this._openNestableAsyncEvents.values()){ -while(eventStack.length) -eventStack.pop().setEndTime(this._maximumRecordTime); -} -this._openNestableAsyncEvents.clear(); -}, - - - - -_addNestableAsyncEvent:function(event) -{ -var phase=WebInspector.TracingModel.Phase; -var key=event.categoriesString+"."+event.id; -var openEventsStack=this._openNestableAsyncEvents.get(key); - -switch(event.phase){ -case phase.NestableAsyncBegin: -if(!openEventsStack){ -openEventsStack=[]; -this._openNestableAsyncEvents.set(key,openEventsStack); -} -var asyncEvent=new WebInspector.TracingModel.AsyncEvent(event); -openEventsStack.push(asyncEvent); -event.thread._addAsyncEvent(asyncEvent); -break; - -case phase.NestableAsyncInstant: -if(openEventsStack&&openEventsStack.length) -openEventsStack.peekLast()._addStep(event); -break; - -case phase.NestableAsyncEnd: -if(!openEventsStack||!openEventsStack.length) -break; -var top=openEventsStack.pop(); -if(top.name!==event.name){ -console.error(`Begin/end event mismatch for nestable async event, ${top.name} vs. ${event.name}, key: ${key}`); -break; -} -top._addStep(event);} - -}, - - - - -_addAsyncEvent:function(event) -{ -var phase=WebInspector.TracingModel.Phase; -var key=event.categoriesString+"."+event.name+"."+event.id; -var asyncEvent=this._openAsyncEvents.get(key); - -if(event.phase===phase.AsyncBegin){ -if(asyncEvent){ -console.error(`Event ${event.name} has already been started`); -return; -} -asyncEvent=new WebInspector.TracingModel.AsyncEvent(event); -this._openAsyncEvents.set(key,asyncEvent); -event.thread._addAsyncEvent(asyncEvent); -return; -} -if(!asyncEvent){ - -return; -} -if(event.phase===phase.AsyncEnd){ -asyncEvent._addStep(event); -this._openAsyncEvents.delete(key); -return; -} -if(event.phase===phase.AsyncStepInto||event.phase===phase.AsyncStepPast){ -var lastStep=asyncEvent.steps.peekLast(); -if(lastStep.phase!==phase.AsyncBegin&&lastStep.phase!==event.phase){ -console.assert(false,"Async event step phase mismatch: "+lastStep.phase+" at "+lastStep.startTime+" vs. "+event.phase+" at "+event.startTime); -return; -} -asyncEvent._addStep(event); -return; -} -console.assert(false,"Invalid async event phase"); -}, - - - - - -_parsedCategoriesForString:function(str) -{ -var parsedCategories=this._parsedCategories.get(str); -if(!parsedCategories){ -parsedCategories=new Set(str.split(",")); -this._parsedCategories.set(str,parsedCategories); -} -return parsedCategories; -}}; - - - - - - - - - - -WebInspector.TracingModel.Event=function(categories,name,phase,startTime,thread) -{ - -this.categoriesString=categories; - -this._parsedCategories=thread._model._parsedCategoriesForString(categories); - -this.name=name; - -this.phase=phase; - -this.startTime=startTime; - -this.thread=thread; - -this.args={}; - - -this.warning=null; - -this.initiator=null; - -this.stackTrace=null; - -this.previewElement=null; - -this.url=null; - -this.backendNodeId=0; - - -this.selfTime=0; -}; - - - - - - -WebInspector.TracingModel.Event.fromPayload=function(payload,thread) -{ -var event=new WebInspector.TracingModel.Event(payload.cat,payload.name,payload.ph,payload.ts/1000,thread); -if(payload.args) -event.addArgs(payload.args);else - -console.error("Missing mandatory event argument 'args' at "+payload.ts/1000); -if(typeof payload.dur==="number") -event.setEndTime((payload.ts+payload.dur)/1000); -var id=WebInspector.TracingModel._extractId(payload); -if(typeof id!=="undefined") -event.id=id; -if(payload.bind_id) -event.bind_id=payload.bind_id; - -return event; -}; - -WebInspector.TracingModel.Event.prototype={ - - - - -hasCategory:function(categoryName) -{ -return this._parsedCategories.has(categoryName); -}, - - - - -setEndTime:function(endTime) -{ -if(endTime<this.startTime){ -console.assert(false,"Event out of order: "+this.name); -return; -} -this.endTime=endTime; -this.duration=endTime-this.startTime; -}, - - - - -addArgs:function(args) -{ - -for(var name in args){ -if(name in this.args) -console.error("Same argument name ("+name+") is used for begin and end phases of "+this.name); -this.args[name]=args[name]; -} -}, - - - - -_complete:function(endEvent) -{ -if(endEvent.args) -this.addArgs(endEvent.args);else - -console.error("Missing mandatory event argument 'args' at "+endEvent.startTime); -this.setEndTime(endEvent.startTime); -}, - - - - -_setBackingStorage:function(backingStorage) -{ -}}; - - - - - - - -WebInspector.TracingModel.Event.compareStartTime=function(a,b) -{ -return a.startTime-b.startTime; -}; - - - - - - -WebInspector.TracingModel.Event.compareStartAndEndTime=function(a,b) -{ -return a.startTime-b.startTime||b.endTime!==undefined&&a.endTime!==undefined&&b.endTime-a.endTime||0; -}; - - - - - - -WebInspector.TracingModel.Event.orderedCompareStartTime=function(a,b) -{ - - - -return a.startTime-b.startTime||a.ordinal-b.ordinal||-1; -}; - - - - - - - - - -WebInspector.TracingModel.ObjectSnapshot=function(category,name,startTime,thread) -{ -WebInspector.TracingModel.Event.call(this,category,name,WebInspector.TracingModel.Phase.SnapshotObject,startTime,thread); -}; - - - - - - -WebInspector.TracingModel.ObjectSnapshot.fromPayload=function(payload,thread) -{ -var snapshot=new WebInspector.TracingModel.ObjectSnapshot(payload.cat,payload.name,payload.ts/1000,thread); -var id=WebInspector.TracingModel._extractId(payload); -if(typeof id!=="undefined") -snapshot.id=id; -if(!payload.args||!payload.args["snapshot"]){ -console.error("Missing mandatory 'snapshot' argument at "+payload.ts/1000); -return snapshot; -} -if(payload.args) -snapshot.addArgs(payload.args); -return snapshot; -}; - -WebInspector.TracingModel.ObjectSnapshot.prototype={ - - - -requestObject:function(callback) -{ -var snapshot=this.args["snapshot"]; -if(snapshot){ -callback(snapshot); -return; -} -this._backingStorage().then(onRead,callback.bind(null,null)); - - - -function onRead(result) -{ -if(!result){ -callback(null); -return; -} -try{ -var payload=JSON.parse(result); -callback(payload["args"]["snapshot"]); -}catch(e){ -WebInspector.console.error("Malformed event data in backing storage"); -callback(null); -} -} -}, - - - - -objectPromise:function() -{ -if(!this._objectPromise) -this._objectPromise=new Promise(this.requestObject.bind(this)); -return this._objectPromise; -}, - - - - - -_setBackingStorage:function(backingStorage) -{ -if(!backingStorage) -return; -this._backingStorage=backingStorage; -this.args={}; -}, - -__proto__:WebInspector.TracingModel.Event.prototype}; - - - - - - - -WebInspector.TracingModel.AsyncEvent=function(startEvent) -{ -WebInspector.TracingModel.Event.call(this,startEvent.categoriesString,startEvent.name,startEvent.phase,startEvent.startTime,startEvent.thread); -this.addArgs(startEvent.args); -this.steps=[startEvent]; -}; - -WebInspector.TracingModel.AsyncEvent.prototype={ - - - -_addStep:function(event) -{ -this.steps.push(event); -if(event.phase===WebInspector.TracingModel.Phase.AsyncEnd||event.phase===WebInspector.TracingModel.Phase.NestableAsyncEnd){ -this.setEndTime(event.startTime); - - -this.steps[0].setEndTime(event.startTime); -} -}, - -__proto__:WebInspector.TracingModel.Event.prototype}; - - - - - -WebInspector.TracingModel.NamedObject=function() -{ -}; - -WebInspector.TracingModel.NamedObject.prototype= -{ - - - -_setName:function(name) -{ -this._name=name; -}, - - - - -name:function() -{ -return this._name; -}, - - - - -_setSortIndex:function(sortIndex) -{ -this._sortIndex=sortIndex; -}}; - - - - - -WebInspector.TracingModel.NamedObject._sort=function(array) -{ - - - - -function comparator(a,b) -{ -return a._sortIndex!==b._sortIndex?a._sortIndex-b._sortIndex:a.name().localeCompare(b.name()); -} -return array.sort(comparator); -}; - - - - - - - -WebInspector.TracingModel.Process=function(model,id) -{ -WebInspector.TracingModel.NamedObject.call(this); -this._setName("Process "+id); -this._id=id; - -this._threads=new Map(); -this._threadByName=new Map(); -this._model=model; -}; - -WebInspector.TracingModel.Process.prototype={ - - - -id:function() -{ -return this._id; -}, - - - - - -threadById:function(id) -{ -var thread=this._threads.get(id); -if(!thread){ -thread=new WebInspector.TracingModel.Thread(this,id); -this._threads.set(id,thread); -} -return thread; -}, - - - - - -threadByName:function(name) -{ -return this._threadByName.get(name)||null; -}, - - - - - -_setThreadByName:function(name,thread) -{ -this._threadByName.set(name,thread); -}, - - - - - -_addEvent:function(payload) -{ -return this.threadById(payload.tid)._addEvent(payload); -}, - - - - -sortedThreads:function() -{ -return WebInspector.TracingModel.NamedObject._sort(this._threads.valuesArray()); -}, - -__proto__:WebInspector.TracingModel.NamedObject.prototype}; - - - - - - - - -WebInspector.TracingModel.Thread=function(process,id) -{ -WebInspector.TracingModel.NamedObject.call(this); -this._process=process; -this._setName("Thread "+id); -this._events=[]; -this._asyncEvents=[]; -this._id=id; -this._model=process._model; -}; - -WebInspector.TracingModel.Thread.prototype={ -tracingComplete:function() -{ -this._asyncEvents.stableSort(WebInspector.TracingModel.Event.compareStartAndEndTime); -this._events.stableSort(WebInspector.TracingModel.Event.compareStartTime); -var phases=WebInspector.TracingModel.Phase; -var stack=[]; -for(var i=0;i<this._events.length;++i){ -var e=this._events[i]; -e.ordinal=i; -switch(e.phase){ -case phases.End: -this._events[i]=null; - -if(!stack.length) -continue; -var top=stack.pop(); -if(top.name!==e.name||top.categoriesString!==e.categoriesString) -console.error("B/E events mismatch at "+top.startTime+" ("+top.name+") vs. "+e.startTime+" ("+e.name+")");else - -top._complete(e); -break; -case phases.Begin: -stack.push(e); -break;} - -} -while(stack.length) -stack.pop().setEndTime(this._model.maximumRecordTime()); -this._events.remove(null,false); -}, - - - - - -_addEvent:function(payload) -{ -var event=payload.ph===WebInspector.TracingModel.Phase.SnapshotObject? -WebInspector.TracingModel.ObjectSnapshot.fromPayload(payload,this): -WebInspector.TracingModel.Event.fromPayload(payload,this); -if(WebInspector.TracingModel.isTopLevelEvent(event)){ - -if(this._lastTopLevelEvent&&this._lastTopLevelEvent.endTime>event.startTime) -return null; -this._lastTopLevelEvent=event; -} -this._events.push(event); -return event; -}, - - - - -_addAsyncEvent:function(asyncEvent) -{ -this._asyncEvents.push(asyncEvent); -}, - - - - - -_setName:function(name) -{ -WebInspector.TracingModel.NamedObject.prototype._setName.call(this,name); -this._process._setThreadByName(name,this); -}, - - - - -id:function() -{ -return this._id; -}, - - - - -process:function() -{ -return this._process; -}, - - - - -events:function() -{ -return this._events; -}, - - - - -asyncEvents:function() -{ -return this._asyncEvents; -}, - -__proto__:WebInspector.TracingModel.NamedObject.prototype}; - - -},{}],123:[function(require,module,exports){ +},{}],115:[function(require,module,exports){ @@ -56172,8230 +52098,7 @@ }}; -},{}],124:[function(require,module,exports){ - - - - - - - - - - -WebInspector.TimelineTreeView=function(model,filters) -{ -WebInspector.VBox.call(this); -this.element.classList.add("timeline-tree-view"); - -this._model=model; -this._linkifier=new WebInspector.Linkifier(); - -this._filters=filters.slice(); - -var columns=[]; -this._populateColumns(columns); - -var mainView=new WebInspector.VBox(); -this._populateToolbar(mainView.element); -this._dataGrid=new WebInspector.SortableDataGrid(columns); -this._dataGrid.addEventListener(WebInspector.DataGrid.Events.SortingChanged,this._sortingChanged,this); -this._dataGrid.element.addEventListener("mousemove",this._onMouseMove.bind(this),true); -this._dataGrid.setResizeMethod(WebInspector.DataGrid.ResizeMethod.Last); -this._dataGrid.asWidget().show(mainView.element); - -this._splitWidget=new WebInspector.SplitWidget(true,true,"timelineTreeViewDetailsSplitWidget"); -this._splitWidget.show(this.element); -this._splitWidget.setMainWidget(mainView); - -this._detailsView=new WebInspector.VBox(); -this._detailsView.element.classList.add("timeline-details-view","timeline-details-view-body"); -this._splitWidget.setSidebarWidget(this._detailsView); -this._dataGrid.addEventListener(WebInspector.DataGrid.Events.SelectedNode,this._updateDetailsForSelection,this); - - -this._lastSelectedNode; -}; - -WebInspector.TimelineTreeView.prototype={ - - - -updateContents:function(selection) -{ -this.setRange(selection.startTime(),selection.endTime()); -}, - - - - - -setRange:function(startTime,endTime) -{ -this._startTime=startTime; -this._endTime=endTime; -this._refreshTree(); -}, - - - - -_exposePercentages:function() -{ -return false; -}, - - - - -_populateToolbar:function(parent){}, - - - - -_onHover:function(node){}, - - - - - -_linkifyLocation:function(event) -{ -var target=this._model.targetByEvent(event); -if(!target) -return null; -var frame=WebInspector.TimelineProfileTree.eventStackFrame(event); -if(!frame) -return null; -return this._linkifier.maybeLinkifyConsoleCallFrame(target,frame); -}, - - - - - -selectProfileNode:function(treeNode,suppressSelectedEvent) -{ -var pathToRoot=[]; -for(var node=treeNode;node;node=node.parent) -pathToRoot.push(node); -for(var i=pathToRoot.length-1;i>0;--i){ -var gridNode=this._dataGridNodeForTreeNode(pathToRoot[i]); -if(gridNode&&gridNode.dataGrid) -gridNode.expand(); -} -var gridNode=this._dataGridNodeForTreeNode(treeNode); -if(gridNode.dataGrid){ -gridNode.reveal(); -gridNode.select(suppressSelectedEvent); -} -}, - -_refreshTree:function() -{ -this._linkifier.reset(); -this._dataGrid.rootNode().removeChildren(); -var tree=this._buildTree(); -if(!tree.children) -return; -var maxSelfTime=0; -var maxTotalTime=0; -for(var child of tree.children.values()){ -maxSelfTime=Math.max(maxSelfTime,child.selfTime); -maxTotalTime=Math.max(maxTotalTime,child.totalTime); -} -for(var child of tree.children.values()){ - -var gridNode=new WebInspector.TimelineTreeView.TreeGridNode(child,tree.totalTime,maxSelfTime,maxTotalTime,this); -this._dataGrid.insertChild(gridNode); -} -this._sortingChanged(); -this._updateDetailsForSelection(); -}, - - - - -_buildTree:function() -{ -throw new Error("Not Implemented"); -}, - - - - - -_buildTopDownTree:function(eventIdCallback) -{ -return WebInspector.TimelineProfileTree.buildTopDown(this._model.mainThreadEvents(),this._filters,this._startTime,this._endTime,eventIdCallback); -}, - - - - -_populateColumns:function(columns) -{ -columns.push({id:"self",title:WebInspector.UIString("Self Time"),width:"110px",fixedWidth:true,sortable:true}); -columns.push({id:"total",title:WebInspector.UIString("Total Time"),width:"110px",fixedWidth:true,sortable:true}); -columns.push({id:"activity",title:WebInspector.UIString("Activity"),disclosure:true,sortable:true}); -}, - -_sortingChanged:function() -{ -var columnIdentifier=this._dataGrid.sortColumnIdentifier(); -if(!columnIdentifier) -return; -var sortFunction; -switch(columnIdentifier){ -case"startTime": -sortFunction=compareStartTime; -break; -case"self": -sortFunction=compareNumericField.bind(null,"selfTime"); -break; -case"total": -sortFunction=compareNumericField.bind(null,"totalTime"); -break; -case"activity": -sortFunction=compareName; -break; -default: -console.assert(false,"Unknown sort field: "+columnIdentifier); -return;} - -this._dataGrid.sortNodes(sortFunction,!this._dataGrid.isSortOrderAscending()); - - - - - - - -function compareNumericField(field,a,b) -{ -var nodeA=a; -var nodeB=b; -return nodeA._profileNode[field]-nodeB._profileNode[field]; -} - - - - - - -function compareStartTime(a,b) -{ -var nodeA=a; -var nodeB=b; -return nodeA._profileNode.event.startTime-nodeB._profileNode.event.startTime; -} - - - - - - -function compareName(a,b) -{ -var nodeA=a; -var nodeB=b; -var nameA=WebInspector.TimelineTreeView.eventNameForSorting(nodeA._profileNode.event); -var nameB=WebInspector.TimelineTreeView.eventNameForSorting(nodeB._profileNode.event); -return nameA.localeCompare(nameB); -} -}, - -_updateDetailsForSelection:function() -{ -var selectedNode=this._dataGrid.selectedNode?this._dataGrid.selectedNode._profileNode:null; -if(selectedNode===this._lastSelectedNode) -return; -this._lastSelectedNode=selectedNode; -this._detailsView.detachChildWidgets(); -this._detailsView.element.removeChildren(); -if(!selectedNode||!this._showDetailsForNode(selectedNode)){ -var banner=this._detailsView.element.createChild("div","full-widget-dimmed-banner"); -banner.createTextChild(WebInspector.UIString("Select item for details.")); -} -}, - - - - - -_showDetailsForNode:function(node) -{ -return false; -}, - - - - -_onMouseMove:function(event) -{ -var gridNode=event.target&&event.target instanceof Node? -this._dataGrid.dataGridNodeFromNode(event.target): -null; -var profileNode=gridNode&&gridNode._profileNode; -if(profileNode===this._lastHoveredProfileNode) -return; -this._lastHoveredProfileNode=profileNode; -this._onHover(profileNode); -}, - - - - - -_dataGridNodeForTreeNode:function(treeNode) -{ -return treeNode[WebInspector.TimelineTreeView.TreeGridNode._gridNodeSymbol]||null; -}, - -__proto__:WebInspector.VBox.prototype}; - - - - - - -WebInspector.TimelineTreeView.eventNameForSorting=function(event) -{ -if(event.name===WebInspector.TimelineModel.RecordType.JSFrame){ -var data=event.args["data"]; -return data["functionName"]+"@"+(data["scriptId"]||data["url"]||""); -} -return event.name+":@"+WebInspector.TimelineProfileTree.eventURL(event); -}; - - - - - - - - - - -WebInspector.TimelineTreeView.GridNode=function(profileNode,grandTotalTime,maxSelfTime,maxTotalTime,treeView) -{ -this._populated=false; -this._profileNode=profileNode; -this._treeView=treeView; -this._grandTotalTime=grandTotalTime; -this._maxSelfTime=maxSelfTime; -this._maxTotalTime=maxTotalTime; -WebInspector.SortableDataGridNode.call(this,null,false); -}; - -WebInspector.TimelineTreeView.GridNode.prototype={ - - - - - -createCell:function(columnIdentifier) -{ -if(columnIdentifier==="activity") -return this._createNameCell(columnIdentifier); -return this._createValueCell(columnIdentifier)||WebInspector.DataGridNode.prototype.createCell.call(this,columnIdentifier); -}, - - - - - -_createNameCell:function(columnIdentifier) -{ -var cell=this.createTD(columnIdentifier); -var container=cell.createChild("div","name-container"); -var icon=container.createChild("div","activity-icon"); -var name=container.createChild("div","activity-name"); -var event=this._profileNode.event; -if(this._profileNode.isGroupNode()){ -var treeView=this._treeView; -var info=treeView._displayInfoForGroupNode(this._profileNode); -name.textContent=info.name; -icon.style.backgroundColor=info.color; -}else if(event){ -var data=event.args["data"]; -var deoptReason=data&&data["deoptReason"]; -if(deoptReason) -container.createChild("div","activity-warning").title=WebInspector.UIString("Not optimized: %s",deoptReason); -name.textContent=event.name===WebInspector.TimelineModel.RecordType.JSFrame? -WebInspector.beautifyFunctionName(event.args["data"]["functionName"]): -WebInspector.TimelineUIUtils.eventTitle(event); -var link=this._treeView._linkifyLocation(event); -if(link) -container.createChild("div","activity-link").appendChild(link); -icon.style.backgroundColor=WebInspector.TimelineUIUtils.eventColor(event); -} -return cell; -}, - - - - - -_createValueCell:function(columnIdentifier) -{ -if(columnIdentifier!=="self"&&columnIdentifier!=="total"&&columnIdentifier!=="startTime") -return null; - -var showPercents=false; -var value; -var maxTime; -switch(columnIdentifier){ -case"startTime": -value=this._profileNode.event.startTime-this._treeView._model.minimumRecordTime(); -break; -case"self": -value=this._profileNode.selfTime; -maxTime=this._maxSelfTime; -showPercents=true; -break; -case"total": -value=this._profileNode.totalTime; -maxTime=this._maxTotalTime; -showPercents=true; -break; -default: -return null;} - -var cell=this.createTD(columnIdentifier); -cell.className="numeric-column"; -var textDiv=cell.createChild("div"); -textDiv.createChild("span").textContent=WebInspector.UIString("%.1f\u2009ms",value); - -if(showPercents&&this._treeView._exposePercentages()) -textDiv.createChild("span","percent-column").textContent=WebInspector.UIString("%.1f\u2009%%",value/this._grandTotalTime*100); -if(maxTime){ -textDiv.classList.add("background-percent-bar"); -cell.createChild("div","background-bar-container").createChild("div","background-bar").style.width=(value*100/maxTime).toFixed(1)+"%"; -} -return cell; -}, - -__proto__:WebInspector.SortableDataGridNode.prototype}; - - - - - - - - - - - -WebInspector.TimelineTreeView.TreeGridNode=function(profileNode,grandTotalTime,maxSelfTime,maxTotalTime,treeView) -{ -WebInspector.TimelineTreeView.GridNode.call(this,profileNode,grandTotalTime,maxSelfTime,maxTotalTime,treeView); -this.hasChildren=this._profileNode.children?this._profileNode.children.size>0:false; -profileNode[WebInspector.TimelineTreeView.TreeGridNode._gridNodeSymbol]=this; -}; - -WebInspector.TimelineTreeView.TreeGridNode._gridNodeSymbol=Symbol("treeGridNode"); - -WebInspector.TimelineTreeView.TreeGridNode.prototype={ - - - -populate:function() -{ -if(this._populated) -return; -this._populated=true; -if(!this._profileNode.children) -return; -for(var node of this._profileNode.children.values()){ -var gridNode=new WebInspector.TimelineTreeView.TreeGridNode(node,this._grandTotalTime,this._maxSelfTime,this._maxTotalTime,this._treeView); -this.insertChildOrdered(gridNode); -} -}, - -__proto__:WebInspector.TimelineTreeView.GridNode.prototype}; - - - - - - - - - -WebInspector.AggregatedTimelineTreeView=function(model,filters) -{ -this._groupBySetting=WebInspector.settings.createSetting("timelineTreeGroupBy",WebInspector.TimelineAggregator.GroupBy.Category); -WebInspector.TimelineTreeView.call(this,model,filters); -var nonessentialEvents=[ -WebInspector.TimelineModel.RecordType.EventDispatch, -WebInspector.TimelineModel.RecordType.FunctionCall, -WebInspector.TimelineModel.RecordType.TimerFire]; - -this._filters.push(new WebInspector.ExclusiveNameFilter(nonessentialEvents)); -this._stackView=new WebInspector.TimelineStackView(this); -this._stackView.addEventListener(WebInspector.TimelineStackView.Events.SelectionChanged,this._onStackViewSelectionChanged,this); -}; - -WebInspector.AggregatedTimelineTreeView.prototype={ - - - - -updateContents:function(selection) -{ -this._updateExtensionResolver(); -WebInspector.TimelineTreeView.prototype.updateContents.call(this,selection); -var rootNode=this._dataGrid.rootNode(); -if(rootNode.children.length) -rootNode.children[0].revealAndSelect(); -}, - -_updateExtensionResolver:function() -{ -this._executionContextNamesByOrigin=new Map(); -for(var target of WebInspector.targetManager.targets()){ -for(var context of target.runtimeModel.executionContexts()) -this._executionContextNamesByOrigin.set(context.origin,context.name); -} -}, - - - - - -_displayInfoForGroupNode:function(node) -{ -var categories=WebInspector.TimelineUIUtils.categories(); -var color=node.id?WebInspector.TimelineUIUtils.eventColor(node.event):categories["other"].color; - -switch(this._groupBySetting.get()){ -case WebInspector.TimelineAggregator.GroupBy.Category: -var category=categories[node.id]||categories["other"]; -return{name:category.title,color:category.color}; - -case WebInspector.TimelineAggregator.GroupBy.Domain: -case WebInspector.TimelineAggregator.GroupBy.Subdomain: -var name=node.id; -if(WebInspector.TimelineAggregator.isExtensionInternalURL(name)) -name=WebInspector.UIString("[Chrome extensions overhead]");else -if(name.startsWith("chrome-extension")) -name=this._executionContextNamesByOrigin.get(name)||name; -return{ -name:name||WebInspector.UIString("unattributed"), -color:color}; - - -case WebInspector.TimelineAggregator.GroupBy.EventName: -var name=node.event.name===WebInspector.TimelineModel.RecordType.JSFrame? -WebInspector.UIString("JavaScript"):WebInspector.TimelineUIUtils.eventTitle(node.event); -return{ -name:name, -color:node.event.name===WebInspector.TimelineModel.RecordType.JSFrame? -WebInspector.TimelineUIUtils.eventStyle(node.event).category.color:color}; - - -case WebInspector.TimelineAggregator.GroupBy.URL: -break; - -default: -console.assert(false,"Unexpected aggregation type");} - -return{ -name:node.id||WebInspector.UIString("unattributed"), -color:color}; - -}, - - - - - -_populateToolbar:function(parent) -{ -var panelToolbar=new WebInspector.Toolbar("",parent); -this._groupByCombobox=new WebInspector.ToolbarComboBox(this._onGroupByChanged.bind(this)); - - - - - -function addGroupingOption(name,id) -{ -var option=this._groupByCombobox.createOption(name,"",id); -this._groupByCombobox.addOption(option); -if(id===this._groupBySetting.get()) -this._groupByCombobox.select(option); -} -addGroupingOption.call(this,WebInspector.UIString("No Grouping"),WebInspector.TimelineAggregator.GroupBy.None); -addGroupingOption.call(this,WebInspector.UIString("Group by Activity"),WebInspector.TimelineAggregator.GroupBy.EventName); -addGroupingOption.call(this,WebInspector.UIString("Group by Category"),WebInspector.TimelineAggregator.GroupBy.Category); -addGroupingOption.call(this,WebInspector.UIString("Group by Domain"),WebInspector.TimelineAggregator.GroupBy.Domain); -addGroupingOption.call(this,WebInspector.UIString("Group by Subdomain"),WebInspector.TimelineAggregator.GroupBy.Subdomain); -addGroupingOption.call(this,WebInspector.UIString("Group by URL"),WebInspector.TimelineAggregator.GroupBy.URL); -panelToolbar.appendToolbarItem(this._groupByCombobox); -}, - - - - - -_buildHeaviestStack:function(treeNode) -{ -console.assert(!!treeNode.parent,"Attempt to build stack for tree root"); -var result=[]; - -for(var node=treeNode;node&&node.parent;node=node.parent) -result.push(node); -result=result.reverse(); -for(node=treeNode;node&&node.children&&node.children.size;){ -var children=Array.from(node.children.values()); -node=children.reduce((a,b)=>a.totalTime>b.totalTime?a:b); -result.push(node); -} -return result; -}, - - - - - -_exposePercentages:function() -{ -return true; -}, - -_onGroupByChanged:function() -{ -this._groupBySetting.set(this._groupByCombobox.selectedOption().value); -this._refreshTree(); -}, - -_onStackViewSelectionChanged:function() -{ -var treeNode=this._stackView.selectedTreeNode(); -if(treeNode) -this.selectProfileNode(treeNode,true); -}, - - - - - - -_showDetailsForNode:function(node) -{ -var stack=this._buildHeaviestStack(node); -this._stackView.setStack(stack,node); -this._stackView.show(this._detailsView.element); -return true; -}, - - - - -_createAggregator:function() -{ -return new WebInspector.TimelineAggregator( -event=>WebInspector.TimelineUIUtils.eventStyle(event).title, -event=>WebInspector.TimelineUIUtils.eventStyle(event).category.name); - -}, - -__proto__:WebInspector.TimelineTreeView.prototype}; - - - - - - - - -WebInspector.CallTreeTimelineTreeView=function(model,filters) -{ -WebInspector.AggregatedTimelineTreeView.call(this,model,filters); -this._dataGrid.markColumnAsSortedBy("total",WebInspector.DataGrid.Order.Descending); -}; - -WebInspector.CallTreeTimelineTreeView.prototype={ - - - - -_buildTree:function() -{ -var topDown=this._buildTopDownTree(WebInspector.TimelineAggregator.eventId); -return this._createAggregator().performGrouping(topDown,this._groupBySetting.get()); -}, - -__proto__:WebInspector.AggregatedTimelineTreeView.prototype}; - - - - - - - - -WebInspector.BottomUpTimelineTreeView=function(model,filters) -{ -WebInspector.AggregatedTimelineTreeView.call(this,model,filters); -this._dataGrid.markColumnAsSortedBy("self",WebInspector.DataGrid.Order.Descending); -}; - -WebInspector.BottomUpTimelineTreeView.prototype={ - - - - -_buildTree:function() -{ -var topDown=this._buildTopDownTree(WebInspector.TimelineAggregator.eventId); -return WebInspector.TimelineProfileTree.buildBottomUp(topDown,this._createAggregator().groupFunction(this._groupBySetting.get())); -}, - -__proto__:WebInspector.AggregatedTimelineTreeView.prototype}; - - - - - - - - - -WebInspector.EventsTimelineTreeView=function(model,filters,delegate) -{ -this._filtersControl=new WebInspector.TimelineFilters(); -this._filtersControl.addEventListener(WebInspector.TimelineFilters.Events.FilterChanged,this._onFilterChanged,this); -WebInspector.TimelineTreeView.call(this,model,filters); -this._delegate=delegate; -this._filters.push.apply(this._filters,this._filtersControl.filters()); -this._dataGrid.markColumnAsSortedBy("startTime",WebInspector.DataGrid.Order.Ascending); -}; - -WebInspector.EventsTimelineTreeView.prototype={ - - - - -updateContents:function(selection) -{ -WebInspector.TimelineTreeView.prototype.updateContents.call(this,selection); -if(selection.type()===WebInspector.TimelineSelection.Type.TraceEvent){ -var event=selection.object(); -this._selectEvent(event,true); -} -}, - - - - - -_buildTree:function() -{ -this._currentTree=this._buildTopDownTree(); -return this._currentTree; -}, - -_onFilterChanged:function() -{ -var selectedEvent=this._lastSelectedNode&&this._lastSelectedNode.event; -this._refreshTree(); -if(selectedEvent) -this._selectEvent(selectedEvent,false); -}, - - - - - -_findNodeWithEvent:function(event) -{ -var iterators=[this._currentTree.children.values()]; - -while(iterators.length){ -var iterator=iterators.peekLast().next(); -if(iterator.done){ -iterators.pop(); -continue; -} -var child=iterator.value; -if(child.event===event) -return child; -if(child.children) -iterators.push(child.children.values()); -} -return null; -}, - - - - - -_selectEvent:function(event,expand) -{ -var node=this._findNodeWithEvent(event); -if(!node) -return; -this.selectProfileNode(node,false); -if(expand) -this._dataGridNodeForTreeNode(node).expand(); -}, - - - - - -_populateColumns:function(columns) -{ -columns.push({id:"startTime",title:WebInspector.UIString("Start Time"),width:"110px",fixedWidth:true,sortable:true}); -WebInspector.TimelineTreeView.prototype._populateColumns.call(this,columns); -}, - - - - - -_populateToolbar:function(parent) -{ -var filtersWidget=this._filtersControl.filtersWidget(); -filtersWidget.forceShowFilterBar(); -filtersWidget.show(parent); -}, - - - - - - -_showDetailsForNode:function(node) -{ -var traceEvent=node.event; -if(!traceEvent) -return false; -WebInspector.TimelineUIUtils.buildTraceEventDetails(traceEvent,this._model,this._linkifier,false,showDetails.bind(this)); -return true; - - - - - -function showDetails(fragment) -{ -this._detailsView.element.appendChild(fragment); -} -}, - - - - - -_onHover:function(node) -{ -this._delegate.highlightEvent(node&&node.event); -}, - -__proto__:WebInspector.TimelineTreeView.prototype}; - - - - - - -WebInspector.TimelineStackView=function(treeView) -{ -WebInspector.VBox.call(this); -var header=this.element.createChild("div","timeline-stack-view-header"); -header.textContent=WebInspector.UIString("Heaviest stack"); -this._treeView=treeView; -var columns=[ -{id:"total",title:WebInspector.UIString("Total Time"),fixedWidth:true,width:"110px"}, -{id:"activity",title:WebInspector.UIString("Activity")}]; - -this._dataGrid=new WebInspector.ViewportDataGrid(columns); -this._dataGrid.setResizeMethod(WebInspector.DataGrid.ResizeMethod.Last); -this._dataGrid.addEventListener(WebInspector.DataGrid.Events.SelectedNode,this._onSelectionChanged,this); -this._dataGrid.asWidget().show(this.element); -}; - - -WebInspector.TimelineStackView.Events={ -SelectionChanged:Symbol("SelectionChanged")}; - - -WebInspector.TimelineStackView.prototype={ - - - - -setStack:function(stack,selectedNode) -{ -var rootNode=this._dataGrid.rootNode(); -rootNode.removeChildren(); -var nodeToReveal=null; -var totalTime=Math.max.apply(Math,stack.map(node=>node.totalTime)); -for(var node of stack){ -var gridNode=new WebInspector.TimelineTreeView.GridNode(node,totalTime,totalTime,totalTime,this._treeView); -rootNode.appendChild(gridNode); -if(node===selectedNode) -nodeToReveal=gridNode; -} -nodeToReveal.revealAndSelect(); -}, - - - - -selectedTreeNode:function() -{ -var selectedNode=this._dataGrid.selectedNode; -return selectedNode&&selectedNode._profileNode; -}, - -_onSelectionChanged:function() -{ -this.dispatchEventToListeners(WebInspector.TimelineStackView.Events.SelectionChanged); -}, - -__proto__:WebInspector.VBox.prototype}; - - -},{}],125:[function(require,module,exports){ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -WebInspector.TimelineUIUtils=function(){}; - - - - - - - -WebInspector.TimelineRecordStyle=function(title,category,hidden) -{ -this.title=title; -this.category=category; -this.hidden=!!hidden; -}; - - - - -WebInspector.TimelineUIUtils._initEventStyles=function() -{ -if(WebInspector.TimelineUIUtils._eventStylesMap) -return WebInspector.TimelineUIUtils._eventStylesMap; - -var recordTypes=WebInspector.TimelineModel.RecordType; -var categories=WebInspector.TimelineUIUtils.categories(); - -var eventStyles={}; -eventStyles[recordTypes.Task]=new WebInspector.TimelineRecordStyle(WebInspector.UIString("Task"),categories["other"]); -eventStyles[recordTypes.Program]=new WebInspector.TimelineRecordStyle(WebInspector.UIString("Other"),categories["other"]); -eventStyles[recordTypes.Animation]=new WebInspector.TimelineRecordStyle(WebInspector.UIString("Animation"),categories["rendering"]); -eventStyles[recordTypes.EventDispatch]=new WebInspector.TimelineRecordStyle(WebInspector.UIString("Event"),categories["scripting"]); -eventStyles[recordTypes.RequestMainThreadFrame]=new WebInspector.TimelineRecordStyle(WebInspector.UIString("Request Main Thread Frame"),categories["rendering"],true); -eventStyles[recordTypes.BeginFrame]=new WebInspector.TimelineRecordStyle(WebInspector.UIString("Frame Start"),categories["rendering"],true); -eventStyles[recordTypes.BeginMainThreadFrame]=new WebInspector.TimelineRecordStyle(WebInspector.UIString("Frame Start (main thread)"),categories["rendering"],true); -eventStyles[recordTypes.DrawFrame]=new WebInspector.TimelineRecordStyle(WebInspector.UIString("Draw Frame"),categories["rendering"],true); -eventStyles[recordTypes.HitTest]=new WebInspector.TimelineRecordStyle(WebInspector.UIString("Hit Test"),categories["rendering"]); -eventStyles[recordTypes.ScheduleStyleRecalculation]=new WebInspector.TimelineRecordStyle(WebInspector.UIString("Schedule Style Recalculation"),categories["rendering"],true); -eventStyles[recordTypes.RecalculateStyles]=new WebInspector.TimelineRecordStyle(WebInspector.UIString("Recalculate Style"),categories["rendering"]); -eventStyles[recordTypes.UpdateLayoutTree]=new WebInspector.TimelineRecordStyle(WebInspector.UIString("Recalculate Style"),categories["rendering"]); -eventStyles[recordTypes.InvalidateLayout]=new WebInspector.TimelineRecordStyle(WebInspector.UIString("Invalidate Layout"),categories["rendering"],true); -eventStyles[recordTypes.Layout]=new WebInspector.TimelineRecordStyle(WebInspector.UIString("Layout"),categories["rendering"]); -eventStyles[recordTypes.PaintSetup]=new WebInspector.TimelineRecordStyle(WebInspector.UIString("Paint Setup"),categories["painting"]); -eventStyles[recordTypes.PaintImage]=new WebInspector.TimelineRecordStyle(WebInspector.UIString("Paint Image"),categories["painting"],true); -eventStyles[recordTypes.UpdateLayer]=new WebInspector.TimelineRecordStyle(WebInspector.UIString("Update Layer"),categories["painting"],true); -eventStyles[recordTypes.UpdateLayerTree]=new WebInspector.TimelineRecordStyle(WebInspector.UIString("Update Layer Tree"),categories["rendering"]); -eventStyles[recordTypes.Paint]=new WebInspector.TimelineRecordStyle(WebInspector.UIString("Paint"),categories["painting"]); -eventStyles[recordTypes.RasterTask]=new WebInspector.TimelineRecordStyle(WebInspector.UIString("Rasterize Paint"),categories["painting"]); -eventStyles[recordTypes.ScrollLayer]=new WebInspector.TimelineRecordStyle(WebInspector.UIString("Scroll"),categories["rendering"]); -eventStyles[recordTypes.CompositeLayers]=new WebInspector.TimelineRecordStyle(WebInspector.UIString("Composite Layers"),categories["painting"]); -eventStyles[recordTypes.ParseHTML]=new WebInspector.TimelineRecordStyle(WebInspector.UIString("Parse HTML"),categories["loading"]); -eventStyles[recordTypes.ParseAuthorStyleSheet]=new WebInspector.TimelineRecordStyle(WebInspector.UIString("Parse Stylesheet"),categories["loading"]); -eventStyles[recordTypes.TimerInstall]=new WebInspector.TimelineRecordStyle(WebInspector.UIString("Install Timer"),categories["scripting"]); -eventStyles[recordTypes.TimerRemove]=new WebInspector.TimelineRecordStyle(WebInspector.UIString("Remove Timer"),categories["scripting"]); -eventStyles[recordTypes.TimerFire]=new WebInspector.TimelineRecordStyle(WebInspector.UIString("Timer Fired"),categories["scripting"]); -eventStyles[recordTypes.XHRReadyStateChange]=new WebInspector.TimelineRecordStyle(WebInspector.UIString("XHR Ready State Change"),categories["scripting"]); -eventStyles[recordTypes.XHRLoad]=new WebInspector.TimelineRecordStyle(WebInspector.UIString("XHR Load"),categories["scripting"]); -eventStyles[recordTypes.CompileScript]=new WebInspector.TimelineRecordStyle(WebInspector.UIString("Compile Script"),categories["scripting"]); -eventStyles[recordTypes.EvaluateScript]=new WebInspector.TimelineRecordStyle(WebInspector.UIString("Evaluate Script"),categories["scripting"]); -eventStyles[recordTypes.ParseScriptOnBackground]=new WebInspector.TimelineRecordStyle(WebInspector.UIString("Parse Script"),categories["scripting"]); -eventStyles[recordTypes.MarkLoad]=new WebInspector.TimelineRecordStyle(WebInspector.UIString("Load event"),categories["scripting"],true); -eventStyles[recordTypes.MarkDOMContent]=new WebInspector.TimelineRecordStyle(WebInspector.UIString("DOMContentLoaded event"),categories["scripting"],true); -eventStyles[recordTypes.MarkFirstPaint]=new WebInspector.TimelineRecordStyle(WebInspector.UIString("First paint"),categories["painting"],true); -eventStyles[recordTypes.TimeStamp]=new WebInspector.TimelineRecordStyle(WebInspector.UIString("Timestamp"),categories["scripting"]); -eventStyles[recordTypes.ConsoleTime]=new WebInspector.TimelineRecordStyle(WebInspector.UIString("Console Time"),categories["scripting"]); -eventStyles[recordTypes.UserTiming]=new WebInspector.TimelineRecordStyle(WebInspector.UIString("User Timing"),categories["scripting"]); -eventStyles[recordTypes.ResourceSendRequest]=new WebInspector.TimelineRecordStyle(WebInspector.UIString("Send Request"),categories["loading"]); -eventStyles[recordTypes.ResourceReceiveResponse]=new WebInspector.TimelineRecordStyle(WebInspector.UIString("Receive Response"),categories["loading"]); -eventStyles[recordTypes.ResourceFinish]=new WebInspector.TimelineRecordStyle(WebInspector.UIString("Finish Loading"),categories["loading"]); -eventStyles[recordTypes.ResourceReceivedData]=new WebInspector.TimelineRecordStyle(WebInspector.UIString("Receive Data"),categories["loading"]); -eventStyles[recordTypes.RunMicrotasks]=new WebInspector.TimelineRecordStyle(WebInspector.UIString("Run Microtasks"),categories["scripting"]); -eventStyles[recordTypes.FunctionCall]=new WebInspector.TimelineRecordStyle(WebInspector.UIString("Function Call"),categories["scripting"]); -eventStyles[recordTypes.GCEvent]=new WebInspector.TimelineRecordStyle(WebInspector.UIString("GC Event"),categories["scripting"]); -eventStyles[recordTypes.MajorGC]=new WebInspector.TimelineRecordStyle(WebInspector.UIString("Major GC"),categories["scripting"]); -eventStyles[recordTypes.MinorGC]=new WebInspector.TimelineRecordStyle(WebInspector.UIString("Minor GC"),categories["scripting"]); -eventStyles[recordTypes.JSFrame]=new WebInspector.TimelineRecordStyle(WebInspector.UIString("JS Frame"),categories["scripting"]); -eventStyles[recordTypes.RequestAnimationFrame]=new WebInspector.TimelineRecordStyle(WebInspector.UIString("Request Animation Frame"),categories["scripting"]); -eventStyles[recordTypes.CancelAnimationFrame]=new WebInspector.TimelineRecordStyle(WebInspector.UIString("Cancel Animation Frame"),categories["scripting"]); -eventStyles[recordTypes.FireAnimationFrame]=new WebInspector.TimelineRecordStyle(WebInspector.UIString("Animation Frame Fired"),categories["scripting"]); -eventStyles[recordTypes.RequestIdleCallback]=new WebInspector.TimelineRecordStyle(WebInspector.UIString("Request Idle Callback"),categories["scripting"]); -eventStyles[recordTypes.CancelIdleCallback]=new WebInspector.TimelineRecordStyle(WebInspector.UIString("Cancel Idle Callback"),categories["scripting"]); -eventStyles[recordTypes.FireIdleCallback]=new WebInspector.TimelineRecordStyle(WebInspector.UIString("Fire Idle Callback"),categories["scripting"]); -eventStyles[recordTypes.WebSocketCreate]=new WebInspector.TimelineRecordStyle(WebInspector.UIString("Create WebSocket"),categories["scripting"]); -eventStyles[recordTypes.WebSocketSendHandshakeRequest]=new WebInspector.TimelineRecordStyle(WebInspector.UIString("Send WebSocket Handshake"),categories["scripting"]); -eventStyles[recordTypes.WebSocketReceiveHandshakeResponse]=new WebInspector.TimelineRecordStyle(WebInspector.UIString("Receive WebSocket Handshake"),categories["scripting"]); -eventStyles[recordTypes.WebSocketDestroy]=new WebInspector.TimelineRecordStyle(WebInspector.UIString("Destroy WebSocket"),categories["scripting"]); -eventStyles[recordTypes.EmbedderCallback]=new WebInspector.TimelineRecordStyle(WebInspector.UIString("Embedder Callback"),categories["scripting"]); -eventStyles[recordTypes.DecodeImage]=new WebInspector.TimelineRecordStyle(WebInspector.UIString("Image Decode"),categories["painting"]); -eventStyles[recordTypes.ResizeImage]=new WebInspector.TimelineRecordStyle(WebInspector.UIString("Image Resize"),categories["painting"]); -eventStyles[recordTypes.GPUTask]=new WebInspector.TimelineRecordStyle(WebInspector.UIString("GPU"),categories["gpu"]); -eventStyles[recordTypes.LatencyInfo]=new WebInspector.TimelineRecordStyle(WebInspector.UIString("Input Latency"),categories["scripting"]); - -eventStyles[recordTypes.GCIdleLazySweep]=new WebInspector.TimelineRecordStyle(WebInspector.UIString("DOM GC"),categories["scripting"]); -eventStyles[recordTypes.GCCompleteSweep]=new WebInspector.TimelineRecordStyle(WebInspector.UIString("DOM GC"),categories["scripting"]); -eventStyles[recordTypes.GCCollectGarbage]=new WebInspector.TimelineRecordStyle(WebInspector.UIString("DOM GC"),categories["scripting"]); - -WebInspector.TimelineUIUtils._eventStylesMap=eventStyles; -return eventStyles; -}; - - - - - -WebInspector.TimelineUIUtils.inputEventDisplayName=function(inputEventType) -{ -if(!WebInspector.TimelineUIUtils._inputEventToDisplayName){ -var inputEvent=WebInspector.TimelineIRModel.InputEvents; - - -WebInspector.TimelineUIUtils._inputEventToDisplayName=new Map([ -[inputEvent.Char,WebInspector.UIString("Key Character")], -[inputEvent.KeyDown,WebInspector.UIString("Key Down")], -[inputEvent.KeyDownRaw,WebInspector.UIString("Key Down")], -[inputEvent.KeyUp,WebInspector.UIString("Key Up")], -[inputEvent.Click,WebInspector.UIString("Click")], -[inputEvent.ContextMenu,WebInspector.UIString("Context Menu")], -[inputEvent.MouseDown,WebInspector.UIString("Mouse Down")], -[inputEvent.MouseMove,WebInspector.UIString("Mouse Move")], -[inputEvent.MouseUp,WebInspector.UIString("Mouse Up")], -[inputEvent.MouseWheel,WebInspector.UIString("Mouse Wheel")], -[inputEvent.ScrollBegin,WebInspector.UIString("Scroll Begin")], -[inputEvent.ScrollEnd,WebInspector.UIString("Scroll End")], -[inputEvent.ScrollUpdate,WebInspector.UIString("Scroll Update")], -[inputEvent.FlingStart,WebInspector.UIString("Fling Start")], -[inputEvent.FlingCancel,WebInspector.UIString("Fling Halt")], -[inputEvent.Tap,WebInspector.UIString("Tap")], -[inputEvent.TapCancel,WebInspector.UIString("Tap Halt")], -[inputEvent.ShowPress,WebInspector.UIString("Tap Begin")], -[inputEvent.TapDown,WebInspector.UIString("Tap Down")], -[inputEvent.TouchCancel,WebInspector.UIString("Touch Cancel")], -[inputEvent.TouchEnd,WebInspector.UIString("Touch End")], -[inputEvent.TouchMove,WebInspector.UIString("Touch Move")], -[inputEvent.TouchStart,WebInspector.UIString("Touch Start")], -[inputEvent.PinchBegin,WebInspector.UIString("Pinch Begin")], -[inputEvent.PinchEnd,WebInspector.UIString("Pinch End")], -[inputEvent.PinchUpdate,WebInspector.UIString("Pinch Update")]]); - -} -return WebInspector.TimelineUIUtils._inputEventToDisplayName.get(inputEventType)||null; -}; - - - - - - -WebInspector.TimelineUIUtils.testContentMatching=function(traceEvent,regExp) -{ -var title=WebInspector.TimelineUIUtils.eventStyle(traceEvent).title; -var tokens=[title]; -if(traceEvent.url) -tokens.push(traceEvent.url); -for(var argName in traceEvent.args){ -var argValue=traceEvent.args[argName]; -for(var key in argValue) -tokens.push(argValue[key]); -} -return regExp.test(tokens.join("|")); -}; - - - - - -WebInspector.TimelineUIUtils.categoryForRecord=function(record) -{ -return WebInspector.TimelineUIUtils.eventStyle(record.traceEvent()).category; -}; - - - - - - -WebInspector.TimelineUIUtils.eventStyle=function(event) -{ -var eventStyles=WebInspector.TimelineUIUtils._initEventStyles(); -if(event.hasCategory(WebInspector.TimelineModel.Category.Console)||event.hasCategory(WebInspector.TimelineModel.Category.UserTiming)) -return{title:event.name,category:WebInspector.TimelineUIUtils.categories()["scripting"]}; - -if(event.hasCategory(WebInspector.TimelineModel.Category.LatencyInfo)){ - -var prefix="InputLatency::"; -var inputEventType=event.name.startsWith(prefix)?event.name.substr(prefix.length):event.name; -var displayName=WebInspector.TimelineUIUtils.inputEventDisplayName(inputEventType); -return{title:displayName||inputEventType,category:WebInspector.TimelineUIUtils.categories()["scripting"]}; -} -var result=eventStyles[event.name]; -if(!result){ -result=new WebInspector.TimelineRecordStyle(event.name,WebInspector.TimelineUIUtils.categories()["other"],true); -eventStyles[event.name]=result; -} -return result; -}; - - - - - -WebInspector.TimelineUIUtils.eventColor=function(event) -{ -if(event.name===WebInspector.TimelineModel.RecordType.JSFrame){ -var frame=event.args["data"]; -if(WebInspector.TimelineUIUtils.isUserFrame(frame)) -return WebInspector.TimelineUIUtils.colorForURL(frame.url); -} -return WebInspector.TimelineUIUtils.eventStyle(event).category.color; -}; - - - - - -WebInspector.TimelineUIUtils.eventTitle=function(event) -{ -var title=WebInspector.TimelineUIUtils.eventStyle(event).title; -if(event.hasCategory(WebInspector.TimelineModel.Category.Console)) -return title; -if(event.name===WebInspector.TimelineModel.RecordType.TimeStamp) -return WebInspector.UIString("%s: %s",title,event.args["data"]["message"]); -if(event.name===WebInspector.TimelineModel.RecordType.Animation&&event.args["data"]&&event.args["data"]["name"]) -return WebInspector.UIString("%s: %s",title,event.args["data"]["name"]); -return title; -}; - - - - - -WebInspector.TimelineUIUtils.eventURL=function(event) -{ -if(event.url) -return event.url; -var data=event.args["data"]||event.args["beginData"]; -return data&&data.url||null; -}; - - - - -WebInspector.TimelineUIUtils._interactionPhaseStyles=function() -{ -var map=WebInspector.TimelineUIUtils._interactionPhaseStylesMap; -if(!map){ -map=new Map([ -[WebInspector.TimelineIRModel.Phases.Idle,{color:"white",label:"Idle"}], -[WebInspector.TimelineIRModel.Phases.Response,{color:"hsl(43, 83%, 64%)",label:WebInspector.UIString("Response")}], -[WebInspector.TimelineIRModel.Phases.Scroll,{color:"hsl(256, 67%, 70%)",label:WebInspector.UIString("Scroll")}], -[WebInspector.TimelineIRModel.Phases.Fling,{color:"hsl(256, 67%, 70%)",label:WebInspector.UIString("Fling")}], -[WebInspector.TimelineIRModel.Phases.Drag,{color:"hsl(256, 67%, 70%)",label:WebInspector.UIString("Drag")}], -[WebInspector.TimelineIRModel.Phases.Animation,{color:"hsl(256, 67%, 70%)",label:WebInspector.UIString("Animation")}], -[WebInspector.TimelineIRModel.Phases.Uncategorized,{color:"hsl(0, 0%, 87%)",label:WebInspector.UIString("Uncategorized")}]]); - -WebInspector.TimelineUIUtils._interactionPhaseStylesMap=map; -} -return map; -}; - - - - - -WebInspector.TimelineUIUtils.interactionPhaseColor=function(phase) -{ -return WebInspector.TimelineUIUtils._interactionPhaseStyles().get(phase).color; -}; - - - - - -WebInspector.TimelineUIUtils.interactionPhaseLabel=function(phase) -{ -return WebInspector.TimelineUIUtils._interactionPhaseStyles().get(phase).label; -}; - - - - - -WebInspector.TimelineUIUtils.isUserFrame=function(frame) -{ -return frame.scriptId!=="0"&&!(frame.url&&frame.url.startsWith("native ")); -}; - - - - - -WebInspector.TimelineUIUtils.topStackFrame=function(event) -{ -var stackTrace=event.stackTrace||event.initiator&&event.initiator.stackTrace; -return stackTrace&&stackTrace.length?stackTrace[0]:null; -}; - - - - -WebInspector.TimelineUIUtils.NetworkCategory={ -HTML:Symbol("HTML"), -Script:Symbol("Script"), -Style:Symbol("Style"), -Media:Symbol("Media"), -Other:Symbol("Other")}; - - - - - - -WebInspector.TimelineUIUtils.networkRequestCategory=function(request) -{ -var categories=WebInspector.TimelineUIUtils.NetworkCategory; -switch(request.mimeType){ -case"text/html": -return categories.HTML; -case"application/javascript": -case"application/x-javascript": -case"text/javascript": -return categories.Script; -case"text/css": -return categories.Style; -case"audio/ogg": -case"image/gif": -case"image/jpeg": -case"image/png": -case"image/svg+xml": -case"image/webp": -case"image/x-icon": -case"font/opentype": -case"font/woff2": -case"application/font-woff": -return categories.Media; -default: -return categories.Other;} - -}; - - - - - -WebInspector.TimelineUIUtils.networkCategoryColor=function(category) -{ -var categories=WebInspector.TimelineUIUtils.NetworkCategory; -switch(category){ -case categories.HTML:return"hsl(214, 67%, 66%)"; -case categories.Script:return"hsl(43, 83%, 64%)"; -case categories.Style:return"hsl(256, 67%, 70%)"; -case categories.Media:return"hsl(109, 33%, 55%)"; -default:return"hsl(0, 0%, 70%)";} - -}; - - - - - - -WebInspector.TimelineUIUtils.buildDetailsTextForTraceEvent=function(event,target) -{ -var recordType=WebInspector.TimelineModel.RecordType; -var detailsText; -var eventData=event.args["data"]; -switch(event.name){ -case recordType.GCEvent: -case recordType.MajorGC: -case recordType.MinorGC: -var delta=event.args["usedHeapSizeBefore"]-event.args["usedHeapSizeAfter"]; -detailsText=WebInspector.UIString("%s collected",Number.bytesToString(delta)); -break; -case recordType.FunctionCall: - -if(eventData) -detailsText=linkifyLocationAsText(eventData["scriptId"],eventData["lineNumber"],0); -break; -case recordType.JSFrame: -detailsText=WebInspector.beautifyFunctionName(eventData["functionName"]); -break; -case recordType.EventDispatch: -detailsText=eventData?eventData["type"]:null; -break; -case recordType.Paint: -var width=WebInspector.TimelineUIUtils.quadWidth(eventData.clip); -var height=WebInspector.TimelineUIUtils.quadHeight(eventData.clip); -if(width&&height) -detailsText=WebInspector.UIString("%d\u2009\u00d7\u2009%d",width,height); -break; -case recordType.ParseHTML: -var endLine=event.args["endData"]&&event.args["endData"]["endLine"]; -var url=WebInspector.displayNameForURL(event.args["beginData"]["url"]); -detailsText=WebInspector.UIString("%s [%s\u2026%s]",url,event.args["beginData"]["startLine"]+1,endLine>=0?endLine+1:""); -break; - -case recordType.CompileScript: -case recordType.EvaluateScript: -var url=eventData["url"]; -if(url) -detailsText=WebInspector.displayNameForURL(url)+":"+(eventData["lineNumber"]+1); -break; -case recordType.ParseScriptOnBackground: -case recordType.XHRReadyStateChange: -case recordType.XHRLoad: -var url=eventData["url"]; -if(url) -detailsText=WebInspector.displayNameForURL(url); -break; -case recordType.TimeStamp: -detailsText=eventData["message"]; -break; - -case recordType.WebSocketCreate: -case recordType.WebSocketSendHandshakeRequest: -case recordType.WebSocketReceiveHandshakeResponse: -case recordType.WebSocketDestroy: -case recordType.ResourceSendRequest: -case recordType.ResourceReceivedData: -case recordType.ResourceReceiveResponse: -case recordType.ResourceFinish: -case recordType.PaintImage: -case recordType.DecodeImage: -case recordType.ResizeImage: -case recordType.DecodeLazyPixelRef: -if(event.url) -detailsText=WebInspector.displayNameForURL(event.url); -break; - -case recordType.EmbedderCallback: -detailsText=eventData["callbackName"]; -break; - -case recordType.Animation: -detailsText=eventData&&eventData["name"]; -break; - -case recordType.GCIdleLazySweep: -detailsText=WebInspector.UIString("idle sweep"); -break; - -case recordType.GCCompleteSweep: -detailsText=WebInspector.UIString("complete sweep"); -break; - -case recordType.GCCollectGarbage: -detailsText=WebInspector.UIString("collect"); -break; - -default: -if(event.hasCategory(WebInspector.TimelineModel.Category.Console)) -detailsText=null;else - -detailsText=linkifyTopCallFrameAsText(); -break;} - - -return detailsText; - - - - - - - -function linkifyLocationAsText(scriptId,lineNumber,columnNumber) -{ -var debuggerModel=WebInspector.DebuggerModel.fromTarget(target); -if(!target||target.isDetached()||!scriptId||!debuggerModel) -return null; -var rawLocation=debuggerModel.createRawLocationByScriptId(scriptId,lineNumber,columnNumber); -if(!rawLocation) -return null; -var uiLocation=WebInspector.debuggerWorkspaceBinding.rawLocationToUILocation(rawLocation); -return uiLocation.linkText(); -} - - - - -function linkifyTopCallFrameAsText() -{ -var frame=WebInspector.TimelineUIUtils.topStackFrame(event); -if(!frame) -return null; -var text=linkifyLocationAsText(frame.scriptId,frame.lineNumber,frame.columnNumber); -if(!text){ -text=frame.url; -if(typeof frame.lineNumber==="number") -text+=":"+(frame.lineNumber+1); -} -return text; -} -}; - - - - - - - -WebInspector.TimelineUIUtils.buildDetailsNodeForTraceEvent=function(event,target,linkifier) -{ -var recordType=WebInspector.TimelineModel.RecordType; -var details=null; -var detailsText; -var eventData=event.args["data"]; -switch(event.name){ -case recordType.GCEvent: -case recordType.MajorGC: -case recordType.MinorGC: -case recordType.EventDispatch: -case recordType.Paint: -case recordType.Animation: -case recordType.EmbedderCallback: -case recordType.ParseHTML: -case recordType.WebSocketCreate: -case recordType.WebSocketSendHandshakeRequest: -case recordType.WebSocketReceiveHandshakeResponse: -case recordType.WebSocketDestroy: -case recordType.GCIdleLazySweep: -case recordType.GCCompleteSweep: -case recordType.GCCollectGarbage: -detailsText=WebInspector.TimelineUIUtils.buildDetailsTextForTraceEvent(event,target); -break; -case recordType.PaintImage: -case recordType.DecodeImage: -case recordType.ResizeImage: -case recordType.DecodeLazyPixelRef: -case recordType.XHRReadyStateChange: -case recordType.XHRLoad: -case recordType.ResourceSendRequest: -case recordType.ResourceReceivedData: -case recordType.ResourceReceiveResponse: -case recordType.ResourceFinish: -if(event.url) -details=WebInspector.linkifyResourceAsNode(event.url); -break; -case recordType.FunctionCall: -case recordType.JSFrame: -details=createElement("span"); -details.createTextChild(WebInspector.beautifyFunctionName(eventData["functionName"])); -var location=linkifyLocation(eventData["scriptId"],eventData["url"],eventData["lineNumber"],eventData["columnNumber"]); -if(location){ -details.createTextChild(" @ "); -details.appendChild(location); -} -break; -case recordType.CompileScript: -case recordType.EvaluateScript: -var url=eventData["url"]; -if(url) -details=linkifyLocation("",url,eventData["lineNumber"],0); -break; -case recordType.ParseScriptOnBackground: -var url=eventData["url"]; -if(url) -details=linkifyLocation("",url,0,0); -break; -default: -if(event.hasCategory(WebInspector.TimelineModel.Category.Console)) -detailsText=null;else - -details=linkifyTopCallFrame(); -break;} - - -if(!details&&detailsText) -details=createTextNode(detailsText); -return details; - - - - - - - - -function linkifyLocation(scriptId,url,lineNumber,columnNumber) -{ -return linkifier.linkifyScriptLocation(target,scriptId,url,lineNumber,columnNumber,"timeline-details"); -} - - - - -function linkifyTopCallFrame() -{ -var frame=WebInspector.TimelineUIUtils.topStackFrame(event); -return frame?linkifier.maybeLinkifyConsoleCallFrame(target,frame,"timeline-details"):null; -} -}; - - - - - - - - -WebInspector.TimelineUIUtils.buildTraceEventDetails=function(event,model,linkifier,detailed,callback) -{ -var target=model.targetByEvent(event); -if(!target){ -callbackWrapper(); -return; -} -var relatedNodes=null; -var barrier=new CallbackBarrier(); -if(!event.previewElement){ -if(event.url) -WebInspector.DOMPresentationUtils.buildImagePreviewContents(target,event.url,false,barrier.createCallback(saveImage));else -if(event.picture) -WebInspector.TimelineUIUtils.buildPicturePreviewContent(event,target,barrier.createCallback(saveImage)); -} -var nodeIdsToResolve=new Set(); -if(event.backendNodeId) -nodeIdsToResolve.add(event.backendNodeId); -if(event.invalidationTrackingEvents) -WebInspector.TimelineUIUtils._collectInvalidationNodeIds(nodeIdsToResolve,event.invalidationTrackingEvents); -if(nodeIdsToResolve.size){ -var domModel=WebInspector.DOMModel.fromTarget(target); -if(domModel) -domModel.pushNodesByBackendIdsToFrontend(nodeIdsToResolve,barrier.createCallback(setRelatedNodeMap)); -} -barrier.callWhenDone(callbackWrapper); - - - - -function saveImage(element) -{ -event.previewElement=element||null; -} - - - - -function setRelatedNodeMap(nodeMap) -{ -relatedNodes=nodeMap; -} - -function callbackWrapper() -{ -callback(WebInspector.TimelineUIUtils._buildTraceEventDetailsSynchronously(event,model,linkifier,detailed,relatedNodes)); -} -}; - - - - - - - - - -WebInspector.TimelineUIUtils._buildTraceEventDetailsSynchronously=function(event,model,linkifier,detailed,relatedNodesMap) -{ -var stats={}; -var recordTypes=WebInspector.TimelineModel.RecordType; - - -var relatedNodeLabel; - -var contentHelper=new WebInspector.TimelineDetailsContentHelper(model.targetByEvent(event),linkifier); -contentHelper.addSection(WebInspector.TimelineUIUtils.eventTitle(event),WebInspector.TimelineUIUtils.eventStyle(event).category); - -var eventData=event.args["data"]; -var initiator=event.initiator; - -if(event.warning) -contentHelper.appendWarningRow(event); -if(event.name===recordTypes.JSFrame&&eventData["deoptReason"]) -contentHelper.appendWarningRow(event,WebInspector.TimelineModel.WarningType.V8Deopt); - -if(detailed){ -contentHelper.appendTextRow(WebInspector.UIString("Self Time"),Number.millisToString(event.selfTime,true)); -contentHelper.appendTextRow(WebInspector.UIString("Total Time"),Number.millisToString(event.duration||0,true)); -} - -switch(event.name){ -case recordTypes.GCEvent: -case recordTypes.MajorGC: -case recordTypes.MinorGC: -var delta=event.args["usedHeapSizeBefore"]-event.args["usedHeapSizeAfter"]; -contentHelper.appendTextRow(WebInspector.UIString("Collected"),Number.bytesToString(delta)); -break; -case recordTypes.JSFrame: -case recordTypes.FunctionCall: -var detailsNode=WebInspector.TimelineUIUtils.buildDetailsNodeForTraceEvent(event,model.targetByEvent(event),linkifier); -if(detailsNode) -contentHelper.appendElementRow(WebInspector.UIString("Function"),detailsNode); -break; -case recordTypes.TimerFire: -case recordTypes.TimerInstall: -case recordTypes.TimerRemove: -contentHelper.appendTextRow(WebInspector.UIString("Timer ID"),eventData["timerId"]); -if(event.name===recordTypes.TimerInstall){ -contentHelper.appendTextRow(WebInspector.UIString("Timeout"),Number.millisToString(eventData["timeout"])); -contentHelper.appendTextRow(WebInspector.UIString("Repeats"),!eventData["singleShot"]); -} -break; -case recordTypes.FireAnimationFrame: -contentHelper.appendTextRow(WebInspector.UIString("Callback ID"),eventData["id"]); -break; -case recordTypes.ResourceSendRequest: -case recordTypes.ResourceReceiveResponse: -case recordTypes.ResourceReceivedData: -case recordTypes.ResourceFinish: -var url=event.name===recordTypes.ResourceSendRequest?eventData["url"]:initiator&&initiator.args["data"]["url"]; -if(url) -contentHelper.appendElementRow(WebInspector.UIString("Resource"),WebInspector.linkifyResourceAsNode(url)); -if(eventData["requestMethod"]) -contentHelper.appendTextRow(WebInspector.UIString("Request Method"),eventData["requestMethod"]); -if(typeof eventData["statusCode"]==="number") -contentHelper.appendTextRow(WebInspector.UIString("Status Code"),eventData["statusCode"]); -if(eventData["mimeType"]) -contentHelper.appendTextRow(WebInspector.UIString("MIME Type"),eventData["mimeType"]); -if("priority"in eventData){ -var priority=WebInspector.uiLabelForPriority(eventData["priority"]); -contentHelper.appendTextRow(WebInspector.UIString("Priority"),priority); -} -if(eventData["encodedDataLength"]) -contentHelper.appendTextRow(WebInspector.UIString("Encoded Data Length"),WebInspector.UIString("%d Bytes",eventData["encodedDataLength"])); -break; -case recordTypes.CompileScript: -case recordTypes.EvaluateScript: -var url=eventData["url"]; -if(url) -contentHelper.appendLocationRow(WebInspector.UIString("Script"),url,eventData["lineNumber"],eventData["columnNumber"]); -break; -case recordTypes.Paint: -var clip=eventData["clip"]; -contentHelper.appendTextRow(WebInspector.UIString("Location"),WebInspector.UIString("(%d, %d)",clip[0],clip[1])); -var clipWidth=WebInspector.TimelineUIUtils.quadWidth(clip); -var clipHeight=WebInspector.TimelineUIUtils.quadHeight(clip); -contentHelper.appendTextRow(WebInspector.UIString("Dimensions"),WebInspector.UIString("%d × %d",clipWidth,clipHeight)); - - -case recordTypes.PaintSetup: -case recordTypes.Rasterize: -case recordTypes.ScrollLayer: -relatedNodeLabel=WebInspector.UIString("Layer Root"); -break; -case recordTypes.PaintImage: -case recordTypes.DecodeLazyPixelRef: -case recordTypes.DecodeImage: -case recordTypes.ResizeImage: -case recordTypes.DrawLazyPixelRef: -relatedNodeLabel=WebInspector.UIString("Owner Element"); -if(event.url) -contentHelper.appendElementRow(WebInspector.UIString("Image URL"),WebInspector.linkifyResourceAsNode(event.url)); -break; -case recordTypes.ParseAuthorStyleSheet: -var url=eventData["styleSheetUrl"]; -if(url) -contentHelper.appendElementRow(WebInspector.UIString("Stylesheet URL"),WebInspector.linkifyResourceAsNode(url)); -break; -case recordTypes.UpdateLayoutTree: -case recordTypes.RecalculateStyles: -contentHelper.appendTextRow(WebInspector.UIString("Elements Affected"),event.args["elementCount"]); -break; -case recordTypes.Layout: -var beginData=event.args["beginData"]; -contentHelper.appendTextRow(WebInspector.UIString("Nodes That Need Layout"),WebInspector.UIString("%s of %s",beginData["dirtyObjects"],beginData["totalObjects"])); -relatedNodeLabel=WebInspector.UIString("Layout root"); -break; -case recordTypes.ConsoleTime: -contentHelper.appendTextRow(WebInspector.UIString("Message"),event.name); -break; -case recordTypes.WebSocketCreate: -case recordTypes.WebSocketSendHandshakeRequest: -case recordTypes.WebSocketReceiveHandshakeResponse: -case recordTypes.WebSocketDestroy: -var initiatorData=initiator?initiator.args["data"]:eventData; -if(typeof initiatorData["webSocketURL"]!=="undefined") -contentHelper.appendTextRow(WebInspector.UIString("URL"),initiatorData["webSocketURL"]); -if(typeof initiatorData["webSocketProtocol"]!=="undefined") -contentHelper.appendTextRow(WebInspector.UIString("WebSocket Protocol"),initiatorData["webSocketProtocol"]); -if(typeof eventData["message"]!=="undefined") -contentHelper.appendTextRow(WebInspector.UIString("Message"),eventData["message"]); -break; -case recordTypes.EmbedderCallback: -contentHelper.appendTextRow(WebInspector.UIString("Callback Function"),eventData["callbackName"]); -break; -case recordTypes.Animation: -if(event.phase===WebInspector.TracingModel.Phase.NestableAsyncInstant) -contentHelper.appendTextRow(WebInspector.UIString("State"),eventData["state"]); -break; -case recordTypes.ParseHTML: -var beginData=event.args["beginData"]; -var url=beginData["url"]; -var startLine=beginData["startLine"]-1; -var endLine=event.args["endData"]?event.args["endData"]["endLine"]-1:undefined; -if(url) -contentHelper.appendLocationRange(WebInspector.UIString("Range"),url,startLine,endLine); -break; - -case recordTypes.FireIdleCallback: -contentHelper.appendTextRow(WebInspector.UIString("Allotted Time"),Number.millisToString(eventData["allottedMilliseconds"])); -contentHelper.appendTextRow(WebInspector.UIString("Invoked by Timeout"),eventData["timedOut"]); - - -case recordTypes.RequestIdleCallback: -case recordTypes.CancelIdleCallback: -contentHelper.appendTextRow(WebInspector.UIString("Callback ID"),eventData["id"]); -break; -case recordTypes.EventDispatch: -contentHelper.appendTextRow(WebInspector.UIString("Type"),eventData["type"]); -break; - -default: -var detailsNode=WebInspector.TimelineUIUtils.buildDetailsNodeForTraceEvent(event,model.targetByEvent(event),linkifier); -if(detailsNode) -contentHelper.appendElementRow(WebInspector.UIString("Details"),detailsNode); -break;} - - -if(event.timeWaitingForMainThread) -contentHelper.appendTextRow(WebInspector.UIString("Time Waiting for Main Thread"),Number.millisToString(event.timeWaitingForMainThread,true)); - -var relatedNode=relatedNodesMap&&relatedNodesMap.get(event.backendNodeId); -if(relatedNode) -contentHelper.appendElementRow(relatedNodeLabel||WebInspector.UIString("Related Node"),WebInspector.DOMPresentationUtils.linkifyNodeReference(relatedNode)); - -if(event.previewElement){ -contentHelper.addSection(WebInspector.UIString("Preview")); -contentHelper.appendElementRow("",event.previewElement); -} - -if(event.stackTrace||event.initiator&&event.initiator.stackTrace||event.invalidationTrackingEvents) -WebInspector.TimelineUIUtils._generateCauses(event,model.targetByEvent(event),relatedNodesMap,contentHelper); - -var showPieChart=detailed&&WebInspector.TimelineUIUtils._aggregatedStatsForTraceEvent(stats,model,event); -if(showPieChart){ -contentHelper.addSection(WebInspector.UIString("Aggregated Time")); -var pieChart=WebInspector.TimelineUIUtils.generatePieChart(stats,WebInspector.TimelineUIUtils.eventStyle(event).category,event.selfTime); -contentHelper.appendElementRow("",pieChart); -} - -return contentHelper.fragment; -}; - -WebInspector.TimelineUIUtils._aggregatedStatsKey=Symbol("aggregatedStats"); - - - - - - - -WebInspector.TimelineUIUtils.buildRangeStats=function(model,startTime,endTime) -{ -var aggregatedStats={}; - - - - - - -function compareEndTime(value,task) -{ -return value<task.endTime()?-1:1; -} -var mainThreadTasks=model.mainThreadTasks(); -var taskIndex=mainThreadTasks.lowerBound(startTime,compareEndTime); -for(;taskIndex<mainThreadTasks.length;++taskIndex){ -var task=mainThreadTasks[taskIndex]; -if(task.startTime()>endTime) -break; -if(task.startTime()>startTime&&task.endTime()<endTime){ - -var taskStats=task[WebInspector.TimelineUIUtils._aggregatedStatsKey]; -if(!taskStats){ -taskStats={}; -WebInspector.TimelineUIUtils._collectAggregatedStatsForRecord(task,startTime,endTime,taskStats); -task[WebInspector.TimelineUIUtils._aggregatedStatsKey]=taskStats; -} -for(var key in taskStats) -aggregatedStats[key]=(aggregatedStats[key]||0)+taskStats[key]; -continue; -} -WebInspector.TimelineUIUtils._collectAggregatedStatsForRecord(task,startTime,endTime,aggregatedStats); -} - -var aggregatedTotal=0; -for(var categoryName in aggregatedStats) -aggregatedTotal+=aggregatedStats[categoryName]; -aggregatedStats["idle"]=Math.max(0,endTime-startTime-aggregatedTotal); - -var startOffset=startTime-model.minimumRecordTime(); -var endOffset=endTime-model.minimumRecordTime(); - -var contentHelper=new WebInspector.TimelineDetailsContentHelper(null,null); -contentHelper.addSection(WebInspector.UIString("Range: %s \u2013 %s",Number.millisToString(startOffset),Number.millisToString(endOffset))); -var pieChart=WebInspector.TimelineUIUtils.generatePieChart(aggregatedStats); -contentHelper.appendElementRow("",pieChart); -return contentHelper.fragment; -}; - - - - - - - -WebInspector.TimelineUIUtils._collectAggregatedStatsForRecord=function(record,startTime,endTime,aggregatedStats) -{ -var records=[]; - -if(!record.endTime()||record.endTime()<startTime||record.startTime()>endTime) -return; - -var childrenTime=0; -var children=record.children()||[]; -for(var i=0;i<children.length;++i){ -var child=children[i]; -if(!child.endTime()||child.endTime()<startTime||child.startTime()>endTime) -continue; -childrenTime+=Math.min(endTime,child.endTime())-Math.max(startTime,child.startTime()); -WebInspector.TimelineUIUtils._collectAggregatedStatsForRecord(child,startTime,endTime,aggregatedStats); -} -var categoryName=WebInspector.TimelineUIUtils.categoryForRecord(record).name; -var ownTime=Math.min(endTime,record.endTime())-Math.max(startTime,record.startTime())-childrenTime; -aggregatedStats[categoryName]=(aggregatedStats[categoryName]||0)+ownTime; -}; - - - - - - - -WebInspector.TimelineUIUtils.buildNetworkRequestDetails=function(request,model,linkifier) -{ -var target=model.targetByEvent(request.children[0]); -var contentHelper=new WebInspector.TimelineDetailsContentHelper(target,linkifier); - -var duration=request.endTime-(request.startTime||-Infinity); -var items=[]; -if(request.url) -contentHelper.appendElementRow(WebInspector.UIString("URL"),WebInspector.linkifyURLAsNode(request.url)); -if(isFinite(duration)) -contentHelper.appendTextRow(WebInspector.UIString("Duration"),Number.millisToString(duration,true)); -if(request.requestMethod) -contentHelper.appendTextRow(WebInspector.UIString("Request Method"),request.requestMethod); -if(typeof request.priority==="string"){ -var priority=WebInspector.uiLabelForPriority(request.priority); -contentHelper.appendTextRow(WebInspector.UIString("Priority"),priority); -} -if(request.mimeType) -contentHelper.appendTextRow(WebInspector.UIString("Mime Type"),request.mimeType); - -var title=WebInspector.UIString("Initiator"); -var sendRequest=request.children[0]; -var topFrame=WebInspector.TimelineUIUtils.topStackFrame(sendRequest); -if(topFrame){ -var link=linkifier.maybeLinkifyConsoleCallFrame(target,topFrame); -if(link) -contentHelper.appendElementRow(title,link); -}else if(sendRequest.initiator){ -var initiatorURL=WebInspector.TimelineUIUtils.eventURL(sendRequest.initiator); -if(initiatorURL){ -var link=linkifier.maybeLinkifyScriptLocation(target,null,initiatorURL,0); -if(link) -contentHelper.appendElementRow(title,link); -} -} - - - - -function action(fulfill) -{ -WebInspector.DOMPresentationUtils.buildImagePreviewContents(target,request.url,false,saveImage); - - - -function saveImage(element) -{ -request.previewElement=element||null; -fulfill(request.previewElement); -} -} -var previewPromise; -if(request.previewElement) -previewPromise=Promise.resolve(request.previewElement);else - -previewPromise=request.url&&target?new Promise(action):Promise.resolve(null); - - - - -function appendPreview(element) -{ -if(element) -contentHelper.appendElementRow(WebInspector.UIString("Preview"),request.previewElement); -return contentHelper.fragment; -} -return previewPromise.then(appendPreview); -}; - - - - - -WebInspector.TimelineUIUtils._stackTraceFromCallFrames=function(callFrames) -{ -return{callFrames:callFrames}; -}; - - - - - - - -WebInspector.TimelineUIUtils._generateCauses=function(event,target,relatedNodesMap,contentHelper) -{ -var recordTypes=WebInspector.TimelineModel.RecordType; - -var callSiteStackLabel; -var stackLabel; -var initiator=event.initiator; - -switch(event.name){ -case recordTypes.TimerFire: -callSiteStackLabel=WebInspector.UIString("Timer Installed"); -break; -case recordTypes.FireAnimationFrame: -callSiteStackLabel=WebInspector.UIString("Animation Frame Requested"); -break; -case recordTypes.FireIdleCallback: -callSiteStackLabel=WebInspector.UIString("Idle Callback Requested"); -break; -case recordTypes.UpdateLayoutTree: -case recordTypes.RecalculateStyles: -stackLabel=WebInspector.UIString("Recalculation Forced"); -break; -case recordTypes.Layout: -callSiteStackLabel=WebInspector.UIString("First Layout Invalidation"); -stackLabel=WebInspector.UIString("Layout Forced"); -break;} - - - -if(event.stackTrace&&event.stackTrace.length){ -contentHelper.addSection(WebInspector.UIString("Call Stacks")); -contentHelper.appendStackTrace(stackLabel||WebInspector.UIString("Stack Trace"),WebInspector.TimelineUIUtils._stackTraceFromCallFrames(event.stackTrace)); -} - - -if(event.invalidationTrackingEvents&&target){ -contentHelper.addSection(WebInspector.UIString("Invalidations")); -WebInspector.TimelineUIUtils._generateInvalidations(event,target,relatedNodesMap,contentHelper); -}else if(initiator&&initiator.stackTrace){ -contentHelper.appendStackTrace(callSiteStackLabel||WebInspector.UIString("First Invalidated"),WebInspector.TimelineUIUtils._stackTraceFromCallFrames(initiator.stackTrace)); -} -}; - - - - - - - -WebInspector.TimelineUIUtils._generateInvalidations=function(event,target,relatedNodesMap,contentHelper) -{ -if(!event.invalidationTrackingEvents) -return; - -var invalidations={}; -event.invalidationTrackingEvents.forEach(function(invalidation){ -if(!invalidations[invalidation.type]) -invalidations[invalidation.type]=[invalidation];else - -invalidations[invalidation.type].push(invalidation); -}); - -Object.keys(invalidations).forEach(function(type){ -WebInspector.TimelineUIUtils._generateInvalidationsForType( -type,target,invalidations[type],relatedNodesMap,contentHelper); -}); -}; - - - - - - - - -WebInspector.TimelineUIUtils._generateInvalidationsForType=function(type,target,invalidations,relatedNodesMap,contentHelper) -{ -var title; -switch(type){ -case WebInspector.TimelineModel.RecordType.StyleRecalcInvalidationTracking: -title=WebInspector.UIString("Style Invalidations"); -break; -case WebInspector.TimelineModel.RecordType.LayoutInvalidationTracking: -title=WebInspector.UIString("Layout Invalidations"); -break; -default: -title=WebInspector.UIString("Other Invalidations"); -break;} - - -var invalidationsTreeOutline=new TreeOutlineInShadow(); -invalidationsTreeOutline.registerRequiredCSS("timeline/invalidationsTree.css"); -invalidationsTreeOutline.element.classList.add("invalidations-tree"); - -var invalidationGroups=groupInvalidationsByCause(invalidations); -invalidationGroups.forEach(function(group){ -var groupElement=new WebInspector.TimelineUIUtils.InvalidationsGroupElement(target,relatedNodesMap,contentHelper,group); -invalidationsTreeOutline.appendChild(groupElement); -}); -contentHelper.appendElementRow(title,invalidationsTreeOutline.element,false,true); - - - - - -function groupInvalidationsByCause(invalidations) -{ - -var causeToInvalidationMap=new Map(); -for(var index=0;index<invalidations.length;index++){ -var invalidation=invalidations[index]; -var causeKey=""; -if(invalidation.cause.reason) -causeKey+=invalidation.cause.reason+"."; -if(invalidation.cause.stackTrace){ -invalidation.cause.stackTrace.forEach(function(stackFrame){ -causeKey+=stackFrame["functionName"]+"."; -causeKey+=stackFrame["scriptId"]+"."; -causeKey+=stackFrame["url"]+"."; -causeKey+=stackFrame["lineNumber"]+"."; -causeKey+=stackFrame["columnNumber"]+"."; -}); -} - -if(causeToInvalidationMap.has(causeKey)) -causeToInvalidationMap.get(causeKey).push(invalidation);else - -causeToInvalidationMap.set(causeKey,[invalidation]); -} -return causeToInvalidationMap.valuesArray(); -} -}; - - - - - -WebInspector.TimelineUIUtils._collectInvalidationNodeIds=function(nodeIds,invalidations) -{ -for(var i=0;i<invalidations.length;++i){ -if(invalidations[i].nodeId) -nodeIds.add(invalidations[i].nodeId); -} -}; - - - - - - - - - -WebInspector.TimelineUIUtils.InvalidationsGroupElement=function(target,relatedNodesMap,contentHelper,invalidations) -{ -TreeElement.call(this,"",true); - -this.listItemElement.classList.add("header"); -this.selectable=false; -this.toggleOnClick=true; - -this._relatedNodesMap=relatedNodesMap; -this._contentHelper=contentHelper; -this._invalidations=invalidations; -this.title=this._createTitle(target); -}; - -WebInspector.TimelineUIUtils.InvalidationsGroupElement.prototype={ - - - - - -_createTitle:function(target) -{ -var first=this._invalidations[0]; -var reason=first.cause.reason; -var topFrame=first.cause.stackTrace&&first.cause.stackTrace[0]; - -var title=createElement("span"); -if(reason) -title.createTextChild(WebInspector.UIString("%s for ",reason));else - -title.createTextChild(WebInspector.UIString("Unknown cause for ")); - -this._appendTruncatedNodeList(title,this._invalidations); - -if(topFrame&&this._contentHelper.linkifier()){ -title.createTextChild(WebInspector.UIString(". ")); -var stack=title.createChild("span","monospace"); -stack.createChild("span").textContent=WebInspector.beautifyFunctionName(topFrame.functionName); -var link=this._contentHelper.linkifier().maybeLinkifyConsoleCallFrame(target,topFrame); -if(link){ -stack.createChild("span").textContent=" @ "; -stack.createChild("span").appendChild(link); -} -} - -return title; -}, - - - - -onpopulate:function() -{ -var content=createElementWithClass("div","content"); - -var first=this._invalidations[0]; -if(first.cause.stackTrace){ -var stack=content.createChild("div"); -stack.createTextChild(WebInspector.UIString("Stack trace:")); -this._contentHelper.createChildStackTraceElement(stack,WebInspector.TimelineUIUtils._stackTraceFromCallFrames(first.cause.stackTrace)); -} - -content.createTextChild(this._invalidations.length>1?WebInspector.UIString("Nodes:"):WebInspector.UIString("Node:")); -var nodeList=content.createChild("div","node-list"); -var firstNode=true; -for(var i=0;i<this._invalidations.length;i++){ -var invalidation=this._invalidations[i]; -var invalidationNode=this._createInvalidationNode(invalidation,true); -if(invalidationNode){ -if(!firstNode) -nodeList.createTextChild(WebInspector.UIString(", ")); -firstNode=false; - -nodeList.appendChild(invalidationNode); - -var extraData=invalidation.extraData?", "+invalidation.extraData:""; -if(invalidation.changedId) -nodeList.createTextChild(WebInspector.UIString("(changed id to \"%s\"%s)",invalidation.changedId,extraData));else -if(invalidation.changedClass) -nodeList.createTextChild(WebInspector.UIString("(changed class to \"%s\"%s)",invalidation.changedClass,extraData));else -if(invalidation.changedAttribute) -nodeList.createTextChild(WebInspector.UIString("(changed attribute to \"%s\"%s)",invalidation.changedAttribute,extraData));else -if(invalidation.changedPseudo) -nodeList.createTextChild(WebInspector.UIString("(changed pesudo to \"%s\"%s)",invalidation.changedPseudo,extraData));else -if(invalidation.selectorPart) -nodeList.createTextChild(WebInspector.UIString("(changed \"%s\"%s)",invalidation.selectorPart,extraData)); -} -} - -var contentTreeElement=new TreeElement(content,false); -contentTreeElement.selectable=false; -this.appendChild(contentTreeElement); -}, - - - - - -_appendTruncatedNodeList:function(parentElement,invalidations) -{ -var invalidationNodes=[]; -var invalidationNodeIdMap={}; -for(var i=0;i<invalidations.length;i++){ -var invalidation=invalidations[i]; -var invalidationNode=this._createInvalidationNode(invalidation,false); -invalidationNode.addEventListener("click",consumeEvent,false); -if(invalidationNode&&!invalidationNodeIdMap[invalidation.nodeId]){ -invalidationNodes.push(invalidationNode); -invalidationNodeIdMap[invalidation.nodeId]=true; -} -} - -if(invalidationNodes.length===1){ -parentElement.appendChild(invalidationNodes[0]); -}else if(invalidationNodes.length===2){ -parentElement.appendChild(invalidationNodes[0]); -parentElement.createTextChild(WebInspector.UIString(" and ")); -parentElement.appendChild(invalidationNodes[1]); -}else if(invalidationNodes.length>=3){ -parentElement.appendChild(invalidationNodes[0]); -parentElement.createTextChild(WebInspector.UIString(", ")); -parentElement.appendChild(invalidationNodes[1]); -parentElement.createTextChild(WebInspector.UIString(", and %s others",invalidationNodes.length-2)); -} -}, - - - - - -_createInvalidationNode:function(invalidation,showUnknownNodes) -{ -var node=invalidation.nodeId&&this._relatedNodesMap?this._relatedNodesMap.get(invalidation.nodeId):null; -if(node) -return WebInspector.DOMPresentationUtils.linkifyNodeReference(node); -if(invalidation.nodeName){ -var nodeSpan=createElement("span"); -nodeSpan.textContent=WebInspector.UIString("[ %s ]",invalidation.nodeName); -return nodeSpan; -} -if(showUnknownNodes){ -var nodeSpan=createElement("span"); -return nodeSpan.createTextChild(WebInspector.UIString("[ unknown node ]")); -} -}, - -__proto__:TreeElement.prototype}; - - - - - - - - -WebInspector.TimelineUIUtils._aggregatedStatsForTraceEvent=function(total,model,event) -{ -var events=model.inspectedTargetEvents(); - - - - - -function eventComparator(startTime,e) -{ -return startTime-e.startTime; -} -var index=events.binaryIndexOf(event.startTime,eventComparator); - -if(index<0) -return false; -var hasChildren=false; -var endTime=event.endTime; -if(endTime){ -for(var i=index;i<events.length;i++){ -var nextEvent=events[i]; -if(nextEvent.startTime>=endTime) -break; -if(!nextEvent.selfTime) -continue; -if(nextEvent.thread!==event.thread) -continue; -if(i>index) -hasChildren=true; -var categoryName=WebInspector.TimelineUIUtils.eventStyle(nextEvent).category.name; -total[categoryName]=(total[categoryName]||0)+nextEvent.selfTime; -} -} -if(WebInspector.TracingModel.isAsyncPhase(event.phase)){ -if(event.endTime){ -var aggregatedTotal=0; -for(var categoryName in total) -aggregatedTotal+=total[categoryName]; -total["idle"]=Math.max(0,event.endTime-event.startTime-aggregatedTotal); -} -return false; -} -return hasChildren; -}; - - - - - - -WebInspector.TimelineUIUtils.buildPicturePreviewContent=function(event,target,callback) -{ -new WebInspector.LayerPaintEvent(event,target).loadSnapshot(onSnapshotLoaded); - - - - -function onSnapshotLoaded(rect,snapshot) -{ -if(!snapshot){ -callback(); -return; -} -snapshot.requestImage(null,null,1,onGotImage); -snapshot.dispose(); -} - - - - -function onGotImage(imageURL) -{ -if(!imageURL){ -callback(); -return; -} -var container=createElement("div"); -container.classList.add("image-preview-container","vbox","link"); -var img=container.createChild("img"); -img.src=imageURL; -var paintProfilerButton=container.createChild("a"); -paintProfilerButton.textContent=WebInspector.UIString("Paint Profiler"); -container.addEventListener("click",showPaintProfiler,false); -callback(container); -} - -function showPaintProfiler() -{ -WebInspector.TimelinePanel.instance().select(WebInspector.TimelineSelection.fromTraceEvent(event),WebInspector.TimelinePanel.DetailsTab.PaintProfiler); -} -}; - - - - - - - -WebInspector.TimelineUIUtils.createEventDivider=function(recordType,title,position) -{ -var eventDivider=createElement("div"); -eventDivider.className="resources-event-divider"; -var recordTypes=WebInspector.TimelineModel.RecordType; - -if(recordType===recordTypes.MarkDOMContent) -eventDivider.className+=" resources-blue-divider";else -if(recordType===recordTypes.MarkLoad) -eventDivider.className+=" resources-red-divider";else -if(recordType===recordTypes.MarkFirstPaint) -eventDivider.className+=" resources-green-divider";else -if(recordType===recordTypes.TimeStamp||recordType===recordTypes.ConsoleTime||recordType===recordTypes.UserTiming) -eventDivider.className+=" resources-orange-divider";else -if(recordType===recordTypes.BeginFrame) -eventDivider.className+=" timeline-frame-divider"; - -if(title) -eventDivider.title=title; -eventDivider.style.left=position+"px"; -return eventDivider; -}; - - - - - - - -WebInspector.TimelineUIUtils.createDividerForRecord=function(record,zeroTime,position) -{ -var startTime=Number.millisToString(record.startTime()-zeroTime); -var title=WebInspector.UIString("%s at %s",WebInspector.TimelineUIUtils.eventTitle(record.traceEvent()),startTime); -return WebInspector.TimelineUIUtils.createEventDivider(record.type(),title,position); -}; - - - - -WebInspector.TimelineUIUtils._visibleTypes=function() -{ -var eventStyles=WebInspector.TimelineUIUtils._initEventStyles(); -var result=[]; -for(var name in eventStyles){ -if(!eventStyles[name].hidden) -result.push(name); -} -return result; -}; - - - - -WebInspector.TimelineUIUtils.visibleEventsFilter=function() -{ -return new WebInspector.TimelineVisibleEventsFilter(WebInspector.TimelineUIUtils._visibleTypes()); -}; - - - - -WebInspector.TimelineUIUtils.categories=function() -{ -if(WebInspector.TimelineUIUtils._categories) -return WebInspector.TimelineUIUtils._categories; -WebInspector.TimelineUIUtils._categories={ -loading:new WebInspector.TimelineCategory("loading",WebInspector.UIString("Loading"),true,"hsl(214, 67%, 74%)","hsl(214, 67%, 66%)"), -scripting:new WebInspector.TimelineCategory("scripting",WebInspector.UIString("Scripting"),true,"hsl(43, 83%, 72%)","hsl(43, 83%, 64%) "), -rendering:new WebInspector.TimelineCategory("rendering",WebInspector.UIString("Rendering"),true,"hsl(256, 67%, 76%)","hsl(256, 67%, 70%)"), -painting:new WebInspector.TimelineCategory("painting",WebInspector.UIString("Painting"),true,"hsl(109, 33%, 64%)","hsl(109, 33%, 55%)"), -gpu:new WebInspector.TimelineCategory("gpu",WebInspector.UIString("GPU"),false,"hsl(109, 33%, 64%)","hsl(109, 33%, 55%)"), -other:new WebInspector.TimelineCategory("other",WebInspector.UIString("Other"),false,"hsl(0, 0%, 87%)","hsl(0, 0%, 79%)"), -idle:new WebInspector.TimelineCategory("idle",WebInspector.UIString("Idle"),false,"hsl(0, 100%, 100%)","hsl(0, 100%, 100%)")}; - -return WebInspector.TimelineUIUtils._categories; -}; - - - - - -WebInspector.TimelineUIUtils.titleForAsyncEventGroup=function(group) -{ -if(!WebInspector.TimelineUIUtils._titleForAsyncEventGroupMap){ -var groups=WebInspector.TimelineModel.AsyncEventGroup; -WebInspector.TimelineUIUtils._titleForAsyncEventGroupMap=new Map([ -[groups.animation,WebInspector.UIString("Animation")], -[groups.console,WebInspector.UIString("Console")], -[groups.userTiming,WebInspector.UIString("User Timing")], -[groups.input,WebInspector.UIString("Input")]]); - -} -return WebInspector.TimelineUIUtils._titleForAsyncEventGroupMap.get(group)||""; -}; - - - - - - - -WebInspector.TimelineUIUtils.generatePieChart=function(aggregatedStats,selfCategory,selfTime) -{ -var total=0; -for(var categoryName in aggregatedStats) -total+=aggregatedStats[categoryName]; - -var element=createElementWithClass("div","timeline-details-view-pie-chart-wrapper hbox"); -var pieChart=new WebInspector.PieChart(100); -pieChart.element.classList.add("timeline-details-view-pie-chart"); -pieChart.setTotal(total); -var pieChartContainer=element.createChild("div","vbox"); -pieChartContainer.appendChild(pieChart.element); -pieChartContainer.createChild("div","timeline-details-view-pie-chart-total").textContent=WebInspector.UIString("Total: %s",Number.millisToString(total,true)); -var footerElement=element.createChild("div","timeline-aggregated-info-legend"); - - - - - - - -function appendLegendRow(name,title,value,color) -{ -if(!value) -return; -pieChart.addSlice(value,color); -var rowElement=footerElement.createChild("div"); -rowElement.createChild("span","timeline-aggregated-legend-value").textContent=Number.preciseMillisToString(value,1); -rowElement.createChild("span","timeline-aggregated-legend-swatch").style.backgroundColor=color; -rowElement.createChild("span","timeline-aggregated-legend-title").textContent=title; -} - - -if(selfCategory){ -if(selfTime) -appendLegendRow(selfCategory.name,WebInspector.UIString("%s (self)",selfCategory.title),selfTime,selfCategory.color); - -var categoryTime=aggregatedStats[selfCategory.name]; -var value=categoryTime-selfTime; -if(value>0) -appendLegendRow(selfCategory.name,WebInspector.UIString("%s (children)",selfCategory.title),value,selfCategory.childColor); -} - - -for(var categoryName in WebInspector.TimelineUIUtils.categories()){ -var category=WebInspector.TimelineUIUtils.categories()[categoryName]; -if(category===selfCategory) -continue; -appendLegendRow(category.name,category.title,aggregatedStats[category.name],category.childColor); -} -return element; -}; - - - - - - - -WebInspector.TimelineUIUtils.generateDetailsContentForFrame=function(frameModel,frame,filmStripFrame) -{ -var pieChart=WebInspector.TimelineUIUtils.generatePieChart(frame.timeByCategory); -var contentHelper=new WebInspector.TimelineDetailsContentHelper(null,null); -contentHelper.addSection(WebInspector.UIString("Frame")); - -var duration=WebInspector.TimelineUIUtils.frameDuration(frame); -contentHelper.appendElementRow(WebInspector.UIString("Duration"),duration,frame.hasWarnings()); -if(filmStripFrame){ -var filmStripPreview=createElementWithClass("img","timeline-filmstrip-preview"); -filmStripFrame.imageDataPromise().then(onGotImageData.bind(null,filmStripPreview)); -contentHelper.appendElementRow("",filmStripPreview); -filmStripPreview.addEventListener("click",frameClicked.bind(null,filmStripFrame),false); -} -var durationInMillis=frame.endTime-frame.startTime; -contentHelper.appendTextRow(WebInspector.UIString("FPS"),Math.floor(1000/durationInMillis)); -contentHelper.appendTextRow(WebInspector.UIString("CPU time"),Number.millisToString(frame.cpuTime,true)); - -if(Runtime.experiments.isEnabled("layersPanel")&&frame.layerTree){ -contentHelper.appendElementRow(WebInspector.UIString("Layer tree"), -WebInspector.Linkifier.linkifyUsingRevealer(frame.layerTree,WebInspector.UIString("show"))); -} - - - - - -function onGotImageData(image,data) -{ -if(data) -image.src="data:image/jpg;base64,"+data; -} - - - - -function frameClicked(filmStripFrame) -{ -new WebInspector.FilmStripView.Dialog(filmStripFrame,0); -} - -return contentHelper.fragment; -}; - - - - - -WebInspector.TimelineUIUtils.frameDuration=function(frame) -{ -var durationText=WebInspector.UIString("%s (at %s)",Number.millisToString(frame.endTime-frame.startTime,true), -Number.millisToString(frame.startTimeOffset,true)); -var element=createElement("span"); -element.createTextChild(durationText); -if(!frame.hasWarnings()) -return element; -element.createTextChild(WebInspector.UIString(". Long frame times are an indication of ")); -element.appendChild(WebInspector.linkifyURLAsNode("https://developers.google.com/web/fundamentals/performance/rendering/", -WebInspector.UIString("jank"),undefined,true)); -element.createTextChild("."); -return element; -}; - - - - - - - - - - -WebInspector.TimelineUIUtils.createFillStyle=function(context,width,height,color0,color1,color2) -{ -var gradient=context.createLinearGradient(0,0,width,height); -gradient.addColorStop(0,color0); -gradient.addColorStop(0.25,color1); -gradient.addColorStop(0.75,color1); -gradient.addColorStop(1,color2); -return gradient; -}; - - - - - -WebInspector.TimelineUIUtils.quadWidth=function(quad) -{ -return Math.round(Math.sqrt(Math.pow(quad[0]-quad[2],2)+Math.pow(quad[1]-quad[3],2))); -}; - - - - - -WebInspector.TimelineUIUtils.quadHeight=function(quad) -{ -return Math.round(Math.sqrt(Math.pow(quad[0]-quad[6],2)+Math.pow(quad[1]-quad[7],2))); -}; - - - - - - - -WebInspector.TimelineUIUtils.EventDispatchTypeDescriptor=function(priority,color,eventTypes) -{ -this.priority=priority; -this.color=color; -this.eventTypes=eventTypes; -}; - - - - -WebInspector.TimelineUIUtils.eventDispatchDesciptors=function() -{ -if(WebInspector.TimelineUIUtils._eventDispatchDesciptors) -return WebInspector.TimelineUIUtils._eventDispatchDesciptors; -var lightOrange="hsl(40,100%,80%)"; -var orange="hsl(40,100%,50%)"; -var green="hsl(90,100%,40%)"; -var purple="hsl(256,100%,75%)"; -WebInspector.TimelineUIUtils._eventDispatchDesciptors=[ -new WebInspector.TimelineUIUtils.EventDispatchTypeDescriptor(1,lightOrange,["mousemove","mouseenter","mouseleave","mouseout","mouseover"]), -new WebInspector.TimelineUIUtils.EventDispatchTypeDescriptor(1,lightOrange,["pointerover","pointerout","pointerenter","pointerleave","pointermove"]), -new WebInspector.TimelineUIUtils.EventDispatchTypeDescriptor(2,green,["wheel"]), -new WebInspector.TimelineUIUtils.EventDispatchTypeDescriptor(3,orange,["click","mousedown","mouseup"]), -new WebInspector.TimelineUIUtils.EventDispatchTypeDescriptor(3,orange,["touchstart","touchend","touchmove","touchcancel"]), -new WebInspector.TimelineUIUtils.EventDispatchTypeDescriptor(3,orange,["pointerdown","pointerup","pointercancel","gotpointercapture","lostpointercapture"]), -new WebInspector.TimelineUIUtils.EventDispatchTypeDescriptor(3,purple,["keydown","keyup","keypress"])]; - -return WebInspector.TimelineUIUtils._eventDispatchDesciptors; -}; - - - - - - - - - - -WebInspector.TimelineCategory=function(name,title,visible,childColor,color) -{ -this.name=name; -this.title=title; -this.visible=visible; -this.childColor=childColor; -this.color=color; -this.hidden=false; -}; - - -WebInspector.TimelineCategory.Events={ -VisibilityChanged:Symbol("VisibilityChanged")}; - - -WebInspector.TimelineCategory.prototype={ - - - -get hidden() -{ -return this._hidden; -}, - -set hidden(hidden) -{ -this._hidden=hidden; -this.dispatchEventToListeners(WebInspector.TimelineCategory.Events.VisibilityChanged,this); -}, - -__proto__:WebInspector.Object.prototype}; - - - - - - - - - - - - -WebInspector.TimelineMarkerStyle; - - - - - -WebInspector.TimelineUIUtils.markerStyleForEvent=function(event) -{ -var red="rgb(255, 0, 0)"; -var blue="rgb(0, 0, 255)"; -var orange="rgb(255, 178, 23)"; -var green="rgb(0, 130, 0)"; -var tallMarkerDashStyle=[10,5]; - -var title=WebInspector.TimelineUIUtils.eventTitle(event); - -if(event.hasCategory(WebInspector.TimelineModel.Category.Console)||event.hasCategory(WebInspector.TimelineModel.Category.UserTiming)){ -return{ -title:title, -dashStyle:tallMarkerDashStyle, -lineWidth:0.5, -color:orange, -tall:false, -lowPriority:false}; - -} -var recordTypes=WebInspector.TimelineModel.RecordType; -var tall=false; -var color=green; -switch(event.name){ -case recordTypes.MarkDOMContent: -color=blue; -tall=true; -break; -case recordTypes.MarkLoad: -color=red; -tall=true; -break; -case recordTypes.MarkFirstPaint: -color=green; -tall=true; -break; -case recordTypes.TimeStamp: -color=orange; -break;} - -return{ -title:title, -dashStyle:tallMarkerDashStyle, -lineWidth:0.5, -color:color, -tall:tall, -lowPriority:false}; - -}; - - - - -WebInspector.TimelineUIUtils.markerStyleForFrame=function() -{ -return{ -title:WebInspector.UIString("Frame"), -color:"rgba(100, 100, 100, 0.4)", -lineWidth:3, -dashStyle:[3], -tall:true, -lowPriority:true}; - -}; - - - - - -WebInspector.TimelineUIUtils.colorForURL=function(url) -{ -if(!WebInspector.TimelineUIUtils.colorForURL._colorGenerator){ -WebInspector.TimelineUIUtils.colorForURL._colorGenerator=new WebInspector.FlameChart.ColorGenerator( -{min:30,max:330}, -{min:50,max:80,count:3}, -85); -} -return WebInspector.TimelineUIUtils.colorForURL._colorGenerator.colorForID(url); -}; - - - - - -WebInspector.TimelinePopupContentHelper=function(title) -{ -this._contentTable=createElement("table"); -var titleCell=this._createCell(WebInspector.UIString("%s - Details",title),"timeline-details-title"); -titleCell.colSpan=2; -var titleRow=createElement("tr"); -titleRow.appendChild(titleCell); -this._contentTable.appendChild(titleRow); -}; - -WebInspector.TimelinePopupContentHelper.prototype={ - - - -contentTable:function() -{ -return this._contentTable; -}, - - - - - -_createCell:function(content,styleName) -{ -var text=createElement("label"); -text.createTextChild(String(content)); -var cell=createElement("td"); -cell.className="timeline-details"; -if(styleName) -cell.className+=" "+styleName; -cell.textContent=content; -return cell; -}, - - - - - -appendTextRow:function(title,content) -{ -var row=createElement("tr"); -row.appendChild(this._createCell(title,"timeline-details-row-title")); -row.appendChild(this._createCell(content,"timeline-details-row-data")); -this._contentTable.appendChild(row); -}, - - - - - -appendElementRow:function(title,content) -{ -var row=createElement("tr"); -var titleCell=this._createCell(title,"timeline-details-row-title"); -row.appendChild(titleCell); -var cell=createElement("td"); -cell.className="details"; -if(content instanceof Node) -cell.appendChild(content);else - -cell.createTextChild(content||""); -row.appendChild(cell); -this._contentTable.appendChild(row); -}}; - - - - - - - -WebInspector.TimelineDetailsContentHelper=function(target,linkifier) -{ -this.fragment=createDocumentFragment(); - -this._linkifier=linkifier; -this._target=target; - -this.element=createElementWithClass("div","timeline-details-view-block"); -this._tableElement=this.element.createChild("div","vbox timeline-details-chip-body"); -this.fragment.appendChild(this.element); -}; - -WebInspector.TimelineDetailsContentHelper.prototype={ - - - - -addSection:function(title,category) -{ -if(!this._tableElement.hasChildNodes()){ -this.element.removeChildren(); -}else{ -this.element=createElementWithClass("div","timeline-details-view-block"); -this.fragment.appendChild(this.element); -} - -if(title){ -var titleElement=this.element.createChild("div","timeline-details-chip-title"); -if(category) -titleElement.createChild("div").style.backgroundColor=category.color; -titleElement.createTextChild(title); -} - -this._tableElement=this.element.createChild("div","vbox timeline-details-chip-body"); -this.fragment.appendChild(this.element); -}, - - - - -linkifier:function() -{ -return this._linkifier; -}, - - - - - -appendTextRow:function(title,value) -{ -var rowElement=this._tableElement.createChild("div","timeline-details-view-row"); -rowElement.createChild("div","timeline-details-view-row-title").textContent=title; -rowElement.createChild("div","timeline-details-view-row-value").textContent=value; -}, - - - - - - - -appendElementRow:function(title,content,isWarning,isStacked) -{ -var rowElement=this._tableElement.createChild("div","timeline-details-view-row"); -if(isWarning) -rowElement.classList.add("timeline-details-warning"); -if(isStacked) -rowElement.classList.add("timeline-details-stack-values"); -var titleElement=rowElement.createChild("div","timeline-details-view-row-title"); -titleElement.textContent=title; -var valueElement=rowElement.createChild("div","timeline-details-view-row-value"); -if(content instanceof Node) -valueElement.appendChild(content);else - -valueElement.createTextChild(content||""); -}, - - - - - - - -appendLocationRow:function(title,url,startLine,startColumn) -{ -if(!this._linkifier||!this._target) -return; -var link=this._linkifier.maybeLinkifyScriptLocation(this._target,null,url,startLine,startColumn); -if(!link) -return; -this.appendElementRow(title,link); -}, - - - - - - - -appendLocationRange:function(title,url,startLine,endLine) -{ -if(!this._linkifier||!this._target) -return; -var locationContent=createElement("span"); -var link=this._linkifier.maybeLinkifyScriptLocation(this._target,null,url,startLine); -if(!link) -return; -locationContent.appendChild(link); -locationContent.createTextChild(String.sprintf(" [%s\u2026%s]",startLine+1,endLine+1||"")); -this.appendElementRow(title,locationContent); -}, - - - - - -appendStackTrace:function(title,stackTrace) -{ -if(!this._linkifier||!this._target) -return; - -var rowElement=this._tableElement.createChild("div","timeline-details-view-row"); -rowElement.createChild("div","timeline-details-view-row-title").textContent=title; -this.createChildStackTraceElement(rowElement,stackTrace); -}, - - - - - -createChildStackTraceElement:function(parentElement,stackTrace) -{ -if(!this._linkifier||!this._target) -return; -parentElement.classList.add("timeline-details-stack-values"); -var stackTraceElement=parentElement.createChild("div","timeline-details-view-row-value timeline-details-view-row-stack-trace"); -var callFrameElem=WebInspector.DOMPresentationUtils.buildStackTracePreviewContents(this._target,this._linkifier,stackTrace); -stackTraceElement.appendChild(callFrameElem); -}, - - - - - -appendWarningRow:function(event,warningType) -{ -var warning=WebInspector.TimelineUIUtils.eventWarning(event,warningType); -if(warning) -this.appendElementRow(WebInspector.UIString("Warning"),warning,true); -}}; - - - - - - - -WebInspector.TimelineUIUtils.eventWarning=function(event,warningType) -{ -var warning=warningType||event.warning; -if(!warning) -return null; -var warnings=WebInspector.TimelineModel.WarningType; -var span=createElement("span"); -var eventData=event.args["data"]; - -switch(warning){ -case warnings.ForcedStyle: -case warnings.ForcedLayout: -span.appendChild(WebInspector.linkifyDocumentationURLAsNode("../../fundamentals/performance/rendering/avoid-large-complex-layouts-and-layout-thrashing#avoid-forced-synchronous-layouts", -WebInspector.UIString("Forced reflow"))); -span.createTextChild(WebInspector.UIString(" is a likely performance bottleneck.")); -break; -case warnings.IdleDeadlineExceeded: -span.textContent=WebInspector.UIString("Idle callback execution extended beyond deadline by "+ -Number.millisToString(event.duration-eventData["allottedMilliseconds"],true)); -break; -case warnings.V8Deopt: -span.appendChild(WebInspector.linkifyURLAsNode("https://github.com/GoogleChrome/devtools-docs/issues/53", -WebInspector.UIString("Not optimized"),undefined,true)); -span.createTextChild(WebInspector.UIString(": %s",eventData["deoptReason"])); -break; -default: -console.assert(false,"Unhandled TimelineModel.WarningType");} - -return span; -}; - -},{}],126:[function(require,module,exports){ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -WebInspector.TracingLayerPayload; - - - - - - - - -WebInspector.TracingLayerTile; - - - - - -WebInspector.LayerTreeModel=function(target) -{ -WebInspector.SDKModel.call(this,WebInspector.LayerTreeModel,target); -target.registerLayerTreeDispatcher(new WebInspector.LayerTreeDispatcher(this)); -WebInspector.targetManager.addEventListener(WebInspector.TargetManager.Events.MainFrameNavigated,this._onMainFrameNavigated,this); - -this._layerTree=null; -}; - - -WebInspector.LayerTreeModel.Events={ -LayerTreeChanged:Symbol("LayerTreeChanged"), -LayerPainted:Symbol("LayerPainted")}; - - -WebInspector.LayerTreeModel.ScrollRectType={ -NonFastScrollable:{name:"NonFastScrollable",description:"Non fast scrollable"}, -TouchEventHandler:{name:"TouchEventHandler",description:"Touch event handler"}, -WheelEventHandler:{name:"WheelEventHandler",description:"Wheel event handler"}, -RepaintsOnScroll:{name:"RepaintsOnScroll",description:"Repaints on scroll"}}; - - -WebInspector.LayerTreeModel.prototype={ -disable:function() -{ -if(!this._enabled) -return; -this._enabled=false; -this._layerTree=null; -this.target().layerTreeAgent().disable(); -}, - -enable:function() -{ -if(this._enabled) -return; -this._enabled=true; -this._forceEnable(); -}, - -_forceEnable:function() -{ -this._layerTree=new WebInspector.AgentLayerTree(this.target()); -this._lastPaintRectByLayerId={}; -this.target().layerTreeAgent().enable(); -}, - - - - -setLayerTree:function(layerTree) -{ -this.disable(); -this._layerTree=layerTree; -this.dispatchEventToListeners(WebInspector.LayerTreeModel.Events.LayerTreeChanged); -}, - - - - -layerTree:function() -{ -return this._layerTree; -}, - - - - -_layerTreeChanged:function(layers) -{ -if(!this._enabled) -return; -var layerTree=this._layerTree; -layerTree.setLayers(layers,onLayersSet.bind(this)); - - - - -function onLayersSet() -{ -for(var layerId in this._lastPaintRectByLayerId){ -var lastPaintRect=this._lastPaintRectByLayerId[layerId]; -var layer=layerTree.layerById(layerId); -if(layer) -layer._lastPaintRect=lastPaintRect; -} -this._lastPaintRectByLayerId={}; - -this.dispatchEventToListeners(WebInspector.LayerTreeModel.Events.LayerTreeChanged); -} -}, - - - - - -_layerPainted:function(layerId,clipRect) -{ -if(!this._enabled) -return; -var layerTree=this._layerTree; -var layer=layerTree.layerById(layerId); -if(!layer){ -this._lastPaintRectByLayerId[layerId]=clipRect; -return; -} -layer._didPaint(clipRect); -this.dispatchEventToListeners(WebInspector.LayerTreeModel.Events.LayerPainted,layer); -}, - -_onMainFrameNavigated:function() -{ -if(this._enabled) -this._forceEnable(); -}, - -__proto__:WebInspector.SDKModel.prototype}; - - - - - - -WebInspector.LayerTreeBase=function(target) -{ -this._target=target; -this._domModel=target?WebInspector.DOMModel.fromTarget(target):null; -this._layersById={}; - -this._backendNodeIdToNode=new Map(); -this._reset(); -}; - -WebInspector.LayerTreeBase.prototype={ -_reset:function() -{ -this._root=null; -this._contentRoot=null; -this._layers=null; -}, - - - - -target:function() -{ -return this._target; -}, - - - - -root:function() -{ -return this._root; -}, - - - - -contentRoot:function() -{ -return this._contentRoot; -}, - - - - -layers:function() -{ -return this._layers; -}, - - - - - - -forEachLayer:function(callback,root) -{ -if(!root){ -root=this.root(); -if(!root) -return false; -} -return callback(root)||root.children().some(this.forEachLayer.bind(this,callback)); -}, - - - - - -layerById:function(id) -{ -return this._layersById[id]||null; -}, - - - - - -_resolveBackendNodeIds:function(requestedNodeIds,callback) -{ -if(!requestedNodeIds.size||!this._domModel){ -callback(); -return; -} -if(this._domModel) -this._domModel.pushNodesByBackendIdsToFrontend(requestedNodeIds,populateBackendNodeMap.bind(this)); - - - - - -function populateBackendNodeMap(nodesMap) -{ -if(nodesMap){ -for(var nodeId of nodesMap.keysArray()) -this._backendNodeIdToNode.set(nodeId,nodesMap.get(nodeId)||null); -} -callback(); -} -}, - - - - -setViewportSize:function(viewportSize) -{ -this._viewportSize=viewportSize; -}, - - - - -viewportSize:function() -{ -return this._viewportSize; -}, - - - - - -_nodeForId:function(id) -{ -return this._domModel?this._domModel.nodeForId(id):null; -}}; - - - - - - - -WebInspector.TracingLayerTree=function(target) -{ -WebInspector.LayerTreeBase.call(this,target); - -this._tileById=new Map(); -}; - -WebInspector.TracingLayerTree.prototype={ - - - - - -setLayers:function(root,layers,callback) -{ -var idsToResolve=new Set(); -if(root){ - - -this._extractNodeIdsToResolve(idsToResolve,{},root); -}else{ -for(var i=0;i<layers.length;++i) -this._extractNodeIdsToResolve(idsToResolve,{},layers[i]); -} -this._resolveBackendNodeIds(idsToResolve,onBackendNodeIdsResolved.bind(this)); - - - - -function onBackendNodeIdsResolved() -{ -var oldLayersById=this._layersById; -this._layersById={}; -this._contentRoot=null; -if(root){ -this._root=this._innerSetLayers(oldLayersById,root); -}else{ -this._layers=layers.map(this._innerSetLayers.bind(this,oldLayersById)); -this._root=this._contentRoot; -for(var i=0;i<this._layers.length;++i){ -if(this._layers[i].id()!==this._contentRoot.id()){ -this._contentRoot.addChild(this._layers[i]); -} -} -} -callback(); -} -}, - - - - -setTiles:function(tiles) -{ -this._tileById=new Map(); -for(var tile of tiles) -this._tileById.set(tile.id,tile); -}, - - - - - -tileById:function(id) -{ -return this._tileById.get(id)||null; -}, - - - - - - -_innerSetLayers:function(oldLayersById,payload) -{ -var layer=oldLayersById[payload.layer_id]; -if(layer) -layer._reset(payload);else - -layer=new WebInspector.TracingLayer(payload); -this._layersById[payload.layer_id]=layer; -if(payload.owner_node) -layer._setNode(this._backendNodeIdToNode.get(payload.owner_node)||null); -if(!this._contentRoot&&layer.drawsContent()) -this._contentRoot=layer; -for(var i=0;payload.children&&i<payload.children.length;++i) -layer.addChild(this._innerSetLayers(oldLayersById,payload.children[i])); -return layer; -}, - - - - - - -_extractNodeIdsToResolve:function(nodeIdsToResolve,seenNodeIds,payload) -{ -var backendNodeId=payload.owner_node; -if(backendNodeId&&!this._backendNodeIdToNode.has(backendNodeId)) -nodeIdsToResolve.add(backendNodeId); -for(var i=0;payload.children&&i<payload.children.length;++i) -this._extractNodeIdsToResolve(nodeIdsToResolve,seenNodeIds,payload.children[i]); -}, - -__proto__:WebInspector.LayerTreeBase.prototype}; - - - - - - - -WebInspector.AgentLayerTree=function(target) -{ -WebInspector.LayerTreeBase.call(this,target); -}; - -WebInspector.AgentLayerTree.prototype={ - - - - -setLayers:function(payload,callback) -{ -if(!payload){ -onBackendNodeIdsResolved.call(this); -return; -} - -var idsToResolve=new Set(); -for(var i=0;i<payload.length;++i){ -var backendNodeId=payload[i].backendNodeId; -if(!backendNodeId||this._backendNodeIdToNode.has(backendNodeId)) -continue; -idsToResolve.add(backendNodeId); -} -this._resolveBackendNodeIds(idsToResolve,onBackendNodeIdsResolved.bind(this)); - - - - -function onBackendNodeIdsResolved() -{ -this._innerSetLayers(payload); -callback(); -} -}, - - - - -_innerSetLayers:function(layers) -{ -this._reset(); - -if(!layers) -return; -var oldLayersById=this._layersById; -this._layersById={}; -for(var i=0;i<layers.length;++i){ -var layerId=layers[i].layerId; -var layer=oldLayersById[layerId]; -if(layer) -layer._reset(layers[i]);else - -layer=new WebInspector.AgentLayer(this._target,layers[i]); -this._layersById[layerId]=layer; -var backendNodeId=layers[i].backendNodeId; -if(backendNodeId) -layer._setNode(this._backendNodeIdToNode.get(backendNodeId)); -if(!this._contentRoot&&layer.drawsContent()) -this._contentRoot=layer; -var parentId=layer.parentId(); -if(parentId){ -var parent=this._layersById[parentId]; -if(!parent) -console.assert(parent,"missing parent "+parentId+" for layer "+layerId); -parent.addChild(layer); -}else{ -if(this._root) -console.assert(false,"Multiple root layers"); -this._root=layer; -} -} -if(this._root) -this._root._calculateQuad(new WebKitCSSMatrix()); -}, - -__proto__:WebInspector.LayerTreeBase.prototype}; - - - - - -WebInspector.Layer=function() -{ -}; - -WebInspector.Layer.prototype={ - - - -id:function(){}, - - - - -parentId:function(){}, - - - - -parent:function(){}, - - - - -isRoot:function(){}, - - - - -children:function(){}, - - - - -addChild:function(child){}, - - - - -node:function(){}, - - - - -nodeForSelfOrAncestor:function(){}, - - - - -offsetX:function(){}, - - - - -offsetY:function(){}, - - - - -width:function(){}, - - - - -height:function(){}, - - - - -transform:function(){}, - - - - -quad:function(){}, - - - - -anchorPoint:function(){}, - - - - -invisible:function(){}, - - - - -paintCount:function(){}, - - - - -lastPaintRect:function(){}, - - - - -scrollRects:function(){}, - - - - -gpuMemoryUsage:function(){}, - - - - -requestCompositingReasons:function(callback){}, - - - - -drawsContent:function(){}}; - - - - - - - - -WebInspector.AgentLayer=function(target,layerPayload) -{ -this._target=target; -this._reset(layerPayload); -}; - -WebInspector.AgentLayer.prototype={ - - - - -id:function() -{ -return this._layerPayload.layerId; -}, - - - - - -parentId:function() -{ -return this._layerPayload.parentLayerId; -}, - - - - - -parent:function() -{ -return this._parent; -}, - - - - - -isRoot:function() -{ -return!this.parentId(); -}, - - - - - -children:function() -{ -return this._children; -}, - - - - - -addChild:function(child) -{ -if(child._parent) -console.assert(false,"Child already has a parent"); -this._children.push(child); -child._parent=this; -}, - - - - -_setNode:function(node) -{ -this._node=node; -}, - - - - - -node:function() -{ -return this._node; -}, - - - - - -nodeForSelfOrAncestor:function() -{ -for(var layer=this;layer;layer=layer._parent){ -if(layer._node) -return layer._node; -} -return null; -}, - - - - - -offsetX:function() -{ -return this._layerPayload.offsetX; -}, - - - - - -offsetY:function() -{ -return this._layerPayload.offsetY; -}, - - - - - -width:function() -{ -return this._layerPayload.width; -}, - - - - - -height:function() -{ -return this._layerPayload.height; -}, - - - - - -transform:function() -{ -return this._layerPayload.transform; -}, - - - - - -quad:function() -{ -return this._quad; -}, - - - - - -anchorPoint:function() -{ -return[ -this._layerPayload.anchorX||0, -this._layerPayload.anchorY||0, -this._layerPayload.anchorZ||0]; - -}, - - - - - -invisible:function() -{ -return this._layerPayload.invisible; -}, - - - - - -paintCount:function() -{ -return this._paintCount||this._layerPayload.paintCount; -}, - - - - - -lastPaintRect:function() -{ -return this._lastPaintRect; -}, - - - - - -scrollRects:function() -{ -return this._scrollRects; -}, - - - - - -requestCompositingReasons:function(callback) -{ -if(!this._target){ -callback([]); -return; -} - -var wrappedCallback=InspectorBackend.wrapClientCallback(callback,"LayerTreeAgent.reasonsForCompositingLayer(): ",undefined,[]); -this._target.layerTreeAgent().compositingReasons(this.id(),wrappedCallback); -}, - - - - - -drawsContent:function() -{ -return this._layerPayload.drawsContent; -}, - - - - - -gpuMemoryUsage:function() -{ - - - -var bytesPerPixel=4; -return this.drawsContent()?this.width()*this.height()*bytesPerPixel:0; -}, - - - - -requestSnapshot:function(callback) -{ -if(!this._target){ -callback(); -return; -} - -var wrappedCallback=InspectorBackend.wrapClientCallback(callback,"LayerTreeAgent.makeSnapshot(): ",WebInspector.PaintProfilerSnapshot.bind(null,this._target)); -this._target.layerTreeAgent().makeSnapshot(this.id(),wrappedCallback); -}, - - - - -_didPaint:function(rect) -{ -this._lastPaintRect=rect; -this._paintCount=this.paintCount()+1; -this._image=null; -}, - - - - -_reset:function(layerPayload) -{ - -this._node=null; -this._children=[]; -this._parent=null; -this._paintCount=0; -this._layerPayload=layerPayload; -this._image=null; -this._scrollRects=this._layerPayload.scrollRects||[]; -}, - - - - - -_matrixFromArray:function(a) -{ -function toFixed9(x){return x.toFixed(9);} -return new WebKitCSSMatrix("matrix3d("+a.map(toFixed9).join(",")+")"); -}, - - - - - -_calculateTransformToViewport:function(parentTransform) -{ -var offsetMatrix=new WebKitCSSMatrix().translate(this._layerPayload.offsetX,this._layerPayload.offsetY); -var matrix=offsetMatrix; - -if(this._layerPayload.transform){ -var transformMatrix=this._matrixFromArray(this._layerPayload.transform); -var anchorVector=new WebInspector.Geometry.Vector(this._layerPayload.width*this.anchorPoint()[0],this._layerPayload.height*this.anchorPoint()[1],this.anchorPoint()[2]); -var anchorPoint=WebInspector.Geometry.multiplyVectorByMatrixAndNormalize(anchorVector,matrix); -var anchorMatrix=new WebKitCSSMatrix().translate(-anchorPoint.x,-anchorPoint.y,-anchorPoint.z); -matrix=anchorMatrix.inverse().multiply(transformMatrix.multiply(anchorMatrix.multiply(matrix))); -} - -matrix=parentTransform.multiply(matrix); -return matrix; -}, - - - - - - -_createVertexArrayForRect:function(width,height) -{ -return[0,0,0,width,0,0,width,height,0,0,height,0]; -}, - - - - -_calculateQuad:function(parentTransform) -{ -var matrix=this._calculateTransformToViewport(parentTransform); -this._quad=[]; -var vertices=this._createVertexArrayForRect(this._layerPayload.width,this._layerPayload.height); -for(var i=0;i<4;++i){ -var point=WebInspector.Geometry.multiplyVectorByMatrixAndNormalize(new WebInspector.Geometry.Vector(vertices[i*3],vertices[i*3+1],vertices[i*3+2]),matrix); -this._quad.push(point.x,point.y); -} - -function calculateQuadForLayer(layer) -{ -layer._calculateQuad(matrix); -} - -this._children.forEach(calculateQuadForLayer); -}}; - - - - - - - -WebInspector.TracingLayer=function(payload) -{ -this._reset(payload); -}; - -WebInspector.TracingLayer.prototype={ - - - -_reset:function(payload) -{ - -this._node=null; -this._layerId=String(payload.layer_id); -this._offsetX=payload.position[0]; -this._offsetY=payload.position[1]; -this._width=payload.bounds.width; -this._height=payload.bounds.height; -this._children=[]; -this._parentLayerId=null; -this._parent=null; -this._quad=payload.layer_quad||[]; -this._createScrollRects(payload); -this._compositingReasons=payload.compositing_reasons||[]; -this._drawsContent=!!payload.draws_content; -this._gpuMemoryUsage=payload.gpu_memory_usage; -}, - - - - - -id:function() -{ -return this._layerId; -}, - - - - - -parentId:function() -{ -return this._parentLayerId; -}, - - - - - -parent:function() -{ -return this._parent; -}, - - - - - -isRoot:function() -{ -return!this.parentId(); -}, - - - - - -children:function() -{ -return this._children; -}, - - - - - -addChild:function(child) -{ -if(child._parent) -console.assert(false,"Child already has a parent"); -this._children.push(child); -child._parent=this; -child._parentLayerId=this._layerId; -}, - - - - - -_setNode:function(node) -{ -this._node=node; -}, - - - - - -node:function() -{ -return this._node; -}, - - - - - -nodeForSelfOrAncestor:function() -{ -for(var layer=this;layer;layer=layer._parent){ -if(layer._node) -return layer._node; -} -return null; -}, - - - - - -offsetX:function() -{ -return this._offsetX; -}, - - - - - -offsetY:function() -{ -return this._offsetY; -}, - - - - - -width:function() -{ -return this._width; -}, - - - - - -height:function() -{ -return this._height; -}, - - - - - -transform:function() -{ -return null; -}, - - - - - -quad:function() -{ -return this._quad; -}, - - - - - -anchorPoint:function() -{ -return[0.5,0.5,0]; -}, - - - - - -invisible:function() -{ -return false; -}, - - - - - -paintCount:function() -{ -return 0; -}, - - - - - -lastPaintRect:function() -{ -return null; -}, - - - - - -scrollRects:function() -{ -return this._scrollRects; -}, - - - - - -gpuMemoryUsage:function() -{ -return this._gpuMemoryUsage; -}, - - - - - - -_scrollRectsFromParams:function(params,type) -{ -return{rect:{x:params[0],y:params[1],width:params[2],height:params[3]},type:type}; -}, - - - - -_createScrollRects:function(payload) -{ -this._scrollRects=[]; -if(payload.non_fast_scrollable_region) -this._scrollRects.push(this._scrollRectsFromParams(payload.non_fast_scrollable_region,WebInspector.LayerTreeModel.ScrollRectType.NonFastScrollable.name)); -if(payload.touch_event_handler_region) -this._scrollRects.push(this._scrollRectsFromParams(payload.touch_event_handler_region,WebInspector.LayerTreeModel.ScrollRectType.TouchEventHandler.name)); -if(payload.wheel_event_handler_region) -this._scrollRects.push(this._scrollRectsFromParams(payload.wheel_event_handler_region,WebInspector.LayerTreeModel.ScrollRectType.WheelEventHandler.name)); -if(payload.scroll_event_handler_region) -this._scrollRects.push(this._scrollRectsFromParams(payload.scroll_event_handler_region,WebInspector.LayerTreeModel.ScrollRectType.RepaintsOnScroll.name)); -}, - - - - - -requestCompositingReasons:function(callback) -{ -callback(this._compositingReasons); -}, - - - - - -drawsContent:function() -{ -return this._drawsContent; -}}; - - - - - - -WebInspector.DeferredLayerTree=function(target) -{ -this._target=target; -}; - -WebInspector.DeferredLayerTree.prototype={ - - - -resolve:function(callback){}, - - - - -target:function() -{ -return this._target; -}}; - - - - - - - -WebInspector.LayerTreeDispatcher=function(layerTreeModel) -{ -this._layerTreeModel=layerTreeModel; -}; - -WebInspector.LayerTreeDispatcher.prototype={ - - - - -layerTreeDidChange:function(layers) -{ -this._layerTreeModel._layerTreeChanged(layers||null); -}, - - - - - - -layerPainted:function(layerId,clipRect) -{ -this._layerTreeModel._layerPainted(layerId,clipRect); -}}; - - - - - - -WebInspector.LayerTreeModel.fromTarget=function(target) -{ -if(!target.hasDOMCapability()) -return null; - -var model=target.model(WebInspector.LayerTreeModel); -if(!model) -model=new WebInspector.LayerTreeModel(target); -return model; -}; - -},{}],127:[function(require,module,exports){ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -WebInspector.TimelineFrameModel=function(categoryMapper) -{ -this._categoryMapper=categoryMapper; -this.reset(); -}; - -WebInspector.TimelineFrameModel._mainFrameMarkers=[ -WebInspector.TimelineModel.RecordType.ScheduleStyleRecalculation, -WebInspector.TimelineModel.RecordType.InvalidateLayout, -WebInspector.TimelineModel.RecordType.BeginMainThreadFrame, -WebInspector.TimelineModel.RecordType.ScrollLayer]; - - -WebInspector.TimelineFrameModel.prototype={ - - - -frames:function() -{ -return this._frames; -}, - - - - - - -filteredFrames:function(startTime,endTime) -{ - - - - - -function compareStartTime(value,object) -{ -return value-object.startTime; -} - - - - - -function compareEndTime(value,object) -{ -return value-object.endTime; -} -var frames=this._frames; -var firstFrame=frames.lowerBound(startTime,compareEndTime); -var lastFrame=frames.lowerBound(endTime,compareStartTime); -return frames.slice(firstFrame,lastFrame); -}, - - - - - -hasRasterTile:function(rasterTask) -{ -var data=rasterTask.args["tileData"]; -if(!data) -return false; -var frameId=data["sourceFrameNumber"]; -var frame=frameId&&this._frameById[frameId]; -if(!frame||!frame.layerTree) -return false; -return true; -}, - - - - - -requestRasterTile:function(rasterTask,callback) -{ -var target=this._target; -if(!target){ -callback(null,null); -return; -} -var data=rasterTask.args["tileData"]; -var frameId=data["sourceFrameNumber"]; -var frame=frameId&&this._frameById[frameId]; -if(!frame||!frame.layerTree){ -callback(null,null); -return; -} - -var tileId=data["tileId"]&&data["tileId"]["id_ref"]; - -var fragments=[]; - -var tile=null; -var x0=Infinity; -var y0=Infinity; - -frame.layerTree.resolve(layerTreeResolved); - - - -function layerTreeResolved(layerTree) -{ -tile=tileId&&layerTree.tileById("cc::Tile/"+tileId); -if(!tile){ -console.error("Tile "+tileId+" missing in frame "+frameId); -callback(null,null); -return; -} -var fetchPictureFragmentsBarrier=new CallbackBarrier(); -for(var paint of frame.paints){ -if(tile.layer_id===paint.layerId()) -paint.loadPicture(fetchPictureFragmentsBarrier.createCallback(pictureLoaded)); -} -fetchPictureFragmentsBarrier.callWhenDone(allPicturesLoaded); -} - - - - - - - - -function segmentsOverlap(a1,a2,b1,b2) -{ -console.assert(a1<=a2&&b1<=b2,"segments should be specified as ordered pairs"); -return a2>b1&&a1<b2; -} - - - - - -function rectsOverlap(a,b) -{ -return segmentsOverlap(a[0],a[0]+a[2],b[0],b[0]+b[2])&&segmentsOverlap(a[1],a[1]+a[3],b[1],b[1]+b[3]); -} - - - - - -function pictureLoaded(rect,picture) -{ -if(!rect||!picture) -return; -if(!rectsOverlap(rect,tile.content_rect)) -return; -var x=rect[0]; -var y=rect[1]; -x0=Math.min(x0,x); -y0=Math.min(y0,y); -fragments.push({x:x,y:y,picture:picture}); -} - -function allPicturesLoaded() -{ -if(!fragments.length){ -callback(null,null); -return; -} -var rectArray=tile.content_rect; - -var rect={x:rectArray[0]-x0,y:rectArray[1]-y0,width:rectArray[2],height:rectArray[3]}; -WebInspector.PaintProfilerSnapshot.loadFromFragments(target,fragments,callback.bind(null,rect)); -} -}, - -reset:function() -{ -this._minimumRecordTime=Infinity; -this._frames=[]; -this._frameById={}; -this._lastFrame=null; -this._lastLayerTree=null; -this._mainFrameCommitted=false; -this._mainFrameRequested=false; -this._framePendingCommit=null; -this._lastBeginFrame=null; -this._lastNeedsBeginFrame=null; -this._framePendingActivation=null; -this._lastTaskBeginTime=null; -this._target=null; -this._sessionId=null; -this._currentTaskTimeByCategory={}; -}, - - - - -handleBeginFrame:function(startTime) -{ -if(!this._lastFrame) -this._startFrame(startTime); -this._lastBeginFrame=startTime; -}, - - - - -handleDrawFrame:function(startTime) -{ -if(!this._lastFrame){ -this._startFrame(startTime); -return; -} - - - -if(this._mainFrameCommitted||!this._mainFrameRequested){ -if(this._lastNeedsBeginFrame){ -var idleTimeEnd=this._framePendingActivation?this._framePendingActivation.triggerTime:this._lastBeginFrame||this._lastNeedsBeginFrame; -if(idleTimeEnd>this._lastFrame.startTime){ -this._lastFrame.idle=true; -this._startFrame(idleTimeEnd); -if(this._framePendingActivation) -this._commitPendingFrame(); -this._lastBeginFrame=null; -} -this._lastNeedsBeginFrame=null; -} -this._startFrame(startTime); -} -this._mainFrameCommitted=false; -}, - -handleActivateLayerTree:function() -{ -if(!this._lastFrame) -return; -if(this._framePendingActivation&&!this._lastNeedsBeginFrame) -this._commitPendingFrame(); -}, - -handleRequestMainThreadFrame:function() -{ -if(!this._lastFrame) -return; -this._mainFrameRequested=true; -}, - -handleCompositeLayers:function() -{ -if(!this._framePendingCommit) -return; -this._framePendingActivation=this._framePendingCommit; -this._framePendingCommit=null; -this._mainFrameRequested=false; -this._mainFrameCommitted=true; -}, - - - - -handleLayerTreeSnapshot:function(layerTree) -{ -this._lastLayerTree=layerTree; -}, - - - - - -handleNeedFrameChanged:function(startTime,needsBeginFrame) -{ -if(needsBeginFrame) -this._lastNeedsBeginFrame=startTime; -}, - - - - -_startFrame:function(startTime) -{ -if(this._lastFrame) -this._flushFrame(this._lastFrame,startTime); -this._lastFrame=new WebInspector.TimelineFrame(startTime,startTime-this._minimumRecordTime); -}, - - - - - -_flushFrame:function(frame,endTime) -{ -frame._setLayerTree(this._lastLayerTree); -frame._setEndTime(endTime); -if(this._frames.length&&(frame.startTime!==this._frames.peekLast().endTime||frame.startTime>frame.endTime)) -console.assert(false,`Inconsistent frame time for frame ${this._frames.length} (${frame.startTime} - ${frame.endTime})`); -this._frames.push(frame); -if(typeof frame._mainFrameId==="number") -this._frameById[frame._mainFrameId]=frame; -}, - -_commitPendingFrame:function() -{ -this._lastFrame._addTimeForCategories(this._framePendingActivation.timeByCategory); -this._lastFrame.paints=this._framePendingActivation.paints; -this._lastFrame._mainFrameId=this._framePendingActivation.mainFrameId; -this._framePendingActivation=null; -}, - - - - - - -_findRecordRecursively:function(types,record) -{ -if(types.indexOf(record.type())>=0) -return record; -if(!record.children()) -return null; -for(var i=0;i<record.children().length;++i){ -var result=this._findRecordRecursively(types,record.children()[i]); -if(result) -return result; -} -return null; -}, - - - - - - -addTraceEvents:function(target,events,sessionId) -{ -this._target=target; -this._sessionId=sessionId; -if(!events.length) -return; -if(events[0].startTime<this._minimumRecordTime) -this._minimumRecordTime=events[0].startTime; -for(var i=0;i<events.length;++i) -this._addTraceEvent(events[i]); -}, - - - - -_addTraceEvent:function(event) -{ -var eventNames=WebInspector.TimelineModel.RecordType; - -if(event.name===eventNames.SetLayerTreeId){ -var sessionId=event.args["sessionId"]||event.args["data"]["sessionId"]; -if(this._sessionId===sessionId) -this._layerTreeId=event.args["layerTreeId"]||event.args["data"]["layerTreeId"]; -}else if(event.name===eventNames.TracingStartedInPage){ -this._mainThread=event.thread; -}else if(event.phase===WebInspector.TracingModel.Phase.SnapshotObject&&event.name===eventNames.LayerTreeHostImplSnapshot&&parseInt(event.id,0)===this._layerTreeId){ -var snapshot=event; -this.handleLayerTreeSnapshot(new WebInspector.DeferredTracingLayerTree(snapshot,this._target)); -}else{ -this._processCompositorEvents(event); -if(event.thread===this._mainThread) -this._addMainThreadTraceEvent(event);else -if(this._lastFrame&&event.selfTime&&!WebInspector.TracingModel.isTopLevelEvent(event)) -this._lastFrame._addTimeForCategory(this._categoryMapper(event),event.selfTime); -} -}, - - - - -_processCompositorEvents:function(event) -{ -var eventNames=WebInspector.TimelineModel.RecordType; - -if(event.args["layerTreeId"]!==this._layerTreeId) -return; - -var timestamp=event.startTime; -if(event.name===eventNames.BeginFrame) -this.handleBeginFrame(timestamp);else -if(event.name===eventNames.DrawFrame) -this.handleDrawFrame(timestamp);else -if(event.name===eventNames.ActivateLayerTree) -this.handleActivateLayerTree();else -if(event.name===eventNames.RequestMainThreadFrame) -this.handleRequestMainThreadFrame();else -if(event.name===eventNames.NeedsBeginFrameChanged) -this.handleNeedFrameChanged(timestamp,event.args["data"]&&event.args["data"]["needsBeginFrame"]); -}, - - - - -_addMainThreadTraceEvent:function(event) -{ -var eventNames=WebInspector.TimelineModel.RecordType; -var timestamp=event.startTime; -var selfTime=event.selfTime||0; - -if(WebInspector.TracingModel.isTopLevelEvent(event)){ -this._currentTaskTimeByCategory={}; -this._lastTaskBeginTime=event.startTime; -} -if(!this._framePendingCommit&&WebInspector.TimelineFrameModel._mainFrameMarkers.indexOf(event.name)>=0) -this._framePendingCommit=new WebInspector.PendingFrame(this._lastTaskBeginTime||event.startTime,this._currentTaskTimeByCategory); -if(!this._framePendingCommit){ -this._addTimeForCategory(this._currentTaskTimeByCategory,event); -return; -} -this._addTimeForCategory(this._framePendingCommit.timeByCategory,event); - -if(event.name===eventNames.BeginMainThreadFrame&&event.args["data"]&&event.args["data"]["frameId"]) -this._framePendingCommit.mainFrameId=event.args["data"]["frameId"]; -if(event.name===eventNames.Paint&&event.args["data"]["layerId"]&&event.picture&&this._target) -this._framePendingCommit.paints.push(new WebInspector.LayerPaintEvent(event,this._target)); -if(event.name===eventNames.CompositeLayers&&event.args["layerTreeId"]===this._layerTreeId) -this.handleCompositeLayers(); -}, - - - - - -_addTimeForCategory:function(timeByCategory,event) -{ -if(!event.selfTime) -return; -var categoryName=this._categoryMapper(event); -timeByCategory[categoryName]=(timeByCategory[categoryName]||0)+event.selfTime; -}}; - - - - - - - - -WebInspector.DeferredTracingLayerTree=function(snapshot,target) -{ -WebInspector.DeferredLayerTree.call(this,target); -this._snapshot=snapshot; -}; - -WebInspector.DeferredTracingLayerTree.prototype={ - - - - -resolve:function(callback) -{ -this._snapshot.requestObject(onGotObject.bind(this)); - - - - -function onGotObject(result) -{ -if(!result) -return; -var viewport=result["device_viewport_size"]; -var tiles=result["active_tiles"]; -var rootLayer=result["active_tree"]["root_layer"]; -var layers=result["active_tree"]["layers"]; -var layerTree=new WebInspector.TracingLayerTree(this._target); -layerTree.setViewportSize(viewport); -layerTree.setTiles(tiles); -layerTree.setLayers(rootLayer,layers,callback.bind(null,layerTree)); -} -}, - -__proto__:WebInspector.DeferredLayerTree.prototype}; - - - - - - - - -WebInspector.TimelineFrame=function(startTime,startTimeOffset) -{ -this.startTime=startTime; -this.startTimeOffset=startTimeOffset; -this.endTime=this.startTime; -this.duration=0; -this.timeByCategory={}; -this.cpuTime=0; -this.idle=false; - -this.layerTree=null; - -this.paints=[]; - -this._mainFrameId=undefined; -}; - -WebInspector.TimelineFrame.prototype={ - - - -hasWarnings:function() -{ -var longFrameDurationThresholdMs=22; -return!this.idle&&this.duration>longFrameDurationThresholdMs; -}, - - - - -_setEndTime:function(endTime) -{ -this.endTime=endTime; -this.duration=this.endTime-this.startTime; -}, - - - - -_setLayerTree:function(layerTree) -{ -this.layerTree=layerTree; -}, - - - - -_addTimeForCategories:function(timeByCategory) -{ -for(var category in timeByCategory) -this._addTimeForCategory(category,timeByCategory[category]); -}, - - - - - -_addTimeForCategory:function(category,time) -{ -this.timeByCategory[category]=(this.timeByCategory[category]||0)+time; -this.cpuTime+=time; -}}; - - - - - - - -WebInspector.LayerPaintEvent=function(event,target) -{ -this._event=event; -this._target=target; -}; - -WebInspector.LayerPaintEvent.prototype={ - - - -layerId:function() -{ -return this._event.args["data"]["layerId"]; -}, - - - - -event:function() -{ -return this._event; -}, - - - - -loadPicture:function(callback) -{ -this._event.picture.requestObject(onGotObject); - - - -function onGotObject(result) -{ -if(!result||!result["skp64"]){ -callback(null,null); -return; -} -var rect=result["params"]&&result["params"]["layer_rect"]; -callback(rect,result["skp64"]); -} -}, - - - - -loadSnapshot:function(callback) -{ -this.loadPicture(onGotPicture.bind(this)); - - - - - -function onGotPicture(rect,picture) -{ -if(!rect||!picture||!this._target){ -callback(null,null); -return; -} -WebInspector.PaintProfilerSnapshot.load(this._target,picture,callback.bind(null,rect)); -} -}}; - - - - - - - -WebInspector.PendingFrame=function(triggerTime,timeByCategory) -{ - -this.timeByCategory=timeByCategory; - -this.paints=[]; - -this.mainFrameId=undefined; -this.triggerTime=triggerTime; -}; - -},{}],128:[function(require,module,exports){ - - - - - - - -WebInspector.TimelineIRModel=function() -{ -this.reset(); -}; - - - - -WebInspector.TimelineIRModel.Phases={ -Idle:"Idle", -Response:"Response", -Scroll:"Scroll", -Fling:"Fling", -Drag:"Drag", -Animation:"Animation", -Uncategorized:"Uncategorized"}; - - - - - -WebInspector.TimelineIRModel.InputEvents={ -Char:"Char", -Click:"GestureClick", -ContextMenu:"ContextMenu", -FlingCancel:"GestureFlingCancel", -FlingStart:"GestureFlingStart", -ImplSideFling:WebInspector.TimelineModel.RecordType.ImplSideFling, -KeyDown:"KeyDown", -KeyDownRaw:"RawKeyDown", -KeyUp:"KeyUp", -LatencyScrollUpdate:"ScrollUpdate", -MouseDown:"MouseDown", -MouseMove:"MouseMove", -MouseUp:"MouseUp", -MouseWheel:"MouseWheel", -PinchBegin:"GesturePinchBegin", -PinchEnd:"GesturePinchEnd", -PinchUpdate:"GesturePinchUpdate", -ScrollBegin:"GestureScrollBegin", -ScrollEnd:"GestureScrollEnd", -ScrollUpdate:"GestureScrollUpdate", -ScrollUpdateRenderer:"ScrollUpdate", -ShowPress:"GestureShowPress", -Tap:"GestureTap", -TapCancel:"GestureTapCancel", -TapDown:"GestureTapDown", -TouchCancel:"TouchCancel", -TouchEnd:"TouchEnd", -TouchMove:"TouchMove", -TouchStart:"TouchStart"}; - - -WebInspector.TimelineIRModel._mergeThresholdsMs={ -animation:1, -mouse:40}; - - -WebInspector.TimelineIRModel._eventIRPhase=Symbol("eventIRPhase"); - - - - - -WebInspector.TimelineIRModel.phaseForEvent=function(event) -{ -return event[WebInspector.TimelineIRModel._eventIRPhase]; -}; - -WebInspector.TimelineIRModel.prototype={ - - - - -populate:function(inputLatencies,animations) -{ -var eventTypes=WebInspector.TimelineIRModel.InputEvents; -var phases=WebInspector.TimelineIRModel.Phases; - -this.reset(); -if(!inputLatencies) -return; -this._processInputLatencies(inputLatencies); -if(animations) -this._processAnimations(animations); -var range=new WebInspector.SegmentedRange(); -range.appendRange(this._drags); -range.appendRange(this._cssAnimations); -range.appendRange(this._scrolls); -range.appendRange(this._responses); -this._segments=range.segments(); -}, - - - - -_processInputLatencies:function(events) -{ -var eventTypes=WebInspector.TimelineIRModel.InputEvents; -var phases=WebInspector.TimelineIRModel.Phases; -var thresholdsMs=WebInspector.TimelineIRModel._mergeThresholdsMs; - -var scrollStart; -var flingStart; -var touchStart; -var firstTouchMove; -var mouseWheel; -var mouseDown; -var mouseMove; - -for(var i=0;i<events.length;++i){ -var event=events[i]; -if(i>0&&events[i].startTime<events[i-1].startTime) -console.assert(false,"Unordered input events"); -var type=this._inputEventType(event.name); -switch(type){ - -case eventTypes.ScrollBegin: -this._scrolls.append(this._segmentForEvent(event,phases.Scroll)); -scrollStart=event; -break; - -case eventTypes.ScrollEnd: -if(scrollStart) -this._scrolls.append(this._segmentForEventRange(scrollStart,event,phases.Scroll));else - -this._scrolls.append(this._segmentForEvent(event,phases.Scroll)); -scrollStart=null; -break; - -case eventTypes.ScrollUpdate: -touchStart=null; -this._scrolls.append(this._segmentForEvent(event,phases.Scroll)); -break; - -case eventTypes.FlingStart: -if(flingStart){ -WebInspector.console.error(WebInspector.UIString("Two flings at the same time? %s vs %s",flingStart.startTime,event.startTime)); -break; -} -flingStart=event; -break; - -case eventTypes.FlingCancel: - -if(!flingStart) -break; -this._scrolls.append(this._segmentForEventRange(flingStart,event,phases.Fling)); -flingStart=null; -break; - -case eventTypes.ImplSideFling: -this._scrolls.append(this._segmentForEvent(event,phases.Fling)); -break; - -case eventTypes.ShowPress: -case eventTypes.Tap: -case eventTypes.KeyDown: -case eventTypes.KeyDownRaw: -case eventTypes.KeyUp: -case eventTypes.Char: -case eventTypes.Click: -case eventTypes.ContextMenu: -this._responses.append(this._segmentForEvent(event,phases.Response)); -break; - -case eventTypes.TouchStart: - - -if(touchStart){ -WebInspector.console.error(WebInspector.UIString("Two touches at the same time? %s vs %s",touchStart.startTime,event.startTime)); -break; -} -touchStart=event; -event.steps[0][WebInspector.TimelineIRModel._eventIRPhase]=phases.Response; -firstTouchMove=null; -break; - -case eventTypes.TouchCancel: -touchStart=null; -break; - -case eventTypes.TouchMove: -if(firstTouchMove){ -this._drags.append(this._segmentForEvent(event,phases.Drag)); -}else if(touchStart){ -firstTouchMove=event; -this._responses.append(this._segmentForEventRange(touchStart,event,phases.Response)); -} -break; - -case eventTypes.TouchEnd: -touchStart=null; -break; - -case eventTypes.MouseDown: -mouseDown=event; -mouseMove=null; -break; - -case eventTypes.MouseMove: -if(mouseDown&&!mouseMove&&mouseDown.startTime+thresholdsMs.mouse>event.startTime){ -this._responses.append(this._segmentForEvent(mouseDown,phases.Response)); -this._responses.append(this._segmentForEvent(event,phases.Response)); -}else if(mouseDown){ -this._drags.append(this._segmentForEvent(event,phases.Drag)); -} -mouseMove=event; -break; - -case eventTypes.MouseUp: -this._responses.append(this._segmentForEvent(event,phases.Response)); -mouseDown=null; -break; - -case eventTypes.MouseWheel: - -if(mouseWheel&&canMerge(thresholdsMs.mouse,mouseWheel,event)) -this._scrolls.append(this._segmentForEventRange(mouseWheel,event,phases.Scroll));else - -this._scrolls.append(this._segmentForEvent(event,phases.Scroll)); -mouseWheel=event; -break;} - -} - - - - - - - -function canMerge(threshold,first,second) -{ -return first.endTime<second.startTime&&second.startTime<first.endTime+threshold; -} -}, - - - - -_processAnimations:function(events) -{ -for(var i=0;i<events.length;++i) -this._cssAnimations.append(this._segmentForEvent(events[i],WebInspector.TimelineIRModel.Phases.Animation)); -}, - - - - - - -_segmentForEvent:function(event,phase) -{ -this._setPhaseForEvent(event,phase); -return new WebInspector.Segment(event.startTime,event.endTime,phase); -}, - - - - - - - -_segmentForEventRange:function(startEvent,endEvent,phase) -{ -this._setPhaseForEvent(startEvent,phase); -this._setPhaseForEvent(endEvent,phase); -return new WebInspector.Segment(startEvent.startTime,endEvent.endTime,phase); -}, - - - - - -_setPhaseForEvent:function(asyncEvent,phase) -{ -asyncEvent.steps[0][WebInspector.TimelineIRModel._eventIRPhase]=phase; -}, - - - - -interactionRecords:function() -{ -return this._segments; -}, - -reset:function() -{ -var thresholdsMs=WebInspector.TimelineIRModel._mergeThresholdsMs; - -this._segments=[]; -this._drags=new WebInspector.SegmentedRange(merge.bind(null,thresholdsMs.mouse)); -this._cssAnimations=new WebInspector.SegmentedRange(merge.bind(null,thresholdsMs.animation)); -this._responses=new WebInspector.SegmentedRange(merge.bind(null,0)); -this._scrolls=new WebInspector.SegmentedRange(merge.bind(null,thresholdsMs.animation)); - - - - - - -function merge(threshold,first,second) -{ -return first.end+threshold>=second.begin&&first.data===second.data?first:null; -} -}, - - - - - -_inputEventType:function(eventName) -{ -var prefix="InputLatency::"; -if(!eventName.startsWith(prefix)){ -if(eventName===WebInspector.TimelineIRModel.InputEvents.ImplSideFling) -return eventName; -console.error("Unrecognized input latency event: "+eventName); -return null; -} -return eventName.substr(prefix.length); -}}; - - - -},{}],129:[function(require,module,exports){ - - - - - -WebInspector.TimelineJSProfileProcessor={}; - - - - - - -WebInspector.TimelineJSProfileProcessor.generateTracingEventsFromCpuProfile=function(jsProfileModel,thread) -{ -var idleNode=jsProfileModel.idleNode; -var programNode=jsProfileModel.programNode; -var gcNode=jsProfileModel.gcNode; -var samples=jsProfileModel.samples; -var timestamps=jsProfileModel.timestamps; -var jsEvents=[]; - -var nodeToStackMap=new Map(); -nodeToStackMap.set(programNode,[]); -for(var i=0;i<samples.length;++i){ -var node=jsProfileModel.nodeByIndex(i); -if(!node){ -console.error(`Node with unknown id ${samples[i]} at index ${i}`); -continue; -} -if(node===gcNode||node===idleNode) -continue; -var callFrames=nodeToStackMap.get(node); -if(!callFrames){ -callFrames=new Array(node.depth+1); -nodeToStackMap.set(node,callFrames); -for(var j=0;node.parent;node=node.parent) -callFrames[j++]=node; -} -var jsSampleEvent=new WebInspector.TracingModel.Event(WebInspector.TracingModel.DevToolsTimelineEventCategory, -WebInspector.TimelineModel.RecordType.JSSample, -WebInspector.TracingModel.Phase.Instant,timestamps[i],thread); -jsSampleEvent.args["data"]={stackTrace:callFrames}; -jsEvents.push(jsSampleEvent); -} -return jsEvents; -}; - - - - - -WebInspector.TimelineJSProfileProcessor.generateJSFrameEvents=function(events) -{ - - - - - -function equalFrames(frame1,frame2) -{ -return frame1.scriptId===frame2.scriptId&&frame1.functionName===frame2.functionName; -} - - - - - -function eventEndTime(e) -{ -return e.endTime||e.startTime; -} - - - - - -function isJSInvocationEvent(e) -{ -switch(e.name){ -case WebInspector.TimelineModel.RecordType.RunMicrotasks: -case WebInspector.TimelineModel.RecordType.FunctionCall: -case WebInspector.TimelineModel.RecordType.EvaluateScript: -return true;} - -return false; -} - -var jsFrameEvents=[]; -var jsFramesStack=[]; -var lockedJsStackDepth=[]; -var ordinal=0; -var filterNativeFunctions=!WebInspector.moduleSetting("showNativeFunctionsInJSProfile").get(); - - - - -function onStartEvent(e) -{ -e.ordinal=++ordinal; -extractStackTrace(e); - -lockedJsStackDepth.push(jsFramesStack.length); -} - - - - - -function onInstantEvent(e,parent) -{ -e.ordinal=++ordinal; -if(parent&&isJSInvocationEvent(parent)) -extractStackTrace(e); -} - - - - -function onEndEvent(e) -{ -truncateJSStack(lockedJsStackDepth.pop(),e.endTime); -} - - - - - -function truncateJSStack(depth,time) -{ -if(lockedJsStackDepth.length){ -var lockedDepth=lockedJsStackDepth.peekLast(); -if(depth<lockedDepth){ -console.error("Child stack is shallower ("+depth+") than the parent stack ("+lockedDepth+") at "+time); -depth=lockedDepth; -} -} -if(jsFramesStack.length<depth){ -console.error("Trying to truncate higher than the current stack size at "+time); -depth=jsFramesStack.length; -} -for(var k=0;k<jsFramesStack.length;++k) -jsFramesStack[k].setEndTime(time); -jsFramesStack.length=depth; -} - - - - -function filterStackFrames(stack) -{ -for(var i=0,j=0;i<stack.length;++i){ -var url=stack[i].url; -if(url&&url.startsWith("native ")) -continue; -stack[j++]=stack[i]; -} -stack.length=j; -} - - - - -function extractStackTrace(e) -{ -var recordTypes=WebInspector.TimelineModel.RecordType; -var callFrames; -if(e.name===recordTypes.JSSample){ -var eventData=e.args["data"]||e.args["beginData"]; -callFrames=eventData&&eventData["stackTrace"]; -}else{ -callFrames=jsFramesStack.map(frameEvent=>frameEvent.args["data"]).reverse(); -} -if(filterNativeFunctions) -filterStackFrames(callFrames); -var endTime=eventEndTime(e); -var numFrames=callFrames.length; -var minFrames=Math.min(numFrames,jsFramesStack.length); -var i; -for(i=lockedJsStackDepth.peekLast()||0;i<minFrames;++i){ -var newFrame=callFrames[numFrames-1-i]; -var oldFrame=jsFramesStack[i].args["data"]; -if(!equalFrames(newFrame,oldFrame)) -break; -jsFramesStack[i].setEndTime(Math.max(jsFramesStack[i].endTime,endTime)); -} -truncateJSStack(i,e.startTime); -for(;i<numFrames;++i){ -var frame=callFrames[numFrames-1-i]; -var jsFrameEvent=new WebInspector.TracingModel.Event(WebInspector.TracingModel.DevToolsTimelineEventCategory,recordTypes.JSFrame, -WebInspector.TracingModel.Phase.Complete,e.startTime,e.thread); -jsFrameEvent.ordinal=e.ordinal; -jsFrameEvent.addArgs({data:frame}); -jsFrameEvent.setEndTime(endTime); -jsFramesStack.push(jsFrameEvent); -jsFrameEvents.push(jsFrameEvent); -} -} - - - - - -function findFirstTopLevelEvent(events) -{ -for(var i=0;i<events.length;++i){ -if(WebInspector.TracingModel.isTopLevelEvent(events[i])) -return events[i]; -} -return null; -} - -var firstTopLevelEvent=findFirstTopLevelEvent(events); -if(firstTopLevelEvent) -WebInspector.TimelineModel.forEachEvent(events,onStartEvent,onEndEvent,onInstantEvent,firstTopLevelEvent.startTime); -return jsFrameEvents; -}; - - - - -WebInspector.TimelineJSProfileProcessor.CodeMap=function() -{ - -this._banks=new Map(); -}; - - - - - - - -WebInspector.TimelineJSProfileProcessor.CodeMap.Entry=function(address,size,callFrame) -{ -this.address=address; -this.size=size; -this.callFrame=callFrame; -}; - - - - - - -WebInspector.TimelineJSProfileProcessor.CodeMap.comparator=function(address,entry) -{ -return address-entry.address; -}; - -WebInspector.TimelineJSProfileProcessor.CodeMap.prototype={ - - - - - -addEntry:function(addressHex,size,callFrame) -{ -var entry=new WebInspector.TimelineJSProfileProcessor.CodeMap.Entry(this._getAddress(addressHex),size,callFrame); -this._addEntry(addressHex,entry); -}, - - - - - - -moveEntry:function(oldAddressHex,newAddressHex,size) -{ -var entry=this._getBank(oldAddressHex).removeEntry(this._getAddress(oldAddressHex)); -if(!entry){ -console.error("Entry at address "+oldAddressHex+" not found"); -return; -} -entry.address=this._getAddress(newAddressHex); -entry.size=size; -this._addEntry(newAddressHex,entry); -}, - - - - - -lookupEntry:function(addressHex) -{ -return this._getBank(addressHex).lookupEntry(this._getAddress(addressHex)); -}, - - - - - -_addEntry:function(addressHex,entry) -{ - -this._getBank(addressHex).addEntry(entry); -}, - - - - - -_getBank:function(addressHex) -{ -addressHex=addressHex.slice(2); - -var bankSizeHexDigits=13; -var maxHexDigits=16; -var bankName=addressHex.slice(-maxHexDigits,-bankSizeHexDigits); -var bank=this._banks.get(bankName); -if(!bank){ -bank=new WebInspector.TimelineJSProfileProcessor.CodeMap.Bank(); -this._banks.set(bankName,bank); -} -return bank; -}, - - - - - -_getAddress:function(addressHex) -{ - -var bankSizeHexDigits=13; -addressHex=addressHex.slice(2); -return parseInt(addressHex.slice(-bankSizeHexDigits),16); -}}; - - - - - -WebInspector.TimelineJSProfileProcessor.CodeMap.Bank=function() -{ - -this._entries=[]; -}; - -WebInspector.TimelineJSProfileProcessor.CodeMap.Bank.prototype={ - - - - -removeEntry:function(address) -{ -var index=this._entries.lowerBound(address,WebInspector.TimelineJSProfileProcessor.CodeMap.comparator); -var entry=this._entries[index]; -if(!entry||entry.address!==address) -return null; -this._entries.splice(index,1); -return entry; -}, - - - - - -lookupEntry:function(address) -{ -var index=this._entries.upperBound(address,WebInspector.TimelineJSProfileProcessor.CodeMap.comparator)-1; -var entry=this._entries[index]; -return entry&&address<entry.address+entry.size?entry.callFrame:null; -}, - - - - -addEntry:function(newEntry) -{ -var endAddress=newEntry.address+newEntry.size; -var lastIndex=this._entries.lowerBound(endAddress,WebInspector.TimelineJSProfileProcessor.CodeMap.comparator); -var index; -for(index=lastIndex-1;index>=0;--index){ -var entry=this._entries[index]; -var entryEndAddress=entry.address+entry.size; -if(entryEndAddress<=newEntry.address) -break; -} -++index; -this._entries.splice(index,lastIndex-index,newEntry); -}}; - - - - - - - -WebInspector.TimelineJSProfileProcessor._buildCallFrame=function(name,scriptId) -{ - - - - - - - - - -function createFrame(functionName,url,scriptId,line,column,isNative) -{ -return{ -"functionName":functionName, -"url":url||"", -"scriptId":scriptId||"0", -"lineNumber":line||0, -"columnNumber":column||0, -"isNative":isNative||false}; - -} - - - - - -var rePrefix=/^(\w*:)?[*~]?(.*)$/m; -var tokens=rePrefix.exec(name); -var prefix=tokens[1]; -var body=tokens[2]; -var rawName; -var rawUrl; -if(prefix==="Script:"){ -rawName=""; -rawUrl=body; -}else{ -var spacePos=body.lastIndexOf(" "); -rawName=spacePos!==-1?body.substr(0,spacePos):body; -rawUrl=spacePos!==-1?body.substr(spacePos+1):""; -} -var nativeSuffix=" native"; -var isNative=rawName.endsWith(nativeSuffix); -var functionName=isNative?rawName.slice(0,-nativeSuffix.length):rawName; -var urlData=WebInspector.ParsedURL.splitLineAndColumn(rawUrl); -var url=urlData.url||""; -var line=urlData.lineNumber||0; -var column=urlData.columnNumber||0; -return createFrame(functionName,url,String(scriptId),line,column,isNative); -}; - - - - - -WebInspector.TimelineJSProfileProcessor.processRawV8Samples=function(events) -{ -var missingAddesses=new Set(); - - - - - -function convertRawFrame(address) -{ -var entry=codeMap.lookupEntry(address); -if(entry) -return entry.isNative?null:entry; -if(!missingAddesses.has(address)){ -missingAddesses.add(address); -console.error("Address "+address+" has missing code entry"); -} -return null; -} - -var recordTypes=WebInspector.TimelineModel.RecordType; -var samples=[]; -var codeMap=new WebInspector.TimelineJSProfileProcessor.CodeMap(); -for(var i=0;i<events.length;++i){ -var e=events[i]; -var data=e.args["data"]; -switch(e.name){ -case recordTypes.JitCodeAdded: -var frame=WebInspector.TimelineJSProfileProcessor._buildCallFrame(data["name"],data["script_id"]); -codeMap.addEntry(data["code_start"],data["code_len"],frame); -break; -case recordTypes.JitCodeMoved: -codeMap.moveEntry(data["code_start"],data["new_code_start"],data["code_len"]); -break; -case recordTypes.V8Sample: -var rawStack=data["stack"]; - - -if(data["vm_state"]==="js"&&!rawStack.length) -break; -var stack=rawStack.map(convertRawFrame); -stack.remove(null); -var sampleEvent=new WebInspector.TracingModel.Event( -WebInspector.TracingModel.DevToolsTimelineEventCategory, -WebInspector.TimelineModel.RecordType.JSSample, -WebInspector.TracingModel.Phase.Instant,e.startTime,e.thread); -sampleEvent.ordinal=e.ordinal; -sampleEvent.args={"data":{"stackTrace":stack}}; -samples.push(sampleEvent); -break;} - -} - -return samples; -}; - -},{}],130:[function(require,module,exports){ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -WebInspector.TimelineModel=function(eventFilter) -{ -this._eventFilter=eventFilter; -this.reset(); -}; - - - - -WebInspector.TimelineModel.RecordType={ -Task:"Task", -Program:"Program", -EventDispatch:"EventDispatch", - -GPUTask:"GPUTask", - -Animation:"Animation", -RequestMainThreadFrame:"RequestMainThreadFrame", -BeginFrame:"BeginFrame", -NeedsBeginFrameChanged:"NeedsBeginFrameChanged", -BeginMainThreadFrame:"BeginMainThreadFrame", -ActivateLayerTree:"ActivateLayerTree", -DrawFrame:"DrawFrame", -HitTest:"HitTest", -ScheduleStyleRecalculation:"ScheduleStyleRecalculation", -RecalculateStyles:"RecalculateStyles", -UpdateLayoutTree:"UpdateLayoutTree", -InvalidateLayout:"InvalidateLayout", -Layout:"Layout", -UpdateLayer:"UpdateLayer", -UpdateLayerTree:"UpdateLayerTree", -PaintSetup:"PaintSetup", -Paint:"Paint", -PaintImage:"PaintImage", -Rasterize:"Rasterize", -RasterTask:"RasterTask", -ScrollLayer:"ScrollLayer", -CompositeLayers:"CompositeLayers", - -ScheduleStyleInvalidationTracking:"ScheduleStyleInvalidationTracking", -StyleRecalcInvalidationTracking:"StyleRecalcInvalidationTracking", -StyleInvalidatorInvalidationTracking:"StyleInvalidatorInvalidationTracking", -LayoutInvalidationTracking:"LayoutInvalidationTracking", -LayerInvalidationTracking:"LayerInvalidationTracking", -PaintInvalidationTracking:"PaintInvalidationTracking", -ScrollInvalidationTracking:"ScrollInvalidationTracking", - -ParseHTML:"ParseHTML", -ParseAuthorStyleSheet:"ParseAuthorStyleSheet", - -TimerInstall:"TimerInstall", -TimerRemove:"TimerRemove", -TimerFire:"TimerFire", - -XHRReadyStateChange:"XHRReadyStateChange", -XHRLoad:"XHRLoad", -CompileScript:"v8.compile", -EvaluateScript:"EvaluateScript", - -CommitLoad:"CommitLoad", -MarkLoad:"MarkLoad", -MarkDOMContent:"MarkDOMContent", -MarkFirstPaint:"MarkFirstPaint", - -TimeStamp:"TimeStamp", -ConsoleTime:"ConsoleTime", -UserTiming:"UserTiming", - -ResourceSendRequest:"ResourceSendRequest", -ResourceReceiveResponse:"ResourceReceiveResponse", -ResourceReceivedData:"ResourceReceivedData", -ResourceFinish:"ResourceFinish", - -RunMicrotasks:"RunMicrotasks", -FunctionCall:"FunctionCall", -GCEvent:"GCEvent", -MajorGC:"MajorGC", -MinorGC:"MinorGC", -JSFrame:"JSFrame", -JSSample:"JSSample", - - - -V8Sample:"V8Sample", -JitCodeAdded:"JitCodeAdded", -JitCodeMoved:"JitCodeMoved", -ParseScriptOnBackground:"v8.parseOnBackground", - -UpdateCounters:"UpdateCounters", - -RequestAnimationFrame:"RequestAnimationFrame", -CancelAnimationFrame:"CancelAnimationFrame", -FireAnimationFrame:"FireAnimationFrame", - -RequestIdleCallback:"RequestIdleCallback", -CancelIdleCallback:"CancelIdleCallback", -FireIdleCallback:"FireIdleCallback", - -WebSocketCreate:"WebSocketCreate", -WebSocketSendHandshakeRequest:"WebSocketSendHandshakeRequest", -WebSocketReceiveHandshakeResponse:"WebSocketReceiveHandshakeResponse", -WebSocketDestroy:"WebSocketDestroy", - -EmbedderCallback:"EmbedderCallback", - -SetLayerTreeId:"SetLayerTreeId", -TracingStartedInPage:"TracingStartedInPage", -TracingSessionIdForWorker:"TracingSessionIdForWorker", - -DecodeImage:"Decode Image", -ResizeImage:"Resize Image", -DrawLazyPixelRef:"Draw LazyPixelRef", -DecodeLazyPixelRef:"Decode LazyPixelRef", - -LazyPixelRef:"LazyPixelRef", -LayerTreeHostImplSnapshot:"cc::LayerTreeHostImpl", -PictureSnapshot:"cc::Picture", -DisplayItemListSnapshot:"cc::DisplayItemList", -LatencyInfo:"LatencyInfo", -LatencyInfoFlow:"LatencyInfo.Flow", -InputLatencyMouseMove:"InputLatency::MouseMove", -InputLatencyMouseWheel:"InputLatency::MouseWheel", -ImplSideFling:"InputHandlerProxy::HandleGestureFling::started", -GCIdleLazySweep:"ThreadState::performIdleLazySweep", -GCCompleteSweep:"ThreadState::completeSweep", -GCCollectGarbage:"BlinkGCMarking", - - - -CpuProfile:"CpuProfile"}; - - -WebInspector.TimelineModel.Category={ -Console:"blink.console", -UserTiming:"blink.user_timing", -LatencyInfo:"latencyInfo"}; - - - - - -WebInspector.TimelineModel.WarningType={ -ForcedStyle:"ForcedStyle", -ForcedLayout:"ForcedLayout", -IdleDeadlineExceeded:"IdleDeadlineExceeded", -V8Deopt:"V8Deopt"}; - - -WebInspector.TimelineModel.MainThreadName="main"; -WebInspector.TimelineModel.WorkerThreadName="DedicatedWorker Thread"; -WebInspector.TimelineModel.RendererMainThreadName="CrRendererMain"; - - - - -WebInspector.TimelineModel.AsyncEventGroup={ -animation:Symbol("animation"), -console:Symbol("console"), -userTiming:Symbol("userTiming"), -input:Symbol("input")}; - - - - - - - - - - -WebInspector.TimelineModel.forEachEvent=function(events,onStartEvent,onEndEvent,onInstantEvent,startTime,endTime) -{ -startTime=startTime||0; -endTime=endTime||Infinity; -var stack=[]; -for(var i=0;i<events.length;++i){ -var e=events[i]; -if((e.endTime||e.startTime)<startTime) -continue; -if(e.startTime>=endTime) -break; -if(WebInspector.TracingModel.isAsyncPhase(e.phase)||WebInspector.TracingModel.isFlowPhase(e.phase)) -continue; -while(stack.length&&stack.peekLast().endTime<=e.startTime) -onEndEvent(stack.pop()); -if(e.duration){ -onStartEvent(e); -stack.push(e); -}else{ -onInstantEvent&&onInstantEvent(e,stack.peekLast()||null); -} -} -while(stack.length) -onEndEvent(stack.pop()); -}; - -WebInspector.TimelineModel.DevToolsMetadataEvent={ -TracingStartedInBrowser:"TracingStartedInBrowser", -TracingStartedInPage:"TracingStartedInPage", -TracingSessionIdForWorker:"TracingSessionIdForWorker"}; - - - - - - -WebInspector.TimelineModel.VirtualThread=function(name) -{ -this.name=name; - -this.events=[]; - -this.asyncEventsByGroup=new Map(); -}; - -WebInspector.TimelineModel.VirtualThread.prototype={ - - - -isWorker:function() -{ -return this.name===WebInspector.TimelineModel.WorkerThreadName; -}}; - - - - - - -WebInspector.TimelineModel.Record=function(traceEvent) -{ -this._event=traceEvent; -this._children=[]; -}; - - - - - - -WebInspector.TimelineModel.Record._compareStartTime=function(a,b) -{ - -return a.startTime()<=b.startTime()?-1:1; -}; - -WebInspector.TimelineModel.Record.prototype={ - - - -target:function() -{ -var threadName=this._event.thread.name(); - -return threadName===WebInspector.TimelineModel.RendererMainThreadName?WebInspector.targetManager.targets()[0]||null:null; -}, - - - - -children:function() -{ -return this._children; -}, - - - - -startTime:function() -{ -return this._event.startTime; -}, - - - - -endTime:function() -{ -return this._event.endTime||this._event.startTime; -}, - - - - -thread:function() -{ -if(this._event.thread.name()===WebInspector.TimelineModel.RendererMainThreadName) -return WebInspector.TimelineModel.MainThreadName; -return this._event.thread.name(); -}, - - - - -type:function() -{ -return WebInspector.TimelineModel._eventType(this._event); -}, - - - - - -getUserObject:function(key) -{ -if(key==="TimelineUIUtils::preview-element") -return this._event.previewElement; -throw new Error("Unexpected key: "+key); -}, - - - - - -setUserObject:function(key,value) -{ -if(key!=="TimelineUIUtils::preview-element") -throw new Error("Unexpected key: "+key); -this._event.previewElement=value; -}, - - - - -traceEvent:function() -{ -return this._event; -}, - - - - -_addChild:function(child) -{ -this._children.push(child); -child.parent=this; -}}; - - - -WebInspector.TimelineModel.MetadataEvents; - - - - -WebInspector.TimelineModel._eventType=function(event) -{ -if(event.hasCategory(WebInspector.TimelineModel.Category.Console)) -return WebInspector.TimelineModel.RecordType.ConsoleTime; -if(event.hasCategory(WebInspector.TimelineModel.Category.UserTiming)) -return WebInspector.TimelineModel.RecordType.UserTiming; -if(event.hasCategory(WebInspector.TimelineModel.Category.LatencyInfo)) -return WebInspector.TimelineModel.RecordType.LatencyInfo; -return event.name; -}; - -WebInspector.TimelineModel.prototype={ - - - - - - -forAllRecords:function(preOrderCallback,postOrderCallback) -{ - - - - - -function processRecords(records,depth) -{ -for(var i=0;i<records.length;++i){ -var record=records[i]; -if(preOrderCallback&&preOrderCallback(record,depth)) -return true; -if(processRecords(record.children(),depth+1)) -return true; -if(postOrderCallback&&postOrderCallback(record,depth)) -return true; -} -return false; -} -return processRecords(this._records,0); -}, - - - - - -forAllFilteredRecords:function(filters,callback) -{ - - - - - - -function processRecord(record,depth) -{ -var visible=WebInspector.TimelineModel.isVisible(filters,record.traceEvent()); -if(visible&&callback(record,depth)) -return true; - -for(var i=0;i<record.children().length;++i){ -if(processRecord.call(this,record.children()[i],visible?depth+1:depth)) -return true; -} -return false; -} - -for(var i=0;i<this._records.length;++i) -processRecord.call(this,this._records[i],0); -}, - - - - -records:function() -{ -return this._records; -}, - - - - -cpuProfiles:function() -{ -return this._cpuProfiles; -}, - - - - -sessionId:function() -{ -return this._sessionId; -}, - - - - - -targetByEvent:function(event) -{ - -var workerId=this._workerIdByThread.get(event.thread); -var mainTarget=WebInspector.targetManager.mainTarget(); -return workerId?mainTarget.workerManager.targetByWorkerId(workerId):mainTarget; -}, - - - - - -setEvents:function(tracingModel,produceTraceStartedInPage) -{ -this.reset(); -this._resetProcessingState(); - -this._minimumRecordTime=tracingModel.minimumRecordTime(); -this._maximumRecordTime=tracingModel.maximumRecordTime(); - -var metadataEvents=this._processMetadataEvents(tracingModel,!!produceTraceStartedInPage); -if(Runtime.experiments.isEnabled("timelineShowAllProcesses")){ -var lastPageMetaEvent=metadataEvents.page.peekLast(); -for(var process of tracingModel.sortedProcesses()){ -for(var thread of process.sortedThreads()) -this._processThreadEvents(0,Infinity,thread,thread===lastPageMetaEvent.thread); -} -}else{ -var startTime=0; -for(var i=0,length=metadataEvents.page.length;i<length;i++){ -var metaEvent=metadataEvents.page[i]; -var process=metaEvent.thread.process(); -var endTime=i+1<length?metadataEvents.page[i+1].startTime:Infinity; -this._currentPage=metaEvent.args["data"]&&metaEvent.args["data"]["page"]; -for(var thread of process.sortedThreads()){ -if(thread.name()===WebInspector.TimelineModel.WorkerThreadName){ -var workerMetaEvent=metadataEvents.workers.find(e=>e.args["data"]["workerThreadId"]===thread.id()); -if(!workerMetaEvent) -continue; -var workerId=workerMetaEvent.args["data"]["workerId"]; -if(workerId) -this._workerIdByThread.set(thread,workerId); -} -this._processThreadEvents(startTime,endTime,thread,thread===metaEvent.thread); -} -startTime=endTime; -} -} -this._inspectedTargetEvents.sort(WebInspector.TracingModel.Event.compareStartTime); - -this._processBrowserEvents(tracingModel); -this._buildTimelineRecords(); -this._buildGPUEvents(tracingModel); -this._insertFirstPaintEvent(); -this._resetProcessingState(); -}, - - - - - - -_processMetadataEvents:function(tracingModel,produceTraceStartedInPage) -{ -var metadataEvents=tracingModel.devToolsMetadataEvents(); - -var pageDevToolsMetadataEvents=[]; -var workersDevToolsMetadataEvents=[]; -for(var event of metadataEvents){ -if(event.name===WebInspector.TimelineModel.DevToolsMetadataEvent.TracingStartedInPage){ -pageDevToolsMetadataEvents.push(event); -}else if(event.name===WebInspector.TimelineModel.DevToolsMetadataEvent.TracingSessionIdForWorker){ -workersDevToolsMetadataEvents.push(event); -}else if(event.name===WebInspector.TimelineModel.DevToolsMetadataEvent.TracingStartedInBrowser){ -console.assert(!this._mainFrameNodeId,"Multiple sessions in trace"); -this._mainFrameNodeId=event.args["frameTreeNodeId"]; -} -} -if(!pageDevToolsMetadataEvents.length){ - -var pageMetaEvent=produceTraceStartedInPage?this._makeMockPageMetadataEvent(tracingModel):null; -if(!pageMetaEvent){ -console.error(WebInspector.TimelineModel.DevToolsMetadataEvent.TracingStartedInPage+" event not found."); -return{page:[],workers:[]}; -} -pageDevToolsMetadataEvents.push(pageMetaEvent); -} -var sessionId=pageDevToolsMetadataEvents[0].args["sessionId"]||pageDevToolsMetadataEvents[0].args["data"]["sessionId"]; -this._sessionId=sessionId; - -var mismatchingIds=new Set(); - - - - -function checkSessionId(event) -{ -var args=event.args; - -if(args["data"]) -args=args["data"]; -var id=args["sessionId"]; -if(id===sessionId) -return true; -mismatchingIds.add(id); -return false; -} -var result={ -page:pageDevToolsMetadataEvents.filter(checkSessionId).sort(WebInspector.TracingModel.Event.compareStartTime), -workers:workersDevToolsMetadataEvents.filter(checkSessionId).sort(WebInspector.TracingModel.Event.compareStartTime)}; - -if(mismatchingIds.size) -WebInspector.console.error("Timeline recording was started in more than one page simultaneously. Session id mismatch: "+this._sessionId+" and "+mismatchingIds.valuesArray()+"."); -return result; -}, - - - - - -_makeMockPageMetadataEvent:function(tracingModel) -{ -var rendererMainThreadName=WebInspector.TimelineModel.RendererMainThreadName; - -var process=tracingModel.sortedProcesses().filter(function(p){return p.threadByName(rendererMainThreadName);})[0]; -var thread=process&&process.threadByName(rendererMainThreadName); -if(!thread) -return null; -var pageMetaEvent=new WebInspector.TracingModel.Event( -WebInspector.TracingModel.DevToolsMetadataEventCategory, -WebInspector.TimelineModel.DevToolsMetadataEvent.TracingStartedInPage, -WebInspector.TracingModel.Phase.Metadata, -tracingModel.minimumRecordTime(),thread); -pageMetaEvent.addArgs({"data":{"sessionId":"mockSessionId"}}); -return pageMetaEvent; -}, - -_insertFirstPaintEvent:function() -{ -if(!this._firstCompositeLayers) -return; - - -var recordTypes=WebInspector.TimelineModel.RecordType; -var i=this._inspectedTargetEvents.lowerBound(this._firstCompositeLayers,WebInspector.TracingModel.Event.compareStartTime); -for(;i<this._inspectedTargetEvents.length&&this._inspectedTargetEvents[i].name!==recordTypes.DrawFrame;++i){} -if(i>=this._inspectedTargetEvents.length) -return; -var drawFrameEvent=this._inspectedTargetEvents[i]; -var firstPaintEvent=new WebInspector.TracingModel.Event(drawFrameEvent.categoriesString,recordTypes.MarkFirstPaint,WebInspector.TracingModel.Phase.Instant,drawFrameEvent.startTime,drawFrameEvent.thread); -this._mainThreadEvents.splice(this._mainThreadEvents.lowerBound(firstPaintEvent,WebInspector.TracingModel.Event.compareStartTime),0,firstPaintEvent); -var firstPaintRecord=new WebInspector.TimelineModel.Record(firstPaintEvent); -this._eventDividerRecords.splice(this._eventDividerRecords.lowerBound(firstPaintRecord,WebInspector.TimelineModel.Record._compareStartTime),0,firstPaintRecord); -}, - - - - -_processBrowserEvents:function(tracingModel) -{ -var browserMain=WebInspector.TracingModel.browserMainThread(tracingModel); -if(!browserMain) -return; - - -browserMain.events().forEach(this._processBrowserEvent,this); - -var asyncEventsByGroup=new Map(); -this._processAsyncEvents(asyncEventsByGroup,browserMain.asyncEvents()); -this._mergeAsyncEvents(this._mainThreadAsyncEventsByGroup,asyncEventsByGroup); -}, - -_buildTimelineRecords:function() -{ -var topLevelRecords=this._buildTimelineRecordsForThread(this.mainThreadEvents()); -for(var i=0;i<topLevelRecords.length;i++){ -var record=topLevelRecords[i]; -if(WebInspector.TracingModel.isTopLevelEvent(record.traceEvent())) -this._mainThreadTasks.push(record); -} - - - - - -function processVirtualThreadEvents(virtualThread) -{ -var threadRecords=this._buildTimelineRecordsForThread(virtualThread.events); -topLevelRecords=topLevelRecords.mergeOrdered(threadRecords,WebInspector.TimelineModel.Record._compareStartTime); -} -this.virtualThreads().forEach(processVirtualThreadEvents.bind(this)); -this._records=topLevelRecords; -}, - - - - -_buildGPUEvents:function(tracingModel) -{ -var thread=tracingModel.threadByName("GPU Process","CrGpuMain"); -if(!thread) -return; -var gpuEventName=WebInspector.TimelineModel.RecordType.GPUTask; -this._gpuEvents=thread.events().filter(event=>event.name===gpuEventName); -}, - - - - - -_buildTimelineRecordsForThread:function(threadEvents) -{ -var recordStack=[]; -var topLevelRecords=[]; - -for(var i=0,size=threadEvents.length;i<size;++i){ -var event=threadEvents[i]; -for(var top=recordStack.peekLast();top&&top._event.endTime<=event.startTime;top=recordStack.peekLast()) -recordStack.pop(); -if(event.phase===WebInspector.TracingModel.Phase.AsyncEnd||event.phase===WebInspector.TracingModel.Phase.NestableAsyncEnd) -continue; -var parentRecord=recordStack.peekLast(); - -if(WebInspector.TracingModel.isAsyncBeginPhase(event.phase)&&parentRecord&&event.endTime>parentRecord._event.endTime) -continue; -var record=new WebInspector.TimelineModel.Record(event); -if(WebInspector.TimelineModel.isMarkerEvent(event)) -this._eventDividerRecords.push(record); -if(!this._eventFilter.accept(event)&&!WebInspector.TracingModel.isTopLevelEvent(event)) -continue; -if(parentRecord) -parentRecord._addChild(record);else - -topLevelRecords.push(record); -if(event.endTime) -recordStack.push(record); -} - -return topLevelRecords; -}, - -_resetProcessingState:function() -{ -this._asyncEventTracker=new WebInspector.TimelineAsyncEventTracker(); -this._invalidationTracker=new WebInspector.InvalidationTracker(); -this._layoutInvalidate={}; -this._lastScheduleStyleRecalculation={}; -this._paintImageEventByPixelRefId={}; -this._lastPaintForLayer={}; -this._lastRecalculateStylesEvent=null; -this._currentScriptEvent=null; -this._eventStack=[]; -this._hadCommitLoad=false; -this._firstCompositeLayers=null; - -this._knownInputEvents=new Set(); -this._currentPage=null; -}, - - - - - - - -_processThreadEvents:function(startTime,endTime,thread,isMainThread) -{ -var events=thread.events(); -var asyncEvents=thread.asyncEvents(); - -var jsSamples; -if(Runtime.experiments.isEnabled("timelineTracingJSProfile")){ -jsSamples=WebInspector.TimelineJSProfileProcessor.processRawV8Samples(events); -}else{ -var cpuProfileEvent=events.peekLast(); -if(cpuProfileEvent&&cpuProfileEvent.name===WebInspector.TimelineModel.RecordType.CpuProfile){ -var cpuProfile=cpuProfileEvent.args["data"]["cpuProfile"]; -if(cpuProfile){ -var jsProfileModel=new WebInspector.CPUProfileDataModel(cpuProfile); -this._cpuProfiles.push(jsProfileModel); -jsSamples=WebInspector.TimelineJSProfileProcessor.generateTracingEventsFromCpuProfile(jsProfileModel,thread); -} -} -} - -if(jsSamples&&jsSamples.length) -events=events.mergeOrdered(jsSamples,WebInspector.TracingModel.Event.orderedCompareStartTime); -if(jsSamples||events.some(function(e){return e.name===WebInspector.TimelineModel.RecordType.JSSample;})){ -var jsFrameEvents=WebInspector.TimelineJSProfileProcessor.generateJSFrameEvents(events); -if(jsFrameEvents&&jsFrameEvents.length) -events=jsFrameEvents.mergeOrdered(events,WebInspector.TracingModel.Event.orderedCompareStartTime); -} - -var threadEvents; -var threadAsyncEventsByGroup; -if(isMainThread){ -threadEvents=this._mainThreadEvents; -threadAsyncEventsByGroup=this._mainThreadAsyncEventsByGroup; -}else{ -var virtualThread=new WebInspector.TimelineModel.VirtualThread(thread.name()); -this._virtualThreads.push(virtualThread); -threadEvents=virtualThread.events; -threadAsyncEventsByGroup=virtualThread.asyncEventsByGroup; -} - -this._eventStack=[]; -var i=events.lowerBound(startTime,function(time,event){return time-event.startTime;}); -var length=events.length; -for(;i<length;i++){ -var event=events[i]; -if(endTime&&event.startTime>=endTime) -break; -if(!this._processEvent(event)) -continue; -threadEvents.push(event); -this._inspectedTargetEvents.push(event); -} -this._processAsyncEvents(threadAsyncEventsByGroup,asyncEvents,startTime,endTime); - -if(thread.name()==="Compositor"){ -this._mergeAsyncEvents(this._mainThreadAsyncEventsByGroup,threadAsyncEventsByGroup); -threadAsyncEventsByGroup.clear(); -} -}, - - - - - - - -_processAsyncEvents:function(asyncEventsByGroup,asyncEvents,startTime,endTime) -{ -var i=startTime?asyncEvents.lowerBound(startTime,function(time,asyncEvent){return time-asyncEvent.startTime;}):0; -for(;i<asyncEvents.length;++i){ -var asyncEvent=asyncEvents[i]; -if(endTime&&asyncEvent.startTime>=endTime) -break; -var asyncGroup=this._processAsyncEvent(asyncEvent); -if(!asyncGroup) -continue; -var groupAsyncEvents=asyncEventsByGroup.get(asyncGroup); -if(!groupAsyncEvents){ -groupAsyncEvents=[]; -asyncEventsByGroup.set(asyncGroup,groupAsyncEvents); -} -groupAsyncEvents.push(asyncEvent); -} -}, - - - - - -_processEvent:function(event) -{ -var eventStack=this._eventStack; -while(eventStack.length&&eventStack.peekLast().endTime<=event.startTime) -eventStack.pop(); - -var recordTypes=WebInspector.TimelineModel.RecordType; - -if(this._currentScriptEvent&&event.startTime>this._currentScriptEvent.endTime) -this._currentScriptEvent=null; - -var eventData=event.args["data"]||event.args["beginData"]||{}; -if(eventData["stackTrace"]) -event.stackTrace=eventData["stackTrace"]; -if(event.stackTrace&&event.name!==recordTypes.JSSample){ - - -for(var i=0;i<event.stackTrace.length;++i){ ---event.stackTrace[i].lineNumber; ---event.stackTrace[i].columnNumber; -} -} - -if(eventStack.length&&eventStack.peekLast().name===recordTypes.EventDispatch) -eventStack.peekLast().hasChildren=true; -this._asyncEventTracker.processEvent(event); -if(event.initiator&&event.initiator.url) -event.url=event.initiator.url; -switch(event.name){ -case recordTypes.ResourceSendRequest: -case recordTypes.WebSocketCreate: -event.url=eventData["url"]; -event.initiator=eventStack.peekLast()||null; -break; - -case recordTypes.ScheduleStyleRecalculation: -this._lastScheduleStyleRecalculation[eventData["frame"]]=event; -break; - -case recordTypes.UpdateLayoutTree: -case recordTypes.RecalculateStyles: -this._invalidationTracker.didRecalcStyle(event); -if(event.args["beginData"]) -event.initiator=this._lastScheduleStyleRecalculation[event.args["beginData"]["frame"]]; -this._lastRecalculateStylesEvent=event; -if(this._currentScriptEvent) -event.warning=WebInspector.TimelineModel.WarningType.ForcedStyle; -break; - -case recordTypes.ScheduleStyleInvalidationTracking: -case recordTypes.StyleRecalcInvalidationTracking: -case recordTypes.StyleInvalidatorInvalidationTracking: -case recordTypes.LayoutInvalidationTracking: -case recordTypes.LayerInvalidationTracking: -case recordTypes.PaintInvalidationTracking: -case recordTypes.ScrollInvalidationTracking: -this._invalidationTracker.addInvalidation(new WebInspector.InvalidationTrackingEvent(event)); -break; - -case recordTypes.InvalidateLayout: - - -var layoutInitator=event; -var frameId=eventData["frame"]; -if(!this._layoutInvalidate[frameId]&&this._lastRecalculateStylesEvent&&this._lastRecalculateStylesEvent.endTime>event.startTime) -layoutInitator=this._lastRecalculateStylesEvent.initiator; -this._layoutInvalidate[frameId]=layoutInitator; -break; - -case recordTypes.Layout: -this._invalidationTracker.didLayout(event); -var frameId=event.args["beginData"]["frame"]; -event.initiator=this._layoutInvalidate[frameId]; - -if(event.args["endData"]){ -event.backendNodeId=event.args["endData"]["rootNode"]; -event.highlightQuad=event.args["endData"]["root"]; -} -this._layoutInvalidate[frameId]=null; -if(this._currentScriptEvent) -event.warning=WebInspector.TimelineModel.WarningType.ForcedLayout; -break; - -case recordTypes.FunctionCall: - -if(typeof eventData["scriptName"]==="string") -eventData["url"]=eventData["scriptName"]; -if(typeof eventData["scriptLine"]==="number") -eventData["lineNumber"]=eventData["scriptLine"]; - -case recordTypes.EvaluateScript: -case recordTypes.CompileScript: -if(typeof eventData["lineNumber"]==="number") ---eventData["lineNumber"]; -if(typeof eventData["columnNumber"]==="number") ---eventData["columnNumber"]; -if(!this._currentScriptEvent) -this._currentScriptEvent=event; -break; - -case recordTypes.SetLayerTreeId: -this._inspectedTargetLayerTreeId=event.args["layerTreeId"]||event.args["data"]["layerTreeId"]; -break; - -case recordTypes.Paint: -this._invalidationTracker.didPaint(event); -event.highlightQuad=eventData["clip"]; -event.backendNodeId=eventData["nodeId"]; - -if(!eventData["layerId"]) -break; -var layerId=eventData["layerId"]; -this._lastPaintForLayer[layerId]=event; -break; - -case recordTypes.DisplayItemListSnapshot: -case recordTypes.PictureSnapshot: -var layerUpdateEvent=this._findAncestorEvent(recordTypes.UpdateLayer); -if(!layerUpdateEvent||layerUpdateEvent.args["layerTreeId"]!==this._inspectedTargetLayerTreeId) -break; -var paintEvent=this._lastPaintForLayer[layerUpdateEvent.args["layerId"]]; -if(paintEvent) -paintEvent.picture=event; -break; - -case recordTypes.ScrollLayer: -event.backendNodeId=eventData["nodeId"]; -break; - -case recordTypes.PaintImage: -event.backendNodeId=eventData["nodeId"]; -event.url=eventData["url"]; -break; - -case recordTypes.DecodeImage: -case recordTypes.ResizeImage: -var paintImageEvent=this._findAncestorEvent(recordTypes.PaintImage); -if(!paintImageEvent){ -var decodeLazyPixelRefEvent=this._findAncestorEvent(recordTypes.DecodeLazyPixelRef); -paintImageEvent=decodeLazyPixelRefEvent&&this._paintImageEventByPixelRefId[decodeLazyPixelRefEvent.args["LazyPixelRef"]]; -} -if(!paintImageEvent) -break; -event.backendNodeId=paintImageEvent.backendNodeId; -event.url=paintImageEvent.url; -break; - -case recordTypes.DrawLazyPixelRef: -var paintImageEvent=this._findAncestorEvent(recordTypes.PaintImage); -if(!paintImageEvent) -break; -this._paintImageEventByPixelRefId[event.args["LazyPixelRef"]]=paintImageEvent; -event.backendNodeId=paintImageEvent.backendNodeId; -event.url=paintImageEvent.url; -break; - -case recordTypes.MarkDOMContent: -case recordTypes.MarkLoad: -var page=eventData["page"]; -if(page&&page!==this._currentPage) -return false; -break; - -case recordTypes.CommitLoad: -var page=eventData["page"]; -if(page&&page!==this._currentPage) -return false; -if(!eventData["isMainFrame"]) -break; -this._hadCommitLoad=true; -this._firstCompositeLayers=null; -break; - -case recordTypes.CompositeLayers: -if(!this._firstCompositeLayers&&this._hadCommitLoad) -this._firstCompositeLayers=event; -break; - -case recordTypes.FireIdleCallback: -if(event.duration>eventData["allottedMilliseconds"]){ -event.warning=WebInspector.TimelineModel.WarningType.IdleDeadlineExceeded; -} -break;} - -if(WebInspector.TracingModel.isAsyncPhase(event.phase)) -return true; -var duration=event.duration; -if(!duration) -return true; -if(eventStack.length){ -var parent=eventStack.peekLast(); -parent.selfTime-=duration; -if(parent.selfTime<0){ -var epsilon=1e-3; -if(parent.selfTime<-epsilon) -console.error("Children are longer than parent at "+event.startTime+" ("+(event.startTime-this.minimumRecordTime()).toFixed(3)+") by "+parent.selfTime.toFixed(3)); -parent.selfTime=0; -} -} -event.selfTime=duration; -eventStack.push(event); -return true; -}, - - - - -_processBrowserEvent:function(event) -{ -if(event.name!==WebInspector.TimelineModel.RecordType.LatencyInfoFlow) -return; -var frameId=event.args["frameTreeNodeId"]; -if(typeof frameId==="number"&&frameId===this._mainFrameNodeId) -this._knownInputEvents.add(event.bind_id); -}, - - - - - -_processAsyncEvent:function(asyncEvent) -{ -var groups=WebInspector.TimelineModel.AsyncEventGroup; -if(asyncEvent.hasCategory(WebInspector.TimelineModel.Category.Console)) -return groups.console; -if(asyncEvent.hasCategory(WebInspector.TimelineModel.Category.UserTiming)) -return groups.userTiming; -if(asyncEvent.name===WebInspector.TimelineModel.RecordType.Animation) -return groups.animation; -if(asyncEvent.hasCategory(WebInspector.TimelineModel.Category.LatencyInfo)||asyncEvent.name===WebInspector.TimelineModel.RecordType.ImplSideFling){ -var lastStep=asyncEvent.steps.peekLast(); - -if(lastStep.phase!==WebInspector.TracingModel.Phase.AsyncEnd) -return null; -var data=lastStep.args["data"]; -asyncEvent.causedFrame=!!(data&&data["INPUT_EVENT_LATENCY_RENDERER_SWAP_COMPONENT"]); -if(asyncEvent.hasCategory(WebInspector.TimelineModel.Category.LatencyInfo)){ -if(!this._knownInputEvents.has(lastStep.id)) -return null; -if(asyncEvent.name===WebInspector.TimelineModel.RecordType.InputLatencyMouseMove&&!asyncEvent.causedFrame) -return null; -var rendererMain=data["INPUT_EVENT_LATENCY_RENDERER_MAIN_COMPONENT"]; -if(rendererMain){ -var time=rendererMain["time"]/1000; -asyncEvent.steps[0].timeWaitingForMainThread=time-asyncEvent.steps[0].startTime; -} -} -return groups.input; -} -return null; -}, - - - - - -_findAncestorEvent:function(name) -{ -for(var i=this._eventStack.length-1;i>=0;--i){ -var event=this._eventStack[i]; -if(event.name===name) -return event; -} -return null; -}, - - - - - -_mergeAsyncEvents:function(target,source) -{ -for(var group of source.keys()){ -var events=target.get(group)||[]; -events=events.mergeOrdered(source.get(group)||[],WebInspector.TracingModel.Event.compareStartAndEndTime); -target.set(group,events); -} -}, - -reset:function() -{ -this._virtualThreads=[]; - -this._mainThreadEvents=[]; - -this._mainThreadAsyncEventsByGroup=new Map(); - -this._inspectedTargetEvents=[]; - -this._records=[]; - -this._mainThreadTasks=[]; - -this._gpuEvents=[]; - -this._eventDividerRecords=[]; - -this._sessionId=null; - -this._mainFrameNodeId=null; - -this._cpuProfiles=[]; - -this._workerIdByThread=new WeakMap(); -this._minimumRecordTime=0; -this._maximumRecordTime=0; -}, - - - - -minimumRecordTime:function() -{ -return this._minimumRecordTime; -}, - - - - -maximumRecordTime:function() -{ -return this._maximumRecordTime; -}, - - - - -inspectedTargetEvents:function() -{ -return this._inspectedTargetEvents; -}, - - - - -mainThreadEvents:function() -{ -return this._mainThreadEvents; -}, - - - - -_setMainThreadEvents:function(events) -{ -this._mainThreadEvents=events; -}, - - - - -mainThreadAsyncEvents:function() -{ -return this._mainThreadAsyncEventsByGroup; -}, - - - - -virtualThreads:function() -{ -return this._virtualThreads; -}, - - - - -isEmpty:function() -{ -return this.minimumRecordTime()===0&&this.maximumRecordTime()===0; -}, - - - - -mainThreadTasks:function() -{ -return this._mainThreadTasks; -}, - - - - -gpuEvents:function() -{ -return this._gpuEvents; -}, - - - - -eventDividerRecords:function() -{ -return this._eventDividerRecords; -}, - - - - -networkRequests:function() -{ - -var requests=new Map(); - -var requestsList=[]; - -var zeroStartRequestsList=[]; -var types=WebInspector.TimelineModel.RecordType; -var resourceTypes=new Set([ -types.ResourceSendRequest, -types.ResourceReceiveResponse, -types.ResourceReceivedData, -types.ResourceFinish]); - -var events=this.mainThreadEvents(); -for(var i=0;i<events.length;++i){ -var e=events[i]; -if(!resourceTypes.has(e.name)) -continue; -var id=e.args["data"]["requestId"]; -var request=requests.get(id); -if(request){ -request.addEvent(e); -}else{ -request=new WebInspector.TimelineModel.NetworkRequest(e); -requests.set(id,request); -if(request.startTime) -requestsList.push(request);else - -zeroStartRequestsList.push(request); -} -} -return zeroStartRequestsList.concat(requestsList); -}}; - - - - - - - -WebInspector.TimelineModel.isVisible=function(filters,event) -{ -for(var i=0;i<filters.length;++i){ -if(!filters[i].accept(event)) -return false; -} -return true; -}; - - - - - -WebInspector.TimelineModel.isMarkerEvent=function(event) -{ -var recordTypes=WebInspector.TimelineModel.RecordType; -switch(event.name){ -case recordTypes.TimeStamp: -case recordTypes.MarkFirstPaint: -return true; -case recordTypes.MarkDOMContent: -case recordTypes.MarkLoad: -return event.args["data"]["isMainFrame"]; -default: -return false;} - -}; - - - - - -WebInspector.TimelineModel.NetworkRequest=function(event) -{ -this.startTime=event.name===WebInspector.TimelineModel.RecordType.ResourceSendRequest?event.startTime:0; -this.endTime=Infinity; - -this.children=[]; -this.addEvent(event); -}; - -WebInspector.TimelineModel.NetworkRequest.prototype={ - - - -addEvent:function(event) -{ -this.children.push(event); -var recordType=WebInspector.TimelineModel.RecordType; -this.startTime=Math.min(this.startTime,event.startTime); -var eventData=event.args["data"]; -if(eventData["mimeType"]) -this.mimeType=eventData["mimeType"]; -if("priority"in eventData) -this.priority=eventData["priority"]; -if(event.name===recordType.ResourceFinish) -this.endTime=event.startTime; -if(!this.responseTime&&(event.name===recordType.ResourceReceiveResponse||event.name===recordType.ResourceReceivedData)) -this.responseTime=event.startTime; -if(!this.url) -this.url=eventData["url"]; -if(!this.requestMethod) -this.requestMethod=eventData["requestMethod"]; -}}; - - - - - -WebInspector.TimelineModel.Filter=function() -{ -}; - -WebInspector.TimelineModel.Filter.prototype={ - - - - -accept:function(event) -{ -return true; -}}; - - - - - - - -WebInspector.TimelineVisibleEventsFilter=function(visibleTypes) -{ -WebInspector.TimelineModel.Filter.call(this); -this._visibleTypes=new Set(visibleTypes); -}; - -WebInspector.TimelineVisibleEventsFilter.prototype={ - - - - - -accept:function(event) -{ -return this._visibleTypes.has(WebInspector.TimelineModel._eventType(event)); -}, - -__proto__:WebInspector.TimelineModel.Filter.prototype}; - - - - - - - -WebInspector.ExclusiveNameFilter=function(excludeNames) -{ -WebInspector.TimelineModel.Filter.call(this); -this._excludeNames=new Set(excludeNames); -}; - -WebInspector.ExclusiveNameFilter.prototype={ - - - - - -accept:function(event) -{ -return!this._excludeNames.has(event.name); -}, - -__proto__:WebInspector.TimelineModel.Filter.prototype}; - - - - - - -WebInspector.ExcludeTopLevelFilter=function() -{ -WebInspector.TimelineModel.Filter.call(this); -}; - -WebInspector.ExcludeTopLevelFilter.prototype={ - - - - - -accept:function(event) -{ -return!WebInspector.TracingModel.isTopLevelEvent(event); -}, - -__proto__:WebInspector.TimelineModel.Filter.prototype}; - - - - - - -WebInspector.InvalidationTrackingEvent=function(event) -{ - -this.type=event.name; - -this.startTime=event.startTime; - -this._tracingEvent=event; - -var eventData=event.args["data"]; - - -this.frame=eventData["frame"]; - -this.nodeId=eventData["nodeId"]; - -this.nodeName=eventData["nodeName"]; - -this.paintId=eventData["paintId"]; - -this.invalidationSet=eventData["invalidationSet"]; - -this.invalidatedSelectorId=eventData["invalidatedSelectorId"]; - -this.changedId=eventData["changedId"]; - -this.changedClass=eventData["changedClass"]; - -this.changedAttribute=eventData["changedAttribute"]; - -this.changedPseudo=eventData["changedPseudo"]; - -this.selectorPart=eventData["selectorPart"]; - -this.extraData=eventData["extraData"]; - -this.invalidationList=eventData["invalidationList"]; - -this.cause={reason:eventData["reason"],stackTrace:eventData["stackTrace"]}; - - -if(!this.cause.reason&&this.cause.stackTrace&&this.type===WebInspector.TimelineModel.RecordType.LayoutInvalidationTracking) -this.cause.reason="Layout forced"; -}; - - -WebInspector.InvalidationCause; - - - - -WebInspector.InvalidationTracker=function() -{ -this._initializePerFrameState(); -}; - -WebInspector.InvalidationTracker.prototype={ - - - -addInvalidation:function(invalidation) -{ -this._startNewFrameIfNeeded(); - -if(!invalidation.nodeId&&!invalidation.paintId){ -console.error("Invalidation lacks node information."); -console.error(invalidation); -return; -} - - - - -var recordTypes=WebInspector.TimelineModel.RecordType; -if(invalidation.type===recordTypes.PaintInvalidationTracking&&invalidation.nodeId){ -var invalidations=this._invalidationsByNodeId[invalidation.nodeId]||[]; -for(var i=0;i<invalidations.length;++i) -invalidations[i].paintId=invalidation.paintId; - - -return; -} - - - - -if(invalidation.type===recordTypes.StyleRecalcInvalidationTracking&&invalidation.cause.reason==="StyleInvalidator") -return; - - - - -var styleRecalcInvalidation=invalidation.type===recordTypes.ScheduleStyleInvalidationTracking|| -invalidation.type===recordTypes.StyleInvalidatorInvalidationTracking|| -invalidation.type===recordTypes.StyleRecalcInvalidationTracking; -if(styleRecalcInvalidation){ -var duringRecalcStyle=invalidation.startTime&&this._lastRecalcStyle&& -invalidation.startTime>=this._lastRecalcStyle.startTime&& -invalidation.startTime<=this._lastRecalcStyle.endTime; -if(duringRecalcStyle) -this._associateWithLastRecalcStyleEvent(invalidation); -} - - -if(this._invalidations[invalidation.type]) -this._invalidations[invalidation.type].push(invalidation);else - -this._invalidations[invalidation.type]=[invalidation]; -if(invalidation.nodeId){ -if(this._invalidationsByNodeId[invalidation.nodeId]) -this._invalidationsByNodeId[invalidation.nodeId].push(invalidation);else - -this._invalidationsByNodeId[invalidation.nodeId]=[invalidation]; -} -}, - - - - -didRecalcStyle:function(recalcStyleEvent) -{ -this._lastRecalcStyle=recalcStyleEvent; -var types=[WebInspector.TimelineModel.RecordType.ScheduleStyleInvalidationTracking, -WebInspector.TimelineModel.RecordType.StyleInvalidatorInvalidationTracking, -WebInspector.TimelineModel.RecordType.StyleRecalcInvalidationTracking]; -for(var invalidation of this._invalidationsOfTypes(types)) -this._associateWithLastRecalcStyleEvent(invalidation); -}, - - - - -_associateWithLastRecalcStyleEvent:function(invalidation) -{ -if(invalidation.linkedRecalcStyleEvent) -return; - -var recordTypes=WebInspector.TimelineModel.RecordType; -var recalcStyleFrameId=this._lastRecalcStyle.args["beginData"]["frame"]; -if(invalidation.type===recordTypes.StyleInvalidatorInvalidationTracking){ - - -this._addSyntheticStyleRecalcInvalidations(this._lastRecalcStyle,recalcStyleFrameId,invalidation); -}else if(invalidation.type===recordTypes.ScheduleStyleInvalidationTracking){ - - -}else{ -this._addInvalidationToEvent(this._lastRecalcStyle,recalcStyleFrameId,invalidation); -} - -invalidation.linkedRecalcStyleEvent=true; -}, - - - - - - -_addSyntheticStyleRecalcInvalidations:function(event,frameId,styleInvalidatorInvalidation) -{ -if(!styleInvalidatorInvalidation.invalidationList){ -this._addSyntheticStyleRecalcInvalidation(styleInvalidatorInvalidation._tracingEvent,styleInvalidatorInvalidation); -return; -} -if(!styleInvalidatorInvalidation.nodeId){ -console.error("Invalidation lacks node information."); -console.error(invalidation); -return; -} -for(var i=0;i<styleInvalidatorInvalidation.invalidationList.length;i++){ -var setId=styleInvalidatorInvalidation.invalidationList[i]["id"]; -var lastScheduleStyleRecalculation; -var nodeInvalidations=this._invalidationsByNodeId[styleInvalidatorInvalidation.nodeId]||[]; -for(var j=0;j<nodeInvalidations.length;j++){ -var invalidation=nodeInvalidations[j]; -if(invalidation.frame!==frameId||invalidation.invalidationSet!==setId||invalidation.type!==WebInspector.TimelineModel.RecordType.ScheduleStyleInvalidationTracking) -continue; -lastScheduleStyleRecalculation=invalidation; -} -if(!lastScheduleStyleRecalculation){ -console.error("Failed to lookup the event that scheduled a style invalidator invalidation."); -continue; -} -this._addSyntheticStyleRecalcInvalidation(lastScheduleStyleRecalculation._tracingEvent,styleInvalidatorInvalidation); -} -}, - - - - - -_addSyntheticStyleRecalcInvalidation:function(baseEvent,styleInvalidatorInvalidation) -{ -var invalidation=new WebInspector.InvalidationTrackingEvent(baseEvent); -invalidation.type=WebInspector.TimelineModel.RecordType.StyleRecalcInvalidationTracking; -invalidation.synthetic=true; -if(styleInvalidatorInvalidation.cause.reason) -invalidation.cause.reason=styleInvalidatorInvalidation.cause.reason; -if(styleInvalidatorInvalidation.selectorPart) -invalidation.selectorPart=styleInvalidatorInvalidation.selectorPart; - -this.addInvalidation(invalidation); -if(!invalidation.linkedRecalcStyleEvent) -this._associateWithLastRecalcStyleEvent(invalidation); -}, - - - - -didLayout:function(layoutEvent) -{ -var layoutFrameId=layoutEvent.args["beginData"]["frame"]; -for(var invalidation of this._invalidationsOfTypes([WebInspector.TimelineModel.RecordType.LayoutInvalidationTracking])){ -if(invalidation.linkedLayoutEvent) -continue; -this._addInvalidationToEvent(layoutEvent,layoutFrameId,invalidation); -invalidation.linkedLayoutEvent=true; -} -}, - - - - -didPaint:function(paintEvent) -{ -this._didPaint=true; - - - -var layerId=paintEvent.args["data"]["layerId"]; -if(layerId) -this._lastPaintWithLayer=paintEvent; - - -if(!this._lastPaintWithLayer) -return; - -var effectivePaintId=this._lastPaintWithLayer.args["data"]["nodeId"]; -var paintFrameId=paintEvent.args["data"]["frame"]; -var types=[WebInspector.TimelineModel.RecordType.StyleRecalcInvalidationTracking, -WebInspector.TimelineModel.RecordType.LayoutInvalidationTracking, -WebInspector.TimelineModel.RecordType.PaintInvalidationTracking, -WebInspector.TimelineModel.RecordType.ScrollInvalidationTracking]; -for(var invalidation of this._invalidationsOfTypes(types)){ -if(invalidation.paintId===effectivePaintId) -this._addInvalidationToEvent(paintEvent,paintFrameId,invalidation); -} -}, - - - - - - -_addInvalidationToEvent:function(event,eventFrameId,invalidation) -{ -if(eventFrameId!==invalidation.frame) -return; -if(!event.invalidationTrackingEvents) -event.invalidationTrackingEvents=[invalidation];else - -event.invalidationTrackingEvents.push(invalidation); -}, - - - - - -_invalidationsOfTypes:function(types) -{ -var invalidations=this._invalidations; -if(!types) -types=Object.keys(invalidations); -function*generator() -{ -for(var i=0;i<types.length;++i){ -var invalidationList=invalidations[types[i]]||[]; -for(var j=0;j<invalidationList.length;++j) -yield invalidationList[j]; -} -} -return generator(); -}, - -_startNewFrameIfNeeded:function() -{ -if(!this._didPaint) -return; - -this._initializePerFrameState(); -}, - -_initializePerFrameState:function() -{ - -this._invalidations={}; - -this._invalidationsByNodeId={}; - -this._lastRecalcStyle=undefined; -this._lastPaintWithLayer=undefined; -this._didPaint=false; -}}; - - - - - -WebInspector.TimelineAsyncEventTracker=function() -{ -WebInspector.TimelineAsyncEventTracker._initialize(); - -this._initiatorByType=new Map(); -for(var initiator of WebInspector.TimelineAsyncEventTracker._asyncEvents.keys()) -this._initiatorByType.set(initiator,new Map()); -}; - -WebInspector.TimelineAsyncEventTracker._initialize=function() -{ -if(WebInspector.TimelineAsyncEventTracker._asyncEvents) -return; -var events=new Map(); -var type=WebInspector.TimelineModel.RecordType; - -events.set(type.TimerInstall,{causes:[type.TimerFire],joinBy:"timerId"}); -events.set(type.ResourceSendRequest,{causes:[type.ResourceReceiveResponse,type.ResourceReceivedData,type.ResourceFinish],joinBy:"requestId"}); -events.set(type.RequestAnimationFrame,{causes:[type.FireAnimationFrame],joinBy:"id"}); -events.set(type.RequestIdleCallback,{causes:[type.FireIdleCallback],joinBy:"id"}); -events.set(type.WebSocketCreate,{causes:[type.WebSocketSendHandshakeRequest,type.WebSocketReceiveHandshakeResponse,type.WebSocketDestroy],joinBy:"identifier"}); - -WebInspector.TimelineAsyncEventTracker._asyncEvents=events; - -WebInspector.TimelineAsyncEventTracker._typeToInitiator=new Map(); -for(var entry of events){ -var types=entry[1].causes; -for(type of types) -WebInspector.TimelineAsyncEventTracker._typeToInitiator.set(type,entry[0]); -} -}; - -WebInspector.TimelineAsyncEventTracker.prototype={ - - - -processEvent:function(event) -{ -var initiatorType=WebInspector.TimelineAsyncEventTracker._typeToInitiator.get(event.name); -var isInitiator=!initiatorType; -if(!initiatorType) -initiatorType=event.name; -var initiatorInfo=WebInspector.TimelineAsyncEventTracker._asyncEvents.get(initiatorType); -if(!initiatorInfo) -return; -var id=event.args["data"][initiatorInfo.joinBy]; -if(!id) -return; - -var initiatorMap=this._initiatorByType.get(initiatorType); -if(isInitiator) -initiatorMap.set(id,event);else - -event.initiator=initiatorMap.get(id)||null; -}}; - - -},{}],131:[function(require,module,exports){ - - - - -WebInspector.TimelineProfileTree={}; - - - - -WebInspector.TimelineProfileTree.Node=function() -{ - -this.totalTime; - -this.selfTime; - -this.id; - -this.event; - -this.children; - -this.parent; -this._isGroupNode=false; -}; - -WebInspector.TimelineProfileTree.Node.prototype={ - - - -isGroupNode:function() -{ -return this._isGroupNode; -}}; - - - - - - - - - - -WebInspector.TimelineProfileTree.buildTopDown=function(events,filters,startTime,endTime,eventIdCallback) -{ - -var initialTime=1e7; -var root=new WebInspector.TimelineProfileTree.Node(); -root.totalTime=initialTime; -root.selfTime=initialTime; -root.children=new Map(); -var parent=root; - - - - -function onStartEvent(e) -{ -if(!WebInspector.TimelineModel.isVisible(filters,e)) -return; -var time=e.endTime?Math.min(endTime,e.endTime)-Math.max(startTime,e.startTime):0; -var id=eventIdCallback?eventIdCallback(e):Symbol("uniqueEventId"); -if(!parent.children) -parent.children=new Map(); -var node=parent.children.get(id); -if(node){ -node.selfTime+=time; -node.totalTime+=time; -}else{ -node=new WebInspector.TimelineProfileTree.Node(); -node.totalTime=time; -node.selfTime=time; -node.parent=parent; -node.id=id; -node.event=e; -parent.children.set(id,node); -} -parent.selfTime-=time; -if(parent.selfTime<0){ -console.log("Error: Negative self of "+parent.selfTime,e); -parent.selfTime=0; -} -if(e.endTime) -parent=node; -} - - - - -function onEndEvent(e) -{ -if(!WebInspector.TimelineModel.isVisible(filters,e)) -return; -parent=parent.parent; -} - -var instantEventCallback=eventIdCallback?undefined:onStartEvent; -WebInspector.TimelineModel.forEachEvent(events,onStartEvent,onEndEvent,instantEventCallback,startTime,endTime); -root.totalTime-=root.selfTime; -root.selfTime=0; -return root; -}; - - - - - - -WebInspector.TimelineProfileTree.buildBottomUp=function(topDownTree,groupingCallback) -{ -var buRoot=new WebInspector.TimelineProfileTree.Node(); -buRoot.selfTime=0; -buRoot.totalTime=0; - -buRoot.children=new Map(); -var nodesOnStack=new Set(); -if(topDownTree.children) -topDownTree.children.forEach(processNode); -buRoot.totalTime=topDownTree.totalTime; - - - - -function processNode(tdNode) -{ -var buParent=groupingCallback&&groupingCallback(tdNode)||buRoot; -if(buParent!==buRoot){ -buRoot.children.set(buParent.id,buParent); -buParent.parent=buRoot; -} -appendNode(tdNode,buParent); -var hadNode=nodesOnStack.has(tdNode.id); -if(!hadNode) -nodesOnStack.add(tdNode.id); -if(tdNode.children) -tdNode.children.forEach(processNode); -if(!hadNode) -nodesOnStack.delete(tdNode.id); -} - - - - - -function appendNode(tdNode,buParent) -{ -var selfTime=tdNode.selfTime; -var totalTime=tdNode.totalTime; -buParent.selfTime+=selfTime; -buParent.totalTime+=selfTime; -while(tdNode.parent){ -if(!buParent.children) -buParent.children=new Map(); -var id=tdNode.id; -var buNode=buParent.children.get(id); -if(!buNode){ -buNode=new WebInspector.TimelineProfileTree.Node(); -buNode.selfTime=selfTime; -buNode.totalTime=totalTime; -buNode.event=tdNode.event; -buNode.id=id; -buNode.parent=buParent; -buParent.children.set(id,buNode); -}else{ -buNode.selfTime+=selfTime; -if(!nodesOnStack.has(id)) -buNode.totalTime+=totalTime; -} -tdNode=tdNode.parent; -buParent=buNode; -} -} - - -var rootChildren=buRoot.children; -for(var item of rootChildren.entries()){ -if(item[1].selfTime===0) -rootChildren.delete(item[0]); -} - -return buRoot; -}; - - - - - -WebInspector.TimelineProfileTree.eventURL=function(event) -{ -var data=event.args["data"]||event.args["beginData"]; -if(data&&data["url"]) -return data["url"]; -var frame=WebInspector.TimelineProfileTree.eventStackFrame(event); -while(frame){ -var url=frame["url"]; -if(url) -return url; -frame=frame.parent; -} -return null; -}; - - - - - -WebInspector.TimelineProfileTree.eventStackFrame=function(event) -{ -if(event.name===WebInspector.TimelineModel.RecordType.JSFrame) -return event.args["data"]||null; -var topFrame=event.stackTrace&&event.stackTrace[0]; -if(topFrame) -return topFrame; -var initiator=event.initiator; -return initiator&&initiator.stackTrace&&initiator.stackTrace[0]||null; -}; - - - - - - -WebInspector.TimelineAggregator=function(titleMapper,categoryMapper) -{ -this._titleMapper=titleMapper; -this._categoryMapper=categoryMapper; - -this._groupNodes=new Map(); -}; - - - - -WebInspector.TimelineAggregator.GroupBy={ -None:"None", -EventName:"EventName", -Category:"Category", -Domain:"Domain", -Subdomain:"Subdomain", -URL:"URL"}; - - - - - - -WebInspector.TimelineAggregator.eventId=function(event) -{ -if(event.name===WebInspector.TimelineModel.RecordType.JSFrame){ -var data=event.args["data"]; -return"f:"+data["functionName"]+"@"+(data["scriptId"]||data["url"]||""); -} -return event.name+":@"+WebInspector.TimelineProfileTree.eventURL(event); -}; - -WebInspector.TimelineAggregator._extensionInternalPrefix="extensions::"; -WebInspector.TimelineAggregator._groupNodeFlag=Symbol("groupNode"); - - - - - -WebInspector.TimelineAggregator.isExtensionInternalURL=function(url) -{ -return url.startsWith(WebInspector.TimelineAggregator._extensionInternalPrefix); -}; - -WebInspector.TimelineAggregator.prototype={ - - - - -groupFunction:function(groupBy) -{ -var idMapper=this._nodeToGroupIdFunction(groupBy); -return idMapper&&this._nodeToGroupNode.bind(this,idMapper); -}, - - - - - - -performGrouping:function(root,groupBy) -{ -var nodeMapper=this.groupFunction(groupBy); -if(!nodeMapper) -return root; -for(var node of root.children.values()){ -var groupNode=nodeMapper(node); -groupNode.parent=root; -groupNode.selfTime+=node.selfTime; -groupNode.totalTime+=node.totalTime; -groupNode.children.set(node.id,node); -node.parent=root; -} -root.children=this._groupNodes; -return root; -}, - - - - - -_nodeToGroupIdFunction:function(groupBy) -{ - - - - -function groupByURL(node) -{ -return WebInspector.TimelineProfileTree.eventURL(node.event)||""; -} - - - - - - -function groupByDomain(groupSubdomains,node) -{ -var url=WebInspector.TimelineProfileTree.eventURL(node.event)||""; -if(WebInspector.TimelineAggregator.isExtensionInternalURL(url)) -return WebInspector.TimelineAggregator._extensionInternalPrefix; -var parsedURL=url.asParsedURL(); -if(!parsedURL) -return""; -if(parsedURL.scheme==="chrome-extension") -return parsedURL.scheme+"://"+parsedURL.host; -if(!groupSubdomains) -return parsedURL.host; -if(/^[.0-9]+$/.test(parsedURL.host)) -return parsedURL.host; -var domainMatch=/([^.]*\.)?[^.]*$/.exec(parsedURL.host); -return domainMatch&&domainMatch[0]||""; -} - -switch(groupBy){ -case WebInspector.TimelineAggregator.GroupBy.None:return null; -case WebInspector.TimelineAggregator.GroupBy.EventName:return node=>node.event?this._titleMapper(node.event):""; -case WebInspector.TimelineAggregator.GroupBy.Category:return node=>node.event?this._categoryMapper(node.event):""; -case WebInspector.TimelineAggregator.GroupBy.Subdomain:return groupByDomain.bind(null,false); -case WebInspector.TimelineAggregator.GroupBy.Domain:return groupByDomain.bind(null,true); -case WebInspector.TimelineAggregator.GroupBy.URL:return groupByURL; -default:return null;} - -}, - - - - - - -_buildGroupNode:function(id,event) -{ -var groupNode=new WebInspector.TimelineProfileTree.Node(); -groupNode.id=id; -groupNode.selfTime=0; -groupNode.totalTime=0; -groupNode.children=new Map(); -groupNode.event=event; -groupNode._isGroupNode=true; -this._groupNodes.set(id,groupNode); -return groupNode; -}, - - - - - - -_nodeToGroupNode:function(nodeToGroupId,node) -{ -var id=nodeToGroupId(node); -return this._groupNodes.get(id)||this._buildGroupNode(id,node.event); -}}; - - -},{}],132:[function(require,module,exports){ - - - - - - - - - - - - - -WebInspector.SortableDataGrid=function(columnsArray,editCallback,deleteCallback,refreshCallback,contextMenuCallback) -{ -WebInspector.ViewportDataGrid.call(this,columnsArray,editCallback,deleteCallback,refreshCallback,contextMenuCallback); - -this._sortingFunction=WebInspector.SortableDataGrid.TrivialComparator; -this.setRootNode(new WebInspector.SortableDataGridNode()); -}; - - -WebInspector.SortableDataGrid.NodeComparator; - - - - - - -WebInspector.SortableDataGrid.TrivialComparator=function(a,b) -{ -return 0; -}; - - - - - - - -WebInspector.SortableDataGrid.NumericComparator=function(columnIdentifier,a,b) -{ -var aValue=a.data[columnIdentifier]; -var bValue=b.data[columnIdentifier]; -var aNumber=Number(aValue instanceof Node?aValue.textContent:aValue); -var bNumber=Number(bValue instanceof Node?bValue.textContent:bValue); -return aNumber<bNumber?-1:aNumber>bNumber?1:0; -}; - - - - - - - -WebInspector.SortableDataGrid.StringComparator=function(columnIdentifier,a,b) -{ -var aValue=a.data[columnIdentifier]; -var bValue=b.data[columnIdentifier]; -var aString=aValue instanceof Node?aValue.textContent:String(aValue); -var bString=bValue instanceof Node?bValue.textContent:String(bValue); -return aString<bString?-1:aString>bString?1:0; -}; - - - - - - - - -WebInspector.SortableDataGrid.Comparator=function(comparator,reverseMode,a,b) -{ -return reverseMode?comparator(b,a):comparator(a,b); -}; - - - - - - -WebInspector.SortableDataGrid.create=function(columnNames,values) -{ -var numColumns=columnNames.length; -if(!numColumns) -return null; - -var columns=[]; -for(var i=0;i<columnNames.length;++i) -columns.push({title:columnNames[i],width:columnNames[i].length,sortable:true}); - -var nodes=[]; -for(var i=0;i<values.length/numColumns;++i){ -var data={}; -for(var j=0;j<columnNames.length;++j) -data[j]=values[numColumns*i+j]; - -var node=new WebInspector.SortableDataGridNode(data); -node.selectable=false; -nodes.push(node); -} - -var dataGrid=new WebInspector.SortableDataGrid(columns); -var length=nodes.length; -var rootNode=dataGrid.rootNode(); -for(var i=0;i<length;++i) -rootNode.appendChild(nodes[i]); - -dataGrid.addEventListener(WebInspector.DataGrid.Events.SortingChanged,sortDataGrid); - -function sortDataGrid() -{ -var nodes=dataGrid.rootNode().children; -var sortColumnIdentifier=dataGrid.sortColumnIdentifier(); -if(!sortColumnIdentifier) -return; - -var columnIsNumeric=true; -for(var i=0;i<nodes.length;i++){ -var value=nodes[i].data[sortColumnIdentifier]; -if(isNaN(value instanceof Node?value.textContent:value)){ -columnIsNumeric=false; -break; -} -} - -var comparator=columnIsNumeric?WebInspector.SortableDataGrid.NumericComparator:WebInspector.SortableDataGrid.StringComparator; -dataGrid.sortNodes(comparator.bind(null,sortColumnIdentifier),!dataGrid.isSortOrderAscending()); -} -return dataGrid; -}; - -WebInspector.SortableDataGrid.prototype={ - - - -insertChild:function(node) -{ -var root=this.rootNode(); -root.insertChildOrdered(node); -}, - - - - - -sortNodes:function(comparator,reverseMode) -{ -this._sortingFunction=WebInspector.SortableDataGrid.Comparator.bind(null,comparator,reverseMode); -this._rootNode._sortChildren(reverseMode); -this.scheduleUpdateStructure(); -}, - -__proto__:WebInspector.ViewportDataGrid.prototype}; - - - - - - - - -WebInspector.SortableDataGridNode=function(data,hasChildren) -{ -WebInspector.ViewportDataGridNode.call(this,data,hasChildren); -}; - -WebInspector.SortableDataGridNode.prototype={ - - - -insertChildOrdered:function(node) -{ -this.insertChild(node,this.children.upperBound(node,this.dataGrid._sortingFunction)); -}, - -_sortChildren:function() -{ -this.children.sort(this.dataGrid._sortingFunction); -for(var i=0;i<this.children.length;++i) -this.children[i].recalculateSiblings(i); -for(var child of this.children) -child._sortChildren(); -}, - -__proto__:WebInspector.ViewportDataGridNode.prototype}; - - -},{}],133:[function(require,module,exports){ +},{}],116:[function(require,module,exports){ (function(process){ @@ -64584,7 +52287,7 @@ } }).call(this,require('_process')); -},{"./debug":134,"_process":77}],134:[function(require,module,exports){ +},{"./debug":117,"_process":77}],117:[function(require,module,exports){ @@ -64788,104 +52491,7 @@ return val; } -},{"ms":146}],135:[function(require,module,exports){ - - - - -module.exports=function(WebInspector){ - -function TimelineModelTreeView(model){ -this._rootNode=model; -} - -TimelineModelTreeView.prototype.sortingChanged=function(sortItem,sortOrder){ -if(!sortItem) -return; -var sortFunction; -switch(sortItem){ -case'startTime': -sortFunction=compareStartTime; -break; -case'self': -sortFunction=compareNumericField.bind(null,'selfTime'); -break; -case'total': -sortFunction=compareNumericField.bind(null,'totalTime'); -break; -case'activity': -sortFunction=compareName; -break; -default: -console.assert(false,'Unknown sort field: '+sortItem); -return;} - -return this.sortNodes(sortFunction,sortOrder!=='asc'); - -function compareNumericField(field,a,b){ -var nodeA=a[1]; -var nodeB=b[1]; -return nodeA[field]-nodeB[field]; -} - -function compareStartTime(a,b){ -var nodeA=a[1]; -var nodeB=b[1]; -return nodeA.event.startTime-nodeB.event.startTime; -} - -function compareName(a,b){ -var nodeA=a[1]; -var nodeB=b[1]; -var nameA=WebInspector.TimelineTreeView.eventNameForSorting(nodeA.event); -var nameB=WebInspector.TimelineTreeView.eventNameForSorting(nodeB.event); -return nameA.localeCompare(nameB); -} -}; - -TimelineModelTreeView.prototype.sortNodes=function(comparator,reverseMode){ -this._sortingFunction=WebInspector.SortableDataGrid.Comparator.bind(null,comparator,reverseMode); -sortChildren(this._rootNode,this._sortingFunction,reverseMode); -}; - - - - - - -function sortChildren(parent,sortingFunction){ -if(!parent.children)return; -parent.children=new Map([...parent.children.entries()].sort(sortingFunction)); -for(var i=0;i<parent.children.length;++i) -recalculateSiblings(parent.children[i],i); -for(var child of parent.children.values()) -sortChildren(child,sortingFunction); -} - - - - - -function recalculateSiblings(node,myIndex){ -if(!node.parent) -return; - -var previousChild=node.parent.children[myIndex-1]||null; -if(previousChild) -previousChild.nextSibling=node; -node.previousSibling=previousChild; - -var nextChild=node.parent.children[myIndex+1]||null; -if(nextChild) -nextChild.previousSibling=node; -node.nextSibling=nextChild; -} - -return TimelineModelTreeView; - -}; - -},{}],136:[function(require,module,exports){ +},{"ms":128}],118:[function(require,module,exports){ (function webpackUniversalModuleDefinition(root,factory){ if(typeof exports==='object'&&typeof module==='object') @@ -71586,7 +59192,7 @@ }); ; -},{}],137:[function(require,module,exports){ +},{}],119:[function(require,module,exports){ (function(Buffer){ var querystring=require('querystring'); var trim=require('./trim'); @@ -71893,12 +59499,12 @@ module.exports=Link; }).call(this,{"isBuffer":require("../../../lighthouse-extension/node_modules/is-buffer/index.js")}); -},{"../../../lighthouse-extension/node_modules/is-buffer/index.js":65,"./trim":138,"querystring":80}],138:[function(require,module,exports){ +},{"../../../lighthouse-extension/node_modules/is-buffer/index.js":65,"./trim":120,"querystring":80}],120:[function(require,module,exports){ module.exports=function trim(value){ return value.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,''); }; -},{}],139:[function(require,module,exports){ +},{}],121:[function(require,module,exports){ @@ -72043,7 +59649,7 @@ })(ImageSSIM||(ImageSSIM={})); module.exports=ImageSSIM; -},{}],140:[function(require,module,exports){ +},{}],122:[function(require,module,exports){ var encode=require('./lib/encoder'), decode=require('./lib/decoder'); @@ -72052,7 +59658,7 @@ decode:decode}; -},{"./lib/decoder":141,"./lib/encoder":142}],141:[function(require,module,exports){ +},{"./lib/decoder":123,"./lib/encoder":124}],123:[function(require,module,exports){ (function(Buffer){ @@ -73042,7 +60648,7 @@ } }).call(this,require("buffer").Buffer); -},{"buffer":60}],142:[function(require,module,exports){ +},{"buffer":60}],124:[function(require,module,exports){ (function(Buffer){ @@ -73812,7 +61418,7 @@ } }).call(this,require("buffer").Buffer); -},{"buffer":60}],143:[function(require,module,exports){ +},{"buffer":60}],125:[function(require,module,exports){ (function(process){ @@ -74028,7 +61634,7 @@ module.exports=Log; }).call(this,require('_process')); -},{"_process":77,"debug":133,"events":62}],144:[function(require,module,exports){ +},{"_process":77,"debug":116,"events":62}],126:[function(require,module,exports){ (function(global){ @@ -75880,7 +63486,7 @@ module.exports=isEqual; }).call(this,typeof global!=="undefined"?global:typeof self!=="undefined"?self:typeof window!=="undefined"?window:{}); -},{}],145:[function(require,module,exports){ +},{}],127:[function(require,module,exports){ exports.getRenderingDataFromViewport=function(viewportProperties,uaDeviceWidth,uaDeviceHeight,uaMaxZoom,uaMinZoom){ var vw=uaDeviceWidth/100; @@ -76223,7 +63829,7 @@ "viewport-fit":["auto","cover"]}; -},{}],146:[function(require,module,exports){ +},{}],128:[function(require,module,exports){ @@ -76377,7 +63983,7 @@ return Math.ceil(ms/n)+' '+name+'s'; } -},{}],147:[function(require,module,exports){ +},{}],129:[function(require,module,exports){ module.exports=function parseCacheControl(field){ if(typeof field!=='string'){ @@ -76416,7 +64022,7 @@ return err?null:header; }; -},{}],148:[function(require,module,exports){ +},{}],130:[function(require,module,exports){ var URL=require('url').URL; @@ -76839,13 +64445,13 @@ module.exports=Robots; -},{"url":"url"}],149:[function(require,module,exports){ +},{"url":"url"}],131:[function(require,module,exports){ var Robots=require('./Robots'); module.exports=function(url,contents){ return new Robots(url,contents); }; -},{"./Robots":148}],150:[function(require,module,exports){ +},{"./Robots":130}],132:[function(require,module,exports){ (function(process){ exports=module.exports=SemVer; @@ -78052,7 +65658,7 @@ } }).call(this,require('_process')); -},{"_process":77}],151:[function(require,module,exports){ +},{"_process":77}],133:[function(require,module,exports){ (function(Buffer){ 'use strict'; @@ -78240,7 +65846,7 @@ }).call(this,require("buffer").Buffer); -},{"buffer":60,"jpeg-js":140}],152:[function(require,module,exports){ +},{"buffer":60,"jpeg-js":122}],134:[function(require,module,exports){ 'use strict'; const frame=require('./frame'); @@ -78298,7 +65904,7 @@ }); }; -},{"./frame":151,"./speed-index":153}],153:[function(require,module,exports){ +},{"./frame":133,"./speed-index":135}],135:[function(require,module,exports){ 'use strict'; const imageSSIM=require('image-ssim'); @@ -78521,11 +66127,11 @@ calculateSpeedIndexes}; -},{"image-ssim":139}],154:[function(require,module,exports){ +},{"image-ssim":121}],136:[function(require,module,exports){ module.exports={ -"version":"3.0.0-beta.0"}; +"version":"3.0.3"}; -},{}],155:[function(require,module,exports){ +},{}],137:[function(require,module,exports){ module.exports={ "npm":{ "angular":[ @@ -78654,6 +66260,12 @@ +const listOfTlds=[ +'com','co','gov','edu','ac','org','go','gob','or','net','in','ne','nic','gouv', +'web','spb','blog','jus','kiev','mil','wi','qc','ca','bel','on']; + + + @@ -78727,6 +66339,54 @@ + +static getTld(hostname){ +const tlds=hostname.split('.').slice(-2); + +if(!listOfTlds.includes(tlds[0])){ +return`.${tlds[tlds.length-1]}`; +} + +return`.${tlds.join('.')}`; +} + + + + + + + +static rootDomainsMatch(urlA,urlB){ +let urlAInfo; +let urlBInfo; +try{ +urlAInfo=new URL(urlA); +urlBInfo=new URL(urlB); +}catch(err){ +return false; +} + +if(!urlAInfo.hostname||!urlBInfo.hostname){ +return false; +} + +const tldA=URLShim.getTld(urlAInfo.hostname); +const tldB=URLShim.getTld(urlBInfo.hostname); + + +const urlARootDomain=urlAInfo.hostname.replace(new RegExp(`${tldA}$`),''). +split('.').splice(-1)[0]; +const urlBRootDomain=urlBInfo.hostname.replace(new RegExp(`${tldB}$`),''). +split('.').splice(-1)[0]; + +return urlARootDomain===urlBRootDomain; +} + + + + + + static getURLDisplayName(url,options){ return Util.getURLDisplayName(new URL(url),options); } @@ -78771,6 +66431,8 @@ URLShim.URLSearchParams=typeof self!=='undefined'&&self.URLSearchParams|| require('url').URLSearchParams; +URLShim.NON_NETWORK_PROTOCOLS=['blob','data']; + URLShim.INVALID_URL_DEBUG_STRING= 'Lighthouse was unable to determine the URL of some script executions. '+ 'It\'s possible a Chrome extension or other eval\'d code is the source.';
diff --git a/third_party/blink/renderer/platform/scheduler/BUILD.gn b/third_party/blink/renderer/platform/scheduler/BUILD.gn index 9cbe439..eb78599 100644 --- a/third_party/blink/renderer/platform/scheduler/BUILD.gn +++ b/third_party/blink/renderer/platform/scheduler/BUILD.gn
@@ -168,13 +168,6 @@ testonly = true sources = [ - "base/intrusive_heap_unittest.cc", - "base/lazily_deallocated_deque_unittest.cc", - "base/sequence_manager_impl_unittest.cc", - "base/task_queue_selector_unittest.cc", - "base/time_domain_unittest.cc", - "base/work_queue_sets_unittest.cc", - "base/work_queue_unittest.cc", "child/webthread_impl_for_worker_scheduler_unittest.cc", "common/background_scheduler_unittest.cc", "common/idle_canceled_delayed_task_sweeper_unittest.cc", @@ -220,7 +213,6 @@ testonly = true sources = [ - "base/sequence_manager_perftest.cc", "test/queueing_time_estimator_perf_test.cc", ]
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/DEPS b/third_party/blink/renderer/platform/scheduler/main_thread/DEPS index 3b1fae2..7e5548f 100644 --- a/third_party/blink/renderer/platform/scheduler/main_thread/DEPS +++ b/third_party/blink/renderer/platform/scheduler/main_thread/DEPS
@@ -9,4 +9,7 @@ "main_thread_task_queue.h": [ "+base/task/sequence_manager/task_queue_impl.h", ], + "frame_scheduler_impl.h": [ + "+net/base/request_priority.h" + ] }
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.cc b/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.cc index 2ddb5be4..419f783a 100644 --- a/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.cc +++ b/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.cc
@@ -428,14 +428,16 @@ void FrameSchedulerImpl::DidChangeResourceLoadingPriority( scoped_refptr<MainThreadTaskQueue> task_queue, - TaskQueue::QueuePriority priority) { + net::RequestPriority priority) { // This check is done since in some cases (when kUseResourceFetchPriority // feature isn't enabled) we use |loading_task_queue_| for resource loading // and the priority of this queue shouldn't be affected by resource // priorities. auto queue_metadata_pair = resource_loading_task_queues_.find(task_queue); if (queue_metadata_pair != resource_loading_task_queues_.end()) { - queue_metadata_pair->value.priority = priority; + queue_metadata_pair->value.priority = + main_thread_scheduler_->scheduling_settings() + .net_to_blink_priority[priority]; UpdateQueuePolicy(task_queue.get(), queue_metadata_pair->value.voter.get()); } }
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.h b/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.h index 255922ba..1f5cb1c 100644 --- a/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.h +++ b/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.h
@@ -14,6 +14,7 @@ #include "base/single_thread_task_runner.h" #include "base/task/sequence_manager/task_queue.h" #include "base/trace_event/trace_event.h" +#include "net/base/request_priority.h" #include "third_party/blink/renderer/platform/platform_export.h" #include "third_party/blink/renderer/platform/scheduler/main_thread/frame_origin_type.h" #include "third_party/blink/renderer/platform/scheduler/main_thread/page_visibility_state.h" @@ -134,7 +135,7 @@ void DidChangeResourceLoadingPriority( scoped_refptr<MainThreadTaskQueue> task_queue, - base::sequence_manager::TaskQueue::QueuePriority priority); + net::RequestPriority priority); private: friend class PageSchedulerImpl;
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl_unittest.cc b/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl_unittest.cc index 3085f84..58b1c1f 100644 --- a/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl_unittest.cc +++ b/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl_unittest.cc
@@ -8,6 +8,7 @@ #include "base/callback.h" #include "base/location.h" +#include "base/metrics/field_trial_params.h" #include "base/run_loop.h" #include "base/task/sequence_manager/test/sequence_manager_for_test.h" #include "base/test/scoped_feature_list.h" @@ -119,10 +120,11 @@ void DidChangeResourceLoadingPriority( scoped_refptr<MainThreadTaskQueue> task_queue, - TaskQueue::QueuePriority priority) { + net::RequestPriority priority) { frame_scheduler_->DidChangeResourceLoadingPriority(task_queue, priority); } + std::unique_ptr<base::FieldTrialList> field_trial_list_; base::test::ScopedFeatureList feature_list_; base::test::ScopedTaskEnvironment task_environment_; std::unique_ptr<MainThreadSchedulerImpl> scheduler_; @@ -1334,7 +1336,18 @@ class ResourceFetchPriorityExperimentTest : public FrameSchedulerImplTest { public: ResourceFetchPriorityExperimentTest() - : FrameSchedulerImplTest({kUseResourceFetchPriority}, {}) {} + : FrameSchedulerImplTest({kUseResourceFetchPriority}, {}) { + std::map<std::string, std::string> params{ + {"HIGHEST", "HIGH"}, {"MEDIUM", "NORMAL"}, {"LOW", "NORMAL"}, + {"LOWEST", "LOW"}, {"IDLE", "LOW"}, {"THROTTLED", "LOW"}}; + + const char kStudyName[] = "BlinkSchedulerResourceFetchPriority"; + const char kGroupName[] = "GroupName"; + + field_trial_list_ = std::make_unique<base::FieldTrialList>(nullptr); + base::AssociateFieldTrialParams(kStudyName, kGroupName, params); + base::FieldTrialList::CreateFieldTrial(kStudyName, kGroupName); + } }; TEST_F(ResourceFetchPriorityExperimentTest, DidChangePriority) { @@ -1345,13 +1358,11 @@ TaskQueue::QueuePriority priority = task_queue->GetQueuePriority(); EXPECT_EQ(priority, TaskQueue::QueuePriority::kNormalPriority); - DidChangeResourceLoadingPriority(task_queue, - TaskQueue::QueuePriority::kLowPriority); + DidChangeResourceLoadingPriority(task_queue, net::RequestPriority::LOWEST); EXPECT_EQ(task_queue->GetQueuePriority(), TaskQueue::QueuePriority::kLowPriority); - DidChangeResourceLoadingPriority(task_queue, - TaskQueue::QueuePriority::kHighPriority); + DidChangeResourceLoadingPriority(task_queue, net::RequestPriority::HIGHEST); EXPECT_EQ(task_queue->GetQueuePriority(), TaskQueue::QueuePriority::kHighPriority); } @@ -1368,12 +1379,10 @@ TaskQueue::QueuePriority priority = task_queue->GetQueuePriority(); - DidChangeResourceLoadingPriority(task_queue, - TaskQueue::QueuePriority::kLowPriority); + DidChangeResourceLoadingPriority(task_queue, net::RequestPriority::LOW); EXPECT_EQ(task_queue->GetQueuePriority(), priority); - DidChangeResourceLoadingPriority(task_queue, - TaskQueue::QueuePriority::kHighPriority); + DidChangeResourceLoadingPriority(task_queue, net::RequestPriority::HIGHEST); EXPECT_EQ(task_queue->GetQueuePriority(), priority); }
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/frame_task_queue_controller.cc b/third_party/blink/renderer/platform/scheduler/main_thread/frame_task_queue_controller.cc index c2fd94b7..5c1a7612 100644 --- a/third_party/blink/renderer/platform/scheduler/main_thread/frame_task_queue_controller.cc +++ b/third_party/blink/renderer/platform/scheduler/main_thread/frame_task_queue_controller.cc
@@ -72,6 +72,16 @@ } scoped_refptr<MainThreadTaskQueue> +FrameTaskQueueController::NewResourceLoadingTaskQueue() { + scoped_refptr<MainThreadTaskQueue> task_queue = + main_thread_scheduler_impl_->NewLoadingTaskQueue( + MainThreadTaskQueue::QueueType::kFrameLoading, frame_scheduler_impl_); + TaskQueueCreated(task_queue); + resource_loading_task_queues_.insert(task_queue); + return task_queue; +} + +scoped_refptr<MainThreadTaskQueue> FrameTaskQueueController::NewNonLoadingTaskQueue(QueueTraits queue_traits) { DCHECK(!GetNonLoadingTaskQueue(queue_traits)); @@ -120,6 +130,29 @@ return it->value.get(); } +bool FrameTaskQueueController::RemoveResourceLoadingTaskQueue( + const scoped_refptr<MainThreadTaskQueue>& task_queue) { + DCHECK(task_queue); + + if (!resource_loading_task_queues_.Contains(task_queue)) + return false; + resource_loading_task_queues_.erase(task_queue); + DCHECK(task_queue_enabled_voters_.Contains(task_queue)); + task_queue_enabled_voters_.erase(task_queue); + + bool found_task_queue = false; + for (auto it = all_task_queues_and_voters_.begin(); + it != all_task_queues_and_voters_.end(); ++it) { + if (it->first == task_queue.get()) { + found_task_queue = true; + all_task_queues_and_voters_.erase(it); + break; + } + } + DCHECK(found_task_queue); + return true; +} + // static MainThreadTaskQueue::QueueType FrameTaskQueueController::QueueTypeFromQueueTraits(QueueTraits queue_traits) {
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/frame_task_queue_controller.h b/third_party/blink/renderer/platform/scheduler/main_thread/frame_task_queue_controller.h index dd563b3..356d27a 100644 --- a/third_party/blink/renderer/platform/scheduler/main_thread/frame_task_queue_controller.h +++ b/third_party/blink/renderer/platform/scheduler/main_thread/frame_task_queue_controller.h
@@ -15,6 +15,7 @@ #include "third_party/blink/renderer/platform/platform_export.h" #include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.h" #include "third_party/blink/renderer/platform/wtf/hash_map.h" +#include "third_party/blink/renderer/platform/wtf/hash_set.h" namespace base { namespace sequence_manager { @@ -68,6 +69,17 @@ base::sequence_manager::TaskQueue::QueueEnabledVoter* GetQueueEnabledVoter( const scoped_refptr<MainThreadTaskQueue>&); + scoped_refptr<MainThreadTaskQueue> NewResourceLoadingTaskQueue(); + + // Remove a resource loading task queue that FrameTaskQueueController created, + // along with its QueueEnabledVoter, if one exists. Returns true if the task + // queue was found and erased and false otherwise. + // + // Removes are linear in the total number of task queues since + // |all_task_queues_and_voters_| needs to be updated. + bool RemoveResourceLoadingTaskQueue( + const scoped_refptr<MainThreadTaskQueue>&); + private: friend class FrameTaskQueueControllerTest; @@ -107,6 +119,10 @@ // Map of all non-loading TaskQueues, indexed by QueueTraits. NonLoadingTaskQueueMap non_loading_task_queues_; + // Set of all resource loading task queues. + WTF::HashSet<scoped_refptr<MainThreadTaskQueue>> + resource_loading_task_queues_; + using TaskQueueEnabledVoterMap = WTF::HashMap< scoped_refptr<MainThreadTaskQueue>, std::unique_ptr<base::sequence_manager::TaskQueue::QueueEnabledVoter>>;
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/frame_task_queue_controller_unittest.cc b/third_party/blink/renderer/platform/scheduler/main_thread/frame_task_queue_controller_unittest.cc index 2b801cf..2f862a9 100644 --- a/third_party/blink/renderer/platform/scheduler/main_thread/frame_task_queue_controller_unittest.cc +++ b/third_party/blink/renderer/platform/scheduler/main_thread/frame_task_queue_controller_unittest.cc
@@ -82,6 +82,10 @@ return frame_task_queue_controller_->NewNonLoadingTaskQueue(queue_traits); } + scoped_refptr<MainThreadTaskQueue> NewResourceLoadingTaskQueue() const { + return frame_task_queue_controller_->NewResourceLoadingTaskQueue(); + } + size_t task_queue_created_count() const { return task_queue_created_count_; } protected: @@ -139,6 +143,17 @@ all_task_queues.insert(task_queue.get(), QueueCheckResult::kDidNotSeeQueue); EXPECT_EQ(all_task_queues.size(), task_queue_created_count()); + // Add a couple resource loading task queues. + task_queue = NewResourceLoadingTaskQueue(); + EXPECT_FALSE(all_task_queues.Contains(task_queue)); + all_task_queues.insert(task_queue.get(), QueueCheckResult::kDidNotSeeQueue); + EXPECT_EQ(all_task_queues.size(), task_queue_created_count()); + + task_queue = NewResourceLoadingTaskQueue(); + EXPECT_FALSE(all_task_queues.Contains(task_queue)); + all_task_queues.insert(task_queue.get(), QueueCheckResult::kDidNotSeeQueue); + EXPECT_EQ(all_task_queues.size(), task_queue_created_count()); + // Verify that we get all of the queues that we added, and only those queues. EXPECT_EQ(all_task_queues.size(), frame_task_queue_controller_->GetAllTaskQueuesAndVoters().size()); @@ -168,5 +183,53 @@ } } +TEST_F(FrameTaskQueueControllerTest, RemoveResourceLoadingTaskQueues) { + scoped_refptr<MainThreadTaskQueue> resource_loading_queue1 = + NewResourceLoadingTaskQueue(); + EXPECT_EQ(1u, + frame_task_queue_controller_->GetAllTaskQueuesAndVoters().size()); + scoped_refptr<MainThreadTaskQueue> resource_loading_queue2 = + NewResourceLoadingTaskQueue(); + EXPECT_EQ(2u, + frame_task_queue_controller_->GetAllTaskQueuesAndVoters().size()); + + // Check that we can remove the resource loading queues. + bool was_removed = + frame_task_queue_controller_->RemoveResourceLoadingTaskQueue( + resource_loading_queue1); + EXPECT_TRUE(was_removed); + EXPECT_EQ(1u, + frame_task_queue_controller_->GetAllTaskQueuesAndVoters().size()); + // Can't delete twice. + was_removed = frame_task_queue_controller_->RemoveResourceLoadingTaskQueue( + resource_loading_queue1); + EXPECT_FALSE(was_removed); + EXPECT_EQ(1u, + frame_task_queue_controller_->GetAllTaskQueuesAndVoters().size()); + + was_removed = frame_task_queue_controller_->RemoveResourceLoadingTaskQueue( + resource_loading_queue2); + EXPECT_TRUE(was_removed); + EXPECT_EQ(0u, + frame_task_queue_controller_->GetAllTaskQueuesAndVoters().size()); + // Can't delete twice. + was_removed = frame_task_queue_controller_->RemoveResourceLoadingTaskQueue( + resource_loading_queue2); + EXPECT_FALSE(was_removed); + EXPECT_EQ(0u, + frame_task_queue_controller_->GetAllTaskQueuesAndVoters().size()); +} + +TEST_F(FrameTaskQueueControllerTest, CannotRemoveNonResourceLoadingTaskQueues) { + scoped_refptr<MainThreadTaskQueue> task_queue = NewLoadingTaskQueue(); + EXPECT_EQ(1u, + frame_task_queue_controller_->GetAllTaskQueuesAndVoters().size()); + bool was_removed = + frame_task_queue_controller_->RemoveResourceLoadingTaskQueue(task_queue); + EXPECT_FALSE(was_removed); + EXPECT_EQ(1u, + frame_task_queue_controller_->GetAllTaskQueuesAndVoters().size()); +} + } // namespace scheduler } // namespace blink
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc index 2303e061..3311f27 100644 --- a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc +++ b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc
@@ -72,6 +72,11 @@ constexpr base::TimeDelta kDefaultWakeUpDuration = base::TimeDelta::FromMilliseconds(3); +// Name of the finch study that enables using resource fetch priorities to +// schedule tasks on Blink. +constexpr const char kResourceFetchPriorityExperiment[] = + "BlinkSchedulerResourceFetchPriority"; + base::TimeDelta GetWakeUpDuration() { int duration_ms; if (!base::StringToInt(base::GetFieldTrialParamValue(kWakeUpThrottlingTrial, @@ -239,6 +244,26 @@ return TaskQueue::PriorityToString(priority.value()); } +TaskQueue::QueuePriority StringToTaskQueuePriority( + const std::string& priority) { + if (priority == "CONTROL") { + return TaskQueue::QueuePriority::kControlPriority; + } else if (priority == "HIGHEST") { + return TaskQueue::QueuePriority::kHighestPriority; + } else if (priority == "HIGH") { + return TaskQueue::QueuePriority::kHighPriority; + } else if (priority == "NORMAL") { + return TaskQueue::QueuePriority::kNormalPriority; + } else if (priority == "LOW") { + return TaskQueue::QueuePriority::kLowPriority; + } else if (priority == "BEST_EFFORT") { + return TaskQueue::QueuePriority::kBestEffortPriority; + } else { + NOTREACHED(); + return TaskQueue::QueuePriority::kQueuePriorityCount; + } +} + bool IsBlockingEvent(const blink::WebInputEvent& web_input_event) { blink::WebInputEvent::Type type = web_input_event.GetType(); DCHECK(type == blink::WebInputEvent::kTouchStart || @@ -664,6 +689,23 @@ use_resource_fetch_priority = base::FeatureList::IsEnabled(kUseResourceFetchPriority); + if (use_resource_fetch_priority) { + std::map<std::string, std::string> params; + base::GetFieldTrialParams(kResourceFetchPriorityExperiment, ¶ms); + for (size_t net_priority = 0; + net_priority < net::RequestPrioritySize::NUM_PRIORITIES; + net_priority++) { + net_to_blink_priority[net_priority] = + TaskQueue::QueuePriority::kNormalPriority; + auto iter = params.find(net::RequestPriorityToString( + static_cast<net::RequestPriority>(net_priority))); + if (iter != params.end()) { + net_to_blink_priority[net_priority] = + StringToTaskQueuePriority(iter->second); + } + } + } + experiment_only_when_loading = base::FeatureList::IsEnabled(kExperimentOnlyWhenLoading); }
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h index 7fefbe0..1ac0745 100644 --- a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h +++ b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h
@@ -108,6 +108,12 @@ // (crbug.com/860545). bool use_resource_fetch_priority; + // Contains a mapping from net::RequestPriority to TaskQueue::QueuePriority + // when use_resource_fetch_priority is enabled. + std::array<base::sequence_manager::TaskQueue::QueuePriority, + net::RequestPrioritySize::NUM_PRIORITIES> + net_to_blink_priority; + // Turn on relevant experiments during the loading phase. bool experiment_only_when_loading; };
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/resource_loading_task_runner_handle_impl.cc b/third_party/blink/renderer/platform/scheduler/main_thread/resource_loading_task_runner_handle_impl.cc index 8e430f7..685fd6a 100644 --- a/third_party/blink/renderer/platform/scheduler/main_thread/resource_loading_task_runner_handle_impl.cc +++ b/third_party/blink/renderer/platform/scheduler/main_thread/resource_loading_task_runner_handle_impl.cc
@@ -13,26 +13,6 @@ using base::sequence_manager::TaskQueue; -namespace { - -TaskQueue::QueuePriority NetPriorityToBlinkSchedulerPriority( - const net::RequestPriority priority) { - switch (priority) { - case net::RequestPriority::HIGHEST: - return TaskQueue::QueuePriority::kHighPriority; - case net::RequestPriority::MEDIUM: - case net::RequestPriority::LOW: - return TaskQueue::QueuePriority::kNormalPriority; - case net::RequestPriority::LOWEST: - case net::RequestPriority::IDLE: - case net::RequestPriority::THROTTLED: - default: - return TaskQueue::QueuePriority::kLowPriority; - } -} - -} // namespace - std::unique_ptr<ResourceLoadingTaskRunnerHandleImpl> ResourceLoadingTaskRunnerHandleImpl::WrapTaskRunner( scoped_refptr<MainThreadTaskQueue> task_queue) { @@ -59,8 +39,7 @@ net::RequestPriority priority) { FrameSchedulerImpl* frame_scheduler = task_queue_->GetFrameScheduler(); if (frame_scheduler) { - frame_scheduler->DidChangeResourceLoadingPriority( - task_queue_, NetPriorityToBlinkSchedulerPriority(priority)); + frame_scheduler->DidChangeResourceLoadingPriority(task_queue_, priority); } }
diff --git a/third_party/blink/renderer/platform/testing/testing_platform_support_with_web_rtc.cc b/third_party/blink/renderer/platform/testing/testing_platform_support_with_web_rtc.cc index a055c71..9fb33ff 100644 --- a/third_party/blink/renderer/platform/testing/testing_platform_support_with_web_rtc.cc +++ b/third_party/blink/renderer/platform/testing/testing_platform_support_with_web_rtc.cc
@@ -65,11 +65,15 @@ webrtc::RtpTransceiverDirection Direction() const override { return webrtc::RtpTransceiverDirection::kInactive; } + void SetDirection(webrtc::RtpTransceiverDirection) override {} base::Optional<webrtc::RtpTransceiverDirection> CurrentDirection() const override { return base::nullopt; } - void Stop() override {} + base::Optional<webrtc::RtpTransceiverDirection> FiredDirection() + const override { + return base::nullopt; + } }; } // namespace
diff --git a/tools/cygprofile/orderfile_generator_backend.py b/tools/cygprofile/orderfile_generator_backend.py index be4d52d..c2a2bfd 100755 --- a/tools/cygprofile/orderfile_generator_backend.py +++ b/tools/cygprofile/orderfile_generator_backend.py
@@ -438,7 +438,8 @@ use_wpr = not options.no_wpr simulate_user = options.simulate_user self._profiler = profile_android_startup.AndroidProfileTool( - output_directory, host_profile_dir, use_wpr, urls, simulate_user) + output_directory, host_profile_dir, use_wpr, urls, simulate_user, + device=options.device) self._output_data = {} self._step_recorder = StepRecorder(options.buildbot) @@ -699,6 +700,9 @@ '--buildbot', action='store_true', help='If true, the script expects to be run on a buildbot') parser.add_argument( + '--device', default=None, type=str, + help='Device serial number on which to run profiling.') + parser.add_argument( '--verify', action='store_true', help='If true, the script only verifies the current orderfile') parser.add_argument('--target-arch', action='store', dest='arch',
diff --git a/tools/cygprofile/profile_android_startup.py b/tools/cygprofile/profile_android_startup.py index 0683ce9f..57499a4 100755 --- a/tools/cygprofile/profile_android_startup.py +++ b/tools/cygprofile/profile_android_startup.py
@@ -191,7 +191,7 @@ os.path.dirname(__file__), 'memory_top_10_mobile_000.wprgo') def __init__(self, output_directory, host_profile_dir, use_wpr, urls, - simulate_user): + simulate_user, device=None): """Constructor. Args: @@ -202,8 +202,12 @@ use_wpr is True. simulate_user: (bool) Whether to simulate a user. """ - devices = device_utils.DeviceUtils.HealthyDevices() - self._device = devices[0] + if device is None: + devices = device_utils.DeviceUtils.HealthyDevices() + assert len(devices) == 1, 'Expected exactly one connected device' + self._device = devices[0] + else: + self._device = device_utils.DeviceUtils(device) self._cygprofile_tests = os.path.join( output_directory, 'cygprofile_unittests') self._host_profile_dir = host_profile_dir
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml index bf4ab31..0f1b85c 100644 --- a/tools/metrics/histograms/enums.xml +++ b/tools/metrics/histograms/enums.xml
@@ -1657,6 +1657,22 @@ label="Failed to enable CloudDPC due to an unspecified error."/> </enum> +<enum name="ArcUserInteraction"> + <summary>Defines Arc User Interactions</summary> + <int value="0" label="Action not user initiated"/> + <int value="1" label="App started from launcher"/> + <int value="2" label="App started from launcher context menu"/> + <int value="3" label="App started from launcher search"/> + <int value="4" label="App started from launcher search context menu"/> + <int value="5" label="App started from launcher suggested apps"/> + <int value="6" label="App started from launcher suggested apps context menu"/> + <int value="7" label="App started from shelf"/> + <int value="8" label="App started from shelf context menu"/> + <int value="9" label="App started from settings"/> + <int value="10" label="Interaction with notification"/> + <int value="11" label="Interaction with app window"/> +</enum> + <enum name="ArcVideoDecodeAcceleratorResult"> <summary>Defines ArcVideoDecodeAccelerator initialization status</summary> <int value="0" label="SUCCESS"/> @@ -5327,6 +5343,13 @@ <int value="2" label="Periodic"/> </enum> +<enum name="ChromeOSMessageCenterScrollActionReason"> + <int value="0" label="Unknown"/> + <int value="1" label="By mouse wheel"/> + <int value="2" label="By touch"/> + <int value="3" label="By arrow key"/> +</enum> + <enum name="ChromeOSPlatformVerificationExpiryStatus"> <summary> Possible results of a platform verification expiry check. See @@ -13810,6 +13833,7 @@ <int value="458" label="DeviceUpdateStagingPercentOfFleetPerWeek"/> <int value="459" label="AutofillProfileEnabled"/> <int value="460" label="TabLifecyclesEnabled"/> + <int value="461" label="UrlKeyedAnonymizedDataCollectionEnabled"/> </enum> <enum name="EnterprisePolicyInvalidations"> @@ -16142,6 +16166,13 @@ <int value="10" label="EXTERNAL_COMPONENT"/> </enum> +<enum name="ExtensionMessagingIncludeChannelIdBehavior"> + <int value="0" label="Not requested"/> + <int value="1" label="Requested but denied"/> + <int value="2" label="Requested but not found"/> + <int value="3" label="Requested and included"/> +</enum> + <enum name="ExtensionNotificationType"> <int value="0" label="NOTIFICATION_TYPE_SIMPLE"/> <int value="1" label="NOTIFICATION_TYPE_BASE_FORMAT"/> @@ -24398,6 +24429,7 @@ <int value="80" label="INSTALLABLE_AMBIENT_BADGE_INFOBAR_DELEGATE"/> <int value="81" label="PAGE_LOAD_CAPPING_INFOBAR_DELEGATE"/> <int value="83" label="AR_CORE_UPGRADE_ANDROID"/> + <int value="84" label="BLOATED_RENDERER_INFOBAR_DELEGATE"/> </enum> <enum name="InfoBarResponse"> @@ -27736,6 +27768,7 @@ <int value="-1426150007" label="ignore-previews-blacklist"/> <int value="-1426034869" label="NoCreditCardAbort:enabled"/> <int value="-1423348289" label="NupPrinting:disabled"/> + <int value="-1420542268" label="AutofillEnableAccountWalletStorage:disabled"/> <int value="-1419788257" label="enable-experimental-hotwording"/> <int value="-1417122729" label="AutofillCreditCardAblationExperiment:disabled"/> @@ -28392,6 +28425,7 @@ <int value="-35388407" label="AshNewSystemMenu:disabled"/> <int value="-30966385" label="enable-hardware-overlays"/> <int value="-29847483" label="MemoryAblation:enabled"/> + <int value="-28295905" label="AutofillEnableAccountWalletStorage:enabled"/> <int value="-23804418" label="NTPArticleSuggestionsExpandableHeader:disabled"/> <int value="-23090520" label="disable-search-button-in-omnibox"/> @@ -30393,6 +30427,13 @@ <int value="627" label="border-inline-width"/> <int value="628" label="border-block"/> <int value="629" label="border-inline"/> + <int value="630" label="inset-block-start"/> + <int value="631" label="inset-block-end"/> + <int value="632" label="inset-block"/> + <int value="633" label="inset-inline-start"/> + <int value="634" label="inset-inline-end"/> + <int value="635" label="inset-inline"/> + <int value="636" label="inset"/> </enum> <enum name="MappedEditingCommands">
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml index f2058ea..db06daf4 100644 --- a/tools/metrics/histograms/histograms.xml +++ b/tools/metrics/histograms/histograms.xml
@@ -3563,6 +3563,17 @@ </summary> </histogram> +<histogram name="Arc.UserInteraction" enum="ArcUserInteraction"> + <owner>jhorwich@chromium.org</owner> + <owner>elijahtaylor@chromium.org</owner> + <owner>shihuis@google.com</owner> + <owner>maajid@chromium.org</owner> + <summary> + Counts user interactions with ARC by type, such as ARC app launches via the + shelf or launcher. + </summary> +</histogram> + <histogram name="ArcAuth.AccountCheckStatus" enum="ArcAuthAccountCheckStatus"> <owner>elijahtaylor@google.com</owner> <summary>The status of account check before GMS Sign-in.</summary> @@ -11433,6 +11444,19 @@ </summary> </histogram> +<histogram name="ChromeOS.MessageCenter.ScrollActionReason" + enum="ChromeOSMessageCenterScrollActionReason" expires_after="2018-11-30"> + <owner>yoshiki@chromium.org</owner> + <owner>omrilio@chromium.org</owner> + <summary> + The cause of the scroll event by the user on the message center. Only the + first event after the message center opens is recorded. For example, the + user opens the message center and scrolls by mouse then by key, only the + mouse event is recorded. If the user closes and reopens the message center + before the second event, both events are recorded. + </summary> +</histogram> + <histogram name="ChromeOS.PlatformVerification.Available" enum="BooleanAvailable"> <owner>dkrahn@chromium.org</owner> @@ -28817,6 +28841,17 @@ </summary> </histogram> +<histogram name="Extensions.Messaging.IncludeChannelIdBehavior" + enum="ExtensionMessagingIncludeChannelIdBehavior" + expires_after="2018-12-01"> + <owner>rdevlin.cronin@chromium.org</owner> + <owner>nharper@hromium.org</owner> + <summary> + The behavior for including the TLS channel ID in the information sent with + extension messaging. Recorded once per opened channel. + </summary> +</histogram> + <histogram name="Extensions.Messaging.MessageSize" units="bytes"> <owner>redevlin.cronin@chromium.org</owner> <summary>
diff --git a/tools/perf/contrib/orderfile/__init__.py b/tools/perf/contrib/orderfile/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/tools/perf/contrib/orderfile/__init__.py
diff --git a/tools/perf/contrib/orderfile/orderfile.py b/tools/perf/contrib/orderfile/orderfile.py new file mode 100644 index 0000000..7e88073 --- /dev/null +++ b/tools/perf/contrib/orderfile/orderfile.py
@@ -0,0 +1,234 @@ +# 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. +""" Orderfile Generation and Testing + +This provides a suite of benchmarks for orderfile generation and testing based +on the mobile system health benchmarks. + +The orderfile_generation.training benchmark is meant to be run with an +orderfile instrumented build to produce function usage profiles. It can be +invoked as follows: + (CHROMIUM_OUTPUT_DIR=out/Instrumented; \ + ./tools/perf/run_benchmark --device=${YOUR_DEVICE_SN} --browser=exact \ + --browser-executable=${CHROMIUM_OUTPUT_DIR}/apks/Monochrome.apk \ + orderfile_generation.training) + +The orderfile_generation.testing benchmark has a smaller test set whose +benchmarks are distinct from those in orderfile_generation.training. Run it as +follows, note the --orderfile-memory-optimization flag is necessary only with +legacy orderfile builds of chrome. + (CHROMIUM_OUTPUT_DIR=out/Official; \ + ./tools/perf/run_benchmark --device=${YOUR_DEVICE_SN} --browser=exact \ + --browser-executable=${CHROMIUM_OUTPUT_DIR}/apks/Monochrome.apk \ + --extra-browser-args=--orderfile-memory-optimization \ + --results-label=${LABEL_CORRESPONDING_TO_YOUR_BUILD} \ + orderfile_generation.training) + +The orderfile_generation.variation.* benchmarks use a smaller training set so +that several test set variations can be produced. They are run as above but +using the following benchmark names. + orderfile_generation.variation.training + orderfile_generation.variation.testing0 + orderfile_generation.variation.testing1 + orderfile_generation.variation.testing2 + orderfile_generation.variation.testing3 + +The orderfile_generation.debugging benchmark is a short benchmark of 3 stories +that is useful for debugging hardware and test setup problems. +""" + +import random + +from benchmarks import system_health +from page_sets.system_health import platforms +from page_sets.system_health import system_health_stories +from telemetry import benchmark +from telemetry import story + + +class OrderfileStorySet(story.StorySet): + """User stories for orderfile generation. + + The run set specified in the constructor splits the stories into training and + testing sets (or debugging, see the code for details). + """ + # Run set names. + TRAINING = 'training' + TESTING = 'testing' + DEBUGGING = 'debugging' + + _PLATFORM = 'mobile' + + _BLACKLIST = set([ + 'browse:chrome:newtab', + 'browse:shopping:flipkart', + ]) + + # The random seed used for reproducible runs. + SEED = 8675309 + + # These defaults are current best practice for production orderfiles. + DEFAULT_TRAINING = 50 + DEFAULT_TESTING = 8 + DEFAULT_VARIATIONS = 1 + + def __init__(self, run_set, num_training=DEFAULT_TRAINING, + num_testing=DEFAULT_TESTING, num_variations=DEFAULT_VARIATIONS, + test_variation=0): + """Create an orderfile training or testing benchmark set. + + Args: + run_set: one of TRAINING, TESTING or DEBUGGING. + num_training: the number of benchmarks to use for training. + num_testing: the number of benchmarks to use in each test set. + num_variations: the number of test set variations. + test_variation: the test set variation to use. + """ + super(OrderfileStorySet, self).__init__( + archive_data_file=('../../page_sets/data/system_health_%s.json' % + self._PLATFORM), + cloud_storage_bucket=story.PARTNER_BUCKET) + + assert self._PLATFORM in platforms.ALL_PLATFORMS, '{} not in {}'.format( + self._PLATFORM, str(platforms.ALL_PLATFORMS)) + assert run_set in (self.TRAINING, self.TESTING, self.DEBUGGING) + assert test_variation >= 0 and test_variation < num_variations + + self._run_set = run_set + self._num_training = num_training + self._num_testing = num_testing + self._num_variations = num_variations + self._test_variation = test_variation + + # We want the story selection to be consistent across runs. + random.seed(self.SEED) + + for story_class in self.RunSetStories(): + # pylint: disable=E1102 + self.AddStory(story_class(self, take_memory_measurement=True)) + + def RunSetStories(self): + possible_stories = [ + s for s in system_health_stories.IterAllSystemHealthStoryClasses() + if (s.NAME not in self._BLACKLIST and + not s.ABSTRACT_STORY and + self._PLATFORM in s.SUPPORTED_PLATFORMS)] + assert (self._num_training + self._num_variations * self._num_testing + <= len(possible_stories)), \ + 'We only have {} stories to work with, but want {} + {}*{}'.format( + len(possible_stories), self._num_training, self._num_variations, + self._num_testing) + + if self._run_set == self.DEBUGGING: + return random.sample(possible_stories, 3) + + random.shuffle(possible_stories) + if self._run_set == self.TRAINING: + return possible_stories[:self._num_training] + elif self._run_set == self.TESTING: + return possible_stories[ + (self._num_training + self._test_variation * self._num_testing): + (self._num_training + (self._test_variation + 1) * self._num_testing)] + assert False, 'Bad run set {}'.format(self._run_set) + + +class _OrderfileBenchmark(system_health.MobileMemorySystemHealth): + """Base benchmark for orderfile generation.""" + STORY_RUN_SET = None # Must be set in subclasses. + + def CreateStorySet(self, options): + return OrderfileStorySet(run_set=self.STORY_RUN_SET) + + +# pylint: disable=R0901 +@benchmark.Owner(emails=['mattcary@chromium.org']) +class OrderfileTraining(_OrderfileBenchmark): + STORY_RUN_SET = OrderfileStorySet.TRAINING + + options = {'pageset_repeat': 2} + + @classmethod + def Name(cls): + return 'orderfile_generation.training' + + +# pylint: disable=R0901 +@benchmark.Owner(emails=['mattcary@chromium.org']) +class OrderfileTesting(_OrderfileBenchmark): + STORY_RUN_SET = OrderfileStorySet.TESTING + + options = {'pageset_repeat': 7} + + @classmethod + def Name(cls): + return 'orderfile_generation.testing' + + +class _OrderfileVariation(system_health.MobileMemorySystemHealth): + """Orderfile generation with test set variations.""" + STORY_RUN_SET = None # Must be set in all subclasses. + TEST_VARIATION = 0 # Can be overridden testing subclasses. + + options = {'pageset_repeat': 7} + + def CreateStorySet(self, options): + return OrderfileStorySet( run_set=self.STORY_RUN_SET, + num_training=25, num_testing=8, num_variations=4, + test_variation=self.TEST_VARIATION) + + @classmethod + def Name(cls): + if cls.STORY_RUN_SET == OrderfileStorySet.TESTING: + return 'orderfile_generation.variation.testing{}'.format( + cls.TEST_VARIATION) + elif cls.STORY_RUN_SET == OrderfileStorySet.TRAINING: + return 'orderfile_generation.variation.training' + assert False, 'Bad STORY_RUN_SET {}'.format(cls.STORY_RUN_SET) + + +# pylint: disable=R0901 +@benchmark.Owner(emails=['mattcary@chromium.org']) +class OrderfileVariationTraining(_OrderfileVariation): + STORY_RUN_SET = OrderfileStorySet.TRAINING + + +# pylint: disable=R0901 +@benchmark.Owner(emails=['mattcary@chromium.org']) +class OrderfileVariationTesting0(_OrderfileVariation): + STORY_RUN_SET = OrderfileStorySet.TESTING + TEST_VARIATION = 0 + + +# pylint: disable=R0901 +@benchmark.Owner(emails=['mattcary@chromium.org']) +class OrderfileTesting1(_OrderfileVariation): + STORY_RUN_SET = OrderfileStorySet.TESTING + TEST_VARIATION = 1 + + +# pylint: disable=R0901 +@benchmark.Owner(emails=['mattcary@chromium.org']) +class OrderfileTesting2(_OrderfileVariation): + STORY_RUN_SET = OrderfileStorySet.TESTING + TEST_VARIATION = 2 + + +# pylint: disable=R0901 +@benchmark.Owner(emails=['mattcary@chromium.org']) +class OrderfileTesting3(_OrderfileVariation): + STORY_RUN_SET = OrderfileStorySet.TESTING + TEST_VARIATION = 3 + + +# pylint: disable=R0901 +@benchmark.Owner(emails=['mattcary@chromium.org']) +class OrderfileDebugging(_OrderfileBenchmark): + """A very short benchmark for debugging metrics collection.""" + STORY_RUN_SET = OrderfileStorySet.DEBUGGING + + options = {'pageset_repeat': 1} + + @classmethod + def Name(cls): + return 'orderfile_generation.debugging'
diff --git a/tools/perf/contrib/orderfile/orderfile_unittest.py b/tools/perf/contrib/orderfile/orderfile_unittest.py new file mode 100755 index 0000000..97720db --- /dev/null +++ b/tools/perf/contrib/orderfile/orderfile_unittest.py
@@ -0,0 +1,181 @@ +#!/usr/bin/env vpython +# 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. + +import os +import sys +import unittest + +sys.path.append(os.path.abspath(os.path.join( + os.path.dirname(__file__), os.pardir, os.pardir))) +from core import path_util + +path_util.AddTelemetryToPath() + +from contrib.orderfile import orderfile + + +class Orderfile(unittest.TestCase): + def setUp(self): + # Increase failed test output to make updating easier. + self.maxDiff = None + + def testDefaults(self): + training = [s.NAME for s in orderfile.OrderfileStorySet( + orderfile.OrderfileStorySet.TRAINING).RunSetStories()] + self.assertListEqual( + ['background:social:facebook', + 'load:media:soundcloud', + 'load:news:wikipedia', + 'browse:media:imgur', + 'browse:tech:discourse_infinite_scroll', + 'browse:news:cricbuzz', + 'load:games:lazors', + 'load:tools:drive', + 'load:search:google', + 'load:tools:stackoverflow', + 'load:news:washingtonpost', + 'load:news:reddit', + 'browse:shopping:avito', + 'load:news:cnn', + 'browse:news:qq', + 'load:search:baidu', + 'load:search:ebay', + 'long_running:tools:gmail-foreground', + 'load:media:imgur', + 'background:news:nytimes', + 'load:tools:dropbox', + 'background:search:google', + 'load:chrome:blank', + 'browse:social:tumblr_infinite_scroll', + 'load:news:qq', + 'load:search:yandex', + 'load:media:dailymotion', + 'browse:tools:maps', + 'load:games:bubbles', + 'browse:shopping:amazon', + 'browse:social:instagram', + 'background:tools:gmail', + 'load:media:youtube', + 'load:media:facebook_photos', + 'browse:media:facebook_photos', + 'browse:social:facebook', + 'browse:news:reddit', + 'load:media:google_images', + 'load:tools:weather', + 'load:social:twitter', + 'browse:news:cnn', + 'browse:media:flickr_infinite_scroll', + 'load:games:spychase', + 'load:tools:docs', + 'load:news:nytimes', + 'browse:news:washingtonpost', + 'browse:social:pinterest_infinite_scroll', + 'load:news:irctc', + 'browse:media:youtube', + 'load:search:yahoo'], training) + testing = [s.NAME for s in orderfile.OrderfileStorySet( + orderfile.OrderfileStorySet.TESTING).RunSetStories()] + self.assertListEqual( + ['browse:shopping:lazada', + 'load:tools:gmail', + 'browse:news:toi', + 'browse:chrome:omnibox', + 'browse:news:globo', + 'browse:social:facebook_infinite_scroll', + 'load:search:taobao', + 'background:media:imgur'], testing) + + def test25TrainingStories(self): + training = [s.NAME for s in orderfile.OrderfileStorySet( + orderfile.OrderfileStorySet.TRAINING, num_training=25).RunSetStories()] + self.assertListEqual( + ['background:social:facebook', + 'load:media:soundcloud', + 'load:news:wikipedia', + 'browse:media:imgur', + 'browse:tech:discourse_infinite_scroll', + 'browse:news:cricbuzz', + 'load:games:lazors', + 'load:tools:drive', + 'load:search:google', + 'load:tools:stackoverflow', + 'load:news:washingtonpost', + 'load:news:reddit', + 'browse:shopping:avito', + 'load:news:cnn', + 'browse:news:qq', + 'load:search:baidu', + 'load:search:ebay', + 'long_running:tools:gmail-foreground', + 'load:media:imgur', + 'background:news:nytimes', + 'load:tools:dropbox', + 'background:search:google', + 'load:chrome:blank', + 'browse:social:tumblr_infinite_scroll', + 'load:news:qq'], + training) + + def testTestingStories(self): + testing = [s.NAME for s in orderfile.OrderfileStorySet( + orderfile.OrderfileStorySet.TESTING, + num_training=25).RunSetStories()] + self.assertListEqual( + ['load:search:yandex', + 'load:media:dailymotion', + 'browse:tools:maps', + 'load:games:bubbles', + 'browse:shopping:amazon', + 'browse:social:instagram', + 'background:tools:gmail', + 'load:media:youtube'], + testing) + + def testTestingVariationStories(self): + testing = [s.NAME for s in orderfile.OrderfileStorySet( + orderfile.OrderfileStorySet.TESTING, num_training=25, + num_variations=4, test_variation=0).RunSetStories()] + self.assertListEqual( + ['load:search:yandex', + 'load:media:dailymotion', + 'browse:tools:maps', + 'load:games:bubbles', + 'browse:shopping:amazon', + 'browse:social:instagram', + 'background:tools:gmail', + 'load:media:youtube'], + testing) + + testing = [s.NAME for s in orderfile.OrderfileStorySet( + orderfile.OrderfileStorySet.TESTING, num_training=25, + num_variations=4, test_variation=1).RunSetStories()] + self.assertListEqual( + ['load:media:facebook_photos', + 'browse:media:facebook_photos', + 'browse:social:facebook', + 'browse:news:reddit', + 'load:media:google_images', + 'load:tools:weather', + 'load:social:twitter', + 'browse:news:cnn'], + testing) + + testing = [s.NAME for s in orderfile.OrderfileStorySet( + orderfile.OrderfileStorySet.TESTING, num_training=25, + num_variations=4, test_variation=3).RunSetStories()] + self.assertListEqual( + ['load:search:yahoo', + 'browse:shopping:lazada', + 'load:tools:gmail', + 'browse:news:toi', + 'browse:chrome:omnibox', + 'browse:news:globo', + 'browse:social:facebook_infinite_scroll', + 'load:search:taobao'], + testing) + + +if __name__ == '__main__': + unittest.main()
diff --git a/tools/perf/core/perf_data_generator.py b/tools/perf/core/perf_data_generator.py index db77fda..5188013 100755 --- a/tools/perf/core/perf_data_generator.py +++ b/tools/perf/core/perf_data_generator.py
@@ -350,26 +350,6 @@ 'device_os_flavor': 'aosp', }, }, - 'Win 10 High-DPI Perf': { - 'tests': [ - { - 'isolate': 'performance_test_suite', - 'num_shards': 5, - 'extra_args': [ - '--run-ref-build', - '--test-shard-map-filename=win10_highdpi_shard_map.json', - '--assert-gpu-compositing', - ], - } - ], - 'platform': 'win', - 'target_bits': 64, - 'dimension': { - 'pool': 'chrome.tests.perf', - 'os': 'Windows-10', - 'gpu': '8086:1616' - }, - }, 'win-10-perf': { 'tests': [ {
diff --git a/tools/perf/core/shard_maps/android_go_shard_map.json b/tools/perf/core/shard_maps/android_go_shard_map.json index 806331361..2b2302fe 100644 --- a/tools/perf/core/shard_maps/android_go_shard_map.json +++ b/tools/perf/core/shard_maps/android_go_shard_map.json
@@ -2,22 +2,22 @@ "0": { "benchmarks": { "system_health.common_mobile": { - "end": 15 + "end": 16 } } }, "1": { "benchmarks": { "system_health.common_mobile": { - "begin": 15, - "end": 27 + "begin": 16, + "end": 29 } } }, "2": { "benchmarks": { "system_health.common_mobile": { - "begin": 27, + "begin": 29, "end": 53 } } @@ -25,16 +25,12 @@ "3": { "benchmarks": { "system_health.common_mobile": { - "begin": 53, - "end": 61 + "begin": 53 } } }, "4": { "benchmarks": { - "system_health.common_mobile": { - "begin": 61 - }, "start_with_url.warm.startup_pages": {}, "start_with_url.cold.startup_pages": { "end": 1 @@ -71,30 +67,30 @@ "benchmarks": { "system_health.memory_mobile": { "begin": 17, - "end": 21 + "end": 22 } } }, "9": { "benchmarks": { "system_health.memory_mobile": { - "begin": 21, - "end": 29 + "begin": 22, + "end": 30 } } }, "10": { "benchmarks": { "system_health.memory_mobile": { - "begin": 29, - "end": 40 + "begin": 30, + "end": 41 } } }, "11": { "benchmarks": { "system_health.memory_mobile": { - "begin": 40, + "begin": 41, "end": 50 } } @@ -128,7 +124,6 @@ "system_health.memory_mobile": { "begin": 61 }, - "system_health.webview_startup": {}, "power.typical_10_mobile": { "end": 8 } @@ -140,14 +135,14 @@ "begin": 8 }, "memory.top_10_mobile": { - "end": 5 + "end": 6 } } }, "17": { "benchmarks": { "memory.top_10_mobile": { - "begin": 5, + "begin": 6, "end": 13 } } @@ -160,10 +155,10 @@ } }, "extra_infos": { - "num_stories": 160, - "predicted_min_shard_time": 1518.0, + "num_stories": 159, + "predicted_min_shard_time": 1566.0, "predicted_min_shard_index": 7, - "predicted_max_shard_time": 2358.0, + "predicted_max_shard_time": 2400.0, "predicted_max_shard_index": 6 } } \ No newline at end of file
diff --git a/tools/perf/core/shard_maps/timing_data/android_go_timing.json b/tools/perf/core/shard_maps/timing_data/android_go_timing.json index ef23b5a..8141d80 100644 --- a/tools/perf/core/shard_maps/timing_data/android_go_timing.json +++ b/tools/perf/core/shard_maps/timing_data/android_go_timing.json
@@ -1,6 +1,6 @@ [ { - "duration": "23.0", + "duration": "33.0", "name": "blink_perf.bindings/append-child.html" }, { @@ -12,7 +12,7 @@ "name": "blink_perf.bindings/document-implementation.html" }, { - "duration": "18.0", + "duration": "19.0", "name": "blink_perf.bindings/dom-attribute-on-prototoype.html" }, { @@ -24,11 +24,11 @@ "name": "blink_perf.bindings/gc-forest.html" }, { - "duration": "40.0", + "duration": "41.0", "name": "blink_perf.bindings/gc-mini-tree.html" }, { - "duration": "131.0", + "duration": "126.0", "name": "blink_perf.bindings/gc-tree.html" }, { @@ -52,11 +52,11 @@ "name": "blink_perf.bindings/id-getter.html" }, { - "duration": "17.0", + "duration": "16.0", "name": "blink_perf.bindings/id-setter.html" }, { - "duration": "22.0", + "duration": "23.0", "name": "blink_perf.bindings/indexed-getter.html" }, { @@ -68,7 +68,7 @@ "name": "blink_perf.bindings/named-property-enumerator.html" }, { - "duration": "123.0", + "duration": "130.0", "name": "blink_perf.bindings/node-list-access.html" }, { @@ -76,7 +76,7 @@ "name": "blink_perf.bindings/node-type.html" }, { - "duration": "11.0", + "duration": "12.0", "name": "blink_perf.bindings/post-message.html" }, { @@ -96,7 +96,7 @@ "name": "blink_perf.bindings/serialize-long-string.html" }, { - "duration": "23.0", + "duration": "22.0", "name": "blink_perf.bindings/serialize-map.html" }, { @@ -112,19 +112,19 @@ "name": "blink_perf.bindings/set-attribute.html" }, { - "duration": "380.0", + "duration": "69.0", "name": "blink_perf.bindings/structured-clone-json-deserialize.html" }, { - "duration": "374.0", + "duration": "69.0", "name": "blink_perf.bindings/structured-clone-json-serialize.html" }, { - "duration": "365.0", + "duration": "24.0", "name": "blink_perf.bindings/structured-clone-long-string-deserialize.html" }, { - "duration": "365.0", + "duration": "23.0", "name": "blink_perf.bindings/structured-clone-long-string-serialize.html" }, { @@ -132,11 +132,11 @@ "name": "blink_perf.bindings/typed-array-construct-from-array.html" }, { - "duration": "19.0", + "duration": "20.0", "name": "blink_perf.bindings/typed-array-construct-from-same-type.html" }, { - "duration": "85.0", + "duration": "17.0", "name": "blink_perf.bindings/typed-array-construct-from-typed.html" }, { @@ -144,11 +144,11 @@ "name": "blink_perf.bindings/typed-array-set-from-typed.html" }, { - "duration": "16.0", + "duration": "17.0", "name": "blink_perf.bindings/undefined-first-child.html" }, { - "duration": "16.0", + "duration": "17.0", "name": "blink_perf.bindings/undefined-get-element-by-id.html" }, { @@ -156,15 +156,15 @@ "name": "blink_perf.bindings/undefined-id-getter.html" }, { - "duration": "25.0", + "duration": "35.0", "name": "blink_perf.canvas/createImageBitmapFromImageData.html" }, { - "duration": "51.0", + "duration": "52.0", "name": "blink_perf.canvas/draw-dynamic-canvas-2d-to-hw-accelerated-canvas-2d.html" }, { - "duration": "72.0", + "duration": "74.0", "name": "blink_perf.canvas/draw-dynamic-webgl-to-hw-accelerated-canvas-2d.html" }, { @@ -176,7 +176,7 @@ "name": "blink_perf.canvas/draw-static-canvas-2d-to-hw-accelerated-canvas-2d.html" }, { - "duration": "44.0", + "duration": "31.0", "name": "blink_perf.canvas/draw-static-webgl-to-hw-accelerated-canvas-2d.html" }, { @@ -192,27 +192,27 @@ "name": "blink_perf.canvas/drawimage.html" }, { - "duration": "25.0", + "duration": "26.0", "name": "blink_perf.canvas/getImageData.html" }, { - "duration": "23.0", + "duration": "21.0", "name": "blink_perf.canvas/getImageDataColorManaged.html" }, { - "duration": "72.0", + "duration": "31.0", "name": "blink_perf.canvas/putImageData.html" }, { - "duration": "29.0", + "duration": "28.0", "name": "blink_perf.canvas/toBlob_duration.html" }, { - "duration": "22.0", + "duration": "21.0", "name": "blink_perf.canvas/toBlob_duration_jpeg.html" }, { - "duration": "14.0", + "duration": "12.0", "name": "blink_perf.canvas/transferFromImageBitmap.html" }, { @@ -224,15 +224,15 @@ "name": "blink_perf.canvas/upload-video-to-sub-texture.html" }, { - "duration": "11.0", + "duration": "12.0", "name": "blink_perf.canvas/upload-video-to-texture.html" }, { - "duration": "50.0", + "duration": "51.0", "name": "blink_perf.canvas/upload-webgl-to-texture.html" }, { - "duration": "38.0", + "duration": "51.0", "name": "blink_perf.css/AttributeDescendantSelector.html" }, { @@ -244,11 +244,11 @@ "name": "blink_perf.css/CSSPropertySetterGetterMethods.html" }, { - "duration": "19.0", + "duration": "18.0", "name": "blink_perf.css/CSSPropertyUpdateValue.html" }, { - "duration": "25.0", + "duration": "30.0", "name": "blink_perf.css/ChangeStyleChildClassSelector.html" }, { @@ -264,7 +264,7 @@ "name": "blink_perf.css/ChangeStyleGrandChildElementSelector.html" }, { - "duration": "22.0", + "duration": "14.0", "name": "blink_perf.css/ChangeStyleMultipleClassSelector.html" }, { @@ -276,7 +276,7 @@ "name": "blink_perf.css/ChangeStyleNestedPseudoSelector.html" }, { - "duration": "13.0", + "duration": "14.0", "name": "blink_perf.css/ChangeStylePairOfNthChildSelector.html" }, { @@ -320,7 +320,7 @@ "name": "blink_perf.css/ChangeStyleUnqualifiedDataAttributeWithValueSelector.html" }, { - "duration": "38.0", + "duration": "39.0", "name": "blink_perf.css/ClassDescendantSelector.html" }, { @@ -328,7 +328,7 @@ "name": "blink_perf.css/ClassInvalidation.html" }, { - "duration": "23.0", + "duration": "24.0", "name": "blink_perf.css/FocusUpdate.html" }, { @@ -352,7 +352,7 @@ "name": "blink_perf.css/SelectorCountScaling.html" }, { - "duration": "58.0", + "duration": "64.0", "name": "blink_perf.dom/addRange.html" }, { @@ -360,7 +360,7 @@ "name": "blink_perf.dom/delete-in-password-field.html" }, { - "duration": "37.0", + "duration": "38.0", "name": "blink_perf.dom/div-editable.html" }, { @@ -384,7 +384,7 @@ "name": "blink_perf.dom/modify-element-title.html" }, { - "duration": "20.0", + "duration": "11.0", "name": "blink_perf.dom/move-down-with-hidden-elements.html" }, { @@ -400,7 +400,7 @@ "name": "blink_perf.dom/select-multiple-add.html" }, { - "duration": "21.0", + "duration": "22.0", "name": "blink_perf.dom/select-single-add.html" }, { @@ -412,27 +412,27 @@ "name": "blink_perf.dom/textarea-dom.html" }, { - "duration": "46.0", + "duration": "47.0", "name": "blink_perf.dom/textarea-edit.html" }, { - "duration": "28.0", + "duration": "33.0", "name": "blink_perf.events/EventsDispatching.html" }, { - "duration": "22.0", + "duration": "21.0", "name": "blink_perf.events/EventsDispatchingInDeeplyNestedShadowTrees.html" }, { - "duration": "92.0", + "duration": "89.0", "name": "blink_perf.events/EventsDispatchingInShadowTrees.html" }, { - "duration": "129.0", + "duration": "131.0", "name": "blink_perf.events/hit-test-lots-of-layers.html" }, { - "duration": "69.0", + "duration": "65.0", "name": "blink_perf.image_decoder/decode-gif.html" }, { @@ -460,15 +460,15 @@ "name": "blink_perf.image_decoder/decode-png.html" }, { - "duration": "29.0", + "duration": "34.0", "name": "blink_perf.layout/ArabicLineLayout.html" }, { - "duration": "11.0", + "duration": "10.0", "name": "blink_perf.layout/Shapes/MultipleShapes.html" }, { - "duration": "20.0", + "duration": "17.0", "name": "blink_perf.layout/SimpleTextPathLineLayout.html" }, { @@ -476,7 +476,7 @@ "name": "blink_perf.layout/add-remove-inline-floats.html" }, { - "duration": "19.0", + "duration": "20.0", "name": "blink_perf.layout/attach-inlines-2.html" }, { @@ -492,11 +492,11 @@ "name": "blink_perf.layout/chapter-reflow-once-random.html" }, { - "duration": "17.0", + "duration": "18.0", "name": "blink_perf.layout/chapter-reflow-once.html" }, { - "duration": "15.0", + "duration": "16.0", "name": "blink_perf.layout/chapter-reflow-thrice.html" }, { @@ -508,7 +508,7 @@ "name": "blink_perf.layout/chapter-reflow.html" }, { - "duration": "17.0", + "duration": "18.0", "name": "blink_perf.layout/character_fallback.html" }, { @@ -516,7 +516,7 @@ "name": "blink_perf.layout/character_fallback_aat.html" }, { - "duration": "21.0", + "duration": "22.0", "name": "blink_perf.layout/fixed-grid-lots-of-data.html" }, { @@ -524,7 +524,7 @@ "name": "blink_perf.layout/fixed-grid-lots-of-stretched-data.html" }, { - "duration": "29.0", + "duration": "34.0", "name": "blink_perf.layout/flexbox-column-nowrap.html" }, { @@ -552,11 +552,11 @@ "name": "blink_perf.layout/flexbox-with-stretch-layout.html" }, { - "duration": "153.0", + "duration": "154.0", "name": "blink_perf.layout/floats_100_100.html" }, { - "duration": "159.0", + "duration": "155.0", "name": "blink_perf.layout/floats_100_100_nested.html" }, { @@ -584,7 +584,7 @@ "name": "blink_perf.layout/floats_50_100.html" }, { - "duration": "56.0", + "duration": "57.0", "name": "blink_perf.layout/floats_50_100_nested.html" }, { @@ -592,15 +592,15 @@ "name": "blink_perf.layout/hindi-line-layout.html" }, { - "duration": "228.0", + "duration": "59.0", "name": "blink_perf.layout/large-table-with-collapsed-borders-and-colspans-wider-than-table.html" }, { - "duration": "229.0", + "duration": "59.0", "name": "blink_perf.layout/large-table-with-collapsed-borders-and-colspans.html" }, { - "duration": "208.0", + "duration": "58.0", "name": "blink_perf.layout/large-table-with-collapsed-borders-and-no-colspans.html" }, { @@ -652,19 +652,19 @@ "name": "blink_perf.layout/multicol/lots-of-text-autofill.html" }, { - "duration": "18.0", + "duration": "19.0", "name": "blink_perf.layout/multicol/lots-of-text-balanced-orphans-widows.html" }, { - "duration": "18.0", + "duration": "19.0", "name": "blink_perf.layout/multicol/lots-of-text-balanced.html" }, { - "duration": "40.0", + "duration": "31.0", "name": "blink_perf.layout/multicol/tall-content-short-columns-realistic.html" }, { - "duration": "39.0", + "duration": "23.0", "name": "blink_perf.layout/multicol/tall-content-short-columns.html" }, { @@ -680,11 +680,11 @@ "name": "blink_perf.layout/nested-percent-height-tables.html" }, { - "duration": "187.0", + "duration": "445.0", "name": "blink_perf.layout/subtree-detaching.html" }, { - "duration": "28.0", + "duration": "32.0", "name": "blink_perf.layout/vertical-japanese-kokoro-insert.html" }, { @@ -700,15 +700,15 @@ "name": "blink_perf.layout/word-wrap-break-word.html" }, { - "duration": "44.0", + "duration": "43.0", "name": "blink_perf.owp_storage/blob-perf-files.html" }, { - "duration": "29.0", + "duration": "28.0", "name": "blink_perf.owp_storage/blob-perf-ipc.html" }, { - "duration": "19.0", + "duration": "20.0", "name": "blink_perf.owp_storage/blob-perf-shm.html" }, { @@ -720,43 +720,43 @@ "name": "blink_perf.owp_storage/idb-load-docs.html" }, { - "duration": "46.0", + "duration": "51.0", "name": "blink_perf.paint/appending-text.html" }, { - "duration": "98.0", + "duration": "65.0", "name": "blink_perf.paint/color-changes.html" }, { - "duration": "103.0", + "duration": "74.0", "name": "blink_perf.paint/complex-content-slow-scroll.html" }, { - "duration": "90.0", + "duration": "63.0", "name": "blink_perf.paint/containment-resize.html" }, { - "duration": "86.0", + "duration": "54.0", "name": "blink_perf.paint/fixed-and-many-layers-scroll.html" }, { - "duration": "105.0", + "duration": "78.0", "name": "blink_perf.paint/large-table-background-change-with-invisible-collapsed-borders.html" }, { - "duration": "86.0", + "duration": "48.0", "name": "blink_perf.paint/large-table-background-change-with-visible-collapsed-borders.html" }, { - "duration": "111.0", + "duration": "81.0", "name": "blink_perf.paint/large-table-background-change-with-zero-width-collapsed-borders.html" }, { - "duration": "83.0", + "duration": "58.0", "name": "blink_perf.paint/large-table-collapsed-border-change-with-backgrounds.html" }, { - "duration": "93.0", + "duration": "66.0", "name": "blink_perf.paint/large-table-collapsed-border-change-with-text.html" }, { @@ -764,7 +764,7 @@ "name": "blink_perf.paint/large-table-collapsed-border-change.html" }, { - "duration": "64.0", + "duration": "63.0", "name": "blink_perf.paint/large-table-repaint.html" }, { @@ -772,27 +772,27 @@ "name": "blink_perf.paint/move-text-with-mask.html" }, { - "duration": "61.0", + "duration": "59.0", "name": "blink_perf.paint/paint-offset-changes.html" }, { - "duration": "79.0", + "duration": "54.0", "name": "blink_perf.paint/transform-changes.html" }, { - "duration": "32.0", + "duration": "39.0", "name": "blink_perf.parser/css-parser-yui.html" }, { - "duration": "253.0", + "duration": "68.0", "name": "blink_perf.parser/html-parser-threaded.html" }, { - "duration": "83.0", + "duration": "84.0", "name": "blink_perf.parser/html-parser.html" }, { - "duration": "269.0", + "duration": "326.0", "name": "blink_perf.parser/html5-full-render.html" }, { @@ -800,7 +800,7 @@ "name": "blink_perf.parser/iframe-append-remove.html" }, { - "duration": "18.0", + "duration": "19.0", "name": "blink_perf.parser/innerHTML-setter-siblings.html" }, { @@ -808,7 +808,7 @@ "name": "blink_perf.parser/innerHTML-setter.html" }, { - "duration": "26.0", + "duration": "17.0", "name": "blink_perf.parser/query-selector-all-attribute-complex.html" }, { @@ -832,7 +832,7 @@ "name": "blink_perf.parser/query-selector-all-class.html" }, { - "duration": "17.0", + "duration": "19.0", "name": "blink_perf.parser/query-selector-all-deep.html" }, { @@ -840,7 +840,7 @@ "name": "blink_perf.parser/query-selector-all-first.html" }, { - "duration": "23.0", + "duration": "24.0", "name": "blink_perf.parser/query-selector-all-id-deep.html" }, { @@ -848,7 +848,7 @@ "name": "blink_perf.parser/query-selector-all-id-first.html" }, { - "duration": "22.0", + "duration": "23.0", "name": "blink_perf.parser/query-selector-all-id-last.html" }, { @@ -864,7 +864,7 @@ "name": "blink_perf.parser/query-selector-first.html" }, { - "duration": "20.0", + "duration": "21.0", "name": "blink_perf.parser/query-selector-id-deep.html" }, { @@ -880,7 +880,7 @@ "name": "blink_perf.parser/simple-url.html" }, { - "duration": "20.0", + "duration": "19.0", "name": "blink_perf.parser/textarea-parsing.html" }, { @@ -888,7 +888,7 @@ "name": "blink_perf.parser/tiny-innerHTML.html" }, { - "duration": "18.0", + "duration": "19.0", "name": "blink_perf.parser/url-parser.html" }, { @@ -896,7 +896,7 @@ "name": "blink_perf.parser/xml-parser.html" }, { - "duration": "35.0", + "duration": "33.0", "name": "blink_perf.shadow_dom/shadow-style-share-attr-selectors.html" }, { @@ -920,7 +920,7 @@ "name": "blink_perf.shadow_dom/v0-changing-classname-with-shadow-dom.html" }, { - "duration": "31.0", + "duration": "36.0", "name": "blink_perf.shadow_dom/v0-changing-classname-without-shadow-dom.html" }, { @@ -960,15 +960,15 @@ "name": "blink_perf.shadow_dom/v1-distribution.html" }, { - "duration": "9.0", + "duration": "10.0", "name": "blink_perf.shadow_dom/v1-host-child-append.html" }, { - "duration": "99.0", + "duration": "100.0", "name": "blink_perf.shadow_dom/v1-large-deep-distribution.html" }, { - "duration": "261.0", + "duration": "249.0", "name": "blink_perf.shadow_dom/v1-large-deep-layout.html" }, { @@ -1008,11 +1008,11 @@ "name": "blink_perf.shadow_dom/v1-slot-append.html" }, { - "duration": "22.0", + "duration": "20.0", "name": "blink_perf.shadow_dom/v1-small-deep-distribution.html" }, { - "duration": "22.0", + "duration": "20.0", "name": "blink_perf.shadow_dom/v1-small-deep-layout.html" }, { @@ -1024,7 +1024,7 @@ "name": "blink_perf.shadow_dom/v1-small-shallow-layout.html" }, { - "duration": "21.0", + "duration": "27.0", "name": "blink_perf.svg/AzLizardBenjiPark.html" }, { @@ -1032,7 +1032,7 @@ "name": "blink_perf.svg/Bamboo.html" }, { - "duration": "12.0", + "duration": "13.0", "name": "blink_perf.svg/Cactus.html" }, { @@ -1040,11 +1040,11 @@ "name": "blink_perf.svg/Cowboy.html" }, { - "duration": "14.0", + "duration": "13.0", "name": "blink_perf.svg/Cowboy_transform.html" }, { - "duration": "10.0", + "duration": "11.0", "name": "blink_perf.svg/CrawFishGanson.html" }, { @@ -1080,11 +1080,11 @@ "name": "blink_perf.svg/HarveyRayner.html" }, { - "duration": "11.0", + "duration": "10.0", "name": "blink_perf.svg/HereGear.html" }, { - "duration": "33.0", + "duration": "26.0", "name": "blink_perf.svg/MtSaintHelens.html" }, { @@ -1092,11 +1092,11 @@ "name": "blink_perf.svg/Samurai.html" }, { - "duration": "360.0", + "duration": "696.0", "name": "blink_perf.svg/SierpinskiCarpet.html" }, { - "duration": "14.0", + "duration": "15.0", "name": "blink_perf.svg/SvgCubics.html" }, { @@ -1104,7 +1104,7 @@ "name": "blink_perf.svg/SvgHitTesting.html" }, { - "duration": "23.0", + "duration": "18.0", "name": "blink_perf.svg/SvgNestedUse.html" }, { @@ -1112,7 +1112,7 @@ "name": "blink_perf.svg/UnderTheSee.html" }, { - "duration": "13.0", + "duration": "14.0", "name": "blink_perf.svg/WorldIso.html" }, { @@ -1120,7 +1120,7 @@ "name": "blink_perf.svg/Worldcup.html" }, { - "duration": "56.0", + "duration": "55.0", "name": "dromaeo/http://dromaeo.com?dom-attr" }, { @@ -1128,7 +1128,7 @@ "name": "dromaeo/http://dromaeo.com?dom-modify" }, { - "duration": "56.0", + "duration": "57.0", "name": "dromaeo/http://dromaeo.com?dom-query" }, { @@ -1136,23 +1136,23 @@ "name": "dromaeo/http://dromaeo.com?dom-traverse" }, { - "duration": "32.0", + "duration": "31.0", "name": "dummy_benchmark.histogram_benchmark_1/dummy_page.html" }, { - "duration": "31.0", + "duration": "29.0", "name": "dummy_benchmark.noisy_benchmark_1/dummy_page.html" }, { - "duration": "24.0", + "duration": "23.0", "name": "dummy_benchmark.stable_benchmark_1/dummy_page.html" }, { - "duration": "938.0", + "duration": "932.0", "name": "jetstream/http://browserbench.org/JetStream/" }, { - "duration": "317.0", + "duration": "315.0", "name": "kraken/http://krakenbenchmark.mozilla.org/kraken-1.1/driver.html" }, { @@ -1168,11 +1168,11 @@ "name": "loading.mobile/BOLNoticias" }, { - "duration": "40.0", + "duration": "25.0", "name": "loading.mobile/Baidu" }, { - "duration": "70.0", + "duration": "71.0", "name": "loading.mobile/Baidu_3g" }, { @@ -1184,7 +1184,7 @@ "name": "loading.mobile/Dailymotion" }, { - "duration": "66.0", + "duration": "65.0", "name": "loading.mobile/Dawn" }, { @@ -1200,7 +1200,7 @@ "name": "loading.mobile/EnquiryIndianRail" }, { - "duration": "44.0", + "duration": "29.0", "name": "loading.mobile/Facebook" }, { @@ -1228,7 +1228,7 @@ "name": "loading.mobile/GSShop" }, { - "duration": "36.0", + "duration": "20.0", "name": "loading.mobile/GoogleBrazil" }, { @@ -1236,7 +1236,7 @@ "name": "loading.mobile/GoogleBrazil_3g" }, { - "duration": "35.0", + "duration": "22.0", "name": "loading.mobile/GoogleIndia" }, { @@ -1244,7 +1244,7 @@ "name": "loading.mobile/GoogleIndia_3g" }, { - "duration": "33.0", + "duration": "20.0", "name": "loading.mobile/GoogleIndonesia" }, { @@ -1252,19 +1252,19 @@ "name": "loading.mobile/GoogleIndonesia_3g" }, { - "duration": "48.0", + "duration": "29.0", "name": "loading.mobile/GoogleRedirectToGoogleJapan" }, { - "duration": "89.0", + "duration": "88.0", "name": "loading.mobile/GoogleRedirectToGoogleJapan_3g" }, { - "duration": "63.0", + "duration": "64.0", "name": "loading.mobile/Hongkiat" }, { - "duration": "72.0", + "duration": "71.0", "name": "loading.mobile/KapanLagi" }, { @@ -1272,7 +1272,7 @@ "name": "loading.mobile/Kaskus" }, { - "duration": "68.0", + "duration": "67.0", "name": "loading.mobile/LocalMoxie" }, { @@ -1284,7 +1284,7 @@ "name": "loading.mobile/OLX" }, { - "duration": "48.0", + "duration": "47.0", "name": "loading.mobile/QQNews" }, { @@ -1320,7 +1320,7 @@ "name": "loading.mobile/Wikipedia" }, { - "duration": "54.0", + "duration": "36.0", "name": "loading.mobile/YahooNews" }, { @@ -1328,7 +1328,7 @@ "name": "loading.mobile/YahooNews_3g" }, { - "duration": "40.0", + "duration": "21.0", "name": "loading.mobile/Youtube" }, { @@ -1336,35 +1336,35 @@ "name": "loading.mobile/Youtube_3g" }, { - "duration": "32.0", + "duration": "33.0", "name": "media.mobile/mse.html?media=aac_audio.mp4" }, { - "duration": "42.0", + "duration": "44.0", "name": "media.mobile/mse.html?media=aac_audio.mp4,h264_video.mp4" }, { - "duration": "41.0", + "duration": "43.0", "name": "media.mobile/mse.html?media=aac_audio.mp4,h264_video.mp4&waitForPageLoaded=true" }, { - "duration": "38.0", + "duration": "40.0", "name": "media.mobile/mse.html?media=h264_video.mp4" }, { - "duration": "55.0", + "duration": "58.0", "name": "media.mobile/video.html?src=crowd.ogg&type=audio" }, { - "duration": "41.0", + "duration": "42.0", "name": "media.mobile/video.html?src=crowd1080_vp9.webm" }, { - "duration": "28.0", + "duration": "29.0", "name": "media.mobile/video.html?src=crowd1080_vp9.webm&seek" }, { - "duration": "47.0", + "duration": "53.0", "name": "media.mobile/video.html?src=crowd720_vp9.webm" }, { @@ -1380,7 +1380,7 @@ "name": "media.mobile/video.html?src=tulip2.mp3&type=audio&seek" }, { - "duration": "49.0", + "duration": "50.0", "name": "media.mobile/video.html?src=tulip2.mp4" }, { @@ -1396,7 +1396,7 @@ "name": "media.mobile/video.html?src=tulip2.ogg&type=audio" }, { - "duration": "19.0", + "duration": "20.0", "name": "media.mobile/video.html?src=tulip2.ogg&type=audio&seek" }, { @@ -1412,7 +1412,7 @@ "name": "media.mobile/video.html?src=tulip2.vp9.webm&seek" }, { - "duration": "77.0", + "duration": "80.0", "name": "media.mobile/video.html?src=tulip2.vp9.webm_Regular-3G" }, { @@ -1424,55 +1424,55 @@ "name": "media.mobile/video.html?src=tulip2.wav&type=audio&seek" }, { - "duration": "396.0", + "duration": "398.0", "name": "memory.long_running_idle_gmail_background_tbmv2/https://mail.google.com/mail/" }, { - "duration": "351.0", + "duration": "352.0", "name": "memory.long_running_idle_gmail_tbmv2/https://mail.google.com/mail/" }, { - "duration": "24.0", + "duration": "25.0", "name": "memory.top_10_mobile/after_http_en_m_wikipedia_org_wiki_Science" }, { - "duration": "29.0", + "duration": "24.0", "name": "memory.top_10_mobile/after_http_m_intl_taobao_com_group_purchase_html" }, { - "duration": "26.0", + "duration": "31.0", "name": "memory.top_10_mobile/after_http_m_youtube_com_results_q_science" }, { - "duration": "26.0", + "duration": "24.0", "name": "memory.top_10_mobile/after_http_search_yahoo_com_search__ylt_p_google" }, { - "duration": "24.0", + "duration": "25.0", "name": "memory.top_10_mobile/after_http_www_amazon_com_gp_aw_s_k_nexus" }, { - "duration": "27.0", + "duration": "24.0", "name": "memory.top_10_mobile/after_http_www_baidu_com_s_word_google" }, { - "duration": "23.0", + "duration": "24.0", "name": "memory.top_10_mobile/after_http_yandex_ru_touchsearch_text_science" }, { - "duration": "26.0", + "duration": "24.0", "name": "memory.top_10_mobile/after_https_m_facebook_com_rihanna" }, { - "duration": "23.0", + "duration": "36.0", "name": "memory.top_10_mobile/after_https_mobile_twitter_com_justinbieber_skip_interstitial_true" }, { - "duration": "26.0", + "duration": "25.0", "name": "memory.top_10_mobile/after_https_www_google_co_uk_hl_en_q_science" }, { - "duration": "32.0", + "duration": "31.0", "name": "memory.top_10_mobile/http_en_m_wikipedia_org_wiki_Science" }, { @@ -1480,19 +1480,19 @@ "name": "memory.top_10_mobile/http_m_intl_taobao_com_group_purchase_html" }, { - "duration": "28.0", + "duration": "30.0", "name": "memory.top_10_mobile/http_m_youtube_com_results_q_science" }, { - "duration": "26.0", + "duration": "28.0", "name": "memory.top_10_mobile/http_search_yahoo_com_search__ylt_p_google" }, { - "duration": "30.0", + "duration": "29.0", "name": "memory.top_10_mobile/http_www_amazon_com_gp_aw_s_k_nexus" }, { - "duration": "29.0", + "duration": "31.0", "name": "memory.top_10_mobile/http_www_baidu_com_s_word_google" }, { @@ -1500,7 +1500,7 @@ "name": "memory.top_10_mobile/http_yandex_ru_touchsearch_text_science" }, { - "duration": "28.0", + "duration": "30.0", "name": "memory.top_10_mobile/https_m_facebook_com_rihanna" }, { @@ -1508,19 +1508,19 @@ "name": "memory.top_10_mobile/https_mobile_twitter_com_justinbieber_skip_interstitial_true" }, { - "duration": "29.0", + "duration": "28.0", "name": "memory.top_10_mobile/https_www_google_co_uk_hl_en_q_science" }, { - "duration": "562.0", + "duration": "183.0", "name": "octane/http://chromium.github.io/octane/index.html?auto=1" }, { - "duration": "32.0", + "duration": "31.0", "name": "oortonline_tbmv2/http://oortonline.gl/#run" }, { - "duration": "34.0", + "duration": "33.0", "name": "power.idle_platform/IdleStory_10s" }, { @@ -1532,55 +1532,55 @@ "name": "power.idle_platform/IdleStory_60s" }, { - "duration": "74.0", + "duration": "76.0", "name": "power.typical_10_mobile/http://de.m.wikipedia.org/wiki/K%C3%B6lner_Dom" }, { - "duration": "79.0", + "duration": "83.0", "name": "power.typical_10_mobile/http://m.chiebukuro.yahoo.co.jp/detail/q10136829180" }, { - "duration": "66.0", + "duration": "75.0", "name": "power.typical_10_mobile/http://m.ebay.com/itm/351157205404" }, { - "duration": "90.0", + "duration": "92.0", "name": "power.typical_10_mobile/http://m.facebook.com/barackobama" }, { - "duration": "84.0", + "duration": "78.0", "name": "power.typical_10_mobile/http://m.huffpost.com/us/entry/6004486" }, { - "duration": "77.0", + "duration": "76.0", "name": "power.typical_10_mobile/http://m.ynet.co.il" }, { - "duration": "77.0", + "duration": "99.0", "name": "power.typical_10_mobile/http://siriuslymeg.tumblr.com/" }, { - "duration": "81.0", + "duration": "71.0", "name": "power.typical_10_mobile/http://wapbaike.baidu.com/" }, { - "duration": "74.0", + "duration": "73.0", "name": "power.typical_10_mobile/http://www.cnn.com/2014/03/31/showbiz/tv/himym-finale/index.html" }, { - "duration": "82.0", + "duration": "85.0", "name": "power.typical_10_mobile/http://www.rg.ru/2014/10/21/cska-site.html" }, { - "duration": "76.0", + "duration": "75.0", "name": "power.typical_10_mobile/https://en.wikipedia.org/wiki/File:Rotating_earth_(large).gif" }, { - "duration": "34.0", + "duration": "33.0", "name": "rasterize_and_record_micro.partial_invalidation/800_relpos_divs.html" }, { - "duration": "52.0", + "duration": "53.0", "name": "rasterize_and_record_micro.top_25/file://static_top_25/amazon.html" }, { @@ -1588,7 +1588,7 @@ "name": "rasterize_and_record_micro.top_25/file://static_top_25/blogger.html" }, { - "duration": "61.0", + "duration": "46.0", "name": "rasterize_and_record_micro.top_25/file://static_top_25/booking.html" }, { @@ -1596,11 +1596,11 @@ "name": "rasterize_and_record_micro.top_25/file://static_top_25/cnn.html" }, { - "duration": "27.0", + "duration": "26.0", "name": "rasterize_and_record_micro.top_25/file://static_top_25/ebay.html" }, { - "duration": "84.0", + "duration": "83.0", "name": "rasterize_and_record_micro.top_25/file://static_top_25/espn.html" }, { @@ -1608,7 +1608,7 @@ "name": "rasterize_and_record_micro.top_25/file://static_top_25/facebook.html" }, { - "duration": "57.0", + "duration": "58.0", "name": "rasterize_and_record_micro.top_25/file://static_top_25/gmail.html" }, { @@ -1640,7 +1640,7 @@ "name": "rasterize_and_record_micro.top_25/file://static_top_25/pinterest.html" }, { - "duration": "68.0", + "duration": "67.0", "name": "rasterize_and_record_micro.top_25/file://static_top_25/techcrunch.html" }, { @@ -1664,19 +1664,19 @@ "name": "rasterize_and_record_micro.top_25/file://static_top_25/yahooanswers.html" }, { - "duration": "57.0", + "duration": "58.0", "name": "rasterize_and_record_micro.top_25/file://static_top_25/yahoogames.html" }, { - "duration": "145.0", + "duration": "144.0", "name": "rasterize_and_record_micro.top_25/file://static_top_25/yahoonews.html" }, { - "duration": "129.0", + "duration": "128.0", "name": "rasterize_and_record_micro.top_25/file://static_top_25/yahoosports.html" }, { - "duration": "65.0", + "duration": "64.0", "name": "rasterize_and_record_micro.top_25/file://static_top_25/youtube.html" }, { @@ -1692,11 +1692,11 @@ "name": "rendering.mobile/analog_clock_svg" }, { - "duration": "38.0", + "duration": "39.0", "name": "rendering.mobile/androidpolice_mobile" }, { - "duration": "38.0", + "duration": "36.0", "name": "rendering.mobile/androidpolice_mobile_sync_scroll" }, { @@ -1716,7 +1716,7 @@ "name": "rendering.mobile/background_color_animation_with_gradient" }, { - "duration": "25.0", + "duration": "26.0", "name": "rendering.mobile/baidu_mobile" }, { @@ -1736,11 +1736,11 @@ "name": "rendering.mobile/balls_css_transition_40_properties" }, { - "duration": "33.0", + "duration": "34.0", "name": "rendering.mobile/balls_css_transition_all_properties" }, { - "duration": "42.0", + "duration": "33.0", "name": "rendering.mobile/balls_javascript_canvas" }, { @@ -1748,7 +1748,7 @@ "name": "rendering.mobile/balls_javascript_css" }, { - "duration": "77.0", + "duration": "86.0", "name": "rendering.mobile/balls_svg_animations" }, { @@ -1756,7 +1756,7 @@ "name": "rendering.mobile/bing_mobile" }, { - "duration": "22.0", + "duration": "21.0", "name": "rendering.mobile/bing_mobile_sync_scroll" }, { @@ -1768,7 +1768,7 @@ "name": "rendering.mobile/blogspot" }, { - "duration": "44.0", + "duration": "45.0", "name": "rendering.mobile/blogspot_desktop_gpu_raster" }, { @@ -1792,15 +1792,15 @@ "name": "rendering.mobile/booking.com_desktop_gpu_raster" }, { - "duration": "34.0", + "duration": "35.0", "name": "rendering.mobile/booking.com_mobile" }, { - "duration": "34.0", + "duration": "35.0", "name": "rendering.mobile/booking.com_mobile_sync_scroll" }, { - "duration": "38.0", + "duration": "39.0", "name": "rendering.mobile/booking_pinch" }, { @@ -1816,7 +1816,7 @@ "name": "rendering.mobile/bouncing_balls_shadow" }, { - "duration": "33.0", + "duration": "36.0", "name": "rendering.mobile/bouncing_clipped_rectangles" }, { @@ -1836,7 +1836,7 @@ "name": "rendering.mobile/canvas_animation_no_clear" }, { - "duration": "27.0", + "duration": "26.0", "name": "rendering.mobile/canvas_arcs" }, { @@ -1848,7 +1848,7 @@ "name": "rendering.mobile/canvas_lines" }, { - "duration": "29.0", + "duration": "30.0", "name": "rendering.mobile/canvas_to_blob" }, { @@ -1868,7 +1868,7 @@ "name": "rendering.mobile/cnn_article_mobile" }, { - "duration": "53.0", + "duration": "52.0", "name": "rendering.mobile/cnn_article_mobile_sync_scroll" }, { @@ -1876,11 +1876,11 @@ "name": "rendering.mobile/cnn_mobile" }, { - "duration": "27.0", + "duration": "28.0", "name": "rendering.mobile/cnn_mobile_sync_scroll" }, { - "duration": "53.0", + "duration": "54.0", "name": "rendering.mobile/cnn_pathological" }, { @@ -1892,7 +1892,7 @@ "name": "rendering.mobile/cnn_pinch_desktop_gpu_raster" }, { - "duration": "31.0", + "duration": "30.0", "name": "rendering.mobile/compositor_heavy_animation" }, { @@ -1916,11 +1916,11 @@ "name": "rendering.mobile/css_animations_simultaneous_style_element" }, { - "duration": "44.0", + "duration": "37.0", "name": "rendering.mobile/css_animations_simultaneous_updating_class" }, { - "duration": "35.0", + "duration": "34.0", "name": "rendering.mobile/css_animations_staggered_infinite_iterations" }, { @@ -1932,7 +1932,7 @@ "name": "rendering.mobile/css_animations_staggered_new_element" }, { - "duration": "44.0", + "duration": "45.0", "name": "rendering.mobile/css_animations_staggered_style_element" }, { @@ -1944,11 +1944,11 @@ "name": "rendering.mobile/css_animations_triggered_inline_style" }, { - "duration": "40.0", + "duration": "41.0", "name": "rendering.mobile/css_animations_triggered_new_element" }, { - "duration": "51.0", + "duration": "59.0", "name": "rendering.mobile/css_animations_triggered_style_element" }, { @@ -1960,11 +1960,11 @@ "name": "rendering.mobile/css_transitions_inline_style" }, { - "duration": "37.0", + "duration": "36.0", "name": "rendering.mobile/css_transitions_new_element" }, { - "duration": "37.0", + "duration": "36.0", "name": "rendering.mobile/css_transitions_staggered_inline_style" }, { @@ -1988,19 +1988,19 @@ "name": "rendering.mobile/css_transitions_triggered_inline_style" }, { - "duration": "39.0", + "duration": "40.0", "name": "rendering.mobile/css_transitions_triggered_new_element" }, { - "duration": "37.0", + "duration": "38.0", "name": "rendering.mobile/css_transitions_triggered_style_element" }, { - "duration": "39.0", + "duration": "38.0", "name": "rendering.mobile/css_transitions_triggered_updating_class" }, { - "duration": "36.0", + "duration": "35.0", "name": "rendering.mobile/css_transitions_updating_class" }, { @@ -2036,7 +2036,7 @@ "name": "rendering.mobile/css_value_type_transform_complex" }, { - "duration": "32.0", + "duration": "31.0", "name": "rendering.mobile/css_value_type_transform_simple" }, { @@ -2076,7 +2076,7 @@ "name": "rendering.mobile/ebay_mobile_sync_scroll" }, { - "duration": "51.0", + "duration": "50.0", "name": "rendering.mobile/ebay_pinch" }, { @@ -2088,7 +2088,7 @@ "name": "rendering.mobile/effect_games" }, { - "duration": "21.0", + "duration": "20.0", "name": "rendering.mobile/espn" }, { @@ -2096,11 +2096,11 @@ "name": "rendering.mobile/espn_desktop_gpu_raster" }, { - "duration": "38.0", + "duration": "39.0", "name": "rendering.mobile/espn_pathological" }, { - "duration": "57.0", + "duration": "58.0", "name": "rendering.mobile/espn_pinch" }, { @@ -2112,7 +2112,7 @@ "name": "rendering.mobile/extra_large_texture_uploads" }, { - "duration": "30.0", + "duration": "31.0", "name": "rendering.mobile/facebook" }, { @@ -2124,7 +2124,7 @@ "name": "rendering.mobile/facebook_mobile" }, { - "duration": "37.0", + "duration": "28.0", "name": "rendering.mobile/facebook_mobile_sync_scroll" }, { @@ -2148,11 +2148,11 @@ "name": "rendering.mobile/geo_apis" }, { - "duration": "73.0", + "duration": "71.0", "name": "rendering.mobile/gmail" }, { - "duration": "84.0", + "duration": "90.0", "name": "rendering.mobile/gmail_desktop_gpu_raster" }, { @@ -2160,7 +2160,7 @@ "name": "rendering.mobile/gmail_pinch" }, { - "duration": "65.0", + "duration": "66.0", "name": "rendering.mobile/gmail_pinch_desktop_gpu_raster" }, { @@ -2180,15 +2180,15 @@ "name": "rendering.mobile/google_calendar_pinch_desktop_gpu_raster" }, { - "duration": "70.0", + "duration": "69.0", "name": "rendering.mobile/google_docs" }, { - "duration": "70.0", + "duration": "68.0", "name": "rendering.mobile/google_docs_desktop_gpu_raster" }, { - "duration": "40.0", + "duration": "41.0", "name": "rendering.mobile/google_image_pinch" }, { @@ -2196,15 +2196,15 @@ "name": "rendering.mobile/google_image_pinch_desktop_gpu_raster" }, { - "duration": "51.0", + "duration": "54.0", "name": "rendering.mobile/google_image_search" }, { - "duration": "51.0", + "duration": "54.0", "name": "rendering.mobile/google_image_search_desktop_gpu_raster" }, { - "duration": "36.0", + "duration": "37.0", "name": "rendering.mobile/google_news_mobile" }, { @@ -2216,7 +2216,7 @@ "name": "rendering.mobile/google_plus" }, { - "duration": "62.0", + "duration": "61.0", "name": "rendering.mobile/google_plus_desktop_gpu_raster" }, { @@ -2232,7 +2232,7 @@ "name": "rendering.mobile/google_search_pinch" }, { - "duration": "34.0", + "duration": "33.0", "name": "rendering.mobile/google_search_pinch_desktop_gpu_raster" }, { @@ -2244,7 +2244,7 @@ "name": "rendering.mobile/google_web_search_desktop_gpu_raster" }, { - "duration": "25.0", + "duration": "26.0", "name": "rendering.mobile/google_web_search_mobile" }, { @@ -2256,11 +2256,11 @@ "name": "rendering.mobile/gsp.ro_mobile" }, { - "duration": "26.0", + "duration": "27.0", "name": "rendering.mobile/gsp.ro_mobile_sync_scroll" }, { - "duration": "30.0", + "duration": "31.0", "name": "rendering.mobile/guardian_pathological" }, { @@ -2284,7 +2284,7 @@ "name": "rendering.mobile/ie_chalkboard" }, { - "duration": "34.0", + "duration": "33.0", "name": "rendering.mobile/jarro_doverson" }, { @@ -2300,7 +2300,7 @@ "name": "rendering.mobile/large_texture_uploads" }, { - "duration": "41.0", + "duration": "42.0", "name": "rendering.mobile/latimes_pathological" }, { @@ -2368,7 +2368,7 @@ "name": "rendering.mobile/microsoft_fireflies" }, { - "duration": "27.0", + "duration": "26.0", "name": "rendering.mobile/microsoft_fish_ie_tank" }, { @@ -2380,7 +2380,7 @@ "name": "rendering.mobile/microsoft_speed_reading" }, { - "duration": "33.0", + "duration": "32.0", "name": "rendering.mobile/microsoft_tweet_map" }, { @@ -2396,7 +2396,7 @@ "name": "rendering.mobile/mix_10k" }, { - "duration": "35.0", + "duration": "34.0", "name": "rendering.mobile/mix_blend_mode_animation_difference" }, { @@ -2404,7 +2404,7 @@ "name": "rendering.mobile/mix_blend_mode_animation_hue" }, { - "duration": "38.0", + "duration": "39.0", "name": "rendering.mobile/mix_blend_mode_animation_propagating_isolation" }, { @@ -2416,19 +2416,19 @@ "name": "rendering.mobile/mlb_mobile_sync_scroll" }, { - "duration": "25.0", + "duration": "26.0", "name": "scheduler.tough_scheduling_cases/raf.html" }, { - "duration": "25.0", + "duration": "26.0", "name": "scheduler.tough_scheduling_cases/raf_animation.html" }, { - "duration": "25.0", + "duration": "26.0", "name": "scheduler.tough_scheduling_cases/raf_canvas.html" }, { - "duration": "25.0", + "duration": "26.0", "name": "scheduler.tough_scheduling_cases/raf_touch_animation.html" }, { @@ -2436,15 +2436,15 @@ "name": "scheduler.tough_scheduling_cases/second_batch_js.html?heavy" }, { - "duration": "17.0", + "duration": "18.0", "name": "scheduler.tough_scheduling_cases/second_batch_js.html?light" }, { - "duration": "17.0", + "duration": "18.0", "name": "scheduler.tough_scheduling_cases/second_batch_js.html?medium" }, { - "duration": "39.0", + "duration": "40.0", "name": "scheduler.tough_scheduling_cases/simple_text_page.html" }, { @@ -2456,7 +2456,7 @@ "name": "scheduler.tough_scheduling_cases/sync_scroll_offset.html" }, { - "duration": "23.0", + "duration": "24.0", "name": "scheduler.tough_scheduling_cases/touch_handler_scrolling.html" }, { @@ -2464,7 +2464,7 @@ "name": "smoothness.gpu_rasterization.top_25_smooth/blogspot" }, { - "duration": "26.0", + "duration": "25.0", "name": "smoothness.gpu_rasterization.top_25_smooth/booking.com" }, { @@ -2476,7 +2476,7 @@ "name": "smoothness.gpu_rasterization.top_25_smooth/espn" }, { - "duration": "23.0", + "duration": "24.0", "name": "smoothness.gpu_rasterization.top_25_smooth/facebook" }, { @@ -2484,19 +2484,19 @@ "name": "smoothness.gpu_rasterization.top_25_smooth/gmail" }, { - "duration": "34.0", + "duration": "33.0", "name": "smoothness.gpu_rasterization.top_25_smooth/google_calendar" }, { - "duration": "51.0", + "duration": "50.0", "name": "smoothness.gpu_rasterization.top_25_smooth/google_docs" }, { - "duration": "38.0", + "duration": "37.0", "name": "smoothness.gpu_rasterization.top_25_smooth/google_image_search" }, { - "duration": "46.0", + "duration": "45.0", "name": "smoothness.gpu_rasterization.top_25_smooth/google_plus" }, { @@ -2504,11 +2504,11 @@ "name": "smoothness.gpu_rasterization.top_25_smooth/google_web_search" }, { - "duration": "22.0", + "duration": "21.0", "name": "smoothness.gpu_rasterization.top_25_smooth/linkedin" }, { - "duration": "34.0", + "duration": "33.0", "name": "smoothness.gpu_rasterization.top_25_smooth/techcrunch" }, { @@ -2532,7 +2532,7 @@ "name": "smoothness.gpu_rasterization.top_25_smooth/yahoo_answers" }, { - "duration": "39.0", + "duration": "38.0", "name": "smoothness.gpu_rasterization.top_25_smooth/yahoo_games" }, { @@ -2552,7 +2552,7 @@ "name": "smoothness.gpu_rasterization.tough_filters_cases/Analog_Clock_SVG" }, { - "duration": "32.0", + "duration": "31.0", "name": "smoothness.gpu_rasterization.tough_filters_cases/Filter_Terrain_SVG" }, { @@ -2568,7 +2568,7 @@ "name": "smoothness.gpu_rasterization.tough_path_rendering_cases/GUIMark_Vector_Chart_Test" }, { - "duration": "39.0", + "duration": "38.0", "name": "smoothness.gpu_rasterization.tough_path_rendering_cases/IE_Chalkboard" }, { @@ -2656,7 +2656,7 @@ "name": "smoothness.gpu_rasterization.tough_scrolling_cases/canvas_05000_pixels_per_second" }, { - "duration": "26.0", + "duration": "25.0", "name": "smoothness.gpu_rasterization.tough_scrolling_cases/canvas_10000_pixels_per_second" }, { @@ -2700,27 +2700,27 @@ "name": "smoothness.gpu_rasterization.tough_scrolling_cases/text_10000_pixels_per_second" }, { - "duration": "25.0", + "duration": "24.0", "name": "smoothness.gpu_rasterization.tough_scrolling_cases/text_15000_pixels_per_second" }, { - "duration": "25.0", + "duration": "24.0", "name": "smoothness.gpu_rasterization.tough_scrolling_cases/text_20000_pixels_per_second" }, { - "duration": "25.0", + "duration": "24.0", "name": "smoothness.gpu_rasterization.tough_scrolling_cases/text_30000_pixels_per_second" }, { - "duration": "25.0", + "duration": "24.0", "name": "smoothness.gpu_rasterization.tough_scrolling_cases/text_40000_pixels_per_second" }, { - "duration": "24.0", + "duration": "23.0", "name": "smoothness.gpu_rasterization.tough_scrolling_cases/text_50000_pixels_per_second" }, { - "duration": "23.0", + "duration": "22.0", "name": "smoothness.gpu_rasterization.tough_scrolling_cases/text_60000_pixels_per_second" }, { @@ -2744,7 +2744,7 @@ "name": "smoothness.gpu_rasterization.tough_scrolling_cases/text_constant_full_page_raster_15000_pixels_per_second" }, { - "duration": "27.0", + "duration": "26.0", "name": "smoothness.gpu_rasterization.tough_scrolling_cases/text_constant_full_page_raster_20000_pixels_per_second" }, { @@ -2764,7 +2764,7 @@ "name": "smoothness.gpu_rasterization.tough_scrolling_cases/text_constant_full_page_raster_60000_pixels_per_second" }, { - "duration": "25.0", + "duration": "24.0", "name": "smoothness.gpu_rasterization.tough_scrolling_cases/text_constant_full_page_raster_75000_pixels_per_second" }, { @@ -2772,7 +2772,7 @@ "name": "smoothness.gpu_rasterization.tough_scrolling_cases/text_constant_full_page_raster_90000_pixels_per_second" }, { - "duration": "24.0", + "duration": "23.0", "name": "smoothness.gpu_rasterization.tough_scrolling_cases/text_hover_05000_pixels_per_second" }, { @@ -2780,19 +2780,19 @@ "name": "smoothness.gpu_rasterization.tough_scrolling_cases/text_hover_10000_pixels_per_second" }, { - "duration": "25.0", + "duration": "24.0", "name": "smoothness.gpu_rasterization.tough_scrolling_cases/text_hover_15000_pixels_per_second" }, { - "duration": "25.0", + "duration": "24.0", "name": "smoothness.gpu_rasterization.tough_scrolling_cases/text_hover_20000_pixels_per_second" }, { - "duration": "25.0", + "duration": "24.0", "name": "smoothness.gpu_rasterization.tough_scrolling_cases/text_hover_30000_pixels_per_second" }, { - "duration": "25.0", + "duration": "24.0", "name": "smoothness.gpu_rasterization.tough_scrolling_cases/text_hover_40000_pixels_per_second" }, { @@ -2828,7 +2828,7 @@ "name": "smoothness.key_mobile_sites_smooth/baidu" }, { - "duration": "18.0", + "duration": "17.0", "name": "smoothness.key_mobile_sites_smooth/bing" }, { @@ -2836,15 +2836,15 @@ "name": "smoothness.key_mobile_sites_smooth/blogspot" }, { - "duration": "35.0", + "duration": "34.0", "name": "smoothness.key_mobile_sites_smooth/boingboing" }, { - "duration": "26.0", + "duration": "25.0", "name": "smoothness.key_mobile_sites_smooth/booking.com" }, { - "duration": "42.0", + "duration": "41.0", "name": "smoothness.key_mobile_sites_smooth/capitolvolkswagen" }, { @@ -2884,7 +2884,7 @@ "name": "smoothness.key_mobile_sites_smooth/google_web_search" }, { - "duration": "21.0", + "duration": "20.0", "name": "smoothness.key_mobile_sites_smooth/gsp.ro" }, { @@ -2900,7 +2900,7 @@ "name": "smoothness.key_mobile_sites_smooth/nytimes" }, { - "duration": "28.0", + "duration": "27.0", "name": "smoothness.key_mobile_sites_smooth/pinterest" }, { @@ -2912,11 +2912,11 @@ "name": "smoothness.key_mobile_sites_smooth/sfgate" }, { - "duration": "30.0", + "duration": "29.0", "name": "smoothness.key_mobile_sites_smooth/slashdot" }, { - "duration": "21.0", + "duration": "20.0", "name": "smoothness.key_mobile_sites_smooth/techcrunch" }, { @@ -2924,7 +2924,7 @@ "name": "smoothness.key_mobile_sites_smooth/theverge" }, { - "duration": "38.0", + "duration": "37.0", "name": "smoothness.key_mobile_sites_smooth/theverge_article" }, { @@ -2944,7 +2944,7 @@ "name": "smoothness.key_mobile_sites_smooth/wordpress" }, { - "duration": "46.0", + "duration": "45.0", "name": "smoothness.key_mobile_sites_smooth/worldjournal" }, { @@ -2996,7 +2996,7 @@ "name": "smoothness.key_silk_cases/http://jsfiddle.net/R8DX9/4/show/" }, { - "duration": "23.0", + "duration": "22.0", "name": "smoothness.key_silk_cases/http://jsfiddle.net/TLXLu/3/show/" }, { @@ -3008,11 +3008,11 @@ "name": "smoothness.key_silk_cases/http://jsfiddle.net/cKB9D/7/show/" }, { - "duration": "24.0", + "duration": "23.0", "name": "smoothness.key_silk_cases/http://jsfiddle.net/jx5De/14/show/" }, { - "duration": "24.0", + "duration": "23.0", "name": "smoothness.key_silk_cases/http://jsfiddle.net/rF9Gh/7/show/" }, { @@ -3032,7 +3032,7 @@ "name": "smoothness.key_silk_cases/http://mobile-news.sandbox.google.com/news/pt0?scroll" }, { - "duration": "21.0", + "duration": "20.0", "name": "smoothness.key_silk_cases/http://mobile-news.sandbox.google.com/news/pt0?swipe" }, { @@ -3060,19 +3060,19 @@ "name": "smoothness.key_silk_cases/inbox_app.html?swipe_to_dismiss" }, { - "duration": "26.0", + "duration": "25.0", "name": "smoothness.key_silk_cases/inbox_app.html?toggle_drawer" }, { - "duration": "27.0", + "duration": "26.0", "name": "smoothness.key_silk_cases/infinite_scrolling.html" }, { - "duration": "21.0", + "duration": "20.0", "name": "smoothness.key_silk_cases/list_animation_simple.html" }, { - "duration": "31.0", + "duration": "30.0", "name": "smoothness.key_silk_cases/masonry.html" }, { @@ -3080,15 +3080,15 @@ "name": "smoothness.key_silk_cases/pushState.html" }, { - "duration": "31.0", + "duration": "30.0", "name": "smoothness.key_silk_cases/silk_finance.html" }, { - "duration": "25.0", + "duration": "29.0", "name": "smoothness.maps/maps_perf_test" }, { - "duration": "58.0", + "duration": "54.0", "name": "smoothness.pathological_mobile_sites/http://edition.cnn.com" }, { @@ -3116,7 +3116,7 @@ "name": "smoothness.pathological_mobile_sites/http://www.theguardian.com/politics/2015/mar/09/ed-balls-tory-spending-plans-nhs-charging" }, { - "duration": "35.0", + "duration": "36.0", "name": "smoothness.pathological_mobile_sites/http://www.wowwiki.com/World_of_Warcraft:_Mists_of_Pandaria" }, { @@ -3124,11 +3124,11 @@ "name": "smoothness.pathological_mobile_sites/http://www.zdnet.com" }, { - "duration": "28.0", + "duration": "27.0", "name": "smoothness.pathological_mobile_sites/https://www.linkedin.com/in/linustorvalds" }, { - "duration": "31.0", + "duration": "32.0", "name": "smoothness.simple_mobile_sites/http://m.nytimes.com/" }, { @@ -3136,19 +3136,19 @@ "name": "smoothness.simple_mobile_sites/http://www.ebay.co.uk/" }, { - "duration": "33.0", + "duration": "34.0", "name": "smoothness.simple_mobile_sites/http://www.nyc.gov" }, { - "duration": "34.0", + "duration": "35.0", "name": "smoothness.simple_mobile_sites/https://www.flickr.com/" }, { - "duration": "7.0", + "duration": "0.0", "name": "smoothness.sync_scroll.key_mobile_sites_smooth/amazon" }, { - "duration": "26.0", + "duration": "27.0", "name": "smoothness.sync_scroll.key_mobile_sites_smooth/androidpolice" }, { @@ -3156,23 +3156,23 @@ "name": "smoothness.sync_scroll.key_mobile_sites_smooth/baidu" }, { - "duration": "17.0", + "duration": "18.0", "name": "smoothness.sync_scroll.key_mobile_sites_smooth/bing" }, { - "duration": "19.0", + "duration": "20.0", "name": "smoothness.sync_scroll.key_mobile_sites_smooth/blogspot" }, { - "duration": "35.0", + "duration": "37.0", "name": "smoothness.sync_scroll.key_mobile_sites_smooth/boingboing" }, { - "duration": "25.0", + "duration": "26.0", "name": "smoothness.sync_scroll.key_mobile_sites_smooth/booking.com" }, { - "duration": "42.0", + "duration": "43.0", "name": "smoothness.sync_scroll.key_mobile_sites_smooth/capitolvolkswagen" }, { @@ -3180,15 +3180,15 @@ "name": "smoothness.sync_scroll.key_mobile_sites_smooth/cnn" }, { - "duration": "37.0", + "duration": "39.0", "name": "smoothness.sync_scroll.key_mobile_sites_smooth/cnn_article" }, { - "duration": "34.0", + "duration": "36.0", "name": "smoothness.sync_scroll.key_mobile_sites_smooth/cuteoverload" }, { - "duration": "28.0", + "duration": "29.0", "name": "smoothness.sync_scroll.key_mobile_sites_smooth/deviantart" }, { @@ -3196,27 +3196,27 @@ "name": "smoothness.sync_scroll.key_mobile_sites_smooth/digg" }, { - "duration": "22.0", + "duration": "23.0", "name": "smoothness.sync_scroll.key_mobile_sites_smooth/ebay" }, { - "duration": "32.0", + "duration": "33.0", "name": "smoothness.sync_scroll.key_mobile_sites_smooth/espn" }, { - "duration": "21.0", + "duration": "22.0", "name": "smoothness.sync_scroll.key_mobile_sites_smooth/facebook" }, { - "duration": "22.0", + "duration": "23.0", "name": "smoothness.sync_scroll.key_mobile_sites_smooth/forecast.io" }, { - "duration": "26.0", + "duration": "27.0", "name": "smoothness.sync_scroll.key_mobile_sites_smooth/google_news" }, { - "duration": "24.0", + "duration": "25.0", "name": "smoothness.sync_scroll.key_mobile_sites_smooth/google_plus" }, { @@ -3224,11 +3224,11 @@ "name": "smoothness.sync_scroll.key_mobile_sites_smooth/google_web_search" }, { - "duration": "20.0", + "duration": "21.0", "name": "smoothness.sync_scroll.key_mobile_sites_smooth/gsp.ro" }, { - "duration": "25.0", + "duration": "26.0", "name": "smoothness.sync_scroll.key_mobile_sites_smooth/linkedin" }, { @@ -3236,23 +3236,23 @@ "name": "smoothness.sync_scroll.key_mobile_sites_smooth/mlb" }, { - "duration": "23.0", + "duration": "24.0", "name": "smoothness.sync_scroll.key_mobile_sites_smooth/nytimes" }, { - "duration": "28.0", + "duration": "29.0", "name": "smoothness.sync_scroll.key_mobile_sites_smooth/pinterest" }, { - "duration": "28.0", + "duration": "29.0", "name": "smoothness.sync_scroll.key_mobile_sites_smooth/reddit" }, { - "duration": "24.0", + "duration": "25.0", "name": "smoothness.sync_scroll.key_mobile_sites_smooth/sfgate" }, { - "duration": "29.0", + "duration": "30.0", "name": "smoothness.sync_scroll.key_mobile_sites_smooth/slashdot" }, { @@ -3260,39 +3260,39 @@ "name": "smoothness.sync_scroll.key_mobile_sites_smooth/techcrunch" }, { - "duration": "22.0", + "duration": "23.0", "name": "smoothness.sync_scroll.key_mobile_sites_smooth/theverge" }, { - "duration": "37.0", + "duration": "38.0", "name": "smoothness.sync_scroll.key_mobile_sites_smooth/theverge_article" }, { - "duration": "23.0", + "duration": "25.0", "name": "smoothness.sync_scroll.key_mobile_sites_smooth/twitter" }, { - "duration": "35.0", + "duration": "37.0", "name": "smoothness.sync_scroll.key_mobile_sites_smooth/usatoday" }, { - "duration": "26.0", + "duration": "27.0", "name": "smoothness.sync_scroll.key_mobile_sites_smooth/wikipedia" }, { - "duration": "26.0", + "duration": "27.0", "name": "smoothness.sync_scroll.key_mobile_sites_smooth/wikipedia_delayed_scroll_start" }, { - "duration": "28.0", + "duration": "29.0", "name": "smoothness.sync_scroll.key_mobile_sites_smooth/wordpress" }, { - "duration": "45.0", + "duration": "49.0", "name": "smoothness.sync_scroll.key_mobile_sites_smooth/worldjournal" }, { - "duration": "41.0", + "duration": "42.0", "name": "smoothness.sync_scroll.key_mobile_sites_smooth/wowwiki" }, { @@ -3300,11 +3300,11 @@ "name": "smoothness.sync_scroll.key_mobile_sites_smooth/wsj" }, { - "duration": "25.0", + "duration": "26.0", "name": "smoothness.sync_scroll.key_mobile_sites_smooth/yahoo_answers" }, { - "duration": "23.0", + "duration": "24.0", "name": "smoothness.sync_scroll.key_mobile_sites_smooth/yahoo_news" }, { @@ -3316,11 +3316,11 @@ "name": "smoothness.top_25_smooth/amazon" }, { - "duration": "32.0", + "duration": "33.0", "name": "smoothness.top_25_smooth/blogspot" }, { - "duration": "25.0", + "duration": "26.0", "name": "smoothness.top_25_smooth/booking.com" }, { @@ -3328,11 +3328,11 @@ "name": "smoothness.top_25_smooth/cnn" }, { - "duration": "18.0", + "duration": "19.0", "name": "smoothness.top_25_smooth/ebay" }, { - "duration": "22.0", + "duration": "23.0", "name": "smoothness.top_25_smooth/espn" }, { @@ -3340,23 +3340,23 @@ "name": "smoothness.top_25_smooth/facebook" }, { - "duration": "93.0", + "duration": "64.0", "name": "smoothness.top_25_smooth/gmail" }, { - "duration": "33.0", + "duration": "34.0", "name": "smoothness.top_25_smooth/google_calendar" }, { - "duration": "50.0", + "duration": "51.0", "name": "smoothness.top_25_smooth/google_docs" }, { - "duration": "38.0", + "duration": "39.0", "name": "smoothness.top_25_smooth/google_image_search" }, { - "duration": "45.0", + "duration": "46.0", "name": "smoothness.top_25_smooth/google_plus" }, { @@ -3364,39 +3364,39 @@ "name": "smoothness.top_25_smooth/google_web_search" }, { - "duration": "21.0", + "duration": "22.0", "name": "smoothness.top_25_smooth/linkedin" }, { - "duration": "26.0", + "duration": "27.0", "name": "smoothness.top_25_smooth/pinterest" }, { - "duration": "32.0", + "duration": "34.0", "name": "smoothness.top_25_smooth/techcrunch" }, { - "duration": "25.0", + "duration": "26.0", "name": "smoothness.top_25_smooth/twitter" }, { - "duration": "27.0", + "duration": "28.0", "name": "smoothness.top_25_smooth/weather.com" }, { - "duration": "36.0", + "duration": "37.0", "name": "smoothness.top_25_smooth/wikipedia" }, { - "duration": "29.0", + "duration": "30.0", "name": "smoothness.top_25_smooth/wordpress" }, { - "duration": "23.0", + "duration": "24.0", "name": "smoothness.top_25_smooth/yahoo_answers" }, { - "duration": "38.0", + "duration": "39.0", "name": "smoothness.top_25_smooth/yahoo_games" }, { @@ -3404,11 +3404,11 @@ "name": "smoothness.top_25_smooth/yahoo_news" }, { - "duration": "25.0", + "duration": "26.0", "name": "smoothness.top_25_smooth/yahoo_sports" }, { - "duration": "36.0", + "duration": "37.0", "name": "smoothness.top_25_smooth/youtube" }, { @@ -3424,7 +3424,7 @@ "name": "smoothness.tough_ad_cases/http://localhost:8000/CICAgKCN39CopQEQoAEY2AQoATIID59gK5hjjIg.swiffy72.html" }, { - "duration": "26.0", + "duration": "27.0", "name": "smoothness.tough_ad_cases/http://localhost:8000/CICAgKCNj4HgyAEQeBjYBCgBMgjQpPkOjyWNdw.1.swiffy72.html" }, { @@ -3444,19 +3444,19 @@ "name": "smoothness.tough_ad_cases/http://localhost:8000/clip-paths-CILZhLqO_-27bxB4GNgEKAEyCC46kMLBXnMT.swiffy72.html" }, { - "duration": "26.0", + "duration": "27.0", "name": "smoothness.tough_ad_cases/http://localhost:8000/filters-CNLa0t2T47qJ_wEQoAEY2AQoATIIFaIdc7VMBr4.swiffy72.html" }, { - "duration": "26.0", + "duration": "27.0", "name": "smoothness.tough_ad_cases/http://localhost:8000/shapes-CICAgMDO7cfIzwEQ1AMYPCgBMghqY8tqyRCArQ.swiffy72.html" }, { - "duration": "26.0", + "duration": "27.0", "name": "smoothness.tough_ad_cases/http://localhost:8000/shapes-CK7ptO3F8bi2KxDQAhiYAigBMgij6QBQtD2gyA.swiffy72.html" }, { - "duration": "32.0", + "duration": "33.0", "name": "smoothness.tough_animation_cases/balls_css_keyframe_animations.html" }, { @@ -3468,7 +3468,7 @@ "name": "smoothness.tough_animation_cases/balls_css_transition_2_properties.html" }, { - "duration": "29.0", + "duration": "28.0", "name": "smoothness.tough_animation_cases/balls_css_transition_40_properties.html" }, { @@ -3476,7 +3476,7 @@ "name": "smoothness.tough_animation_cases/balls_css_transition_all_properties.html" }, { - "duration": "27.0", + "duration": "28.0", "name": "smoothness.tough_animation_cases/balls_javascript_canvas.html" }, { @@ -3484,11 +3484,11 @@ "name": "smoothness.tough_animation_cases/balls_javascript_css.html" }, { - "duration": "50.0", + "duration": "51.0", "name": "smoothness.tough_animation_cases/balls_svg_animations.html" }, { - "duration": "26.0", + "duration": "27.0", "name": "smoothness.tough_animation_cases/compositor_heavy_animation.html?N=0200" }, { @@ -3496,19 +3496,19 @@ "name": "smoothness.tough_animation_cases/css_animations_many_keyframes.html?N=0316" }, { - "duration": "31.0", + "duration": "32.0", "name": "smoothness.tough_animation_cases/css_animations_simultaneous_by_inserting_new_element.html?N=0316" }, { - "duration": "29.0", + "duration": "30.0", "name": "smoothness.tough_animation_cases/css_animations_simultaneous_by_inserting_style_element.html?N=0316" }, { - "duration": "29.0", + "duration": "30.0", "name": "smoothness.tough_animation_cases/css_animations_simultaneous_by_updating_class.html?N=0316" }, { - "duration": "29.0", + "duration": "31.0", "name": "smoothness.tough_animation_cases/css_animations_simultaneous_by_updating_inline_style.html?N=0316" }, { @@ -3516,19 +3516,19 @@ "name": "smoothness.tough_animation_cases/css_animations_staggered_chaining_by_inserting_new_element.html?N=0316" }, { - "duration": "35.0", + "duration": "36.0", "name": "smoothness.tough_animation_cases/css_animations_staggered_chaining_by_inserting_style_element.html?N=0316" }, { - "duration": "31.0", + "duration": "32.0", "name": "smoothness.tough_animation_cases/css_animations_staggered_chaining_by_updating_class.html?N=0316" }, { - "duration": "31.0", + "duration": "33.0", "name": "smoothness.tough_animation_cases/css_animations_staggered_chaining_by_updating_inline_style.html?N=0316" }, { - "duration": "27.0", + "duration": "29.0", "name": "smoothness.tough_animation_cases/css_animations_staggered_infinite_iterations.html?N=0316" }, { @@ -3536,75 +3536,75 @@ "name": "smoothness.tough_animation_cases/css_animations_staggered_triggering_by_inserting_new_element.html?N=0316" }, { - "duration": "31.0", + "duration": "32.0", "name": "smoothness.tough_animation_cases/css_animations_staggered_triggering_by_inserting_style_element.html?N=0316" }, { - "duration": "31.0", + "duration": "32.0", "name": "smoothness.tough_animation_cases/css_animations_staggered_triggering_by_updating_class.html?N=0316" }, { - "duration": "31.0", + "duration": "33.0", "name": "smoothness.tough_animation_cases/css_animations_staggered_triggering_by_updating_inline_style.html?N=0316" }, { - "duration": "30.0", + "duration": "31.0", "name": "smoothness.tough_animation_cases/css_transitions_simultaneous_by_inserting_new_element.html?N=0316" }, { - "duration": "30.0", + "duration": "31.0", "name": "smoothness.tough_animation_cases/css_transitions_simultaneous_by_inserting_style_element.html?N=0316" }, { - "duration": "30.0", + "duration": "31.0", "name": "smoothness.tough_animation_cases/css_transitions_simultaneous_by_updating_class.html?N=0316" }, { - "duration": "30.0", + "duration": "31.0", "name": "smoothness.tough_animation_cases/css_transitions_simultaneous_by_updating_inline_style.html?N=0316" }, { - "duration": "30.0", + "duration": "32.0", "name": "smoothness.tough_animation_cases/css_transitions_staggered_chaining_by_inserting_new_element.html?N=0316" }, { - "duration": "30.0", + "duration": "31.0", "name": "smoothness.tough_animation_cases/css_transitions_staggered_chaining_by_inserting_style_element.html?N=0316" }, { - "duration": "30.0", + "duration": "31.0", "name": "smoothness.tough_animation_cases/css_transitions_staggered_chaining_by_updating_class.html?N=0316" }, { - "duration": "30.0", + "duration": "31.0", "name": "smoothness.tough_animation_cases/css_transitions_staggered_chaining_by_updating_inline_style.html?N=0316" }, { - "duration": "31.0", + "duration": "33.0", "name": "smoothness.tough_animation_cases/css_transitions_staggered_triggering_by_inserting_new_element.html?N=0316" }, { - "duration": "30.0", + "duration": "31.0", "name": "smoothness.tough_animation_cases/css_transitions_staggered_triggering_by_inserting_style_element.html?N=0316" }, { - "duration": "31.0", + "duration": "32.0", "name": "smoothness.tough_animation_cases/css_transitions_staggered_triggering_by_updating_class.html?N=0316" }, { - "duration": "31.0", + "duration": "32.0", "name": "smoothness.tough_animation_cases/css_transitions_staggered_triggering_by_updating_inline_style.html?N=0316" }, { - "duration": "27.0", + "duration": "28.0", "name": "smoothness.tough_animation_cases/css_value_type_color.html?api=css_animations&N=0316" }, { - "duration": "27.0", + "duration": "28.0", "name": "smoothness.tough_animation_cases/css_value_type_color.html?api=web_animations&N=0316" }, { - "duration": "29.0", + "duration": "30.0", "name": "smoothness.tough_animation_cases/css_value_type_filter.html?api=css_animations&N=0316" }, { @@ -3616,39 +3616,39 @@ "name": "smoothness.tough_animation_cases/css_value_type_length_3d.html?api=web_animations&N=0316" }, { - "duration": "27.0", + "duration": "28.0", "name": "smoothness.tough_animation_cases/css_value_type_length_complex.html?api=css_animations&N=0316" }, { - "duration": "27.0", + "duration": "28.0", "name": "smoothness.tough_animation_cases/css_value_type_length_complex.html?api=web_animations&N=0316" }, { - "duration": "27.0", + "duration": "28.0", "name": "smoothness.tough_animation_cases/css_value_type_length_simple.html?api=css_animations&N=0316" }, { - "duration": "27.0", + "duration": "28.0", "name": "smoothness.tough_animation_cases/css_value_type_length_simple.html?api=web_animations&N=0316" }, { - "duration": "27.0", + "duration": "28.0", "name": "smoothness.tough_animation_cases/css_value_type_path.html?api=css_animations&N=0316" }, { - "duration": "27.0", + "duration": "28.0", "name": "smoothness.tough_animation_cases/css_value_type_path.html?api=web_animations&N=0316" }, { - "duration": "27.0", + "duration": "28.0", "name": "smoothness.tough_animation_cases/css_value_type_shadow.html?api=css_animations&N=0316" }, { - "duration": "27.0", + "duration": "28.0", "name": "smoothness.tough_animation_cases/css_value_type_shadow.html?api=web_animations&N=0316" }, { - "duration": "26.0", + "duration": "27.0", "name": "smoothness.tough_animation_cases/css_value_type_transform_complex.html?api=css_animations&N=0316" }, { @@ -3664,7 +3664,7 @@ "name": "smoothness.tough_animation_cases/css_value_type_transform_simple.html?api=web_animations&N=0316" }, { - "duration": "26.0", + "duration": "28.0", "name": "smoothness.tough_animation_cases/keyframed_animations.html" }, { @@ -3676,11 +3676,11 @@ "name": "smoothness.tough_animation_cases/mix_blend_mode_animation_hue.html" }, { - "duration": "27.0", + "duration": "28.0", "name": "smoothness.tough_animation_cases/mix_blend_mode_animation_screen.html" }, { - "duration": "28.0", + "duration": "29.0", "name": "smoothness.tough_animation_cases/mix_blend_mode_propagating_isolation.html" }, { @@ -3692,15 +3692,15 @@ "name": "smoothness.tough_animation_cases/robohornetpro" }, { - "duration": "26.0", + "duration": "27.0", "name": "smoothness.tough_animation_cases/transform_transition_js_block.html" }, { - "duration": "27.0", + "duration": "29.0", "name": "smoothness.tough_animation_cases/transform_transitions.html" }, { - "duration": "68.0", + "duration": "43.0", "name": "smoothness.tough_animation_cases/web_animations_many_keyframes.html?N=0316" }, { @@ -3708,23 +3708,23 @@ "name": "smoothness.tough_animation_cases/web_animations_set_current_time_in_raf.html?N=0316" }, { - "duration": "29.0", + "duration": "30.0", "name": "smoothness.tough_animation_cases/web_animations_simultaneous.html?N=0316" }, { - "duration": "31.0", + "duration": "32.0", "name": "smoothness.tough_animation_cases/web_animations_staggered_chaining.html?N=0316" }, { - "duration": "27.0", + "duration": "29.0", "name": "smoothness.tough_animation_cases/web_animations_staggered_infinite_iterations.html?N=0316" }, { - "duration": "31.0", + "duration": "32.0", "name": "smoothness.tough_animation_cases/web_animations_staggered_triggering.html?N=0316" }, { - "duration": "28.0", + "duration": "29.0", "name": "smoothness.tough_canvas_cases/../../../chrome/test/data/perf/canvas_bench/many_images.html" }, { @@ -3732,11 +3732,11 @@ "name": "smoothness.tough_canvas_cases/http://geoapis.appspot.com/agdnZW9hcGlzchMLEgtFeGFtcGxlQ29kZRjh1wIM" }, { - "duration": "22.0", + "duration": "23.0", "name": "smoothness.tough_canvas_cases/http://hakim.se/experiments/html5/magnetic/02/" }, { - "duration": "23.0", + "duration": "24.0", "name": "smoothness.tough_canvas_cases/http://ie.microsoft.com/testdrive/Graphics/TweetMap/Default.html" }, { @@ -3768,11 +3768,11 @@ "name": "smoothness.tough_canvas_cases/http://jarrodoverson.com/static/demos/particleSystem/" }, { - "duration": "22.0", + "duration": "23.0", "name": "smoothness.tough_canvas_cases/http://mix10k.visitmix.com/Entry/Details/169" }, { - "duration": "20.0", + "duration": "21.0", "name": "smoothness.tough_canvas_cases/http://runway.countlessprojects.com/prototype/performance_test.html" }, { @@ -3784,27 +3784,27 @@ "name": "smoothness.tough_canvas_cases/http://themaninblue.com/experiment/AnimationBenchmark/canvas/" }, { - "duration": "22.0", + "duration": "23.0", "name": "smoothness.tough_canvas_cases/http://www.chiptune.com/starfield/starfield.html" }, { - "duration": "21.0", + "duration": "22.0", "name": "smoothness.tough_canvas_cases/http://www.craftymind.com/factory/guimark2/HTML5ChartingTest.html" }, { - "duration": "23.0", + "duration": "24.0", "name": "smoothness.tough_canvas_cases/http://www.effectgames.com/demos/canvascycle/" }, { - "duration": "21.0", + "duration": "22.0", "name": "smoothness.tough_canvas_cases/http://www.kevs3d.co.uk/dev/canvask3d/k3d_test.html" }, { - "duration": "22.0", + "duration": "23.0", "name": "smoothness.tough_canvas_cases/http://www.megidish.net/awjs/" }, { - "duration": "22.0", + "duration": "23.0", "name": "smoothness.tough_canvas_cases/http://www.smashcat.org/av/canvas_test/" }, { @@ -3816,15 +3816,15 @@ "name": "smoothness.tough_canvas_cases/tough_canvas_cases/canvas-font-cycler.html" }, { - "duration": "43.0", + "duration": "44.0", "name": "smoothness.tough_canvas_cases/tough_canvas_cases/canvas2d_balls_common/bouncing_balls.html?ball=image_with_shadow&back=image" }, { - "duration": "21.0", + "duration": "22.0", "name": "smoothness.tough_canvas_cases/tough_canvas_cases/canvas2d_balls_common/bouncing_balls.html?ball=text&back=white&ball_count=15" }, { - "duration": "23.0", + "duration": "24.0", "name": "smoothness.tough_canvas_cases/tough_canvas_cases/canvas_toBlob.html" }, { @@ -3836,11 +3836,11 @@ "name": "smoothness.tough_canvas_cases/tough_canvas_cases/rendering_throughput/bouncing_gradient_circles.html" }, { - "duration": "19.0", + "duration": "20.0", "name": "smoothness.tough_canvas_cases/tough_canvas_cases/rendering_throughput/bouncing_png_images.html" }, { - "duration": "35.0", + "duration": "36.0", "name": "smoothness.tough_canvas_cases/tough_canvas_cases/rendering_throughput/bouncing_svg_images.html" }, { @@ -3848,7 +3848,7 @@ "name": "smoothness.tough_canvas_cases/tough_canvas_cases/rendering_throughput/canvas_arcs.html" }, { - "duration": "21.0", + "duration": "22.0", "name": "smoothness.tough_canvas_cases/tough_canvas_cases/rendering_throughput/canvas_lines.html" }, { @@ -3860,23 +3860,23 @@ "name": "smoothness.tough_canvas_cases/tough_canvas_cases/rendering_throughput/put_get_image_data.html" }, { - "duration": "25.0", + "duration": "26.0", "name": "smoothness.tough_canvas_cases/tough_canvas_cases/rendering_throughput/stroke_shapes.html" }, { - "duration": "26.0", + "duration": "27.0", "name": "smoothness.tough_filters_cases/Analog_Clock_SVG" }, { - "duration": "26.0", + "duration": "27.0", "name": "smoothness.tough_filters_cases/Filter_Terrain_SVG" }, { - "duration": "31.0", + "duration": "32.0", "name": "smoothness.tough_filters_cases/IE_PirateMark" }, { - "duration": "42.0", + "duration": "43.0", "name": "smoothness.tough_filters_cases/MotionMark_Focus" }, { @@ -3884,11 +3884,11 @@ "name": "smoothness.tough_image_decode_cases/http://localhost:9000/cats-unscaled.html" }, { - "duration": "15.0", + "duration": "16.0", "name": "smoothness.tough_image_decode_cases/http://localhost:9000/cats-viewport-width.html" }, { - "duration": "44.0", + "duration": "43.0", "name": "smoothness.tough_path_rendering_cases/GUIMark_Vector_Chart_Test" }, { @@ -3900,19 +3900,19 @@ "name": "smoothness.tough_path_rendering_cases/MotionMark_Canvas_Fill_Shapes" }, { - "duration": "28.0", + "duration": "27.0", "name": "smoothness.tough_path_rendering_cases/MotionMark_Canvas_Stroke_Shapes" }, { - "duration": "30.0", + "duration": "31.0", "name": "smoothness.tough_pinch_zoom_cases/Blogger" }, { - "duration": "37.0", + "duration": "38.0", "name": "smoothness.tough_pinch_zoom_cases/ESPN" }, { - "duration": "27.0", + "duration": "28.0", "name": "smoothness.tough_pinch_zoom_cases/Facebook" }, { @@ -3920,39 +3920,39 @@ "name": "smoothness.tough_pinch_zoom_cases/LinkedIn" }, { - "duration": "27.0", + "duration": "28.0", "name": "smoothness.tough_pinch_zoom_cases/Twitter" }, { - "duration": "33.0", + "duration": "35.0", "name": "smoothness.tough_pinch_zoom_cases/Weather.com" }, { - "duration": "26.0", + "duration": "27.0", "name": "smoothness.tough_pinch_zoom_cases/http://booking.com" }, { - "duration": "33.0", + "duration": "37.0", "name": "smoothness.tough_pinch_zoom_cases/http://games.yahoo.com" }, { - "duration": "32.0", + "duration": "35.0", "name": "smoothness.tough_pinch_zoom_cases/http://news.yahoo.com" }, { - "duration": "37.0", + "duration": "41.0", "name": "smoothness.tough_pinch_zoom_cases/http://sports.yahoo.com/" }, { - "duration": "22.0", + "duration": "23.0", "name": "smoothness.tough_pinch_zoom_cases/http://www.amazon.com" }, { - "duration": "33.0", + "duration": "35.0", "name": "smoothness.tough_pinch_zoom_cases/http://www.cnn.com" }, { - "duration": "32.0", + "duration": "33.0", "name": "smoothness.tough_pinch_zoom_cases/http://www.ebay.com" }, { @@ -3960,15 +3960,15 @@ "name": "smoothness.tough_pinch_zoom_cases/http://www.youtube.com" }, { - "duration": "45.0", + "duration": "47.0", "name": "smoothness.tough_pinch_zoom_cases/https://mail.google.com/mail/" }, { - "duration": "41.0", + "duration": "42.0", "name": "smoothness.tough_pinch_zoom_cases/https://www.google.com/#hl=en&q=barack+obama" }, { - "duration": "26.0", + "duration": "27.0", "name": "smoothness.tough_pinch_zoom_cases/https://www.google.com/calendar/" }, { @@ -3976,7 +3976,7 @@ "name": "smoothness.tough_pinch_zoom_cases/https://www.google.com/search?q=cats&tbm=isch" }, { - "duration": "26.0", + "duration": "27.0", "name": "smoothness.tough_scrolling_cases/canvas_05000_pixels_per_second" }, { @@ -3984,23 +3984,23 @@ "name": "smoothness.tough_scrolling_cases/canvas_10000_pixels_per_second" }, { - "duration": "26.0", + "duration": "27.0", "name": "smoothness.tough_scrolling_cases/canvas_15000_pixels_per_second" }, { - "duration": "26.0", + "duration": "27.0", "name": "smoothness.tough_scrolling_cases/canvas_20000_pixels_per_second" }, { - "duration": "26.0", + "duration": "27.0", "name": "smoothness.tough_scrolling_cases/canvas_30000_pixels_per_second" }, { - "duration": "26.0", + "duration": "27.0", "name": "smoothness.tough_scrolling_cases/canvas_40000_pixels_per_second" }, { - "duration": "25.0", + "duration": "26.0", "name": "smoothness.tough_scrolling_cases/canvas_50000_pixels_per_second" }, { @@ -4008,11 +4008,11 @@ "name": "smoothness.tough_scrolling_cases/canvas_60000_pixels_per_second" }, { - "duration": "22.0", + "duration": "23.0", "name": "smoothness.tough_scrolling_cases/canvas_75000_pixels_per_second" }, { - "duration": "21.0", + "duration": "22.0", "name": "smoothness.tough_scrolling_cases/canvas_90000_pixels_per_second" }, { @@ -4020,23 +4020,23 @@ "name": "smoothness.tough_scrolling_cases/text_05000_pixels_per_second" }, { - "duration": "24.0", + "duration": "25.0", "name": "smoothness.tough_scrolling_cases/text_10000_pixels_per_second" }, { - "duration": "24.0", + "duration": "25.0", "name": "smoothness.tough_scrolling_cases/text_15000_pixels_per_second" }, { - "duration": "24.0", + "duration": "25.0", "name": "smoothness.tough_scrolling_cases/text_20000_pixels_per_second" }, { - "duration": "24.0", + "duration": "25.0", "name": "smoothness.tough_scrolling_cases/text_30000_pixels_per_second" }, { - "duration": "24.0", + "duration": "25.0", "name": "smoothness.tough_scrolling_cases/text_40000_pixels_per_second" }, { @@ -4044,7 +4044,7 @@ "name": "smoothness.tough_scrolling_cases/text_50000_pixels_per_second" }, { - "duration": "22.0", + "duration": "23.0", "name": "smoothness.tough_scrolling_cases/text_60000_pixels_per_second" }, { @@ -4056,7 +4056,7 @@ "name": "smoothness.tough_scrolling_cases/text_90000_pixels_per_second" }, { - "duration": "27.0", + "duration": "28.0", "name": "smoothness.tough_scrolling_cases/text_constant_full_page_raster_05000_pixels_per_second" }, { @@ -4068,19 +4068,19 @@ "name": "smoothness.tough_scrolling_cases/text_constant_full_page_raster_15000_pixels_per_second" }, { - "duration": "27.0", + "duration": "28.0", "name": "smoothness.tough_scrolling_cases/text_constant_full_page_raster_20000_pixels_per_second" }, { - "duration": "27.0", + "duration": "28.0", "name": "smoothness.tough_scrolling_cases/text_constant_full_page_raster_30000_pixels_per_second" }, { - "duration": "27.0", + "duration": "28.0", "name": "smoothness.tough_scrolling_cases/text_constant_full_page_raster_40000_pixels_per_second" }, { - "duration": "27.0", + "duration": "28.0", "name": "smoothness.tough_scrolling_cases/text_constant_full_page_raster_50000_pixels_per_second" }, { @@ -4088,43 +4088,43 @@ "name": "smoothness.tough_scrolling_cases/text_constant_full_page_raster_60000_pixels_per_second" }, { - "duration": "24.0", + "duration": "25.0", "name": "smoothness.tough_scrolling_cases/text_constant_full_page_raster_75000_pixels_per_second" }, { - "duration": "23.0", + "duration": "24.0", "name": "smoothness.tough_scrolling_cases/text_constant_full_page_raster_90000_pixels_per_second" }, { - "duration": "23.0", + "duration": "24.0", "name": "smoothness.tough_scrolling_cases/text_hover_05000_pixels_per_second" }, { - "duration": "24.0", + "duration": "25.0", "name": "smoothness.tough_scrolling_cases/text_hover_10000_pixels_per_second" }, { - "duration": "24.0", + "duration": "25.0", "name": "smoothness.tough_scrolling_cases/text_hover_15000_pixels_per_second" }, { - "duration": "24.0", + "duration": "25.0", "name": "smoothness.tough_scrolling_cases/text_hover_20000_pixels_per_second" }, { - "duration": "24.0", + "duration": "25.0", "name": "smoothness.tough_scrolling_cases/text_hover_30000_pixels_per_second" }, { - "duration": "24.0", + "duration": "25.0", "name": "smoothness.tough_scrolling_cases/text_hover_40000_pixels_per_second" }, { - "duration": "24.0", + "duration": "25.0", "name": "smoothness.tough_scrolling_cases/text_hover_50000_pixels_per_second" }, { - "duration": "22.0", + "duration": "23.0", "name": "smoothness.tough_scrolling_cases/text_hover_60000_pixels_per_second" }, { @@ -4136,7 +4136,7 @@ "name": "smoothness.tough_scrolling_cases/text_hover_90000_pixels_per_second" }, { - "duration": "45.0", + "duration": "46.0", "name": "smoothness.tough_texture_upload_cases/background_color_animation.html" }, { @@ -4144,7 +4144,7 @@ "name": "smoothness.tough_texture_upload_cases/background_color_animation_with_gradient.html" }, { - "duration": "32.0", + "duration": "33.0", "name": "smoothness.tough_texture_upload_cases/extra_large_texture_uploads.html" }, { @@ -4152,15 +4152,15 @@ "name": "smoothness.tough_texture_upload_cases/large_texture_uploads.html" }, { - "duration": "27.0", + "duration": "28.0", "name": "smoothness.tough_texture_upload_cases/medium_texture_uploads.html" }, { - "duration": "28.0", + "duration": "29.0", "name": "smoothness.tough_texture_upload_cases/small_texture_uploads.html" }, { - "duration": "43.0", + "duration": "42.0", "name": "smoothness.tough_webgl_ad_cases/http://localhost:8000/CICAgICQ15a9NxDIARjIASgBMghBC1XuTk8ezw.swf.webglbeta.html" }, { @@ -4176,7 +4176,7 @@ "name": "smoothness.tough_webgl_ad_cases/http://localhost:8000/CICAgKCNj4HgyAEQeBjYBCgBMgjQpPkOjyWNdw.1.swf.webglbeta.html" }, { - "duration": "27.0", + "duration": "26.0", "name": "smoothness.tough_webgl_ad_cases/http://localhost:8000/CICAgMDOrcnRGRB4GNgEKAEyCP_ZBSfwUFsj.swf.webglbeta.html" }, { @@ -4188,7 +4188,7 @@ "name": "smoothness.tough_webgl_ad_cases/http://localhost:8000/clip-paths-CICAgMDO7Ye9-gEQ2AUYWigBMgjZxDii6aoK9w.swf.webglbeta.html" }, { - "duration": "27.0", + "duration": "26.0", "name": "smoothness.tough_webgl_ad_cases/http://localhost:8000/clip-paths-CILZhLqO_-27bxB4GNgEKAEyCC46kMLBXnMT.swf.webglbeta.html" }, { @@ -4200,7 +4200,7 @@ "name": "smoothness.tough_webgl_ad_cases/http://localhost:8000/shapes-CICAgMDO7cfIzwEQ1AMYPCgBMghqY8tqyRCArQ.swf.webglbeta.html" }, { - "duration": "27.0", + "duration": "26.0", "name": "smoothness.tough_webgl_ad_cases/http://localhost:8000/shapes-CK7ptO3F8bi2KxDQAhiYAigBMgij6QBQtD2gyA.swf.webglbeta.html" }, { @@ -4208,15 +4208,15 @@ "name": "start_with_url.cold.startup_pages/about:blank" }, { - "duration": "37.0", + "duration": "43.0", "name": "start_with_url.cold.startup_pages/http://bbc.co.uk" }, { - "duration": "35.0", + "duration": "36.0", "name": "start_with_url.warm.startup_pages/about:blank" }, { - "duration": "37.0", + "duration": "38.0", "name": "start_with_url.warm.startup_pages/http://bbc.co.uk" }, { @@ -4232,95 +4232,95 @@ "name": "system_health.common_mobile/background:social:facebook" }, { - "duration": "50.0", + "duration": "49.0", "name": "system_health.common_mobile/background:tools:gmail" }, { - "duration": "186.0", + "duration": "210.0", "name": "system_health.common_mobile/browse:chrome:newtab" }, { - "duration": "54.0", + "duration": "56.0", "name": "system_health.common_mobile/browse:chrome:omnibox" }, { - "duration": "131.0", + "duration": "128.0", "name": "system_health.common_mobile/browse:media:facebook_photos" }, { - "duration": "72.0", + "duration": "70.0", "name": "system_health.common_mobile/browse:media:flickr_infinite_scroll" }, { - "duration": "110.0", + "duration": "108.0", "name": "system_health.common_mobile/browse:media:imgur" }, { - "duration": "163.0", + "duration": "182.0", "name": "system_health.common_mobile/browse:media:youtube" }, { - "duration": "268.0", + "duration": "259.0", "name": "system_health.common_mobile/browse:news:cnn" }, { - "duration": "95.0", + "duration": "90.0", "name": "system_health.common_mobile/browse:news:cricbuzz" }, { - "duration": "79.0", + "duration": "78.0", "name": "system_health.common_mobile/browse:news:qq" }, { - "duration": "91.0", + "duration": "88.0", "name": "system_health.common_mobile/browse:news:reddit" }, { - "duration": "80.0", + "duration": "78.0", "name": "system_health.common_mobile/browse:news:washingtonpost" }, { - "duration": "121.0", + "duration": "117.0", "name": "system_health.common_mobile/browse:shopping:amazon" }, { - "duration": "120.0", + "duration": "117.0", "name": "system_health.common_mobile/browse:shopping:avito" }, { - "duration": "59.0", + "duration": "58.0", "name": "system_health.common_mobile/browse:shopping:lazada" }, { - "duration": "103.0", + "duration": "122.0", "name": "system_health.common_mobile/browse:social:facebook" }, { - "duration": "165.0", + "duration": "159.0", "name": "system_health.common_mobile/browse:social:facebook_infinite_scroll" }, { - "duration": "126.0", + "duration": "121.0", "name": "system_health.common_mobile/browse:social:instagram" }, { - "duration": "132.0", + "duration": "128.0", "name": "system_health.common_mobile/browse:social:pinterest_infinite_scroll" }, { - "duration": "165.0", + "duration": "160.0", "name": "system_health.common_mobile/browse:social:tumblr_infinite_scroll" }, { - "duration": "79.0", + "duration": "78.0", "name": "system_health.common_mobile/browse:social:twitter" }, { - "duration": "105.0", + "duration": "99.0", "name": "system_health.common_mobile/browse:tech:discourse_infinite_scroll" }, { - "duration": "71.0", + "duration": "69.0", "name": "system_health.common_mobile/browse:tools:maps" }, { @@ -4340,7 +4340,7 @@ "name": "system_health.common_mobile/load:media:dailymotion" }, { - "duration": "32.0", + "duration": "33.0", "name": "system_health.common_mobile/load:media:facebook_photos" }, { @@ -4360,11 +4360,11 @@ "name": "system_health.common_mobile/load:media:youtube" }, { - "duration": "71.0", + "duration": "72.0", "name": "system_health.common_mobile/load:news:cnn" }, { - "duration": "48.0", + "duration": "47.0", "name": "system_health.common_mobile/load:news:irctc" }, { @@ -4396,7 +4396,7 @@ "name": "system_health.common_mobile/load:search:ebay" }, { - "duration": "29.0", + "duration": "30.0", "name": "system_health.common_mobile/load:search:google" }, { @@ -4404,7 +4404,7 @@ "name": "system_health.common_mobile/load:search:taobao" }, { - "duration": "29.0", + "duration": "30.0", "name": "system_health.common_mobile/load:search:yahoo" }, { @@ -4416,15 +4416,15 @@ "name": "system_health.common_mobile/load:social:twitter" }, { - "duration": "30.0", + "duration": "31.0", "name": "system_health.common_mobile/load:tools:docs" }, { - "duration": "42.0", + "duration": "59.0", "name": "system_health.common_mobile/load:tools:drive" }, { - "duration": "33.0", + "duration": "29.0", "name": "system_health.common_mobile/load:tools:dropbox" }, { @@ -4440,7 +4440,7 @@ "name": "system_health.common_mobile/long_running:tools:gmail-background" }, { - "duration": "147.0", + "duration": "146.0", "name": "system_health.common_mobile/long_running:tools:gmail-foreground" }, { @@ -4448,19 +4448,19 @@ "name": "system_health.memory_mobile/background:media:imgur" }, { - "duration": "32.0", + "duration": "33.0", "name": "system_health.memory_mobile/background:search:google" }, { - "duration": "37.0", + "duration": "38.0", "name": "system_health.memory_mobile/background:social:facebook" }, { - "duration": "43.0", + "duration": "44.0", "name": "system_health.memory_mobile/background:tools:gmail" }, { - "duration": "174.0", + "duration": "182.0", "name": "system_health.memory_mobile/browse:chrome:newtab" }, { @@ -4480,15 +4480,15 @@ "name": "system_health.memory_mobile/browse:media:imgur" }, { - "duration": "102.0", + "duration": "108.0", "name": "system_health.memory_mobile/browse:media:youtube" }, { - "duration": "190.0", + "duration": "187.0", "name": "system_health.memory_mobile/browse:news:cnn" }, { - "duration": "69.0", + "duration": "70.0", "name": "system_health.memory_mobile/browse:news:cricbuzz" }, { @@ -4496,11 +4496,11 @@ "name": "system_health.memory_mobile/browse:news:qq" }, { - "duration": "73.0", + "duration": "82.0", "name": "system_health.memory_mobile/browse:news:reddit" }, { - "duration": "59.0", + "duration": "60.0", "name": "system_health.memory_mobile/browse:news:washingtonpost" }, { @@ -4508,7 +4508,7 @@ "name": "system_health.memory_mobile/browse:shopping:amazon" }, { - "duration": "86.0", + "duration": "95.0", "name": "system_health.memory_mobile/browse:shopping:avito" }, { @@ -4520,11 +4520,11 @@ "name": "system_health.memory_mobile/browse:social:facebook" }, { - "duration": "91.0", + "duration": "97.0", "name": "system_health.memory_mobile/browse:social:facebook_infinite_scroll" }, { - "duration": "90.0", + "duration": "98.0", "name": "system_health.memory_mobile/browse:social:instagram" }, { @@ -4536,11 +4536,11 @@ "name": "system_health.memory_mobile/browse:social:tumblr_infinite_scroll" }, { - "duration": "62.0", + "duration": "63.0", "name": "system_health.memory_mobile/browse:social:twitter" }, { - "duration": "75.0", + "duration": "83.0", "name": "system_health.memory_mobile/browse:tech:discourse_infinite_scroll" }, { @@ -4560,19 +4560,19 @@ "name": "system_health.memory_mobile/load:games:lazors" }, { - "duration": "38.0", + "duration": "39.0", "name": "system_health.memory_mobile/load:games:spychase" }, { - "duration": "37.0", + "duration": "38.0", "name": "system_health.memory_mobile/load:media:dailymotion" }, { - "duration": "32.0", + "duration": "33.0", "name": "system_health.memory_mobile/load:media:facebook_photos" }, { - "duration": "32.0", + "duration": "33.0", "name": "system_health.memory_mobile/load:media:google_images" }, { @@ -4580,15 +4580,15 @@ "name": "system_health.memory_mobile/load:media:imgur" }, { - "duration": "31.0", + "duration": "40.0", "name": "system_health.memory_mobile/load:media:soundcloud" }, { - "duration": "32.0", + "duration": "41.0", "name": "system_health.memory_mobile/load:media:youtube" }, { - "duration": "61.0", + "duration": "62.0", "name": "system_health.memory_mobile/load:news:cnn" }, { @@ -4604,11 +4604,11 @@ "name": "system_health.memory_mobile/load:news:qq" }, { - "duration": "34.0", + "duration": "35.0", "name": "system_health.memory_mobile/load:news:reddit" }, { - "duration": "34.0", + "duration": "35.0", "name": "system_health.memory_mobile/load:news:washingtonpost" }, { @@ -4616,15 +4616,15 @@ "name": "system_health.memory_mobile/load:news:wikipedia" }, { - "duration": "32.0", + "duration": "33.0", "name": "system_health.memory_mobile/load:search:baidu" }, { - "duration": "31.0", + "duration": "40.0", "name": "system_health.memory_mobile/load:search:ebay" }, { - "duration": "30.0", + "duration": "31.0", "name": "system_health.memory_mobile/load:search:google" }, { @@ -4632,7 +4632,7 @@ "name": "system_health.memory_mobile/load:search:taobao" }, { - "duration": "30.0", + "duration": "31.0", "name": "system_health.memory_mobile/load:search:yahoo" }, { @@ -4640,7 +4640,7 @@ "name": "system_health.memory_mobile/load:search:yandex" }, { - "duration": "31.0", + "duration": "32.0", "name": "system_health.memory_mobile/load:social:twitter" }, { @@ -4648,11 +4648,11 @@ "name": "system_health.memory_mobile/load:tools:docs" }, { - "duration": "36.0", + "duration": "34.0", "name": "system_health.memory_mobile/load:tools:drive" }, { - "duration": "32.0", + "duration": "30.0", "name": "system_health.memory_mobile/load:tools:dropbox" }, { @@ -4664,15 +4664,15 @@ "name": "system_health.memory_mobile/load:tools:weather" }, { - "duration": "287.0", + "duration": "290.0", "name": "system_health.memory_mobile/long_running:tools:gmail-foreground" }, { - "duration": "44.0", + "duration": "45.0", "name": "thread_times.key_idle_power_cases/animated-gif.html" }, { - "duration": "52.0", + "duration": "57.0", "name": "thread_times.key_idle_power_cases/blank.html" }, { @@ -4680,7 +4680,7 @@ "name": "thread_times.key_idle_power_cases/css-animation.html" }, { - "duration": "47.0", + "duration": "46.0", "name": "thread_times.key_idle_power_cases/request-animation-frame.html" }, { @@ -4692,19 +4692,19 @@ "name": "thread_times.key_idle_power_cases/set-timeout.html (Long Idle)" }, { - "duration": "48.0", + "duration": "47.0", "name": "thread_times.key_mobile_sites_smooth/boingboing" }, { - "duration": "46.0", + "duration": "45.0", "name": "thread_times.key_mobile_sites_smooth/cuteoverload" }, { - "duration": "45.0", + "duration": "51.0", "name": "thread_times.key_mobile_sites_smooth/nytimes" }, { - "duration": "35.0", + "duration": "34.0", "name": "thread_times.key_mobile_sites_smooth/reddit" }, { @@ -4728,23 +4728,23 @@ "name": "thread_times.key_noop_cases/no_op_touch_handler.html" }, { - "duration": "22.0", + "duration": "23.0", "name": "thread_times.key_silk_cases/font_wipe.html" }, { - "duration": "48.0", + "duration": "51.0", "name": "thread_times.key_silk_cases/http://groupcloned.com/test/plain/list-recycle-transform.html" }, { - "duration": "21.0", + "duration": "22.0", "name": "thread_times.key_silk_cases/http://groupcloned.com/test/plain/sticky-using-webkit-backface-visibility.html" }, { - "duration": "20.0", + "duration": "21.0", "name": "thread_times.key_silk_cases/http://jsbin.com/UVIgUTa/38/quiet" }, { - "duration": "26.0", + "duration": "28.0", "name": "thread_times.key_silk_cases/http://jsfiddle.net/3yDKh/15/show/" }, { @@ -4752,47 +4752,47 @@ "name": "thread_times.key_silk_cases/http://jsfiddle.net/3yDKh/16/show/" }, { - "duration": "26.0", + "duration": "28.0", "name": "thread_times.key_silk_cases/http://jsfiddle.net/R8DX9/4/show/" }, { - "duration": "26.0", + "duration": "28.0", "name": "thread_times.key_silk_cases/http://jsfiddle.net/TLXLu/3/show/" }, { - "duration": "26.0", + "duration": "28.0", "name": "thread_times.key_silk_cases/http://jsfiddle.net/bNp2h/3/show/" }, { - "duration": "29.0", + "duration": "32.0", "name": "thread_times.key_silk_cases/http://jsfiddle.net/cKB9D/7/show/" }, { - "duration": "29.0", + "duration": "31.0", "name": "thread_times.key_silk_cases/http://jsfiddle.net/jx5De/14/show/" }, { - "duration": "28.0", + "duration": "31.0", "name": "thread_times.key_silk_cases/http://jsfiddle.net/rF9Gh/7/show/" }, { - "duration": "26.0", + "duration": "27.0", "name": "thread_times.key_silk_cases/http://jsfiddle.net/ugkd4/10/show/" }, { - "duration": "31.0", + "duration": "33.0", "name": "thread_times.key_silk_cases/http://jsfiddle.net/vBQHH/11/show/" }, { - "duration": "25.0", + "duration": "26.0", "name": "thread_times.key_silk_cases/http://jsfiddle.net/xLuvC/1/show/" }, { - "duration": "26.0", + "duration": "27.0", "name": "thread_times.key_silk_cases/http://mobile-news.sandbox.google.com/news/pt0?scroll" }, { - "duration": "21.0", + "duration": "22.0", "name": "thread_times.key_silk_cases/http://mobile-news.sandbox.google.com/news/pt0?swipe" }, { @@ -4804,7 +4804,7 @@ "name": "thread_times.key_silk_cases/http://s.codepen.io/befamous/fullpage/pFsqb?scroll" }, { - "duration": "34.0", + "duration": "35.0", "name": "thread_times.key_silk_cases/http://wiltzius.github.io/shape-shifter/" }, { @@ -4816,7 +4816,7 @@ "name": "thread_times.key_silk_cases/https://polymer-topeka.appspot.com/" }, { - "duration": "23.0", + "duration": "24.0", "name": "thread_times.key_silk_cases/https://www.google.com/search?hl=en&q=define%3Aboogie" }, { @@ -4824,7 +4824,7 @@ "name": "thread_times.key_silk_cases/inbox_app.html?slide_drawer" }, { - "duration": "44.0", + "duration": "46.0", "name": "thread_times.key_silk_cases/inbox_app.html?stress_hidey_bars" }, { @@ -4832,15 +4832,15 @@ "name": "thread_times.key_silk_cases/inbox_app.html?swipe_to_dismiss" }, { - "duration": "28.0", + "duration": "29.0", "name": "thread_times.key_silk_cases/inbox_app.html?toggle_drawer" }, { - "duration": "33.0", + "duration": "34.0", "name": "thread_times.key_silk_cases/infinite_scrolling.html" }, { - "duration": "22.0", + "duration": "23.0", "name": "thread_times.key_silk_cases/list_animation_simple.html" }, { @@ -4848,43 +4848,43 @@ "name": "thread_times.key_silk_cases/masonry.html" }, { - "duration": "29.0", + "duration": "32.0", "name": "thread_times.key_silk_cases/pushState.html" }, { - "duration": "41.0", + "duration": "45.0", "name": "thread_times.key_silk_cases/silk_finance.html" }, { - "duration": "38.0", + "duration": "37.0", "name": "thread_times.simple_mobile_sites/http://m.nytimes.com/" }, { - "duration": "63.0", + "duration": "64.0", "name": "thread_times.simple_mobile_sites/http://www.ebay.co.uk/" }, { - "duration": "43.0", + "duration": "42.0", "name": "thread_times.simple_mobile_sites/http://www.nyc.gov" }, { - "duration": "14.0", + "duration": "40.0", "name": "thread_times.simple_mobile_sites/https://www.flickr.com/" }, { - "duration": "51.0", + "duration": "50.0", "name": "thread_times.tough_compositor_cases/http://jsbin.com/beqojupo/1/quiet?JS_FULL_SCREEN_INVALIDATION" }, { - "duration": "41.0", + "duration": "40.0", "name": "thread_times.tough_compositor_cases/http://jsbin.com/covoqi/1/quiet?NEW_TILINGS" }, { - "duration": "36.0", + "duration": "35.0", "name": "thread_times.tough_compositor_cases/http://jsbin.com/falefice/1/quiet?CC_POSTER_CIRCLE" }, { - "duration": "39.0", + "duration": "38.0", "name": "thread_times.tough_compositor_cases/http://jsbin.com/giqafofe/1/quiet?JS_POSTER_CIRCLE" }, { @@ -4892,11 +4892,11 @@ "name": "thread_times.tough_compositor_cases/http://jsbin.com/jevibahi/4/quiet?JS_SCROLL_200_LAYER_GRID" }, { - "duration": "49.0", + "duration": "54.0", "name": "thread_times.tough_compositor_cases/http://jsbin.com/pixavefe/1/quiet?CC_SCROLL_TEXT_ONLY" }, { - "duration": "34.0", + "duration": "33.0", "name": "thread_times.tough_compositor_cases/http://jsbin.com/wixadinu/2/quiet?JS_SCROLL_TEXT_ONLY" }, { @@ -4904,15 +4904,15 @@ "name": "thread_times.tough_compositor_cases/http://jsbin.com/yakagevo/1/quiet?CC_SCROLL_200_LAYER_GRID" }, { - "duration": "34.0", + "duration": "33.0", "name": "thread_times.tough_scrolling_cases/canvas_05000_pixels_per_second" }, { - "duration": "34.0", + "duration": "33.0", "name": "thread_times.tough_scrolling_cases/canvas_10000_pixels_per_second" }, { - "duration": "34.0", + "duration": "33.0", "name": "thread_times.tough_scrolling_cases/canvas_15000_pixels_per_second" }, { @@ -4920,11 +4920,11 @@ "name": "thread_times.tough_scrolling_cases/canvas_20000_pixels_per_second" }, { - "duration": "35.0", + "duration": "34.0", "name": "thread_times.tough_scrolling_cases/canvas_30000_pixels_per_second" }, { - "duration": "35.0", + "duration": "34.0", "name": "thread_times.tough_scrolling_cases/canvas_40000_pixels_per_second" }, { @@ -4940,7 +4940,7 @@ "name": "thread_times.tough_scrolling_cases/canvas_75000_pixels_per_second" }, { - "duration": "25.0", + "duration": "24.0", "name": "thread_times.tough_scrolling_cases/canvas_90000_pixels_per_second" }, { @@ -4952,7 +4952,7 @@ "name": "thread_times.tough_scrolling_cases/text_10000_pixels_per_second" }, { - "duration": "30.0", + "duration": "29.0", "name": "thread_times.tough_scrolling_cases/text_15000_pixels_per_second" }, { @@ -4960,31 +4960,31 @@ "name": "thread_times.tough_scrolling_cases/text_20000_pixels_per_second" }, { - "duration": "31.0", + "duration": "30.0", "name": "thread_times.tough_scrolling_cases/text_30000_pixels_per_second" }, { - "duration": "31.0", + "duration": "30.0", "name": "thread_times.tough_scrolling_cases/text_40000_pixels_per_second" }, { - "duration": "30.0", + "duration": "29.0", "name": "thread_times.tough_scrolling_cases/text_50000_pixels_per_second" }, { - "duration": "27.0", + "duration": "26.0", "name": "thread_times.tough_scrolling_cases/text_60000_pixels_per_second" }, { - "duration": "25.0", + "duration": "24.0", "name": "thread_times.tough_scrolling_cases/text_75000_pixels_per_second" }, { - "duration": "23.0", + "duration": "22.0", "name": "thread_times.tough_scrolling_cases/text_90000_pixels_per_second" }, { - "duration": "33.0", + "duration": "32.0", "name": "thread_times.tough_scrolling_cases/text_constant_full_page_raster_05000_pixels_per_second" }, { @@ -4992,15 +4992,15 @@ "name": "thread_times.tough_scrolling_cases/text_constant_full_page_raster_10000_pixels_per_second" }, { - "duration": "33.0", + "duration": "32.0", "name": "thread_times.tough_scrolling_cases/text_constant_full_page_raster_15000_pixels_per_second" }, { - "duration": "33.0", + "duration": "32.0", "name": "thread_times.tough_scrolling_cases/text_constant_full_page_raster_20000_pixels_per_second" }, { - "duration": "33.0", + "duration": "32.0", "name": "thread_times.tough_scrolling_cases/text_constant_full_page_raster_30000_pixels_per_second" }, { @@ -5008,7 +5008,7 @@ "name": "thread_times.tough_scrolling_cases/text_constant_full_page_raster_40000_pixels_per_second" }, { - "duration": "32.0", + "duration": "31.0", "name": "thread_times.tough_scrolling_cases/text_constant_full_page_raster_50000_pixels_per_second" }, { @@ -5016,7 +5016,7 @@ "name": "thread_times.tough_scrolling_cases/text_constant_full_page_raster_60000_pixels_per_second" }, { - "duration": "28.0", + "duration": "27.0", "name": "thread_times.tough_scrolling_cases/text_constant_full_page_raster_75000_pixels_per_second" }, { @@ -5032,7 +5032,7 @@ "name": "thread_times.tough_scrolling_cases/text_hover_10000_pixels_per_second" }, { - "duration": "30.0", + "duration": "29.0", "name": "thread_times.tough_scrolling_cases/text_hover_15000_pixels_per_second" }, { @@ -5040,11 +5040,11 @@ "name": "thread_times.tough_scrolling_cases/text_hover_20000_pixels_per_second" }, { - "duration": "31.0", + "duration": "29.0", "name": "thread_times.tough_scrolling_cases/text_hover_30000_pixels_per_second" }, { - "duration": "31.0", + "duration": "30.0", "name": "thread_times.tough_scrolling_cases/text_hover_40000_pixels_per_second" }, { @@ -5064,71 +5064,71 @@ "name": "thread_times.tough_scrolling_cases/text_hover_90000_pixels_per_second" }, { - "duration": "20.0", + "duration": "19.0", "name": "tracing.tracing_with_background_memory_infra/Facebook" }, { - "duration": "23.0", + "duration": "22.0", "name": "tracing.tracing_with_background_memory_infra/Wikipedia" }, { - "duration": "17.0", + "duration": "15.0", "name": "tracing.tracing_with_background_memory_infra/http://www.amazon.com" }, { - "duration": "17.0", + "duration": "16.0", "name": "tracing.tracing_with_background_memory_infra/http://www.ask.com/" }, { - "duration": "17.0", + "duration": "16.0", "name": "tracing.tracing_with_background_memory_infra/http://www.bing.com/" }, { - "duration": "18.0", + "duration": "17.0", "name": "tracing.tracing_with_background_memory_infra/http://www.yahoo.com/" }, { - "duration": "19.0", + "duration": "18.0", "name": "tracing.tracing_with_background_memory_infra/http://www.youtube.com" }, { - "duration": "34.0", + "duration": "33.0", "name": "tracing.tracing_with_background_memory_infra/https://www.google.com/#hl=en&q=barack+obama" }, { - "duration": "21.0", + "duration": "20.0", "name": "tracing.tracing_with_background_memory_infra/https://www.google.com/calendar/" }, { - "duration": "206.0", + "duration": "207.0", "name": "v8.browsing_mobile-future/browse:chrome:newtab" }, { - "duration": "60.0", + "duration": "59.0", "name": "v8.browsing_mobile-future/browse:chrome:omnibox" }, { - "duration": "160.0", + "duration": "158.0", "name": "v8.browsing_mobile-future/browse:media:facebook_photos" }, { - "duration": "88.0", + "duration": "85.0", "name": "v8.browsing_mobile-future/browse:media:flickr_infinite_scroll" }, { - "duration": "135.0", + "duration": "133.0", "name": "v8.browsing_mobile-future/browse:media:imgur" }, { - "duration": "208.0", + "duration": "205.0", "name": "v8.browsing_mobile-future/browse:media:youtube" }, { - "duration": "388.0", + "duration": "381.0", "name": "v8.browsing_mobile-future/browse:news:cnn" }, { - "duration": "111.0", + "duration": "109.0", "name": "v8.browsing_mobile-future/browse:news:cricbuzz" }, { @@ -5136,11 +5136,11 @@ "name": "v8.browsing_mobile-future/browse:news:globo" }, { - "duration": "95.0", + "duration": "93.0", "name": "v8.browsing_mobile-future/browse:news:qq" }, { - "duration": "113.0", + "duration": "111.0", "name": "v8.browsing_mobile-future/browse:news:reddit" }, { @@ -5148,15 +5148,15 @@ "name": "v8.browsing_mobile-future/browse:news:toi" }, { - "duration": "96.0", + "duration": "93.0", "name": "v8.browsing_mobile-future/browse:news:washingtonpost" }, { - "duration": "176.0", + "duration": "180.0", "name": "v8.browsing_mobile-future/browse:shopping:amazon" }, { - "duration": "182.0", + "duration": "175.0", "name": "v8.browsing_mobile-future/browse:shopping:avito" }, { @@ -5164,39 +5164,39 @@ "name": "v8.browsing_mobile-future/browse:shopping:flipkart" }, { - "duration": "75.0", + "duration": "74.0", "name": "v8.browsing_mobile-future/browse:shopping:lazada" }, { - "duration": "140.0", + "duration": "138.0", "name": "v8.browsing_mobile-future/browse:social:facebook" }, { - "duration": "212.0", + "duration": "203.0", "name": "v8.browsing_mobile-future/browse:social:facebook_infinite_scroll" }, { - "duration": "189.0", + "duration": "184.0", "name": "v8.browsing_mobile-future/browse:social:instagram" }, { - "duration": "178.0", + "duration": "176.0", "name": "v8.browsing_mobile-future/browse:social:pinterest_infinite_scroll" }, { - "duration": "210.0", + "duration": "207.0", "name": "v8.browsing_mobile-future/browse:social:tumblr_infinite_scroll" }, { - "duration": "90.0", + "duration": "88.0", "name": "v8.browsing_mobile-future/browse:social:twitter" }, { - "duration": "138.0", + "duration": "136.0", "name": "v8.browsing_mobile-future/browse:tech:discourse_infinite_scroll" }, { - "duration": "90.0", + "duration": "89.0", "name": "v8.browsing_mobile-future/browse:tools:maps" }, { @@ -5204,31 +5204,31 @@ "name": "v8.browsing_mobile/browse:chrome:newtab" }, { - "duration": "59.0", + "duration": "57.0", "name": "v8.browsing_mobile/browse:chrome:omnibox" }, { - "duration": "161.0", + "duration": "155.0", "name": "v8.browsing_mobile/browse:media:facebook_photos" }, { - "duration": "87.0", + "duration": "85.0", "name": "v8.browsing_mobile/browse:media:flickr_infinite_scroll" }, { - "duration": "135.0", + "duration": "134.0", "name": "v8.browsing_mobile/browse:media:imgur" }, { - "duration": "210.0", + "duration": "206.0", "name": "v8.browsing_mobile/browse:media:youtube" }, { - "duration": "391.0", + "duration": "375.0", "name": "v8.browsing_mobile/browse:news:cnn" }, { - "duration": "111.0", + "duration": "108.0", "name": "v8.browsing_mobile/browse:news:cricbuzz" }, { @@ -5236,11 +5236,11 @@ "name": "v8.browsing_mobile/browse:news:globo" }, { - "duration": "95.0", + "duration": "93.0", "name": "v8.browsing_mobile/browse:news:qq" }, { - "duration": "113.0", + "duration": "109.0", "name": "v8.browsing_mobile/browse:news:reddit" }, { @@ -5248,15 +5248,15 @@ "name": "v8.browsing_mobile/browse:news:toi" }, { - "duration": "97.0", + "duration": "94.0", "name": "v8.browsing_mobile/browse:news:washingtonpost" }, { - "duration": "176.0", + "duration": "175.0", "name": "v8.browsing_mobile/browse:shopping:amazon" }, { - "duration": "182.0", + "duration": "171.0", "name": "v8.browsing_mobile/browse:shopping:avito" }, { @@ -5264,39 +5264,39 @@ "name": "v8.browsing_mobile/browse:shopping:flipkart" }, { - "duration": "74.0", + "duration": "73.0", "name": "v8.browsing_mobile/browse:shopping:lazada" }, { - "duration": "140.0", + "duration": "135.0", "name": "v8.browsing_mobile/browse:social:facebook" }, { - "duration": "214.0", + "duration": "206.0", "name": "v8.browsing_mobile/browse:social:facebook_infinite_scroll" }, { - "duration": "189.0", + "duration": "184.0", "name": "v8.browsing_mobile/browse:social:instagram" }, { - "duration": "180.0", + "duration": "173.0", "name": "v8.browsing_mobile/browse:social:pinterest_infinite_scroll" }, { - "duration": "215.0", + "duration": "213.0", "name": "v8.browsing_mobile/browse:social:tumblr_infinite_scroll" }, { - "duration": "91.0", + "duration": "89.0", "name": "v8.browsing_mobile/browse:social:twitter" }, { - "duration": "139.0", + "duration": "134.0", "name": "v8.browsing_mobile/browse:tech:discourse_infinite_scroll" }, { - "duration": "90.0", + "duration": "89.0", "name": "v8.browsing_mobile/browse:tools:maps" } ] \ No newline at end of file
diff --git a/ui/android/java/src/org/chromium/ui/base/ActivityWindowAndroid.java b/ui/android/java/src/org/chromium/ui/base/ActivityWindowAndroid.java index 93375c5..76c47651b 100644 --- a/ui/android/java/src/org/chromium/ui/base/ActivityWindowAndroid.java +++ b/ui/android/java/src/org/chromium/ui/base/ActivityWindowAndroid.java
@@ -15,6 +15,7 @@ import org.chromium.base.ActivityState; import org.chromium.base.ApplicationStatus; import org.chromium.base.Callback; +import org.chromium.base.ContextUtils; import org.chromium.ui.UiUtils; import java.lang.ref.WeakReference; @@ -204,7 +205,7 @@ private void storeCallbackData(int requestCode, IntentCallback callback, Integer errorId) { mOutstandingIntents.put(requestCode, callback); - mIntentErrors.put( - requestCode, errorId == null ? null : mApplicationContext.getString(errorId)); + mIntentErrors.put(requestCode, + errorId == null ? null : ContextUtils.getApplicationContext().getString(errorId)); } }
diff --git a/ui/android/java/src/org/chromium/ui/base/WindowAndroid.java b/ui/android/java/src/org/chromium/ui/base/WindowAndroid.java index 3c7fe8c..eea4740 100644 --- a/ui/android/java/src/org/chromium/ui/base/WindowAndroid.java +++ b/ui/android/java/src/org/chromium/ui/base/WindowAndroid.java
@@ -85,7 +85,6 @@ // Error code returned when an Intent fails to start an Activity. public static final int START_INTENT_FAILURE = -1; - protected Context mApplicationContext; protected SparseArray<IntentCallback> mOutstandingIntents; // We use a weak reference here to prevent this from leaking in WebView. private WeakReference<Context> mContextRef; @@ -226,7 +225,6 @@ */ @SuppressLint("UseSparseArrays") protected WindowAndroid(Context context, DisplayAndroid display) { - mApplicationContext = context.getApplicationContext(); // context does not have the same lifetime guarantees as an application context so we can't // hold a strong reference to it. mContextRef = new WeakReference<>(context); @@ -235,8 +233,9 @@ // Temporary solution for flaky tests, see https://crbug.com/767624 for context try (StrictModeContext unused = StrictModeContext.allowDiskReads()) { mVSyncMonitor = new VSyncMonitor(context, mVSyncListener); - mAccessibilityManager = (AccessibilityManager) mApplicationContext.getSystemService( - Context.ACCESSIBILITY_SERVICE); + mAccessibilityManager = + (AccessibilityManager) ContextUtils.getApplicationContext().getSystemService( + Context.ACCESSIBILITY_SERVICE); } mDisplayAndroid = display; // Configuration.isDisplayServerWideColorGamut must be queried from the window's context. @@ -374,8 +373,8 @@ public final boolean hasPermission(String permission) { if (mPermissionDelegate != null) return mPermissionDelegate.hasPermission(permission); - return ApiCompatibilityUtils.checkPermission( - mApplicationContext, permission, Process.myPid(), Process.myUid()) + return ApiCompatibilityUtils.checkPermission(ContextUtils.getApplicationContext(), + permission, Process.myPid(), Process.myUid()) == PackageManager.PERMISSION_GRANTED; } @@ -456,7 +455,7 @@ */ public void showError(String error) { if (error != null) { - Toast.makeText(mApplicationContext, error, Toast.LENGTH_SHORT).show(); + Toast.makeText(ContextUtils.getApplicationContext(), error, Toast.LENGTH_SHORT).show(); } } @@ -465,7 +464,7 @@ * @param resId The error message string's resource id. */ public void showError(int resId) { - showError(mApplicationContext.getString(resId)); + showError(ContextUtils.getApplicationContext().getString(resId)); } /** @@ -480,7 +479,7 @@ * Broadcasts the given intent to all interested BroadcastReceivers. */ public void sendBroadcast(Intent intent) { - mApplicationContext.sendBroadcast(intent); + ContextUtils.getApplicationContext().sendBroadcast(intent); } /** @@ -503,7 +502,7 @@ * @return The application context for this activity. */ public Context getApplicationContext() { - return mApplicationContext; + return ContextUtils.getApplicationContext(); } /** @@ -621,7 +620,11 @@ * Context.startActivity will not throw ActivityNotFoundException. */ public boolean canResolveActivity(Intent intent) { - return mApplicationContext.getPackageManager().queryIntentActivities(intent, 0).size() > 0; + return ContextUtils.getApplicationContext() + .getPackageManager() + .queryIntentActivities(intent, 0) + .size() + > 0; } /**
diff --git a/ui/events/BUILD.gn b/ui/events/BUILD.gn index 681eaef..33603b8 100644 --- a/ui/events/BUILD.gn +++ b/ui/events/BUILD.gn
@@ -151,13 +151,6 @@ "Carbon.framework", ] } - - if (is_chromecast && !is_android) { - sources += [ - "chromecast/scroller.cc", - "chromecast/scroller.h", - ] - } } component("events") {
diff --git a/ui/events/chromecast/scroller.cc b/ui/events/chromecast/scroller.cc deleted file mode 100644 index d484529b..0000000 --- a/ui/events/chromecast/scroller.cc +++ /dev/null
@@ -1,481 +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. - -#include "ui/events/chromecast/scroller.h" - -#include <cmath> - -#include "base/lazy_instance.h" -#include "base/macros.h" - -namespace ui { -namespace { - -// Default scroll duration from android.widget.Scroller. -const int kDefaultDurationMs = 250; - -// Default friction constant in android.view.ViewConfiguration. -const float kDefaultFriction = 0.015f; - -// == std::log(0.78f) / std::log(0.9f) -const float kDecelerationRate = 2.3582018f; - -// Tension lines cross at (kInflexion, 1). -const float kInflexion = 0.35f; - -const float kEpsilon = 1e-5f; - -// Fling scroll is stopped when the scroll position is |kThresholdForFlingEnd| -// pixels or closer from the end. -const float kThresholdForFlingEnd = 0.1f; - -// Scale factor applied to incoming fling velocity. -const float kFlingVelocityAttenuationFactor = 1.0f; - -bool ApproxEquals(float a, float b) { - return std::abs(a - b) < kEpsilon; -} - -struct ViscosityConstants { - ViscosityConstants() - : viscous_fluid_scale_(8.f), viscous_fluid_normalize_(1.f) { - viscous_fluid_normalize_ = 1.0f / ApplyViscosity(1.0f); - } - - float ApplyViscosity(float x) { - x *= viscous_fluid_scale_; - if (x < 1.0f) { - x -= (1.0f - std::exp(-x)); - } else { - float start = 0.36787944117f; // 1/e == exp(-1) - x = 1.0f - std::exp(1.0f - x); - x = start + x * (1.0f - start); - } - x *= viscous_fluid_normalize_; - return x; - } - - private: - // This controls the intensity of the viscous fluid effect. - float viscous_fluid_scale_; - float viscous_fluid_normalize_; - - DISALLOW_COPY_AND_ASSIGN(ViscosityConstants); -}; - -struct SplineConstants { - SplineConstants() { - const float kStartTension = 0.5f; - const float kEndTension = 1.0f; - const float kP1 = kStartTension * kInflexion; - const float kP2 = 1.0f - kEndTension * (1.0f - kInflexion); - - float x_min = 0.0f; - float y_min = 0.0f; - for (int i = 0; i < NUM_SAMPLES; i++) { - const float alpha = static_cast<float>(i) / NUM_SAMPLES; - - float x_max = 1.0f; - float x, tx, coef; - while (true) { - x = x_min + (x_max - x_min) / 2.0f; - coef = 3.0f * x * (1.0f - x); - tx = coef * ((1.0f - x) * kP1 + x * kP2) + x * x * x; - if (ApproxEquals(tx, alpha)) - break; - if (tx > alpha) - x_max = x; - else - x_min = x; - } - spline_position_[i] = coef * ((1.0f - x) * kStartTension + x) + x * x * x; - - float y_max = 1.0f; - float y, dy; - while (true) { - y = y_min + (y_max - y_min) / 2.0f; - coef = 3.0f * y * (1.0f - y); - dy = coef * ((1.0f - y) * kStartTension + y) + y * y * y; - if (ApproxEquals(dy, alpha)) - break; - if (dy > alpha) - y_max = y; - else - y_min = y; - } - spline_time_[i] = coef * ((1.0f - y) * kP1 + y * kP2) + y * y * y; - } - spline_position_[NUM_SAMPLES] = spline_time_[NUM_SAMPLES] = 1.0f; - } - - void CalculateCoefficients(float t, - float* distance_coef, - float* velocity_coef) { - *distance_coef = 1.f; - *velocity_coef = 0.f; - const int index = static_cast<int>(NUM_SAMPLES * t); - if (index < NUM_SAMPLES) { - const float t_inf = static_cast<float>(index) / NUM_SAMPLES; - const float t_sup = static_cast<float>(index + 1) / NUM_SAMPLES; - const float d_inf = spline_position_[index]; - const float d_sup = spline_position_[index + 1]; - *velocity_coef = (d_sup - d_inf) / (t_sup - t_inf); - *distance_coef = d_inf + (t - t_inf) * *velocity_coef; - } - } - - private: - enum { NUM_SAMPLES = 100 }; - - float spline_position_[NUM_SAMPLES + 1]; - float spline_time_[NUM_SAMPLES + 1]; - - DISALLOW_COPY_AND_ASSIGN(SplineConstants); -}; - -float ComputeDeceleration(float friction) { - const float kGravityEarth = 9.80665f; - return kGravityEarth // g (m/s^2) - * 39.37f // inch/meter - * 160.f // pixels/inch - * friction; -} - -template <typename T> -int Signum(T t) { - return (T(0) < t) - (t < T(0)); -} - -template <typename T> -T Clamped(T t, T a, T b) { - return t < a ? a : (t > b ? b : t); -} - -// Leaky to allow access from the impl thread. -base::LazyInstance<ViscosityConstants>::Leaky g_viscosity_constants = - LAZY_INSTANCE_INITIALIZER; - -base::LazyInstance<SplineConstants>::Leaky g_spline_constants = - LAZY_INSTANCE_INITIALIZER; - -} // namespace - -Scroller::Config::Config() - : fling_friction(kDefaultFriction), flywheel_enabled(false) { -} - -Scroller::Scroller(const Config& config) - : mode_(UNDEFINED), - start_x_(0), - start_y_(0), - final_x_(0), - final_y_(0), - min_x_(0), - max_x_(0), - min_y_(0), - max_y_(0), - curr_x_(0), - curr_y_(0), - duration_seconds_reciprocal_(1), - delta_x_(0), - delta_x_norm_(1), - delta_y_(0), - delta_y_norm_(1), - finished_(true), - flywheel_enabled_(config.flywheel_enabled), - velocity_(0), - curr_velocity_(0), - distance_(0), - fling_friction_(config.fling_friction), - deceleration_(ComputeDeceleration(fling_friction_)), - tuning_coeff_(ComputeDeceleration(0.9f)) { -} - -Scroller::~Scroller() { -} - -bool Scroller::ComputeScrollOffset(base::TimeTicks time, - gfx::Vector2dF* offset, - gfx::Vector2dF* velocity) { - DCHECK(offset); - DCHECK(velocity); - if (!ComputeScrollOffsetInternal(time)) { - *offset = gfx::Vector2dF(GetFinalX(), GetFinalY()); - *velocity = gfx::Vector2dF(); - return false; - } - - *offset = gfx::Vector2dF(GetCurrX(), GetCurrY()); - *velocity = gfx::Vector2dF(GetCurrVelocityX(), GetCurrVelocityY()); - return true; -} - -void Scroller::StartScroll(float start_x, - float start_y, - float dx, - float dy, - base::TimeTicks start_time) { - StartScroll(start_x, - start_y, - dx, - dy, - start_time, - base::TimeDelta::FromMilliseconds(kDefaultDurationMs)); -} - -void Scroller::StartScroll(float start_x, - float start_y, - float dx, - float dy, - base::TimeTicks start_time, - base::TimeDelta duration) { - DCHECK_GT(duration, base::TimeDelta()); - mode_ = SCROLL_MODE; - finished_ = false; - duration_ = duration; - duration_seconds_reciprocal_ = 1.0 / duration_.InSecondsF(); - start_time_ = start_time; - curr_x_ = start_x_ = start_x; - curr_y_ = start_y_ = start_y; - final_x_ = start_x + dx; - final_y_ = start_y + dy; - RecomputeDeltas(); - curr_time_ = start_time_; -} - -void Scroller::Fling(float start_x, - float start_y, - float velocity_x, - float velocity_y, - float min_x, - float max_x, - float min_y, - float max_y, - base::TimeTicks start_time) { - DCHECK(velocity_x || velocity_y); - - // Continue a scroll or fling in progress. - if (flywheel_enabled_ && !finished_) { - float old_velocity_x = GetCurrVelocityX(); - float old_velocity_y = GetCurrVelocityY(); - if (Signum(velocity_x) == Signum(old_velocity_x) && - Signum(velocity_y) == Signum(old_velocity_y)) { - velocity_x += old_velocity_x; - velocity_y += old_velocity_y; - } - } - - mode_ = FLING_MODE; - finished_ = false; - - float velocity = std::sqrt(velocity_x * velocity_x + velocity_y * velocity_y); - velocity *= kFlingVelocityAttenuationFactor; - - velocity_ = velocity; - duration_ = GetSplineFlingDuration(velocity); - DCHECK_GT(duration_, base::TimeDelta()); - duration_seconds_reciprocal_ = 1.0 / duration_.InSecondsF(); - start_time_ = start_time; - curr_time_ = start_time_; - curr_x_ = start_x_ = start_x; - curr_y_ = start_y_ = start_y; - - float coeff_x = velocity == 0 ? 1.0f : velocity_x / velocity; - float coeff_y = velocity == 0 ? 1.0f : velocity_y / velocity; - - double total_distance = GetSplineFlingDistance(velocity); - distance_ = total_distance * Signum(velocity); - - min_x_ = min_x; - max_x_ = max_x; - min_y_ = min_y; - max_y_ = max_y; - - final_x_ = start_x + total_distance * coeff_x; - final_x_ = Clamped(final_x_, min_x_, max_x_); - - final_y_ = start_y + total_distance * coeff_y; - final_y_ = Clamped(final_y_, min_y_, max_y_); - - RecomputeDeltas(); -} - -void Scroller::ExtendDuration(base::TimeDelta extend) { - base::TimeDelta passed = GetTimePassed(); - duration_ = passed + extend; - duration_seconds_reciprocal_ = 1.0 / duration_.InSecondsF(); - finished_ = false; -} - -void Scroller::SetFinalX(float new_x) { - final_x_ = new_x; - finished_ = false; - RecomputeDeltas(); -} - -void Scroller::SetFinalY(float new_y) { - final_y_ = new_y; - finished_ = false; - RecomputeDeltas(); -} - -void Scroller::AbortAnimation() { - curr_x_ = final_x_; - curr_y_ = final_y_; - curr_velocity_ = 0; - curr_time_ = start_time_ + duration_; - finished_ = true; -} - -void Scroller::ForceFinished(bool finished) { - finished_ = finished; -} - -bool Scroller::IsFinished() const { - return finished_; -} - -base::TimeDelta Scroller::GetTimePassed() const { - return curr_time_ - start_time_; -} - -base::TimeDelta Scroller::GetDuration() const { - return duration_; -} - -float Scroller::GetCurrX() const { - return curr_x_; -} - -float Scroller::GetCurrY() const { - return curr_y_; -} - -float Scroller::GetCurrVelocity() const { - if (finished_) - return 0; - if (mode_ == FLING_MODE) - return curr_velocity_; - return velocity_ - deceleration_ * GetTimePassed().InSecondsF() * 0.5f; -} - -float Scroller::GetCurrVelocityX() const { - return delta_x_norm_ * GetCurrVelocity(); -} - -float Scroller::GetCurrVelocityY() const { - return delta_y_norm_ * GetCurrVelocity(); -} - -float Scroller::GetStartX() const { - return start_x_; -} - -float Scroller::GetStartY() const { - return start_y_; -} - -float Scroller::GetFinalX() const { - return final_x_; -} - -float Scroller::GetFinalY() const { - return final_y_; -} - -bool Scroller::IsScrollingInDirection(float xvel, float yvel) const { - return !finished_ && Signum(xvel) == Signum(delta_x_) && - Signum(yvel) == Signum(delta_y_); -} - -bool Scroller::ComputeScrollOffsetInternal(base::TimeTicks time) { - if (finished_) - return false; - - if (time <= start_time_) - return true; - - if (time == curr_time_) - return true; - - base::TimeDelta time_passed = time - start_time_; - if (time_passed >= duration_) { - AbortAnimation(); - return false; - } - - curr_time_ = time; - - const float u = time_passed.InSecondsF() * duration_seconds_reciprocal_; - switch (mode_) { - case UNDEFINED: - NOTREACHED() << "|StartScroll()| or |Fling()| must be called prior to " - "scroll offset computation."; - return false; - - case SCROLL_MODE: { - float x = g_viscosity_constants.Get().ApplyViscosity(u); - - curr_x_ = start_x_ + x * delta_x_; - curr_y_ = start_y_ + x * delta_y_; - } break; - - case FLING_MODE: { - float distance_coef = 1.f; - float velocity_coef = 0.f; - g_spline_constants.Get().CalculateCoefficients( - u, &distance_coef, &velocity_coef); - - curr_velocity_ = velocity_coef * distance_ * duration_seconds_reciprocal_; - - curr_x_ = start_x_ + distance_coef * delta_x_; - curr_x_ = Clamped(curr_x_, min_x_, max_x_); - - curr_y_ = start_y_ + distance_coef * delta_y_; - curr_y_ = Clamped(curr_y_, min_y_, max_y_); - - float diff_x = std::abs(curr_x_ - final_x_); - float diff_y = std::abs(curr_y_ - final_y_); - if (diff_x < kThresholdForFlingEnd && diff_y < kThresholdForFlingEnd) - AbortAnimation(); - } break; - } - - return !finished_; -} - -void Scroller::RecomputeDeltas() { - delta_x_ = final_x_ - start_x_; - delta_y_ = final_y_ - start_y_; - - const float hyp = std::sqrt(delta_x_ * delta_x_ + delta_y_ * delta_y_); - if (hyp > kEpsilon) { - delta_x_norm_ = delta_x_ / hyp; - delta_y_norm_ = delta_y_ / hyp; - } else { - delta_x_norm_ = delta_y_norm_ = 1; - } -} - -double Scroller::GetSplineDeceleration(float velocity) const { - return std::log(kInflexion * std::abs(velocity) / - (fling_friction_ * tuning_coeff_)); -} - -base::TimeDelta Scroller::GetSplineFlingDuration(float velocity) const { - const double l = GetSplineDeceleration(velocity); - const double decel_minus_one = kDecelerationRate - 1.0; - const double time_seconds = std::exp(l / decel_minus_one); - return base::TimeDelta::FromMicroseconds(time_seconds * - base::Time::kMicrosecondsPerSecond); -} - -double Scroller::GetSplineFlingDistance(float velocity) const { - const double l = GetSplineDeceleration(velocity); - const double decel_minus_one = kDecelerationRate - 1.0; - return fling_friction_ * tuning_coeff_ * - std::exp(kDecelerationRate / decel_minus_one * l); -} - -} // namespace ui
diff --git a/ui/events/chromecast/scroller.h b/ui/events/chromecast/scroller.h deleted file mode 100644 index f4ef836..0000000 --- a/ui/events/chromecast/scroller.h +++ /dev/null
@@ -1,151 +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. - -#ifndef UI_EVENTS_CHROMECAST_SCROLLER_H_ -#define UI_EVENTS_CHROMECAST_SCROLLER_H_ - -#include "base/time/time.h" -#include "ui/events/events_base_export.h" -#include "ui/events/gesture_curve.h" -#include "ui/gfx/geometry/vector2d_f.h" - -namespace ui { - -// Native port of android.widget.Scroller. -// * Change-Id: I4365946f890a76fcfa78ca9d69f2a8e0848095a9 -// * Please update the Change-Id as upstream Android changes are pulled. -class EVENTS_BASE_EXPORT Scroller : public GestureCurve { - public: - struct Config { - Config(); - - // Controls fling deceleration. Defaults to 0.015f. - float fling_friction; - - // Controls fling accumulation. Defaults to disabled. - bool flywheel_enabled; - }; - - explicit Scroller(const Config& config); - ~Scroller() override; - - // GestureCurve implementation. - bool ComputeScrollOffset(base::TimeTicks time, - gfx::Vector2dF* offset, - gfx::Vector2dF* velocity) override; - - // Start scrolling by providing a starting point and the distance to travel. - // The default value of 250 milliseconds will be used for the duration. - void StartScroll(float start_x, - float start_y, - float dx, - float dy, - base::TimeTicks start_time); - - // Start scrolling by providing a starting point, the distance to travel, - // and the duration of the scroll. - void StartScroll(float start_x, - float start_y, - float dx, - float dy, - base::TimeTicks start_time, - base::TimeDelta duration); - - // Start scrolling based on a fling gesture. The distance travelled will - // depend on the initial velocity of the fling. - void Fling(float start_x, - float start_y, - float velocity_x, - float velocity_y, - float min_x, - float max_x, - float min_y, - float max_y, - base::TimeTicks start_time); - - // Extend the scroll animation by |extend|. This allows a running animation - // to scroll further and longer when used with |SetFinalX()| or |SetFinalY()|. - void ExtendDuration(base::TimeDelta extend); - void SetFinalX(float new_x); - void SetFinalY(float new_y); - - // Stops the animation. Contrary to |ForceFinished()|, aborting the animation - // causes the scroller to move to the final x and y position. - void AbortAnimation(); - - // Terminate the scroll without affecting the current x and y positions. - void ForceFinished(bool finished); - - // Returns whether the scroller has finished scrolling. - bool IsFinished() const; - - // Returns the time elapsed since the beginning of the scrolling. - base::TimeDelta GetTimePassed() const; - - // Returns how long the scroll event will take. - base::TimeDelta GetDuration() const; - - float GetStartX() const; - float GetStartY() const; - float GetCurrX() const; - float GetCurrY() const; - float GetCurrVelocity() const; - float GetCurrVelocityX() const; - float GetCurrVelocityY() const; - float GetFinalX() const; - float GetFinalY() const; - - bool IsScrollingInDirection(float xvel, float yvel) const; - - private: - enum Mode { - UNDEFINED, - SCROLL_MODE, - FLING_MODE, - }; - - bool ComputeScrollOffsetInternal(base::TimeTicks time); - void RecomputeDeltas(); - - double GetSplineDeceleration(float velocity) const; - base::TimeDelta GetSplineFlingDuration(float velocity) const; - double GetSplineFlingDistance(float velocity) const; - - Mode mode_; - - float start_x_; - float start_y_; - float final_x_; - float final_y_; - - float min_x_; - float max_x_; - float min_y_; - float max_y_; - - float curr_x_; - float curr_y_; - base::TimeTicks start_time_; - base::TimeTicks curr_time_; - base::TimeDelta duration_; - double duration_seconds_reciprocal_; - float delta_x_; - float delta_x_norm_; - float delta_y_; - float delta_y_norm_; - bool finished_; - bool flywheel_enabled_; - - float velocity_; - float curr_velocity_; - float distance_; - - float fling_friction_; - float deceleration_; - float tuning_coeff_; -}; - -} // namespace ui - -#endif // UI_EVENTS_CHROMECAST_SCROLLER_H_
diff --git a/ui/events/chromecast/scroller_unittest.cc b/ui/events/chromecast/scroller_unittest.cc index 1303f7f8..d955f318 100644 --- a/ui/events/chromecast/scroller_unittest.cc +++ b/ui/events/chromecast/scroller_unittest.cc
@@ -6,7 +6,7 @@ #include "base/time/time.h" #include "testing/gtest/include/gtest/gtest.h" -#include "ui/events/chromecast/scroller.h" +#include "ui/events/mobile_scroller.h" namespace ui { namespace { @@ -19,8 +19,8 @@ const float kDefaultVelocityY = 220.f; const float kEpsilon = 1e-3f; -Scroller::Config DefaultConfig() { - return Scroller::Config(); +MobileScroller::Config DefaultConfig() { + return MobileScroller::Config(); } } // namespace @@ -28,7 +28,7 @@ class ScrollerTest : public testing::Test {}; TEST_F(ScrollerTest, Scroll) { - Scroller scroller(DefaultConfig()); + MobileScroller scroller(DefaultConfig()); base::TimeTicks start_time = base::TimeTicks::Now(); // Start a scroll and verify initialized values. @@ -99,7 +99,7 @@ } TEST_F(ScrollerTest, Fling) { - Scroller scroller(DefaultConfig()); + MobileScroller scroller(DefaultConfig()); base::TimeTicks start_time = base::TimeTicks::Now(); // Start a fling and verify initialized values.
diff --git a/ui/events/gestures/blink/BUILD.gn b/ui/events/gestures/blink/BUILD.gn index 5ba80cc..bac40dbc 100644 --- a/ui/events/gestures/blink/BUILD.gn +++ b/ui/events/gestures/blink/BUILD.gn
@@ -2,12 +2,18 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +import("//build/config/chromecast_build.gni") + source_set("blink") { sources = [ "web_gesture_curve_impl.cc", "web_gesture_curve_impl.h", ] + if (is_chromecast && !is_android) { + defines = [ "USE_MOBILE_FLING_CURVE" ] + } + deps = [ "//base", "//third_party/blink/public:blink_headers",
diff --git a/ui/events/gestures/blink/web_gesture_curve_impl.cc b/ui/events/gestures/blink/web_gesture_curve_impl.cc index 0ea17b68..dcabb069 100644 --- a/ui/events/gestures/blink/web_gesture_curve_impl.cc +++ b/ui/events/gestures/blink/web_gesture_curve_impl.cc
@@ -19,10 +19,6 @@ #include "ui/gfx/geometry/vector2d.h" #include "ui/gfx/geometry/vector2d_f.h" -#if !defined(OS_ANDROID) && defined(CHROMECAST_BUILD) -#include "ui/events/chromecast/scroller.h" -#endif - using blink::WebGestureCurve; namespace ui { @@ -37,21 +33,22 @@ base::TimeTicks()); } -#if !defined(OS_ANDROID) && defined(CHROMECAST_BUILD) - auto scroller = std::make_unique<Scroller>(Scroller::Config()); - scroller->Fling(0, 0, initial_velocity.x(), initial_velocity.y(), INT_MIN, - INT_MAX, INT_MIN, INT_MAX, base::TimeTicks()); - return std::move(scroller); -#else +#ifdef USE_MOBILE_FLING_CURVE + use_mobile_fling_curve = true; +#endif + if (use_mobile_fling_curve) { - auto scroller = std::make_unique<MobileScroller>(MobileScroller::Config()); + MobileScroller::Config config; +#ifdef USE_MOBILE_FLING_CURVE + config.chromecast_optimized = true; +#endif + auto scroller = std::make_unique<MobileScroller>(config); scroller->Fling(0, 0, initial_velocity.x(), initial_velocity.y(), INT_MIN, INT_MAX, INT_MIN, INT_MAX, base::TimeTicks()); return std::move(scroller); } return std::make_unique<FlingCurve>(initial_velocity, base::TimeTicks()); -#endif } } // namespace
diff --git a/ui/events/mobile_scroller.cc b/ui/events/mobile_scroller.cc index 794bbdc..c6651516 100644 --- a/ui/events/mobile_scroller.cc +++ b/ui/events/mobile_scroller.cc
@@ -159,7 +159,9 @@ } // namespace MobileScroller::Config::Config() - : fling_friction(kDefaultFriction), flywheel_enabled(false) {} + : fling_friction(kDefaultFriction), + flywheel_enabled(false), + chromecast_optimized(false) {} MobileScroller::MobileScroller(const Config& config) : mode_(UNDEFINED), @@ -185,7 +187,8 @@ distance_(0), fling_friction_(config.fling_friction), deceleration_(ComputeDeceleration(fling_friction_)), - tuning_coeff_(ComputeDeceleration(0.84f)) {} + tuning_coeff_( + ComputeDeceleration(config.chromecast_optimized ? 0.9f : 0.84f)) {} MobileScroller::~MobileScroller() {}
diff --git a/ui/events/mobile_scroller.h b/ui/events/mobile_scroller.h index 9ebd6d7e..903cb667 100644 --- a/ui/events/mobile_scroller.h +++ b/ui/events/mobile_scroller.h
@@ -25,6 +25,10 @@ // Controls fling accumulation. Defaults to disabled. bool flywheel_enabled; + + // Controls whether to use chromecast optimized + // scrolling. Defaults to false, mimic normal Android scrolling. + bool chromecast_optimized; }; explicit MobileScroller(const Config& config);
diff --git a/ui/message_center/BUILD.gn b/ui/message_center/BUILD.gn index 1092359..ac31e4d 100644 --- a/ui/message_center/BUILD.gn +++ b/ui/message_center/BUILD.gn
@@ -124,6 +124,8 @@ "views/message_view.h", "views/message_view_factory.cc", "views/message_view_factory.h", + "views/notification_background_painter.cc", + "views/notification_background_painter.h", "views/notification_button.cc", "views/notification_button.h", "views/notification_control_buttons_view.cc",
diff --git a/ui/message_center/views/message_view.cc b/ui/message_center/views/message_view.cc index 8296950..82d8ea92 100644 --- a/ui/message_center/views/message_view.cc +++ b/ui/message_center/views/message_view.cc
@@ -18,6 +18,7 @@ #include "ui/message_center/message_center.h" #include "ui/message_center/public/cpp/features.h" #include "ui/message_center/public/cpp/message_center_constants.h" +#include "ui/message_center/views/notification_background_painter.h" #include "ui/message_center/views/notification_control_buttons_view.h" #include "ui/strings/grit/ui_strings.h" #include "ui/views/background.h" @@ -26,7 +27,6 @@ #include "ui/views/controls/image_view.h" #include "ui/views/controls/scroll_view.h" #include "ui/views/focus/focus_manager.h" -#include "ui/views/painter.h" #include "ui/views/widget/widget.h" namespace message_center { @@ -59,38 +59,6 @@ return base::FeatureList::IsEnabled(message_center::kNewStyleNotifications); } -class BackgroundPainter : public views::Painter { - public: - BackgroundPainter(int top_radius, int bottom_radius) - : top_radius_(SkIntToScalar(top_radius)), - bottom_radius_(SkIntToScalar(bottom_radius)) {} - - ~BackgroundPainter() override = default; - - // views::Painter - gfx::Size GetMinimumSize() const override { return gfx::Size(); } - - void Paint(gfx::Canvas* canvas, const gfx::Size& size) override { - SkPath path; - SkScalar radii[8] = {top_radius_, top_radius_, top_radius_, - top_radius_, bottom_radius_, bottom_radius_, - bottom_radius_, bottom_radius_}; - path.addRoundRect(gfx::RectToSkRect(gfx::Rect(size)), radii); - - cc::PaintFlags flags; - flags.setAntiAlias(true); - flags.setStyle(cc::PaintFlags::kFill_Style); - flags.setColor(kNotificationBackgroundColor); - canvas->DrawPath(path, flags); - } - - private: - const SkScalar top_radius_; - const SkScalar bottom_radius_; - - DISALLOW_COPY_AND_ASSIGN(BackgroundPainter); -}; - } // namespace // static @@ -190,7 +158,8 @@ void MessageView::UpdateCornerRadius(int top_radius, int bottom_radius) { background_view_->SetBackground(views::CreateBackgroundFromPainter( - std::make_unique<BackgroundPainter>(top_radius, bottom_radius))); + std::make_unique<NotificationBackgroundPainter>(top_radius, + bottom_radius))); SchedulePaint(); }
diff --git a/ui/message_center/views/notification_background_painter.cc b/ui/message_center/views/notification_background_painter.cc new file mode 100644 index 0000000..b488f19 --- /dev/null +++ b/ui/message_center/views/notification_background_painter.cc
@@ -0,0 +1,42 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/message_center/views/notification_background_painter.h" + +#include "third_party/skia/include/core/SkPath.h" +#include "ui/gfx/canvas.h" +#include "ui/gfx/geometry/rect.h" +#include "ui/gfx/geometry/size.h" +#include "ui/gfx/skia_util.h" +#include "ui/message_center/public/cpp/message_center_constants.h" + +namespace message_center { + +NotificationBackgroundPainter::NotificationBackgroundPainter(int top_radius, + int bottom_radius) + : top_radius_(SkIntToScalar(top_radius)), + bottom_radius_(SkIntToScalar(bottom_radius)) {} + +NotificationBackgroundPainter::~NotificationBackgroundPainter() = default; + +gfx::Size NotificationBackgroundPainter::GetMinimumSize() const { + return gfx::Size(); +} + +void NotificationBackgroundPainter::Paint(gfx::Canvas* canvas, + const gfx::Size& size) { + SkPath path; + SkScalar radii[8] = {top_radius_, top_radius_, top_radius_, + top_radius_, bottom_radius_, bottom_radius_, + bottom_radius_, bottom_radius_}; + path.addRoundRect(gfx::RectToSkRect(gfx::Rect(size)), radii); + + cc::PaintFlags flags; + flags.setAntiAlias(true); + flags.setStyle(cc::PaintFlags::kFill_Style); + flags.setColor(kNotificationBackgroundColor); + canvas->DrawPath(path, flags); +} + +} // namespace message_center
diff --git a/ui/message_center/views/notification_background_painter.h b/ui/message_center/views/notification_background_painter.h new file mode 100644 index 0000000..1ef6d27 --- /dev/null +++ b/ui/message_center/views/notification_background_painter.h
@@ -0,0 +1,36 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_MESSAGE_CENTER_VIEWS_NOTIFICATION_BACKGROUND_PAINTER_H_ +#define UI_MESSAGE_CENTER_VIEWS_NOTIFICATION_BACKGROUND_PAINTER_H_ + +#include "ui/message_center/message_center_export.h" +#include "ui/views/painter.h" + +namespace message_center { + +// Background Painter for notification. This is for notifications with rounded +// corners inside the unified message center. This draws the rectangle with +// rounded corners. +class MESSAGE_CENTER_EXPORT NotificationBackgroundPainter + : public views::Painter { + public: + NotificationBackgroundPainter(int top_radius, int bottom_radius); + ~NotificationBackgroundPainter() override; + + // views::Painter + gfx::Size GetMinimumSize() const override; + + void Paint(gfx::Canvas* canvas, const gfx::Size& size) override; + + private: + const SkScalar top_radius_; + const SkScalar bottom_radius_; + + DISALLOW_COPY_AND_ASSIGN(NotificationBackgroundPainter); +}; + +} // namespace message_center + +#endif // UI_MESSAGE_CENTER_VIEWS_NOTIFICATION_BACKGROUND_PAINTER_H_
diff --git a/ui/ozone/platform/drm/host/drm_device_connector.cc b/ui/ozone/platform/drm/host/drm_device_connector.cc index 44a87e4..02ef83ad 100644 --- a/ui/ozone/platform/drm/host/drm_device_connector.cc +++ b/ui/ozone/platform/drm/host/drm_device_connector.cc
@@ -58,8 +58,7 @@ } void DrmDeviceConnector::OnChannelDestroyed(int host_id) { - // TODO(rjkroege): Handle Viz restarting. - NOTIMPLEMENTED(); + host_drm_device_->OnGpuServiceLost(); } void DrmDeviceConnector::OnGpuServiceLaunched(
diff --git a/ui/ozone/platform/drm/host/host_drm_device.cc b/ui/ozone/platform/drm/host/host_drm_device.cc index 9911cbb2..e595e30 100644 --- a/ui/ozone/platform/drm/host/host_drm_device.cc +++ b/ui/ozone/platform/drm/host/host_drm_device.cc
@@ -421,4 +421,13 @@ drm_device_ptr_compositor_ = std::move(drm_device_ptr_compositor); } +void HostDrmDevice::OnGpuServiceLost() { + cursor_proxy_.reset(); + connected_ = false; + drm_device_ptr_.reset(); + // TODO(rjkroege): OnGpuThreadRetired is not currently used. + for (GpuThreadObserver& observer : gpu_thread_observers_) + observer.OnGpuThreadRetired(); +} + } // namespace ui
diff --git a/ui/ozone/platform/drm/host/host_drm_device.h b/ui/ozone/platform/drm/host/host_drm_device.h index 8a3f3578..eb7ddc5 100644 --- a/ui/ozone/platform/drm/host/host_drm_device.h +++ b/ui/ozone/platform/drm/host/host_drm_device.h
@@ -55,6 +55,9 @@ void OnGpuServiceLaunchedCompositor( ui::ozone::mojom::DrmDevicePtr drm_device_ptr_compositor); + // Invoked by DrmDeviceConnector on loss of GPU service. + void OnGpuServiceLost(); + // GpuThreadAdapter void AddGpuThreadObserver(GpuThreadObserver* observer) override; void RemoveGpuThreadObserver(GpuThreadObserver* observer) override; @@ -143,6 +146,7 @@ // When running under mus, this is the UI thread specific DrmDevice ptr for // use by the compositor. + // TODO(rjkroege): When mash is removed, this code can also be removed. ui::ozone::mojom::DrmDevicePtr drm_device_ptr_compositor_; DrmDisplayHostManager* display_manager_; // Not owned.
diff --git a/ui/views/controls/native/native_view_host.cc b/ui/views/controls/native/native_view_host.cc index f15b828..32019ba 100644 --- a/ui/views/controls/native/native_view_host.cc +++ b/ui/views/controls/native/native_view_host.cc
@@ -8,6 +8,7 @@ #include "ui/base/cursor/cursor.h" #include "ui/gfx/canvas.h" #include "ui/views/controls/native/native_view_host_wrapper.h" +#include "ui/views/painter.h" #include "ui/views/widget/widget.h" namespace views { @@ -49,7 +50,14 @@ } bool NativeViewHost::SetCornerRadius(int corner_radius) { - return native_wrapper_->SetCornerRadius(corner_radius); + return SetCustomMask(views::Painter::CreatePaintedLayer( + views::Painter::CreateSolidRoundRectPainter(SK_ColorBLACK, + corner_radius))); +} + +bool NativeViewHost::SetCustomMask(std::unique_ptr<ui::LayerOwner> mask) { + DCHECK(native_wrapper_); + return native_wrapper_->SetCustomMask(std::move(mask)); } void NativeViewHost::SetNativeViewSize(const gfx::Size& size) {
diff --git a/ui/views/controls/native/native_view_host.h b/ui/views/controls/native/native_view_host.h index c81cb74..5bd897cf 100644 --- a/ui/views/controls/native/native_view_host.h +++ b/ui/views/controls/native/native_view_host.h
@@ -49,9 +49,14 @@ // Sets the corner radius for clipping gfx::NativeView. Returns true on // success or false if the platform doesn't support the operation. - // NB: This does not interact nicely with fast_resize. + // This method calls SetCustomMask internally. bool SetCornerRadius(int corner_radius); + // Sets the custom layer mask for clipping gfx::NativeView. Returns true on + // success or false if the platform doesn't support the operation. + // NB: This does not interact nicely with fast_resize. + bool SetCustomMask(std::unique_ptr<ui::LayerOwner> mask); + // Sets the size for the NativeView that may or may not match the size of this // View when it is being captured. If the size does not match, scaling will // occur. Pass an empty size to revert to the default behavior, where the
diff --git a/ui/views/controls/native/native_view_host_aura.cc b/ui/views/controls/native/native_view_host_aura.cc index 78bfea1..6afc8fb 100644 --- a/ui/views/controls/native/native_view_host_aura.cc +++ b/ui/views/controls/native/native_view_host_aura.cc
@@ -149,16 +149,16 @@ } } -bool NativeViewHostAura::SetCornerRadius(int corner_radius) { +bool NativeViewHostAura::SetCustomMask(std::unique_ptr<ui::LayerOwner> mask) { #if defined(OS_WIN) // TODO(crbug/843250): On Aura, layer masks don't play with HiDPI. Fix this // and enable this on Windows. return false; #else - mask_ = views::Painter::CreatePaintedLayer( - views::Painter::CreateSolidRoundRectPainter(SK_ColorBLACK, - corner_radius)); - mask_->layer()->SetFillsBoundsOpaquely(false); + UninstallMask(); + mask_ = std::move(mask); + if (mask_) + mask_->layer()->SetFillsBoundsOpaquely(false); InstallMask(); return true; #endif @@ -312,4 +312,12 @@ } } +void NativeViewHostAura::UninstallMask() { + if (!host_->native_view() || !mask_) + return; + + host_->native_view()->layer()->SetMaskLayer(nullptr); + mask_.reset(); +} + } // namespace views
diff --git a/ui/views/controls/native/native_view_host_aura.h b/ui/views/controls/native/native_view_host_aura.h index 41a56cc6..0ea7896 100644 --- a/ui/views/controls/native/native_view_host_aura.h +++ b/ui/views/controls/native/native_view_host_aura.h
@@ -30,7 +30,7 @@ void NativeViewDetaching(bool destroyed) override; void AddedToWidget() override; void RemovedFromWidget() override; - bool SetCornerRadius(int corner_radius) override; + bool SetCustomMask(std::unique_ptr<ui::LayerOwner> mask) override; void InstallClip(int x, int y, int w, int h) override; bool HasInstalledClip() override; void UninstallClip() override; @@ -64,6 +64,9 @@ // Sets or updates the mask layer on the native view's layer. void InstallMask(); + // Unsets the mask layer on the native view's layer. + void UninstallMask(); + // Our associated NativeViewHost. NativeViewHost* host_;
diff --git a/ui/views/controls/native/native_view_host_mac.h b/ui/views/controls/native/native_view_host_mac.h index 5db28b4..69659c9 100644 --- a/ui/views/controls/native/native_view_host_mac.h +++ b/ui/views/controls/native/native_view_host_mac.h
@@ -10,6 +10,10 @@ #include "ui/views/controls/native/native_view_host_wrapper.h" #include "ui/views/views_export.h" +namespace ui { +class LayerOwner; +} + namespace views { class NativeViewHost; @@ -25,7 +29,7 @@ void NativeViewDetaching(bool destroyed) override; void AddedToWidget() override; void RemovedFromWidget() override; - bool SetCornerRadius(int corner_radius) override; + bool SetCustomMask(std::unique_ptr<ui::LayerOwner> mask) override; void InstallClip(int x, int y, int w, int h) override; bool HasInstalledClip() override; void UninstallClip() override;
diff --git a/ui/views/controls/native/native_view_host_mac.mm b/ui/views/controls/native/native_view_host_mac.mm index 0ca2c4a..16e16b35 100644 --- a/ui/views/controls/native/native_view_host_mac.mm +++ b/ui/views/controls/native/native_view_host_mac.mm
@@ -111,7 +111,7 @@ NativeViewDetaching(false); } -bool NativeViewHostMac::SetCornerRadius(int corner_radius) { +bool NativeViewHostMac::SetCustomMask(std::unique_ptr<ui::LayerOwner> mask) { NOTIMPLEMENTED(); return false; }
diff --git a/ui/views/controls/native/native_view_host_wrapper.h b/ui/views/controls/native/native_view_host_wrapper.h index b8c156f..513c35f 100644 --- a/ui/views/controls/native/native_view_host_wrapper.h +++ b/ui/views/controls/native/native_view_host_wrapper.h
@@ -8,6 +8,10 @@ #include "ui/gfx/native_widget_types.h" #include "ui/views/views_export.h" +namespace ui { +class LayerOwner; +} + namespace views { class NativeViewHost; @@ -38,9 +42,9 @@ // rooted at a valid Widget. virtual void RemovedFromWidget() = 0; - // Sets the corner radius for clipping gfx::NativeView. Returns true on + // Sets the custom mask for clipping gfx::NativeView. Returns true on // success or false if the platform doesn't support the operation. - virtual bool SetCornerRadius(int corner_radius) = 0; + virtual bool SetCustomMask(std::unique_ptr<ui::LayerOwner> mask) = 0; // Installs a clip on the gfx::NativeView. These values are in the coordinate // space of the Widget, so if this method is called from ShowWidget