diff options
Diffstat (limited to 'gfx/layers/apz/util/APZTaskRunnable.cpp')
-rw-r--r-- | gfx/layers/apz/util/APZTaskRunnable.cpp | 142 |
1 files changed, 142 insertions, 0 deletions
diff --git a/gfx/layers/apz/util/APZTaskRunnable.cpp b/gfx/layers/apz/util/APZTaskRunnable.cpp new file mode 100644 index 0000000000..6192f385a7 --- /dev/null +++ b/gfx/layers/apz/util/APZTaskRunnable.cpp @@ -0,0 +1,142 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "APZTaskRunnable.h" + +#include "mozilla/PresShell.h" +#include "nsRefreshDriver.h" + +namespace mozilla::layers { + +NS_IMETHODIMP +APZTaskRunnable::Run() { + if (!mController) { + mRegisteredPresShellId = 0; + return NS_OK; + } + + // Move these variables first since below RequestContentPaint and + // NotifyFlushComplete might spin event loop so that any new incoming requests + // will be properly queued and run in the next refresh driver's tick. + const bool needsFlushCompleteNotification = mNeedsFlushCompleteNotification; + auto requests = std::move(mPendingRepaintRequestQueue); + mPendingRepaintRequestMap.clear(); + mNeedsFlushCompleteNotification = false; + mRegisteredPresShellId = 0; + RefPtr<GeckoContentController> controller = mController; + + // We need to process pending RepaintRequests first. + while (!requests.empty()) { + controller->RequestContentRepaint(requests.front()); + requests.pop_front(); + } + + if (needsFlushCompleteNotification) { + // Then notify "apz-repaints-flushed" so that we can ensure that all pending + // scroll position updates have finished when the "apz-repaints-flushed" + // arrives. + controller->NotifyFlushComplete(); + } + + return NS_OK; +} + +void APZTaskRunnable::QueueRequest(const RepaintRequest& aRequest) { + // If we are in test-controlled refreshes mode, process this |aRequest| + // synchronously. + if (IsTestControllingRefreshesEnabled()) { + // Flush all pending requests and notification just in case the refresh + // driver mode was changed before flushing them. + RefPtr<GeckoContentController> controller = mController; + Run(); + controller->RequestContentRepaint(aRequest); + return; + } + EnsureRegisterAsEarlyRunner(); + + RepaintRequestKey key{aRequest.GetScrollId(), aRequest.GetScrollUpdateType()}; + + auto lastDiscardableRequest = mPendingRepaintRequestMap.find(key); + // If there's an existing request with the same key, we can discard it and we + // push the incoming one into the queue's tail so that we can ensure the order + // of processing requests. + if (lastDiscardableRequest != mPendingRepaintRequestMap.end()) { + for (auto it = mPendingRepaintRequestQueue.begin(); + it != mPendingRepaintRequestQueue.end(); it++) { + if (RepaintRequestKey{it->GetScrollId(), it->GetScrollUpdateType()} == + key) { + mPendingRepaintRequestQueue.erase(it); + break; + } + } + } + mPendingRepaintRequestMap.insert(key); + mPendingRepaintRequestQueue.push_back(aRequest); +} + +void APZTaskRunnable::QueueFlushCompleteNotification() { + // If we are in test-controlled refreshes mode, notify apz-repaints-flushed + // synchronously. + if (IsTestControllingRefreshesEnabled()) { + // Flush all pending requests and notification just in case the refresh + // driver mode was changed before flushing them. + RefPtr<GeckoContentController> controller = mController; + Run(); + controller->NotifyFlushComplete(); + return; + } + + EnsureRegisterAsEarlyRunner(); + + mNeedsFlushCompleteNotification = true; +} + +bool APZTaskRunnable::IsRegisteredWithCurrentPresShell() const { + MOZ_ASSERT(mController); + + uint32_t current = 0; + if (PresShell* presShell = mController->GetTopLevelPresShell()) { + current = presShell->GetPresShellId(); + } + return mRegisteredPresShellId == current; +} + +void APZTaskRunnable::EnsureRegisterAsEarlyRunner() { + if (IsRegisteredWithCurrentPresShell()) { + return; + } + + // If the registered presshell id has been changed, we need to discard pending + // requests and notification since all of them are for documents which + // have been torn down. + if (mRegisteredPresShellId) { + mPendingRepaintRequestMap.clear(); + mPendingRepaintRequestQueue.clear(); + mNeedsFlushCompleteNotification = false; + } + + if (PresShell* presShell = mController->GetTopLevelPresShell()) { + if (nsRefreshDriver* driver = presShell->GetRefreshDriver()) { + driver->AddEarlyRunner(this); + mRegisteredPresShellId = presShell->GetPresShellId(); + } + } +} + +bool APZTaskRunnable::IsTestControllingRefreshesEnabled() const { + if (!mController) { + return false; + } + + if (PresShell* presShell = mController->GetTopLevelPresShell()) { + if (nsRefreshDriver* driver = presShell->GetRefreshDriver()) { + return driver->IsTestControllingRefreshesEnabled(); + } + } + return false; +} + +} // namespace mozilla::layers |