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