3d02b19c31fe644ebd5db76c7e5e66175037f575
[WebKit-https.git] / Source / WebCore / platform / win / SharedTimerWin.cpp
1 /*
2  * Copyright (C) 2006 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 "SharedTimer.h"
28
29 #include "Page.h"
30 #include "Settings.h"
31 #include "WebCoreInstanceHandle.h"
32 #include "Widget.h"
33 #include <wtf/Assertions.h>
34 #include <wtf/CurrentTime.h>
35 #include <wtf/WindowsExtras.h>
36
37 #include <mmsystem.h>
38
39 // These aren't in winuser.h with the MSVS 2003 Platform SDK, 
40 // so use default values in that case.
41 #ifndef USER_TIMER_MINIMUM
42 #define USER_TIMER_MINIMUM 0x0000000A
43 #endif
44
45 #ifndef USER_TIMER_MAXIMUM
46 #define USER_TIMER_MAXIMUM 0x7FFFFFFF
47 #endif
48
49 #ifndef QS_RAWINPUT
50 #define QS_RAWINPUT         0x0400
51 #endif
52
53 namespace WebCore {
54
55 static UINT timerID;
56 static void (*sharedTimerFiredFunction)();
57
58 static HANDLE timer;
59 static HWND timerWindowHandle = 0;
60 const LPCWSTR kTimerWindowClassName = L"TimerWindowClass";
61
62 static UINT timerFiredMessage = 0;
63 static HANDLE timerQueue;
64 static bool highResTimerActive;
65 static bool processingCustomTimerMessage = false;
66 static LONG pendingTimers;
67
68 const int timerResolution = 1; // To improve timer resolution, we call timeBeginPeriod/timeEndPeriod with this value to increase timer resolution to 1ms.
69 const int highResolutionThresholdMsec = 16; // Only activate high-res timer for sub-16ms timers (Windows can fire timers at 16ms intervals without changing the system resolution).
70 const int stopHighResTimerInMsec = 300; // Stop high-res timer after 0.3 seconds to lessen power consumption (we don't use a smaller time since oscillating between high and low resolution breaks timer accuracy on XP).
71
72 enum {
73     sharedTimerID = 1000,
74     endHighResTimerID = 1001,
75 };
76
77 LRESULT CALLBACK TimerWindowWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
78 {
79     if (message == WM_TIMER) {
80         if (wParam == sharedTimerID) {
81             KillTimer(timerWindowHandle, sharedTimerID);
82             sharedTimerFiredFunction();
83         }
84         else if (wParam == endHighResTimerID) {
85             KillTimer(timerWindowHandle, endHighResTimerID);
86             highResTimerActive = false;
87             timeEndPeriod(timerResolution);
88         }
89     } else if (message == timerFiredMessage) {
90         InterlockedExchange(&pendingTimers, 0);
91         processingCustomTimerMessage = true;
92         sharedTimerFiredFunction();
93         processingCustomTimerMessage = false;
94     } else
95         return DefWindowProc(hWnd, message, wParam, lParam);
96
97     return 0;
98 }
99
100 static void initializeOffScreenTimerWindow()
101 {
102     if (timerWindowHandle)
103         return;
104
105     WNDCLASSEX wcex;
106     memset(&wcex, 0, sizeof(WNDCLASSEX));
107     wcex.cbSize = sizeof(WNDCLASSEX);
108
109     wcex.lpfnWndProc    = TimerWindowWndProc;
110     wcex.hInstance      = WebCore::instanceHandle();
111     wcex.lpszClassName  = kTimerWindowClassName;
112     RegisterClassEx(&wcex);
113
114     timerWindowHandle = CreateWindow(kTimerWindowClassName, 0, 0,
115        CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, HWND_MESSAGE, 0, WebCore::instanceHandle(), 0);
116
117     timerFiredMessage = RegisterWindowMessage(L"com.apple.WebKit.TimerFired");
118 }
119
120 void setSharedTimerFiredFunction(void (*f)())
121 {
122     sharedTimerFiredFunction = f;
123 }
124
125 static void NTAPI queueTimerProc(PVOID, BOOLEAN)
126 {
127     if (InterlockedIncrement(&pendingTimers) == 1)
128         PostMessage(timerWindowHandle, timerFiredMessage, 0, 0);
129 }
130
131 void setSharedTimerFireInterval(double interval)
132 {
133     ASSERT(sharedTimerFiredFunction);
134
135     unsigned intervalInMS;
136     interval *= 1000;
137     if (interval > USER_TIMER_MAXIMUM)
138         intervalInMS = USER_TIMER_MAXIMUM;
139     else
140         intervalInMS = static_cast<unsigned>(interval);
141
142     initializeOffScreenTimerWindow();
143     bool timerSet = false;
144
145     if (Settings::shouldUseHighResolutionTimers()) {
146         if (interval < highResolutionThresholdMsec) {
147             if (!highResTimerActive) {
148                 highResTimerActive = true;
149                 timeBeginPeriod(timerResolution);
150             }
151             SetTimer(timerWindowHandle, endHighResTimerID, stopHighResTimerInMsec, 0);
152         }
153
154         DWORD queueStatus = LOWORD(GetQueueStatus(QS_PAINT | QS_MOUSEBUTTON | QS_KEY | QS_RAWINPUT));
155
156         // Win32 has a tri-level queue with application messages > user input > WM_PAINT/WM_TIMER.
157
158         // If the queue doesn't contains input events, we use a higher priorty timer event posting mechanism.
159         if (!(queueStatus & (QS_MOUSEBUTTON | QS_KEY | QS_RAWINPUT))) {
160             if (intervalInMS < USER_TIMER_MINIMUM && !processingCustomTimerMessage && !(queueStatus & QS_PAINT)) {
161                 // Call PostMessage immediately if the timer is already expired, unless a paint is pending.
162                 // (we prioritize paints over timers)
163                 if (InterlockedIncrement(&pendingTimers) == 1)
164                     PostMessage(timerWindowHandle, timerFiredMessage, 0, 0);
165                 timerSet = true;
166             } else {
167                 // Otherwise, delay the PostMessage via a CreateTimerQueueTimer
168                 if (!timerQueue)
169                     timerQueue = CreateTimerQueue();
170                 if (timer)
171                     DeleteTimerQueueTimer(timerQueue, timer, 0);
172                 timerSet = CreateTimerQueueTimer(&timer, timerQueue, queueTimerProc, 0, intervalInMS, 0, WT_EXECUTEINTIMERTHREAD | WT_EXECUTEONLYONCE);
173             }
174         }
175     }
176
177     if (timerSet) {
178         if (timerID) {
179             KillTimer(timerWindowHandle, timerID);
180             timerID = 0;
181         }
182     } else {
183         timerID = SetTimer(timerWindowHandle, sharedTimerID, intervalInMS, 0);
184         timer = 0;
185     }
186 }
187
188 void stopSharedTimer()
189 {
190     if (timerQueue && timer) {
191         DeleteTimerQueueTimer(timerQueue, timer, 0);
192         timer = 0;
193     }
194
195     if (timerID) {
196         KillTimer(timerWindowHandle, timerID);
197         timerID = 0;
198     }
199 }
200
201 void invalidateSharedTimer()
202 {
203 }
204
205 }