1/* 2 * Copyright (C) 2006 Apple Computer, 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 COMPUTER, 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#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 36#include "WindowsExtras.h" 37#include <mmsystem.h> 38 39#if PLATFORM(WIN) 40#include "PluginView.h" 41#endif 42 43// These aren't in winuser.h with the MSVS 2003 Platform SDK, 44// so use default values in that case. 45#ifndef USER_TIMER_MINIMUM 46#define USER_TIMER_MINIMUM 0x0000000A 47#endif 48 49#ifndef USER_TIMER_MAXIMUM 50#define USER_TIMER_MAXIMUM 0x7FFFFFFF 51#endif 52 53#ifndef QS_RAWINPUT 54#define QS_RAWINPUT 0x0400 55#endif 56 57namespace WebCore { 58 59static UINT timerID; 60static void (*sharedTimerFiredFunction)(); 61 62static HANDLE timer; 63static HWND timerWindowHandle = 0; 64const LPCWSTR kTimerWindowClassName = L"TimerWindowClass"; 65 66#if !OS(WINCE) 67static UINT timerFiredMessage = 0; 68static HANDLE timerQueue; 69static bool highResTimerActive; 70static bool processingCustomTimerMessage = false; 71static LONG pendingTimers; 72 73const int timerResolution = 1; // To improve timer resolution, we call timeBeginPeriod/timeEndPeriod with this value to increase timer resolution to 1ms. 74const int highResolutionThresholdMsec = 16; // Only activate high-res timer for sub-16ms timers (Windows can fire timers at 16ms intervals without changing the system resolution). 75const 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). 76#endif 77 78enum { 79 sharedTimerID = 1000, 80 endHighResTimerID = 1001, 81}; 82 83LRESULT CALLBACK TimerWindowWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) 84{ 85#if PLATFORM(WIN) 86 // Windows Media Player has a modal message loop that will deliver messages 87 // to us at inappropriate times and we will crash if we handle them when 88 // they are delivered. We repost all messages so that we will get to handle 89 // them once the modal loop exits. 90 if (PluginView::isCallingPlugin()) { 91 PostMessage(hWnd, message, wParam, lParam); 92 return 0; 93 } 94#endif 95 96 if (message == WM_TIMER) { 97 if (wParam == sharedTimerID) { 98 KillTimer(timerWindowHandle, sharedTimerID); 99 sharedTimerFiredFunction(); 100 } 101#if !OS(WINCE) 102 else if (wParam == endHighResTimerID) { 103 KillTimer(timerWindowHandle, endHighResTimerID); 104 highResTimerActive = false; 105 timeEndPeriod(timerResolution); 106 } 107 } else if (message == timerFiredMessage) { 108 InterlockedExchange(&pendingTimers, 0); 109 processingCustomTimerMessage = true; 110 sharedTimerFiredFunction(); 111 processingCustomTimerMessage = false; 112#endif 113 } else 114 return DefWindowProc(hWnd, message, wParam, lParam); 115 116 return 0; 117} 118 119static void initializeOffScreenTimerWindow() 120{ 121 if (timerWindowHandle) 122 return; 123 124#if OS(WINCE) 125 WNDCLASS wcex; 126 memset(&wcex, 0, sizeof(WNDCLASS)); 127#else 128 WNDCLASSEX wcex; 129 memset(&wcex, 0, sizeof(WNDCLASSEX)); 130 wcex.cbSize = sizeof(WNDCLASSEX); 131#endif 132 133 wcex.lpfnWndProc = TimerWindowWndProc; 134 wcex.hInstance = WebCore::instanceHandle(); 135 wcex.lpszClassName = kTimerWindowClassName; 136#if OS(WINCE) 137 RegisterClass(&wcex); 138#else 139 RegisterClassEx(&wcex); 140#endif 141 142 timerWindowHandle = CreateWindow(kTimerWindowClassName, 0, 0, 143 CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, HWND_MESSAGE, 0, WebCore::instanceHandle(), 0); 144 145#if !OS(WINCE) 146 timerFiredMessage = RegisterWindowMessage(L"com.apple.WebKit.TimerFired"); 147#endif 148} 149 150void setSharedTimerFiredFunction(void (*f)()) 151{ 152 sharedTimerFiredFunction = f; 153} 154 155#if !OS(WINCE) 156static void NTAPI queueTimerProc(PVOID, BOOLEAN) 157{ 158 if (InterlockedIncrement(&pendingTimers) == 1) 159 PostMessage(timerWindowHandle, timerFiredMessage, 0, 0); 160} 161#endif 162 163void setSharedTimerFireInterval(double interval) 164{ 165 ASSERT(sharedTimerFiredFunction); 166 167 unsigned intervalInMS; 168 interval *= 1000; 169 if (interval > USER_TIMER_MAXIMUM) 170 intervalInMS = USER_TIMER_MAXIMUM; 171 else 172 intervalInMS = static_cast<unsigned>(interval); 173 174 initializeOffScreenTimerWindow(); 175 bool timerSet = false; 176 177#if !OS(WINCE) 178 if (Settings::shouldUseHighResolutionTimers()) { 179 if (interval < highResolutionThresholdMsec) { 180 if (!highResTimerActive) { 181 highResTimerActive = true; 182 timeBeginPeriod(timerResolution); 183 } 184 SetTimer(timerWindowHandle, endHighResTimerID, stopHighResTimerInMsec, 0); 185 } 186 187 DWORD queueStatus = LOWORD(GetQueueStatus(QS_PAINT | QS_MOUSEBUTTON | QS_KEY | QS_RAWINPUT)); 188 189 // Win32 has a tri-level queue with application messages > user input > WM_PAINT/WM_TIMER. 190 191 // If the queue doesn't contains input events, we use a higher priorty timer event posting mechanism. 192 if (!(queueStatus & (QS_MOUSEBUTTON | QS_KEY | QS_RAWINPUT))) { 193 if (intervalInMS < USER_TIMER_MINIMUM && !processingCustomTimerMessage && !(queueStatus & QS_PAINT)) { 194 // Call PostMessage immediately if the timer is already expired, unless a paint is pending. 195 // (we prioritize paints over timers) 196 if (InterlockedIncrement(&pendingTimers) == 1) 197 PostMessage(timerWindowHandle, timerFiredMessage, 0, 0); 198 timerSet = true; 199 } else { 200 // Otherwise, delay the PostMessage via a CreateTimerQueueTimer 201 if (!timerQueue) 202 timerQueue = CreateTimerQueue(); 203 if (timer) 204 DeleteTimerQueueTimer(timerQueue, timer, 0); 205 timerSet = CreateTimerQueueTimer(&timer, timerQueue, queueTimerProc, 0, intervalInMS, 0, WT_EXECUTEINTIMERTHREAD | WT_EXECUTEONLYONCE); 206 } 207 } 208 } 209#endif // !OS(WINCE) 210 211 if (timerSet) { 212 if (timerID) { 213 KillTimer(timerWindowHandle, timerID); 214 timerID = 0; 215 } 216 } else { 217 timerID = SetTimer(timerWindowHandle, sharedTimerID, intervalInMS, 0); 218 timer = 0; 219 } 220} 221 222void stopSharedTimer() 223{ 224#if !OS(WINCE) 225 if (timerQueue && timer) { 226 DeleteTimerQueueTimer(timerQueue, timer, 0); 227 timer = 0; 228 } 229#endif 230 231 if (timerID) { 232 KillTimer(timerWindowHandle, timerID); 233 timerID = 0; 234 } 235} 236 237} 238