1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #ifndef mozilla_InputTaskManager_h
8 #define mozilla_InputTaskManager_h
9 
10 #include "nsXULAppAPI.h"
11 #include "TaskController.h"
12 #include "mozilla/StaticPtr.h"
13 #include "mozilla/StaticPrefs_dom.h"
14 #include "nsXULAppAPI.h"
15 
16 namespace mozilla {
17 
18 class InputTaskManager : public TaskManager {
19  public:
20   int32_t GetPriorityModifierForEventLoopTurn(
21       const MutexAutoLock& aProofOfLock) final;
22   void WillRunTask() final;
23   void DidRunTask() final;
24 
25   enum InputEventQueueState {
26     STATE_DISABLED,
27     STATE_FLUSHING,
28     STATE_SUSPEND,
29     STATE_ENABLED
30   };
31 
32   void EnableInputEventPrioritization();
33   void FlushInputEventPrioritization();
34   void SuspendInputEventPrioritization();
35   void ResumeInputEventPrioritization();
36 
State()37   InputEventQueueState State() { return mInputQueueState; }
38 
SetState(InputEventQueueState aState)39   void SetState(InputEventQueueState aState) { mInputQueueState = aState; }
40 
InputHandlingStartTime()41   TimeStamp InputHandlingStartTime() { return mInputHandlingStartTime; }
42 
SetInputHandlingStartTime(TimeStamp aStartTime)43   void SetInputHandlingStartTime(TimeStamp aStartTime) {
44     mInputHandlingStartTime = aStartTime;
45   }
46 
Get()47   static InputTaskManager* Get() { return gInputTaskManager.get(); }
Cleanup()48   static void Cleanup() { gInputTaskManager = nullptr; }
49   static void Init();
50 
IsSuspended(const MutexAutoLock & aProofOfLock)51   bool IsSuspended(const MutexAutoLock& aProofOfLock) override {
52     MOZ_ASSERT(NS_IsMainThread());
53     return mInputQueueState == STATE_SUSPEND || mSuspensionLevel > 0;
54   }
55 
IsSuspended()56   bool IsSuspended() {
57     MOZ_ASSERT(NS_IsMainThread());
58     return mSuspensionLevel > 0;
59   }
60 
IncSuspensionLevel()61   void IncSuspensionLevel() {
62     MOZ_ASSERT(NS_IsMainThread());
63     ++mSuspensionLevel;
64   }
65 
DecSuspensionLevel()66   void DecSuspensionLevel() {
67     MOZ_ASSERT(NS_IsMainThread());
68     --mSuspensionLevel;
69   }
70 
CanSuspendInputEvent()71   static bool CanSuspendInputEvent() {
72     // Ensure it's content process because InputTaskManager only
73     // works in e10s.
74     //
75     // Input tasks will have nullptr as their task manager when the
76     // event queue state is STATE_DISABLED, so we can't suspend
77     // input events.
78     return XRE_IsContentProcess() &&
79            StaticPrefs::dom_input_events_canSuspendInBCG_enabled() &&
80            InputTaskManager::Get()->State() !=
81                InputEventQueueState::STATE_DISABLED;
82   }
83 
NotifyVsync()84   void NotifyVsync() {
85     MOZ_ASSERT(StaticPrefs::dom_input_events_strict_input_vsync_alignment());
86     mInputPriorityController.WillRunVsync();
87   }
88 
89  private:
InputTaskManager()90   InputTaskManager() : mInputQueueState(STATE_DISABLED) {}
91 
92   class InputPriorityController {
93    public:
94     InputPriorityController();
95     // Determines whether we should use the highest input priority for input
96     // tasks
97     bool ShouldUseHighestPriority(InputTaskManager*);
98 
99     void WillRunVsync();
100 
101     // Gets called when a input task is going to run; If the current
102     // input vsync state is `HasPendingVsync`, determines whether we
103     // should continue running input tasks or leave the `HasPendingVsync` state
104     // based on
105     //    1. Whether we still have time to process input tasks
106     //    2. Whether we have processed the max number of tasks that
107     //    we should process.
108     void WillRunTask();
109 
110    private:
111     // Used to represents the relationship between Input and Vsync.
112     //
113     // HasPendingVsync: There are pending vsync tasks and we are using
114     // InputHighest priority for inputs.
115     // NoPendingVsync: No pending vsync tasks and no need to use InputHighest
116     // priority.
117     // RunVsync: Finished running input tasks and the vsync task
118     // should be run.
119     enum class InputVsyncState {
120       HasPendingVsync,
121       NoPendingVsync,
122       RunVsync,
123     };
124 
125     void EnterPendingVsyncState(uint32_t aNumPendingTasks);
126     void LeavePendingVsyncState(bool aRunVsync);
127 
128     // Stores the number of pending input tasks when we enter the
129     // InputVsyncState::HasPendingVsync state.
130     uint32_t mMaxInputTasksToRun = 0;
131 
132     bool mIsInitialized;
133     InputVsyncState mInputVsyncState;
134 
135     TimeStamp mRunInputStartTime;
136     TimeDuration mMaxInputHandlingDuration;
137   };
138 
139   int32_t GetPriorityModifierForEventLoopTurnForStrictVsyncAlignment();
140 
141   TimeStamp mInputHandlingStartTime;
142   Atomic<InputEventQueueState> mInputQueueState;
143   AutoTArray<TimeStamp, 4> mStartTimes;
144 
145   static StaticRefPtr<InputTaskManager> gInputTaskManager;
146 
147   // Number of BCGs have asked InputTaskManager to suspend input events
148   uint32_t mSuspensionLevel = 0;
149 
150   InputPriorityController mInputPriorityController;
151 };
152 
153 }  // namespace mozilla
154 
155 #endif  // mozilla_InputTaskManager_h
156