| // 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_PAGE_LOAD_METRICS_BROWSER_PAGE_LOAD_METRICS_TEST_WAITER_H_ |
| #define COMPONENTS_PAGE_LOAD_METRICS_BROWSER_PAGE_LOAD_METRICS_TEST_WAITER_H_ |
| |
| #include <memory> |
| #include <unordered_set> |
| |
| #include "base/memory/weak_ptr.h" |
| #include "base/run_loop.h" |
| #include "base/time/time.h" |
| #include "components/page_load_metrics/browser/metrics_lifecycle_observer.h" |
| #include "components/page_load_metrics/browser/page_load_tracker.h" |
| #include "content/public/browser/render_frame_host.h" |
| #include "third_party/blink/public/common/use_counter/use_counter_feature_tracker.h" |
| #include "ui/gfx/geometry/size.h" |
| |
| namespace page_load_metrics { |
| |
| class WaiterMetricsObserver; |
| |
| class PageLoadMetricsTestWaiter : public MetricsLifecycleObserver { |
| public: |
| // A bitvector to express which timing fields to match on. |
| enum class TimingField : int { |
| kFirstPaint = 1 << 0, |
| kFirstContentfulPaint = 1 << 1, |
| kFirstMeaningfulPaint = 1 << 2, |
| // kDocumentWriteBlockReload is deprecated. |
| kDocumentWriteBlockReload = 1 << 3, |
| kLoadEvent = 1 << 4, |
| // kLoadTimingInfo waits for main frame timing info only. |
| kLoadTimingInfo = 1 << 5, |
| kLargestContentfulPaint = 1 << 6, |
| kFirstInputOrScroll = 1 << 7, |
| kFirstInputDelay = 1 << 8, |
| kFirstPaintAfterBackForwardCacheRestore = 1 << 9, |
| kFirstInputDelayAfterBackForwardCacheRestore = 1 << 10, |
| kLayoutShift = 1 << 11, |
| kRequestAnimationFrameAfterBackForwardCacheRestore = 1 << 12, |
| }; |
| using FrameTreeNodeId = |
| page_load_metrics::PageLoadMetricsObserver::FrameTreeNodeId; |
| |
| explicit PageLoadMetricsTestWaiter(content::WebContents* web_contents); |
| |
| ~PageLoadMetricsTestWaiter() override; |
| |
| // Add a page-level expectation. |
| void AddPageExpectation(TimingField field); |
| |
| // Add a subframe-level expectation. |
| void AddSubFrameExpectation(TimingField field); |
| |
| // Add a frame size expectation. Expects that at least one frame receives a |
| // size update of |size|. |
| void AddFrameSizeExpectation(const gfx::Size& size); |
| |
| // Add a main frame intersection expectation. Expects that a frame |
| // receives an intersection update with a main frame intersection |
| // of |rect|. Subsequent calls overwrite unmet expectations. |
| void AddMainFrameIntersectionExpectation(const gfx::Rect& rect); |
| |
| // Indicates that we expect at least one main frame intersection update, with |
| // any rect allowed. |
| // TODO(skobes): Unify this API with AddMainFrameIntersectionExpectation. |
| void SetMainFrameIntersectionExpectation(); |
| |
| // Add a single WebFeature expectation. |
| void AddWebFeatureExpectation(blink::mojom::WebFeature web_feature); |
| |
| // Add a single UseCounterFeature expectation. |
| void AddUseCounterFeatureExpectation(const blink::UseCounterFeature& feature); |
| |
| // Wait for the subframe to navigate at least once. |
| void AddSubframeNavigationExpectation(); |
| |
| // Wait for the subframe to load at least one byte. |
| void AddSubframeDataExpectation(); |
| |
| // Add a minimum completed resource expectation. |
| void AddMinimumCompleteResourcesExpectation( |
| int expected_minimum_complete_resources); |
| |
| // Add aggregate received resource bytes expectation. |
| void AddMinimumNetworkBytesExpectation(int expected_minimum_network_bytes); |
| |
| // Add aggregate time spent in cpu for page expectation. |
| void AddMinimumAggregateCpuTimeExpectation(base::TimeDelta minimum); |
| |
| // Inserts `routing_id` into `expected_.memory_update_frame_ids_`, the set of |
| // frame routing IDs expected to receive a memory measurement update. |
| void AddMemoryUpdateExpectation(content::GlobalRenderFrameHostId routing_id); |
| |
| // Adds all |blink::LoadingBehaviorFlag|s set in |behavior_flags| to the |
| // set of expected behaviors. |
| void AddLoadingBehaviorExpectation(int behavior_flags); |
| |
| // Whether the given TimingField was observed in the page. |
| bool DidObserveInPage(TimingField field) const; |
| |
| // Whether the given WebFeature was observed in the page. |
| bool DidObserveWebFeature(blink::mojom::WebFeature feature) const; |
| |
| // Waits for PageLoadMetrics events that match the fields set by the add |
| // expectation methods. All matching fields must be set to end this wait. |
| // All expectations are reset when the wait ends. |
| void Wait(); |
| |
| int64_t current_network_bytes() const { return current_network_bytes_; } |
| |
| int64_t current_network_body_bytes() const { |
| return current_network_body_bytes_; |
| } |
| |
| protected: |
| virtual bool ExpectationsSatisfied() const; |
| |
| // Intended to be overridden in tests to allow tests to wait on other resource |
| // conditions. |
| virtual void HandleResourceUpdate( |
| const page_load_metrics::mojom::ResourceDataUpdatePtr& resource) {} |
| |
| // Resets all expectations. |
| virtual void ResetExpectations(); |
| |
| private: |
| // Manages a bitset of TimingFields. |
| class TimingFieldBitSet { |
| public: |
| TimingFieldBitSet() {} |
| |
| // Returns whether this bitset has all bits unset. |
| bool Empty() const { return bitmask_ == 0; } |
| |
| // Returns whether this bitset has the given bit set. |
| bool IsSet(TimingField field) const { |
| return (bitmask_ & static_cast<int>(field)) != 0; |
| } |
| |
| // Sets the bit for the given |field|. |
| void Set(TimingField field) { bitmask_ |= static_cast<int>(field); } |
| |
| // Clears the bit for the given |field|. |
| void Clear(TimingField field) { bitmask_ &= ~static_cast<int>(field); } |
| |
| // Merges bits set in |other| into this bitset. |
| void Merge(const TimingFieldBitSet& other) { bitmask_ |= other.bitmask_; } |
| |
| // Clears all bits set in the |other| bitset. |
| void ClearMatching(const TimingFieldBitSet& other) { |
| bitmask_ &= ~other.bitmask_; |
| } |
| |
| // Returns whether all the bits in this bitset are set in |other|. |
| bool AreAllSetIn(const TimingFieldBitSet& other) const { |
| return !((bitmask_ & other.bitmask_) ^ bitmask_); |
| } |
| |
| private: |
| int bitmask_ = 0; |
| }; |
| |
| struct FrameSizeComparator { |
| bool operator()(const gfx::Size a, const gfx::Size b) const; |
| }; |
| |
| TimingFieldBitSet GetMatchedBits( |
| const page_load_metrics::mojom::PageLoadTiming& timing, |
| const page_load_metrics::mojom::FrameMetadata& metadata, |
| const PageRenderData* render_data); |
| |
| // Updates observed page fields when a timing update is received by the |
| // MetricsWebContentsObserver. Stops waiting if expectations are satsfied |
| // after update. |
| void OnTimingUpdated(content::RenderFrameHost* subframe_rfh, |
| const page_load_metrics::mojom::PageLoadTiming& timing); |
| |
| // Updates observed page fields when a timing update is received by the |
| // MetricsWebContentsObserver. Stops waiting if expectations are satsfied |
| // after update. |
| void OnCpuTimingUpdated(content::RenderFrameHost* subframe_rfh, |
| const page_load_metrics::mojom::CpuTiming& timing); |
| |
| // Updates observed page fields when a loading behavior (see |
| // |blink::LoadingBehaviorFlag|) is observed by MetricsWebContentsObserver. |
| // Stops waiting if expectations are satsfied after update. |
| void OnLoadingBehaviorObserved(int behavior_flags); |
| |
| // Updates observed page fields when a resource load is observed by |
| // MetricsWebContentsObserver. Stops waiting if expectations are satsfied |
| // after update. |
| void OnLoadedResource(const page_load_metrics::ExtraRequestCompleteInfo& |
| extra_request_complete_info); |
| |
| // Updates counters as updates are received from a resource load. Stops |
| // waiting if expectations are satisfied after update. |
| void OnResourceDataUseObserved( |
| content::RenderFrameHost* rfh, |
| const std::vector<page_load_metrics::mojom::ResourceDataUpdatePtr>& |
| resources); |
| |
| // Updates |observed_.web_features_| to record any new feature observed. |
| // Stops waiting if expectations are satisfied after update. |
| void OnFeaturesUsageObserved( |
| content::RenderFrameHost* rfh, |
| const std::vector<blink::UseCounterFeature>& features); |
| |
| void FrameSizeChanged(content::RenderFrameHost* render_frame_host, |
| const gfx::Size& frame_size); |
| |
| void OnFrameIntersectionUpdate( |
| content::RenderFrameHost* rfh, |
| const page_load_metrics::mojom::FrameIntersectionUpdate& |
| frame_intersection_update); |
| |
| void OnDidFinishSubFrameNavigation( |
| content::NavigationHandle* navigation_handle); |
| |
| // Called when V8 per-frame memory usage updates are available. |
| void OnV8MemoryChanged(const std::vector<MemoryUpdate>& memory_updates); |
| |
| void OnTrackerCreated(page_load_metrics::PageLoadTracker* tracker) override; |
| |
| void OnCommit(page_load_metrics::PageLoadTracker* tracker) override; |
| |
| void OnActivate(page_load_metrics::PageLoadTracker* tracker) override; |
| |
| // These methods check whether expectations are satisfied for specific fields |
| // inside the State object, by comparing them in expected_ and observed_. |
| bool CpuTimeExpectationsSatisfied() const; |
| bool LoadingBehaviorExpectationsSatisfied() const; |
| bool ResourceUseExpectationsSatisfied() const; |
| bool UseCounterExpectationsSatisfied() const; |
| bool SubframeNavigationExpectationsSatisfied() const; |
| bool SubframeDataExpectationsSatisfied() const; |
| bool MainFrameIntersectionExpectationsSatisfied() const; |
| bool MemoryUpdateExpectationsSatisfied() const; |
| |
| void AddObserver(page_load_metrics::PageLoadTracker* tracker); |
| |
| std::unique_ptr<base::RunLoop> run_loop_; |
| |
| // Holds information about events that can be expected or observed. Each call |
| // to Wait() compares expected_ to observed_, and resets both. |
| struct State { |
| State(); |
| ~State(); |
| |
| TimingFieldBitSet page_fields_; |
| TimingFieldBitSet subframe_fields_; |
| blink::UseCounterFeatureTracker feature_tracker_; |
| int loading_behavior_flags_ = 0; |
| bool subframe_navigation_ = false; |
| bool subframe_data_ = false; |
| std::set<gfx::Size, FrameSizeComparator> frame_sizes_; |
| bool did_set_main_frame_intersection_ = false; |
| std::vector<gfx::Rect> main_frame_intersections_; |
| std::unordered_set<content::GlobalRenderFrameHostId, |
| content::GlobalRenderFrameHostIdHasher> |
| memory_update_frame_ids_; |
| }; |
| State expected_; |
| State observed_; |
| |
| TimingFieldBitSet observed_page_fields_; |
| |
| int current_complete_resources_ = 0; |
| int64_t current_network_bytes_ = 0; |
| |
| // Network body bytes are only counted for complete resources. |
| int64_t current_network_body_bytes_ = 0; |
| int expected_minimum_complete_resources_ = 0; |
| int expected_minimum_network_bytes_ = 0; |
| |
| // Total time spent int the cpu aggregated across the frames on the page. |
| base::TimeDelta current_aggregate_cpu_time_; |
| base::TimeDelta expected_minimum_aggregate_cpu_time_; |
| |
| bool attach_on_tracker_creation_ = false; |
| bool did_add_observer_ = false; |
| |
| double last_main_frame_layout_shift_score_ = 0; |
| |
| base::WeakPtrFactory<PageLoadMetricsTestWaiter> weak_factory_{this}; |
| |
| friend class WaiterMetricsObserver; |
| }; |
| |
| } // namespace page_load_metrics |
| |
| #endif // COMPONENTS_PAGE_LOAD_METRICS_BROWSER_PAGE_LOAD_METRICS_TEST_WAITER_H_ |