/* * Copyright (C) 2015-2019 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of Apple Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" #include "WheelEventTestMonitor.h" #include "Logging.h" #include "Page.h" #include #include #include #if !LOG_DISABLED #include #include #endif namespace WebCore { WheelEventTestMonitor::WheelEventTestMonitor(Page& page) : m_page(page) { } void WheelEventTestMonitor::clearAllTestDeferrals() { Locker locker { m_lock }; ASSERT(isMainThread()); m_deferCompletionReasons.clear(); m_completionCallback = nullptr; m_everHadDeferral = false; m_receivedWheelEndOrCancel = false; m_receivedMomentumEnd = false; LOG_WITH_STREAM(WheelEventTestMonitor, stream << " WheelEventTestMonitor::clearAllTestDeferrals: cleared all test state."); } void WheelEventTestMonitor::setTestCallbackAndStartMonitoring(bool expectWheelEndOrCancel, bool expectMomentumEnd, WTF::Function&& functionCallback) { Locker locker { m_lock }; ASSERT(isMainThread()); m_completionCallback = WTFMove(functionCallback); #if ENABLE(KINETIC_SCROLLING) m_expectWheelEndOrCancel = expectWheelEndOrCancel; m_expectMomentumEnd = expectMomentumEnd; #else UNUSED_PARAM(expectWheelEndOrCancel); UNUSED_PARAM(expectMomentumEnd); #endif m_page.scheduleRenderingUpdate(RenderingUpdateStep::WheelEventMonitorCallbacks); LOG_WITH_STREAM(WheelEventTestMonitor, stream << " WheelEventTestMonitor::setTestCallbackAndStartMonitoring - expect end/cancel " << expectWheelEndOrCancel << ", expect momentum end " << expectMomentumEnd); } void WheelEventTestMonitor::deferForReason(ScrollableAreaIdentifier identifier, DeferReason reason) { Locker locker { m_lock }; m_deferCompletionReasons.ensure(identifier, [] { return OptionSet(); }).iterator->value.add(reason); m_everHadDeferral = true; LOG_WITH_STREAM(WheelEventTestMonitor, stream << " (=) WheelEventTestMonitor::deferForReason: id=" << identifier << ", reason=" << reason); } void WheelEventTestMonitor::removeDeferralForReason(ScrollableAreaIdentifier identifier, DeferReason reason) { Locker locker { m_lock }; auto it = m_deferCompletionReasons.find(identifier); if (it == m_deferCompletionReasons.end()) { LOG_WITH_STREAM(WheelEventTestMonitor, stream << " (=) WheelEventTestMonitor::removeDeferralForReason: failed to find defer for id=" << identifier << ", reason=" << reason); return; } LOG_WITH_STREAM(WheelEventTestMonitor, stream << " (=) WheelEventTestMonitor::removeDeferralForReason: id=" << identifier << ", reason=" << reason); it->value.remove(reason); if (it->value.isEmpty()) m_deferCompletionReasons.remove(it); scheduleCallbackCheck(); } void WheelEventTestMonitor::receivedWheelEvent(const PlatformWheelEvent& event) { #if ENABLE(KINETIC_SCROLLING) Locker locker { m_lock }; if (event.phase() == PlatformWheelEventPhase::Ended || event.phase() == PlatformWheelEventPhase::Cancelled) m_receivedWheelEndOrCancel = true; if (event.momentumPhase() == PlatformWheelEventPhase::Ended) m_receivedMomentumEnd = true; #endif } void WheelEventTestMonitor::scheduleCallbackCheck() { ensureOnMainThread([weakPage = makeWeakPtr(m_page)] { if (weakPage) weakPage->scheduleRenderingUpdate(RenderingUpdateStep::WheelEventMonitorCallbacks); }); } void WheelEventTestMonitor::checkShouldFireCallbacks() { ASSERT(isMainThread()); { Locker locker { m_lock }; if (!m_deferCompletionReasons.isEmpty()) { LOG_WITH_STREAM(WheelEventTestMonitor, stream << " WheelEventTestMonitor::checkShouldFireCallbacks - scrolling still active, reasons " << m_deferCompletionReasons); return; } if (!m_everHadDeferral) { LOG_WITH_STREAM(WheelEventTestMonitor, stream << " WheelEventTestMonitor::checkShouldFireCallbacks - have not yet seen any deferral reasons"); return; } if (m_expectWheelEndOrCancel && !m_receivedWheelEndOrCancel) { LOG_WITH_STREAM(WheelEventTestMonitor, stream << " WheelEventTestMonitor::checkShouldFireCallbacks - have not seen end of of wheel phase"); return; } if (m_expectMomentumEnd && !m_receivedMomentumEnd) { LOG_WITH_STREAM(WheelEventTestMonitor, stream << " WheelEventTestMonitor::checkShouldFireCallbacks - have not seen end of of momentum phase"); return; } } if (auto functionCallback = WTFMove(m_completionCallback)) { LOG_WITH_STREAM(WheelEventTestMonitor, stream << " WheelEventTestMonitor::checkShouldFireCallbacks: scrolling is idle, FIRING TEST"); functionCallback(); } else LOG_WITH_STREAM(WheelEventTestMonitor, stream << " WheelEventTestMonitor::checkShouldFireCallbacks - no callback"); } TextStream& operator<<(TextStream& ts, WheelEventTestMonitor::DeferReason reason) { switch (reason) { case WheelEventTestMonitor::HandlingWheelEvent: ts << "handling wheel event"; break; case WheelEventTestMonitor::HandlingWheelEventOnMainThread: ts << "handling wheel event on main thread"; break; case WheelEventTestMonitor::PostMainThreadWheelEventHandling: ts << "post-main thread event handling"; break; case WheelEventTestMonitor::RubberbandInProgress: ts << "rubberbanding"; break; case WheelEventTestMonitor::ScrollSnapInProgress: ts << "scroll-snapping"; break; case WheelEventTestMonitor::ScrollingThreadSyncNeeded: ts << "scrolling thread sync needed"; break; case WheelEventTestMonitor::ContentScrollInProgress: ts << "content scrolling"; break; case WheelEventTestMonitor::RequestedScrollPosition: ts << "requested scroll position"; break; } return ts; } TextStream& operator<<(TextStream& ts, const WheelEventTestMonitor::ScrollableAreaReasonMap& reasonMap) { for (const auto& regionReasonsPair : reasonMap) ts << " scroll region: " << regionReasonsPair.key << " reasons: " << regionReasonsPair.value; return ts; } } // namespace WebCore