chromium/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl_unittest.cc

// Copyright 2016 The Chromium Authors
// 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/platform/scheduler/main_thread/frame_scheduler_impl.h"

#include <map>
#include <memory>
#include <string>
#include <vector>

#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/functional/callback_helpers.h"
#include "base/location.h"
#include "base/memory/raw_ptr.h"
#include "base/metrics/field_trial_param_associator.h"
#include "base/metrics/field_trial_params.h"
#include "base/run_loop.h"
#include "base/task/common/task_annotator.h"
#include "base/task/sequence_manager/test/sequence_manager_for_test.h"
#include "base/task/single_thread_task_runner.h"
#include "base/task/thread_pool.h"
#include "base/test/bind.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_command_line.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/task_environment.h"
#include "base/time/time.h"
#include "base/unguessable_token.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/abseil-cpp/absl/types/variant.h"
#include "third_party/blink/public/common/features.h"
#include "third_party/blink/public/common/switches.h"
#include "third_party/blink/public/platform/scheduler/web_agent_group_scheduler.h"
#include "third_party/blink/public/platform/web_runtime_features.h"
#include "third_party/blink/renderer/platform/scheduler/common/features.h"
#include "third_party/blink/renderer/platform/scheduler/common/task_priority.h"
#include "third_party/blink/renderer/platform/scheduler/main_thread/frame_task_queue_controller.h"
#include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h"
#include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.h"
#include "third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.h"
#include "third_party/blink/renderer/platform/scheduler/main_thread/task_type_names.h"
#include "third_party/blink/renderer/platform/scheduler/public/frame_or_worker_scheduler.h"
#include "third_party/blink/renderer/platform/scheduler/public/web_scheduling_priority.h"
#include "third_party/blink/renderer/platform/scheduler/public/web_scheduling_queue_type.h"
#include "third_party/blink/renderer/platform/scheduler/public/web_scheduling_task_queue.h"
#include "third_party/blink/renderer/platform/scheduler/test/web_scheduling_test_helper.h"

TaskQueue;
UnorderedElementsAre;

