Source/WebCore: Use after free in WebCore::DisplayRefreshMonitorClient::fireDisplayRe...
[WebKit-https.git] / Source / WebCore / platform / graphics / DisplayRefreshMonitor.cpp
1 /*
2  * Copyright (C) 2010 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 COMPUTER, 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
28 #if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
29
30 #include "DisplayRefreshMonitor.h"
31 #include <wtf/CurrentTime.h>
32 #include <wtf/Ref.h>
33
34 namespace WebCore {
35
36 DisplayRefreshMonitorClient::DisplayRefreshMonitorClient()
37     : m_scheduled(false)
38     , m_displayIDIsSet(false)
39 {
40 }
41
42 DisplayRefreshMonitorClient::~DisplayRefreshMonitorClient()
43 {
44     DisplayRefreshMonitorManager::sharedManager()->unregisterClient(this);
45 }
46
47 void DisplayRefreshMonitorClient::fireDisplayRefreshIfNeeded(double timestamp)
48 {
49     if (m_scheduled) {
50         m_scheduled = false;
51         displayRefreshFired(timestamp);
52     }
53 }
54
55 DisplayRefreshMonitor::DisplayRefreshMonitor(PlatformDisplayID displayID)
56     : m_monotonicAnimationStartTime(0)
57     , m_active(true)
58     , m_scheduled(false)
59     , m_previousFrameDone(true)
60     , m_unscheduledFireCount(0)
61     , m_displayID(displayID)
62     , m_clientsToBeNotified(nullptr)
63 #if PLATFORM(MAC)
64     , m_displayLink(0)
65 #endif
66 #if PLATFORM(BLACKBERRY)
67     , m_animationClient(0)
68 #endif
69 {
70 }
71
72 void DisplayRefreshMonitor::handleDisplayRefreshedNotificationOnMainThread(void* data)
73 {
74     DisplayRefreshMonitor* monitor = static_cast<DisplayRefreshMonitor*>(data);
75     monitor->displayDidRefresh();
76 }
77
78 void DisplayRefreshMonitor::addClient(DisplayRefreshMonitorClient* client)
79 {
80     m_clients.add(client);
81 }
82
83 bool DisplayRefreshMonitor::removeClient(DisplayRefreshMonitorClient* client)
84 {
85     if (m_clientsToBeNotified)
86         m_clientsToBeNotified->remove(client);
87     return m_clients.remove(client);
88 }
89
90 void DisplayRefreshMonitor::displayDidRefresh()
91 {
92     double monotonicAnimationStartTime;
93     {
94         MutexLocker lock(m_mutex);
95         if (!m_scheduled)
96             ++m_unscheduledFireCount;
97         else
98             m_unscheduledFireCount = 0;
99
100         m_scheduled = false;
101         monotonicAnimationStartTime = m_monotonicAnimationStartTime;
102     }
103
104     // The call back can cause all our clients to be unregistered, so we need to protect
105     // against deletion until the end of the method.
106     Ref<DisplayRefreshMonitor> protect(*this);
107
108     // Copy the hash table and remove clients from it one by one so we don't notify
109     // any client twice, but can respond to removal of clients during the delivery process.
110     HashSet<DisplayRefreshMonitorClient*> clientsToBeNotified = m_clients;
111     m_clientsToBeNotified = &clientsToBeNotified;
112     while (!clientsToBeNotified.isEmpty()) {
113         // Take a random client out of the set. Ordering doesn't matter.
114         // FIXME: Would read more cleanly if HashSet had a take function.
115         auto it = clientsToBeNotified.begin();
116         DisplayRefreshMonitorClient* client = *it;
117         clientsToBeNotified.remove(it);
118
119         client->fireDisplayRefreshIfNeeded(monotonicAnimationStartTime);
120
121         // This checks if this function was reentered. In that case, stop iterating
122         // since it's not safe to use the set any more.
123         if (m_clientsToBeNotified != &clientsToBeNotified)
124             break;
125     }
126     if (m_clientsToBeNotified == &clientsToBeNotified)
127         m_clientsToBeNotified = nullptr;
128
129     {
130         MutexLocker lock(m_mutex);
131         m_previousFrameDone = true;
132     }
133     
134     DisplayRefreshMonitorManager::sharedManager()->displayDidRefresh(this);
135 }
136
137 DisplayRefreshMonitorManager* DisplayRefreshMonitorManager::sharedManager()
138 {
139     DEFINE_STATIC_LOCAL(DisplayRefreshMonitorManager, manager, ());
140     return &manager;
141 }
142
143 DisplayRefreshMonitor* DisplayRefreshMonitorManager::ensureMonitorForClient(DisplayRefreshMonitorClient* client)
144 {
145     DisplayRefreshMonitorMap::iterator it = m_monitors.find(client->m_displayID);
146     if (it == m_monitors.end()) {
147         RefPtr<DisplayRefreshMonitor> monitor = DisplayRefreshMonitor::create(client->m_displayID);
148         monitor->addClient(client);
149         DisplayRefreshMonitor* result = monitor.get();
150         m_monitors.add(client->m_displayID, monitor.release());
151         return result;
152     }
153     it->value->addClient(client);
154     return it->value.get();
155 }
156
157 void DisplayRefreshMonitorManager::registerClient(DisplayRefreshMonitorClient* client)
158 {
159     if (!client->m_displayIDIsSet)
160         return;
161         
162     ensureMonitorForClient(client);
163 }
164
165 void DisplayRefreshMonitorManager::unregisterClient(DisplayRefreshMonitorClient* client)
166 {
167     if (!client->m_displayIDIsSet)
168         return;
169
170     DisplayRefreshMonitorMap::iterator it = m_monitors.find(client->m_displayID);
171     if (it == m_monitors.end())
172         return;
173     
174     DisplayRefreshMonitor* monitor = it->value.get();
175     if (monitor->removeClient(client)) {
176         if (!monitor->hasClients())
177             m_monitors.remove(it);
178     }
179 }
180
181 bool DisplayRefreshMonitorManager::scheduleAnimation(DisplayRefreshMonitorClient* client)
182 {
183     if (!client->m_displayIDIsSet)
184         return false;
185         
186     DisplayRefreshMonitor* monitor = ensureMonitorForClient(client);
187
188     client->m_scheduled = true;
189     return monitor->requestRefreshCallback();
190 }
191
192 void DisplayRefreshMonitorManager::displayDidRefresh(DisplayRefreshMonitor* monitor)
193 {
194     if (monitor->shouldBeTerminated()) {
195         ASSERT(m_monitors.contains(monitor->displayID()));
196         m_monitors.remove(monitor->displayID());
197     }
198 }
199
200 void DisplayRefreshMonitorManager::windowScreenDidChange(PlatformDisplayID displayID, DisplayRefreshMonitorClient* client)
201 {
202     if (client->m_displayIDIsSet && client->m_displayID == displayID)
203         return;
204     
205     unregisterClient(client);
206     client->setDisplayID(displayID);
207     registerClient(client);
208     if (client->m_scheduled)
209         scheduleAnimation(client);
210 }
211
212 }
213
214 #endif // USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)