2006-03-09 Eric Seidel <eseidel@apple.com>
authoreseidel <eseidel@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 9 Mar 2006 09:49:54 +0000 (09:49 +0000)
committereseidel <eseidel@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 9 Mar 2006 09:49:54 +0000 (09:49 +0000)
        Reviewed by mjs.

        Add support for Timers under 10ms.
        Fix random crashers due to multi-threaded loading.

        * platform/TransferJob.h:
        * platform/TransferJobInternal.h:
        (WebCore::TransferJobInternal::TransferJobInternal):
        * platform/Widget.h:
        * platform/win/SharedTimerWin.cpp:
        (WebCore::TimerWindowWndProc):
        (WebCore::initializeOffScreenTimerWindow):
        (WebCore::setSharedTimerFireTime):
        * platform/win/TemporaryLinkStubs.cpp:
        (WebCore::TransferJob::assembleResponseHeaders):
        (WebCore::TransferJob::retrieveCharset):
        * platform/win/TransferJobWin.cpp:
        (WebCore::addToOutstandingJobs):
        (WebCore::removeFromOutstandingJobs):
        (WebCore::lookupTransferJob):
        (WebCore::TransferJobWndProc):
        (WebCore::initializeOffScreenTransferJobWindow):
        (WebCore::TransferJob::~TransferJob):
        (WebCore::transferJobStatusCallback):
        (WebCore::TransferJob::start):
        (WebCore::TransferJob::cancel):
        * platform/win/WidgetWin.cpp:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@13226 268f45cc-cd09-0410-ab3c-d52691b4dbfc

WebCore/ChangeLog
WebCore/platform/TransferJob.h
WebCore/platform/TransferJobInternal.h
WebCore/platform/Widget.h
WebCore/platform/win/SharedTimerWin.cpp
WebCore/platform/win/TemporaryLinkStubs.cpp
WebCore/platform/win/TransferJobWin.cpp
WebCore/platform/win/WidgetWin.cpp

index 6a679ee..78d19b7 100644 (file)
@@ -1,3 +1,33 @@
+2006-03-09  Eric Seidel  <eseidel@apple.com>
+
+        Reviewed by mjs.
+
+        Add support for Timers under 10ms.
+        Fix random crashers due to multi-threaded loading.
+
+        * platform/TransferJob.h:
+        * platform/TransferJobInternal.h:
+        (WebCore::TransferJobInternal::TransferJobInternal):
+        * platform/Widget.h:
+        * platform/win/SharedTimerWin.cpp:
+        (WebCore::TimerWindowWndProc):
+        (WebCore::initializeOffScreenTimerWindow):
+        (WebCore::setSharedTimerFireTime):
+        * platform/win/TemporaryLinkStubs.cpp:
+        (WebCore::TransferJob::assembleResponseHeaders):
+        (WebCore::TransferJob::retrieveCharset):
+        * platform/win/TransferJobWin.cpp:
+        (WebCore::addToOutstandingJobs):
+        (WebCore::removeFromOutstandingJobs):
+        (WebCore::lookupTransferJob):
+        (WebCore::TransferJobWndProc):
+        (WebCore::initializeOffScreenTransferJobWindow):
+        (WebCore::TransferJob::~TransferJob):
+        (WebCore::transferJobStatusCallback):
+        (WebCore::TransferJob::start):
+        (WebCore::TransferJob::cancel):
+        * platform/win/WidgetWin.cpp:
+
 2006-03-08  Justin Garcia  <justin.garcia@apple.com>
 
         Reviewed by darin
index 698ec94..7e5c9d5 100644 (file)
@@ -37,6 +37,11 @@ typedef unsigned long DWORD_PTR;
 typedef void* LPVOID;
 typedef LPVOID HINTERNET;
 typedef LPVOID HANDLE;
+typedef unsigned    WPARAM;
+typedef long        LPARAM;
+typedef struct HWND__ *HWND;
+typedef _W64 long LONG_PTR, *PLONG_PTR;
+typedef LONG_PTR            LRESULT;
 #endif
 
 #if __APPLE__
@@ -82,6 +87,7 @@ public:
 #if WIN32
     void fileLoadTimer(Timer<TransferJob>* timer);
     friend void __stdcall transferJobStatusCallback(HINTERNET, DWORD_PTR, DWORD, LPVOID, DWORD);
+    friend LRESULT __stdcall TransferJobWndProc(HWND hWnd, unsigned message, WPARAM wParam, LPARAM lParam);
 #endif
 
     void cancel();