namespace blink {
namespace scheduler {
// To avoid symbol collisions in jumbo builds.
namespace frame_scheduler_impl_unittest {

FeatureHandle;
PrioritisationType;
Return;

namespace {

constexpr base::TimeDelta kDefaultThrottledWakeUpInterval =;
constexpr base::TimeDelta kIntensiveThrottledWakeUpInterval =;
constexpr auto kShortDelay =;

// This is a wrapper around MainThreadSchedulerImpl::CreatePageScheduler, that
// returns the PageScheduler as a PageSchedulerImpl.
std::unique_ptr<PageSchedulerImpl> CreatePageScheduler(
    PageScheduler::Delegate* page_scheduler_delegate,
    MainThreadSchedulerImpl* scheduler,
    AgentGroupScheduler& agent_group_scheduler) {}

// This is a wrapper around PageSchedulerImpl::CreateFrameScheduler, that
// returns the FrameScheduler as a FrameSchedulerImpl.
std::unique_ptr<FrameSchedulerImpl> CreateFrameScheduler(
    PageSchedulerImpl* page_scheduler,
    FrameScheduler::Delegate* delegate,
    bool is_in_embedded_frame_tree,
    FrameScheduler::FrameType frame_type) {}

void RecordRunTime(std::vector<base::TimeTicks>* run_times) {}

class TestObject {};

}  // namespace

// All TaskTypes that can be passed to
// FrameSchedulerImpl::CreateQueueTraitsForTaskType().
constexpr TaskType kAllFrameTaskTypes[] =;

static_assert;

void AppendToVectorTestTask(Vector<String>* vector, String value) {}

class FrameSchedulerDelegateForTesting : public FrameScheduler::Delegate {};

MATCHER(BlockingDetailsHasCCNS, "Blocking details has CCNS.") {}

MATCHER_P(BlockingDetailsHasWebSocket,
          handle,
          "BlockingDetails has WebSocket.") {}

MATCHER(BlockingDetailsIsEmpty, "BlockingDetails is empty.") {}
class FrameSchedulerImplTest : public testing::Test {};

class FrameSchedulerImplStopInBackgroundDisabledTest
    : public FrameSchedulerImplTest,
      public ::testing::WithParamInterface<TaskType> {};

namespace {

class MockLifecycleObserver {};

void IncrementCounter(int* counter) {}

// Simulate running a task of a particular length by fast forwarding the task
// environment clock, which is used to determine the wall time of a task.
void RunTaskOfLength(base::test::TaskEnvironment* task_environment,
                     base::TimeDelta length) {}

class FrameSchedulerImplTestWithIntensiveWakeUpThrottlingBase
    : public FrameSchedulerImplTest {};

// Test param for FrameSchedulerImplTestWithIntensiveWakeUpThrottling
struct IntensiveWakeUpThrottlingTestParam {};

class FrameSchedulerImplTestWithIntensiveWakeUpThrottling
    : public FrameSchedulerImplTestWithIntensiveWakeUpThrottlingBase,
      public ::testing::WithParamInterface<IntensiveWakeUpThrottlingTestParam> {};

class FrameSchedulerImplTestWithIntensiveWakeUpThrottlingPolicyOverride
    : public FrameSchedulerImplTestWithIntensiveWakeUpThrottlingBase {};

}  // namespace

// Throttleable task queue is initialized lazily, so there're two scenarios:
// - Task queue created first and throttling decision made later;
// - Scheduler receives relevant signals to make a throttling decision but
//   applies one once task queue gets created.
// We test both (ExplicitInit/LazyInit) of them.

TEST_F(FrameSchedulerImplTest, PageVisible) {}

TEST_F(FrameSchedulerImplTest, PageHidden_ExplicitInit) {}

TEST_F(FrameSchedulerImplTest, PageHidden_LazyInit) {}

TEST_F(FrameSchedulerImplTest, PageHiddenThenVisible_ExplicitInit) {}

TEST_F(FrameSchedulerImplTest,
       FrameHiddenThenVisible_CrossOrigin_ExplicitInit) {}

TEST_F(FrameSchedulerImplTest, FrameHidden_CrossOrigin_LazyInit) {}

TEST_F(FrameSchedulerImplTest, FrameHidden_SameOrigin_ExplicitInit) {}

TEST_F(FrameSchedulerImplTest, FrameHidden_SameOrigin_LazyInit) {}

TEST_F(FrameSchedulerImplTest, FrameVisible_CrossOrigin_ExplicitInit) {}

TEST_F(FrameSchedulerImplTest, FrameVisible_CrossOrigin_LazyInit) {}

TEST_F(FrameSchedulerImplTest, PauseAndResume) {}

TEST_F(FrameSchedulerImplTest, PauseAndResumeForCooperativeScheduling) {}

namespace {

// A task that re-posts itself with a delay in order until it has run
// |num_remaining_tasks| times.
void RePostTask(scoped_refptr<base::SingleThreadTaskRunner> task_runner,
                base::TimeDelta delay,
                int* num_remaining_tasks) {}

}  // namespace

// Verify that tasks in a throttled task queue cause:
// - Before intensive wake up throttling kicks in: 1 wake up per second
// - After intensive wake up throttling kick in:
//    - Low nesting level: 1 wake up per second
//    - High nesting level: 1 wake up per minute
// Disable the kStopInBackground feature because it hides the effect of
// intensive wake up throttling.
// Flake test: crbug.com/1328967
TEST_P(FrameSchedulerImplStopInBackgroundDisabledTest,
       DISABLED_ThrottledTaskExecution) {}

INSTANTIATE_TEST_SUITE_P();

TEST_F(FrameSchedulerImplTest, FreezeForegroundOnlyTasks) {}

TEST_F(FrameSchedulerImplTest, PageFreezeAndUnfreeze) {}

// Similar to PageFreezeAndUnfreeze, but unfreezes task queues by making the
// page visible instead of by invoking SetPageFrozen(false).
TEST_F(FrameSchedulerImplTest, PageFreezeAndPageVisible) {}

TEST_F(FrameSchedulerImplTest, PagePostsCpuTasks) {}

TEST_F(FrameSchedulerImplTest, FramePostsCpuTasksThroughReloadRenavigate) {}

class FrameSchedulerImplTestWithUnfreezableLoading
    : public FrameSchedulerImplTest {};

TEST_F(FrameSchedulerImplTestWithUnfreezableLoading,
       LoadingTasksKeepRunningWhenFrozen) {}

// Tests if throttling observer callbacks work.
TEST_F(FrameSchedulerImplTest, LifecycleObserver) {}

TEST_F(FrameSchedulerImplTest, DefaultSchedulingLifecycleState) {}

TEST_F(FrameSchedulerImplTest, SubesourceLoadingPaused) {}

TEST_F(FrameSchedulerImplTest, LogIpcsPostedToFramesInBackForwardCache) {}

TEST_F(FrameSchedulerImplTest,
       LogIpcsFromMultipleThreadsPostedToFramesInBackForwardCache) {}

// TODO(farahcharab) Move priority testing to MainThreadTaskQueueTest after
// landing the change that moves priority computation to MainThreadTaskQueue.

TEST_F(FrameSchedulerImplTest, HighestPriorityInputBlockingTaskQueue) {}

TEST_F(FrameSchedulerImplTest, RenderBlockingRenderBlockingLoading) {}

TEST_F(FrameSchedulerImplTest, TaskTypeToTaskQueueMapping) {}

// Verify that kJavascriptTimer* are the only non-internal TaskType that can be
// throttled. This ensures that the Javascript timer throttling experiment only
// affects wake ups from Javascript timers https://crbug.com/1075553
TEST_F(FrameSchedulerImplTest, ThrottledTaskTypes) {}

class FrameSchedulerImplDatabaseAccessWithoutHighPriority
    : public FrameSchedulerImplTest {};

TEST_F(FrameSchedulerImplDatabaseAccessWithoutHighPriority, QueueTraits) {}

class FrameSchedulerImplDatabaseAccessWithHighPriority
    : public FrameSchedulerImplTest {};

TEST_F(FrameSchedulerImplDatabaseAccessWithHighPriority, QueueTraits) {}

TEST_F(FrameSchedulerImplDatabaseAccessWithHighPriority, RunOrder) {}

TEST_F(FrameSchedulerImplDatabaseAccessWithHighPriority,
       NormalPriorityInBackground) {}

TEST_F(FrameSchedulerImplTest, ContentCaptureHasIdleTaskQueue) {}

TEST_F(FrameSchedulerImplTest, ComputePriorityForDetachedFrame) {}

class FrameSchedulerImplLowPriorityAsyncScriptExecutionTest
    : public FrameSchedulerImplTest,
      public testing::WithParamInterface<std::string> {};

INSTANTIATE_TEST_SUITE_P();

TEST_P(FrameSchedulerImplLowPriorityAsyncScriptExecutionTest,
       LowPriorityScriptExecutionHasBestEffortPriority) {}

TEST_F(FrameSchedulerImplTest, BackForwardCacheOptOut) {}

TEST_F(FrameSchedulerImplTest, BackForwardCacheOptOut_FrameNavigated) {}

TEST_F(FrameSchedulerImplTest, FeatureUpload) {}

TEST_F(FrameSchedulerImplTest, FeatureUpload_FrameDestruction) {}

TEST_F(FrameSchedulerImplTest, TasksRunAfterDetach) {}

TEST_F(FrameSchedulerImplTest, DetachedWebSchedulingTaskQueue) {}

class WebSchedulingTaskQueueTest : public FrameSchedulerImplTest,
                                   public WebSchedulingTestHelper::Delegate {};

TEST_F(WebSchedulingTaskQueueTest, TasksRunInPriorityOrder) {}

TEST_F(WebSchedulingTaskQueueTest, DynamicTaskPriorityOrder) {}

TEST_F(WebSchedulingTaskQueueTest, DynamicTaskPriorityOrderDelayedTasks) {}

TEST_F(WebSchedulingTaskQueueTest, TasksAndContinuations) {}

TEST_F(WebSchedulingTaskQueueTest, DynamicPriorityContinuations) {}

TEST_F(WebSchedulingTaskQueueTest, WebScheduingAndNonWebScheduingTasks) {}

// Verify that tasks posted with TaskType::kJavascriptTimerDelayed* and
// delayed web scheduling tasks run at the expected time when throttled.
TEST_F(FrameSchedulerImplTest, ThrottledJSTimerTasksRunTime) {}

namespace {
class MockMainThreadScheduler : public MainThreadSchedulerImpl {};
}  // namespace

TEST_F(FrameSchedulerImplTest, ReportFMPAndFCPForMainFrames) {}

TEST_F(FrameSchedulerImplTest, DontReportFMPAndFCPForSubframes) {}

// Verify that tasks run at the expected time in a frame that is same-origin
// with the main frame, on a page that isn't loading when hidden ("quick"
// intensive wake up throttling kicks in).
TEST_P(FrameSchedulerImplTestWithIntensiveWakeUpThrottling,
       TaskExecutionSameOriginFrame) {}

// Verify that tasks run at the expected time in a frame that is cross-origin
// with the main frame with intensive wake up throttling.
TEST_P(FrameSchedulerImplTestWithIntensiveWakeUpThrottling,
       TaskExecutionCrossOriginFrame) {}

// Verify that tasks from different frames that are same-origin with the main
// frame run at the expected time.
TEST_P(FrameSchedulerImplTestWithIntensiveWakeUpThrottling,
       ManySameOriginFrames) {}

// Verify that intensive wake up throttling starts after 5 minutes instead of 1
// minute if the page is loading when hidden.
TEST_P(FrameSchedulerImplTestWithIntensiveWakeUpThrottling,
       TaskExecutionPageLoadingWhenHidden) {}

// Verify that intensive throttling is disabled when there is an opt-out.
TEST_P(FrameSchedulerImplTestWithIntensiveWakeUpThrottling,
       AggressiveThrottlingOptOut) {}

// Verify that tasks run at the same time when a frame switches between being
// same-origin and cross-origin with the main frame.
TEST_P(FrameSchedulerImplTestWithIntensiveWakeUpThrottling,
       FrameChangesOriginType) {}

INSTANTIATE_TEST_SUITE_P();

TEST_F(FrameSchedulerImplTestWithIntensiveWakeUpThrottlingPolicyOverride,
       PolicyForceEnable) {}

TEST_F(FrameSchedulerImplTestWithIntensiveWakeUpThrottlingPolicyOverride,
       PolicyForceDisable) {}

class FrameSchedulerImplTestQuickIntensiveWakeUpThrottlingEnabled
    : public FrameSchedulerImplTest {};

TEST_F(FrameSchedulerImplTestQuickIntensiveWakeUpThrottlingEnabled,
       LoadingPageGracePeriod) {}

TEST_F(FrameSchedulerImplTestQuickIntensiveWakeUpThrottlingEnabled,
       LoadedPageGracePeriod) {}

// Verify that non-delayed kWebSchedulingPostedTask tasks are not throttled.
TEST_F(FrameSchedulerImplTest, ImmediateWebSchedulingTasksAreNotThrottled) {}

TEST_F(FrameSchedulerImplTest, PostMessageForwardingHasVeryHighPriority) {}

class FrameSchedulerImplThrottleUnimportantFrameTimersEnabledTest
    : public FrameSchedulerImplTest {};

TEST_F(FrameSchedulerImplThrottleUnimportantFrameTimersEnabledTest,
       VisibleSizeChange_CrossOrigin_ExplicitInit) {}

TEST_F(FrameSchedulerImplThrottleUnimportantFrameTimersEnabledTest,
       UserActivationChange_CrossOrigin_ExplicitInit) {}

TEST_F(FrameSchedulerImplThrottleUnimportantFrameTimersEnabledTest,
       UnimportantFrameThrottling) {}

TEST_F(FrameSchedulerImplThrottleUnimportantFrameTimersEnabledTest,
       HiddenCrossOriginFrameThrottling) {}

TEST_F(FrameSchedulerImplThrottleUnimportantFrameTimersEnabledTest,
       BackgroundPageTimerThrottling) {}

TEST_F(FrameSchedulerImplThrottleUnimportantFrameTimersEnabledTest,
       LargeCrossOriginFrameNoThrottling) {}

TEST_F(FrameSchedulerImplThrottleUnimportantFrameTimersEnabledTest,
       UserActivatedCrossOriginFrameNoThrottling) {}

class FrameSchedulerImplNoThrottlingVisibleAgentTest
    : public FrameSchedulerImplTest,
      // True iff the other frame belongs to a different page.
      public testing::WithParamInterface<bool> {};

class FrameSchedulerImplNoThrottlingVisibleAgentAndThrottleUnimportantTest
    : public FrameSchedulerImplNoThrottlingVisibleAgentTest {};

// Verify the throttled state on frame visibility changes.
TEST_P(FrameSchedulerImplNoThrottlingVisibleAgentTest, FrameVisibilityChange) {}

// Verify the throttled state when page visibility changes and there is a
// visible same-agent frame.
TEST_P(FrameSchedulerImplNoThrottlingVisibleAgentTest, PageVisibilityChange) {}

// Verify the throttled state when the page visibility of a same-agent frame
// changes.
TEST_P(FrameSchedulerImplNoThrottlingVisibleAgentTest,
       SameAgentFramePageVisibilityChange) {}

// Verify the throttled state when a same-agent visible frame is deleted.
TEST_P(FrameSchedulerImplNoThrottlingVisibleAgentTest, VisibleFrameDeletion) {}

// Verify the throttled state when a same-agent visible frame on a hidden page
// is deleted. This test exists to confirm that ~FrameSchedulerImpl checks
// `AreFrameAndPageVisible()`, not just `frame_visible_`, before invoking
// `DecrementVisibleFramesForAgent()`.
TEST_P(FrameSchedulerImplNoThrottlingVisibleAgentTest,
       VisibleFrameOnHiddenPageDeletion) {}

// Verify the throttled state when the page scheduler of a same-agent frame is
// deleted.
//
// Note: Ideally, we would enforce that a page scheduler is deleted after its
// frame schedulers. But until this enforcement is in place, it is important
// that throttling deletion of a page scheduler that still has frame schedulers
// correctly.
TEST_P(FrameSchedulerImplNoThrottlingVisibleAgentTest,
       PageSchedulerWithSameAgentFrameDeleted) {}

// Verify the throttled state when frame agent changes.
TEST_P(FrameSchedulerImplNoThrottlingVisibleAgentTest, AgentChange) {}

// Verify the throttled state for a frame that is same-origin with the nearest
// main frame.
TEST_P(FrameSchedulerImplNoThrottlingVisibleAgentTest,
       SameOriginWithNearestMainFrame) {}

// Verify that tasks are throttled to 32ms (not 1 second) in a frame that is
// hidden but same-agent with a visible frame, when the
// "ThrottleUnimportantFrameTimers" feature is enabled.
TEST_P(FrameSchedulerImplNoThrottlingVisibleAgentAndThrottleUnimportantTest,
       SameAgentWithVisibleFrameIs32msThrottled) {}

INSTANTIATE_TEST_SUITE_P();

INSTANTIATE_TEST_SUITE_P();

TEST_F(FrameSchedulerImplTest, DeleteSoonUsesBackupTaskRunner) {}

TEST_F(FrameSchedulerImplTest, DeleteSoonAfterShutdown) {}

}  // namespace frame_scheduler_impl_unittest
}  // namespace scheduler
}  // namespace blink