1/*
2 * Copyright (C) 2010, 2014 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "DisplayRefreshMonitor.h"
28
29#if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
30
31#include "DisplayRefreshMonitorClient.h"
32#include "DisplayRefreshMonitorManager.h"
33#include "Logging.h"
34
35#if PLATFORM(IOS_FAMILY)
36#include "DisplayRefreshMonitorIOS.h"
37#elif PLATFORM(MAC)
38#include "DisplayRefreshMonitorMac.h"
39#elif PLATFORM(GTK)
40#include "DisplayRefreshMonitorGtk.h"
41#endif
42
43namespace WebCore {
44
45RefPtr<DisplayRefreshMonitor> DisplayRefreshMonitor::createDefaultDisplayRefreshMonitor(PlatformDisplayID displayID)
46{
47#if PLATFORM(MAC)
48 return DisplayRefreshMonitorMac::create(displayID);
49#endif
50#if PLATFORM(IOS_FAMILY)
51 return DisplayRefreshMonitorIOS::create(displayID);
52#endif
53#if PLATFORM(GTK)
54 return DisplayRefreshMonitorGtk::create(displayID);
55#endif
56 UNUSED_PARAM(displayID);
57 return nullptr;
58}
59
60RefPtr<DisplayRefreshMonitor> DisplayRefreshMonitor::create(DisplayRefreshMonitorClient& client)
61{
62 return client.createDisplayRefreshMonitor(client.displayID());
63}
64
65DisplayRefreshMonitor::DisplayRefreshMonitor(PlatformDisplayID displayID)
66 : m_displayID(displayID)
67{
68}
69
70DisplayRefreshMonitor::~DisplayRefreshMonitor() = default;
71
72void DisplayRefreshMonitor::handleDisplayRefreshedNotificationOnMainThread(void* data)
73{
74 DisplayRefreshMonitor* monitor = static_cast<DisplayRefreshMonitor*>(data);
75 monitor->displayDidRefresh();
76}
77
78void DisplayRefreshMonitor::addClient(DisplayRefreshMonitorClient& client)
79{
80 m_clients.add(&client);
81}
82
83bool DisplayRefreshMonitor::removeClient(DisplayRefreshMonitorClient& client)
84{
85 if (m_clientsToBeNotified)
86 m_clientsToBeNotified->remove(&client);
87 return m_clients.remove(&client);
88}
89
90void DisplayRefreshMonitor::displayDidRefresh()
91{
92 {
93 LockHolder lock(m_mutex);
94 LOG(RequestAnimationFrame, "DisplayRefreshMonitor::displayDidRefresh(%p) - m_scheduled(%d), m_unscheduledFireCount(%d)", this, m_scheduled, m_unscheduledFireCount);
95 if (!m_scheduled)
96 ++m_unscheduledFireCount;
97 else
98 m_unscheduledFireCount = 0;
99
100 m_scheduled = false;
101 }
102
103 // The call back can cause all our clients to be unregistered, so we need to protect
104 // against deletion until the end of the method.
105 Ref<DisplayRefreshMonitor> protectedThis(*this);
106
107 // Copy the hash table and remove clients from it one by one so we don't notify
108 // any client twice, but can respond to removal of clients during the delivery process.
109 HashSet<DisplayRefreshMonitorClient*> clientsToBeNotified = m_clients;
110 m_clientsToBeNotified = &clientsToBeNotified;
111 while (!clientsToBeNotified.isEmpty()) {
112 DisplayRefreshMonitorClient* client = clientsToBeNotified.takeAny();
113 client->fireDisplayRefreshIfNeeded();
114
115 // This checks if this function was reentered. In that case, stop iterating
116 // since it's not safe to use the set any more.
117 if (m_clientsToBeNotified != &clientsToBeNotified)
118 break;
119 }
120
121 if (m_clientsToBeNotified == &clientsToBeNotified)
122 m_clientsToBeNotified = nullptr;
123
124 {
125 LockHolder lock(m_mutex);
126 setIsPreviousFrameDone(true);
127 }
128
129 DisplayRefreshMonitorManager::sharedManager().displayDidRefresh(*this);
130}
131
132}
133
134#endif // USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
135