APZTaskRunnable.cpp (6177B)
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 #include "APZTaskRunnable.h" 8 9 #include "mozilla/PresShell.h" 10 #include "nsRefreshDriver.h" 11 12 namespace mozilla::layers { 13 14 NS_IMETHODIMP 15 APZTaskRunnable::Run() { 16 if (!mController) { 17 mRegisteredPresShellId = 0; 18 return NS_OK; 19 } 20 21 // Move these variables first since below RequestContentPaint and 22 // NotifyFlushComplete might spin event loop so that any new incoming requests 23 // will be properly queued and run in the next refresh driver's tick. 24 const bool needsFlushCompleteNotification = mNeedsFlushCompleteNotification; 25 auto requests = std::move(mPendingRequestQueue); 26 mPendingRepaintRequestMap.clear(); 27 mNeedsFlushCompleteNotification = false; 28 mRegisteredPresShellId = 0; 29 RefPtr<GeckoContentController> controller = mController; 30 31 // We need to process pending RepaintRequests first. 32 while (!requests.empty()) { 33 struct RequestProcessor { 34 GeckoContentController* mController; 35 void operator()(const RepaintRequest& aRequest) { 36 mController->RequestContentRepaint(aRequest); 37 } 38 void operator()(const APZStateChangeRequest& aRequest) { 39 mController->NotifyAPZStateChange(aRequest.mGuid, aRequest.mChange, 40 aRequest.mArg, 41 aRequest.mInputBlockId); 42 } 43 }; 44 requests.front().match(RequestProcessor{controller.get()}); 45 requests.pop_front(); 46 } 47 48 if (needsFlushCompleteNotification) { 49 // Then notify "apz-repaints-flushed" so that we can ensure that all pending 50 // scroll position updates have finished when the "apz-repaints-flushed" 51 // arrives. 52 controller->NotifyFlushComplete(); 53 } 54 55 return NS_OK; 56 } 57 58 void APZTaskRunnable::QueueRequest(const RepaintRequest& aRequest) { 59 // If we are in test-controlled refreshes mode, process this |aRequest| 60 // synchronously. 61 if (IsTestControllingRefreshesEnabled()) { 62 // Flush all pending requests and notification just in case the refresh 63 // driver mode was changed before flushing them. 64 RefPtr<GeckoContentController> controller = mController; 65 Run(); 66 controller->RequestContentRepaint(aRequest); 67 return; 68 } 69 EnsureRegisterAsEarlyRunner(); 70 71 RepaintRequestKey key{aRequest.GetScrollId(), aRequest.GetScrollUpdateType()}; 72 73 auto lastDiscardableRequest = mPendingRepaintRequestMap.find(key); 74 // If there's an existing request with the same key, we can discard it and we 75 // push the incoming one into the queue's tail so that we can ensure the order 76 // of processing requests. 77 if (lastDiscardableRequest != mPendingRepaintRequestMap.end()) { 78 for (auto it = mPendingRequestQueue.begin(); 79 it != mPendingRequestQueue.end(); it++) { 80 if (it->is<RepaintRequest>()) { 81 const RepaintRequest& request = it->as<RepaintRequest>(); 82 if (RepaintRequestKey{request.GetScrollId(), 83 request.GetScrollUpdateType()} == key) { 84 mPendingRequestQueue.erase(it); 85 break; 86 } 87 } 88 } 89 } 90 mPendingRepaintRequestMap.insert(key); 91 mPendingRequestQueue.push_back(AsVariant(aRequest)); 92 } 93 94 void APZTaskRunnable::QueueAPZStateChange(const ScrollableLayerGuid& aGuid, 95 const APZStateChange& aChange, 96 const int& aArg, 97 Maybe<uint64_t> aInputBlockId) { 98 // If we are in test-controlled refreshes mode, process this |aRequest| 99 // synchronously. 100 if (IsTestControllingRefreshesEnabled()) { 101 // Flush all pending requests and notification just in case the refresh 102 // driver mode was changed before flushing them. 103 RefPtr<GeckoContentController> controller = mController; 104 Run(); 105 controller->NotifyAPZStateChange(aGuid, aChange, aArg, aInputBlockId); 106 return; 107 } 108 EnsureRegisterAsEarlyRunner(); 109 110 mPendingRequestQueue.push_back( 111 AsVariant(APZStateChangeRequest{aGuid, aChange, aArg, aInputBlockId})); 112 } 113 114 void APZTaskRunnable::QueueFlushCompleteNotification() { 115 // If we are in test-controlled refreshes mode, notify apz-repaints-flushed 116 // synchronously. 117 if (IsTestControllingRefreshesEnabled()) { 118 // Flush all pending requests and notification just in case the refresh 119 // driver mode was changed before flushing them. 120 RefPtr<GeckoContentController> controller = mController; 121 Run(); 122 controller->NotifyFlushComplete(); 123 return; 124 } 125 126 EnsureRegisterAsEarlyRunner(); 127 128 mNeedsFlushCompleteNotification = true; 129 } 130 131 bool APZTaskRunnable::IsRegisteredWithCurrentPresShell() const { 132 MOZ_ASSERT(mController); 133 134 uint32_t current = 0; 135 if (PresShell* presShell = mController->GetTopLevelPresShell()) { 136 current = presShell->GetPresShellId(); 137 } 138 return mRegisteredPresShellId == current; 139 } 140 141 void APZTaskRunnable::EnsureRegisterAsEarlyRunner() { 142 if (IsRegisteredWithCurrentPresShell()) { 143 return; 144 } 145 146 // If the registered presshell id has been changed, we need to discard pending 147 // requests and notification since all of them are for documents which 148 // have been torn down. 149 if (mRegisteredPresShellId) { 150 mPendingRepaintRequestMap.clear(); 151 mPendingRequestQueue.clear(); 152 mNeedsFlushCompleteNotification = false; 153 } 154 155 if (PresShell* presShell = mController->GetTopLevelPresShell()) { 156 if (nsRefreshDriver* driver = presShell->GetRefreshDriver()) { 157 driver->AddEarlyRunner(this); 158 mRegisteredPresShellId = presShell->GetPresShellId(); 159 } 160 } 161 } 162 163 bool APZTaskRunnable::IsTestControllingRefreshesEnabled() const { 164 if (!mController) { 165 return false; 166 } 167 168 if (PresShell* presShell = mController->GetTopLevelPresShell()) { 169 if (nsRefreshDriver* driver = presShell->GetRefreshDriver()) { 170 return driver->IsTestControllingRefreshesEnabled(); 171 } 172 } 173 return false; 174 } 175 176 } // namespace mozilla::layers