index 45b7d32..00673a4 100644 (file)
@@ -54,6 +54,8 @@ namespace WebCore {
 #if WIN32
             , m_fileHandle(0)
             , m_fileLoadTimer(job, &TransferJob::fileLoadTimer)
+            , m_jobId(0)
+            , m_threadId(0)
 #endif
         {
         }
@@ -73,6 +75,8 @@ namespace WebCore {
 #if WIN32
             , m_fileHandle(0)
             , m_fileLoadTimer(job, &TransferJob::fileLoadTimer)
+            , m_jobId(0)
+            , m_threadId(0)
 #endif
         {
         }
@@ -99,6 +103,8 @@ namespace WebCore {
         HANDLE m_fileHandle;
         Timer<TransferJob> m_fileLoadTimer;
         HINTERNET m_resourceHandle;
+        unsigned m_jobId;
+        DWORD m_threadId;
 #endif
         };
 
index f23abef..2b23cfb 100644 (file)
@@ -40,6 +40,7 @@ class NSView;
 
 #if WIN32
 typedef struct HWND__ *HWND;
+typedef struct HINSTANCE__ *HINSTANCE;
 #endif
 
 namespace WebCore {
@@ -130,6 +131,8 @@ namespace WebCore {
         Widget(HWND);
         HWND windowHandle();
         void setWindowHandle(HWND);
+        // The global DLL or application instance used for all WebCore windows.
+        static HINSTANCE instanceHandle;
 #endif
 
 #if __APPLE__
index 7e52221..c9a4de5 100644 (file)
@@ -27,6 +27,7 @@
 #include "SharedTimer.h"
 
 #include "SystemTime.h"
+#include "Widget.h"
 #include <kxmlcore/Assertions.h>
 #include <windows.h>
 
@@ -35,6 +36,37 @@ namespace WebCore {
 static UINT timerID;
 static void (*sharedTimerFiredFunction)();
 
+static HWND timerWindowHandle = 0;
+static UINT timerFiredMessage = 0;
+const LPCWSTR kTimerWindowClassName = L"TimerWindowClass";
+
+LRESULT CALLBACK TimerWindowWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+    if (message == timerFiredMessage)
+        sharedTimerFiredFunction();
+    else
+        return DefWindowProc(hWnd, message, wParam, lParam);
+    return 0;
+}
+
+static void initializeOffScreenTimerWindow()
+{
+    if (timerWindowHandle)
+        return;
+    
+    WNDCLASSEX wcex;
+    memset(&wcex, 0, sizeof(WNDCLASSEX));
+    wcex.cbSize = sizeof(WNDCLASSEX);
+    wcex.lpfnWndProc    = TimerWindowWndProc;
+    wcex.hInstance      = Widget::instanceHandle;
+    wcex.lpszClassName  = kTimerWindowClassName;
+    RegisterClassEx(&wcex);
+
+    timerWindowHandle = CreateWindow(kTimerWindowClassName, 0, 0,
+       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, HWND_MESSAGE, 0, Widget::instanceHandle, 0);
+    timerFiredMessage = RegisterWindowMessage(L"com.apple.WebKit.TimerFired");
+}
+
 void setSharedTimerFiredFunction(void (*f)())
 {
     sharedTimerFiredFunction = f;
@@ -63,7 +95,15 @@ void setSharedTimerFireTime(double fireTime)
 
     if (timerID)
         KillTimer(0, timerID);
-    timerID = SetTimer(0, 0, intervalInMS, timerFired);
+
+    if (intervalInMS == 0) {
+        timerID = 0;
+        // Windows SetTimer does not allow timeouts smaller than 10ms (USER_TIMER_MINIMUM)
+        initializeOffScreenTimerWindow();
+        PostMessage(timerWindowHandle, timerFiredMessage, 0, 0);
+    } else
+        // FIXME: 1-9ms timeouts may fire too late.
+        timerID = SetTimer(0, 0, intervalInMS, timerFired);
 }
 
 void stopSharedTimer()
index 236d5db..4f49087 100644 (file)
@@ -220,9 +220,6 @@ void BrowserExtensionWin::goBackOrForward(int) { notImplemented(); }
 void BrowserExtensionWin::setIconURL(KURL const&) { notImplemented(); }
 void BrowserExtensionWin::createNewWindow(KURL const&,struct WebCore::URLArgs const&) { notImplemented(); }
 
-void WebCore::TransferJob::retrieveCharset() const { notImplemented(); }
-void WebCore::TransferJob::assembleResponseHeaders() const { notImplemented(); }
-
 IntRect Font::selectionRectForText(int, int, int, int, int, const QChar*, int, int, int, int, bool, bool, int, int) const { notImplemented(); return IntRect(); }
 
 
@@ -241,6 +238,9 @@ PluginInfo*PlugInInfoStore::createPluginInfoForPluginAtIndex(unsigned) { return
 unsigned PlugInInfoStore::pluginCount() const { return 0; }
 void WebCore::refreshPlugins(bool) { }
 
+void WebCore::TransferJob::assembleResponseHeaders() const { }
+void WebCore::TransferJob::retrieveCharset() const { }
+
 void FrameWin::restoreDocumentState() { }
 void FrameWin::partClearedInBegin() { }
 void FrameWin::createEmptyDocument() { }
index cd0057c..c4e335c 100644 (file)
 
 #include "config.h"
 #include "TransferJob.h"
-
 #include "TransferJobInternal.h"
-#include "KURL.h"
+
 #include "formdata.h"
+#include "kxmlcore/HashMap.h"
+#include "KURL.h"
+#include "Widget.h"
 #include <windows.h>
 #include <wininet.h>
 
 namespace WebCore {
-    
+
+static unsigned transferJobId = 0;
+static HashMap<int, TransferJob*>* jobIdMap = 0;
+
+static HWND transferJobWindowHandle = 0;
+static UINT loadStatusMessage = 0;
+const LPCWSTR kTransferJobWindowClassName = L"TransferJobWindowClass";
+
+static int addToOutstandingJobs(TransferJob* job)
+{
+    if (!jobIdMap)
+        jobIdMap = new HashMap<int, TransferJob*>;
+    transferJobId++;
+    jobIdMap->set(transferJobId, job);
+    return transferJobId;
+}
+
+static void removeFromOutstandingJobs(int jobId)
+{
+    if (!jobIdMap)
+        return;
+    jobIdMap->remove(jobId);
+}
+
+static TransferJob* lookupTransferJob(int jobId)
+{
+    if (!jobIdMap)
+        return 0;
+    return jobIdMap->get(jobId);
+}
+
+struct JobLoadStatus {
+    DWORD internetStatus;
+    DWORD_PTR dwResult;
+};
+
+LRESULT CALLBACK TransferJobWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+    if (message == loadStatusMessage) {
+        JobLoadStatus* jobLoadStatus = (JobLoadStatus*)lParam;
+        DWORD internetStatus = jobLoadStatus->internetStatus;
+        DWORD_PTR dwResult = jobLoadStatus->dwResult;
+        delete jobLoadStatus;
+        jobLoadStatus = 0;
+
+        // If we get a message about a job we no longer know about (already deleted), ignore it.
+        unsigned jobId = (unsigned)wParam;
+        TransferJob* job = lookupTransferJob(jobId);
+        if (!job)
+            return 0;
+
+        ASSERT(job->d->m_jobId == jobId);
+        ASSERT(job->d->m_threadId == GetCurrentThreadId());
+
+        if (internetStatus == INTERNET_STATUS_HANDLE_CREATED) {
+            job->d->m_resourceHandle = HINTERNET(dwResult);
+            if (job->d->status != 0) {
+                // We were canceled before Windows actually created a handle for us, close and delete now.
+                InternetCloseHandle(job->d->m_resourceHandle);
+                delete job;
+            }
+        } else if (internetStatus == INTERNET_STATUS_REQUEST_COMPLETE) {
+            char buffer[32728];
+            DWORD bytesRead = 0;
+            DWORD totalBytes = 0;
+            bool ok = false;
+            while ((ok = InternetReadFile(job->d->m_resourceHandle, buffer, 32728, &bytesRead)) && bytesRead) {
+                job->client()->receivedData(job, buffer, bytesRead);
+                totalBytes += bytesRead;
+                bytesRead = 0;
+            }
+
+            if (!ok) {
+                int error = GetLastError();
+                if (error == ERROR_IO_PENDING)
+                    return 0;
+                else
+                    _RPTF1(_CRT_WARN, "Load error: %i\n", error);
+            }
+            
+            InternetCloseHandle(job->d->m_resourceHandle);
+            job->client()->receivedAllData(job, 0);
+            job->client()->receivedAllData(job);
+            delete job;
+        }
+    } else
+        return DefWindowProc(hWnd, message, wParam, lParam);
+    return 0;
+}
+
+static void initializeOffScreenTransferJobWindow()
+{
+    if (transferJobWindowHandle)
+        return;
+
+    WNDCLASSEX wcex;
+    memset(&wcex, 0, sizeof(WNDCLASSEX));
+    wcex.cbSize = sizeof(WNDCLASSEX);
+    wcex.lpfnWndProc    = TransferJobWndProc;
+    wcex.hInstance      = Widget::instanceHandle;
+    wcex.lpszClassName  = kTransferJobWindowClassName;
+    RegisterClassEx(&wcex);
+
+    transferJobWindowHandle = CreateWindow(kTransferJobWindowClassName, 0, 0, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
+        HWND_MESSAGE, 0, Widget::instanceHandle, 0);
+    loadStatusMessage = RegisterWindowMessage(L"com.apple.WebKit.TransferJobLoadStatus");
+}
+
 TransferJobInternal::~TransferJobInternal()
 {
     if (m_fileHandle)
@@ -42,32 +151,19 @@ TransferJobInternal::~TransferJobInternal()
 
 TransferJob::~TransferJob()
 {
+    if (d->m_jobId)
+        removeFromOutstandingJobs(d->m_jobId);
 }
 
-static void __stdcall transferJobStatusCallback(HINTERNET internetHandle, DWORD_PTR context, DWORD internetStatus, LPVOID statusInformation, DWORD statusInformationLength)
+static void __stdcall transferJobStatusCallback(HINTERNET internetHandle, DWORD_PTR timerId, DWORD internetStatus, LPVOID statusInformation, DWORD statusInformationLength)
 {
-    TransferJob* job = (TransferJob*)context;
-
-    if (internetStatus == INTERNET_STATUS_HANDLE_CREATED)
-        job->d->m_resourceHandle = HINTERNET(LPINTERNET_ASYNC_RESULT(statusInformation)->dwResult);
-    else if (internetStatus == INTERNET_STATUS_REQUEST_COMPLETE) {
-        HINTERNET resourceHandle = job->d->m_resourceHandle;
-
-        char buffer[32728];
-        DWORD bytesRead = 0;
-        DWORD totalBytes = 0;
-        bool ok = false;
-        while ((ok = InternetReadFile(resourceHandle, buffer, 32728, &bytesRead)) && bytesRead) {
-            job->client()->receivedData(job, buffer, bytesRead);
-            totalBytes += bytesRead;
-            bytesRead = 0;
-        }
-         
-        if (ok) {
-            InternetCloseHandle(resourceHandle);
-            job->client()->receivedAllData(job, 0);
-            job->client()->receivedAllData(job);
-        }
+    switch (internetStatus) {
+    case INTERNET_STATUS_HANDLE_CREATED:
+    case INTERNET_STATUS_REQUEST_COMPLETE:
+        JobLoadStatus* jobLoadStatus = new JobLoadStatus;
+        jobLoadStatus->internetStatus = internetStatus;
+        jobLoadStatus->dwResult = LPINTERNET_ASYNC_RESULT(statusInformation)->dwResult;
+        PostMessage(transferJobWindowHandle, loadStatusMessage, (WPARAM)timerId, (LPARAM)jobLoadStatus);
     }
 }
 
@@ -96,13 +192,16 @@ bool TransferJob::start(DocLoader* docLoader)
         }
         static INTERNET_STATUS_CALLBACK callbackHandle = InternetSetStatusCallback(internetHandle, transferJobStatusCallback);
 
-        HINTERNET urlHandle =
-            InternetOpenUrlA(internetHandle, d->URL.url().ascii(), NULL, -1, 0, (DWORD_PTR)this);
+        initializeOffScreenTransferJobWindow();
+        d->m_jobId = addToOutstandingJobs(this);
+
+        HINTERNET urlHandle = InternetOpenUrlA(internetHandle, d->URL.url().ascii(), NULL, -1, 0, (DWORD_PTR)d->m_jobId);
 
         if (urlHandle == INVALID_HANDLE_VALUE) {
             delete this;
             return false;
         }
+        d->m_threadId = GetCurrentThreadId();
 
         return true;
     }
@@ -130,9 +229,17 @@ void TransferJob::fileLoadTimer(Timer<TransferJob>* timer)
 
 void TransferJob::cancel()
 {
-    d->m_fileLoadTimer.stop();
+    if (d->m_resourceHandle)
+        InternetCloseHandle(d->m_resourceHandle);
+    else
+        d->m_fileLoadTimer.stop();
+
     d->client->receivedAllData(this, 0);
     d->client->receivedAllData(this);
+
+    if (!d->m_resourceHandle)
+        // Async load canceled before we have a handle -- mark ourselves as in error, to be deleted later.
+        setError(1);
 }
 
 } // namespace WebCore
index 9b9863f..c314cb1 100644 (file)
@@ -34,6 +34,8 @@
 
 namespace WebCore {
 
+HINSTANCE Widget::instanceHandle = 0;
+
 class WidgetPrivate
 {
 public: