2 * Copyright (C) 2004, 2006 Apple Computer, Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
27 #include "ResourceLoader.h"
28 #include "ResourceLoaderInternal.h"
29 #include "ResourceLoaderWin.h"
32 #include "DocLoader.h"
40 static unsigned transferJobId = 0;
41 static HashMap<int, ResourceLoader*>* jobIdMap = 0;
43 static HWND transferJobWindowHandle = 0;
44 static UINT loadStatusMessage = 0;
45 const LPCWSTR kResourceLoaderWindowClassName = L"ResourceLoaderWindowClass";
47 static int addToOutstandingJobs(ResourceLoader* job)
50 jobIdMap = new HashMap<int, ResourceLoader*>;
52 jobIdMap->set(transferJobId, job);
56 static void removeFromOutstandingJobs(int jobId)
60 jobIdMap->remove(jobId);
63 static ResourceLoader* lookupResourceLoader(int jobId)
67 return jobIdMap->get(jobId);
70 struct JobLoadStatus {
75 LRESULT CALLBACK ResourceLoaderWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
77 if (message == loadStatusMessage) {
78 JobLoadStatus* jobLoadStatus = (JobLoadStatus*)lParam;
79 DWORD internetStatus = jobLoadStatus->internetStatus;
80 DWORD_PTR dwResult = jobLoadStatus->dwResult;
84 // If we get a message about a job we no longer know about (already deleted), ignore it.
85 unsigned jobId = (unsigned)wParam;
86 ResourceLoader* job = lookupResourceLoader(jobId);
90 ASSERT(job->d->m_jobId == jobId);
91 ASSERT(job->d->m_threadId == GetCurrentThreadId());
93 if (internetStatus == INTERNET_STATUS_HANDLE_CREATED) {
94 if (!job->d->m_resourceHandle) {
95 job->d->m_resourceHandle = HINTERNET(dwResult);
96 if (job->d->status != 0) {
97 // We were canceled before Windows actually created a handle for us, close and delete now.
98 InternetCloseHandle(job->d->m_resourceHandle);
102 if (job->method() == "POST") {
103 // FIXME: Too late to set referrer properly.
104 DeprecatedString urlStr = job->d->URL.path();
105 int fragmentIndex = urlStr.find('#');
106 if (fragmentIndex != -1)
107 urlStr = urlStr.left(fragmentIndex);
108 static LPCSTR accept[2]={"*/*", NULL};
109 HINTERNET urlHandle = HttpOpenRequestA(job->d->m_resourceHandle,
110 "POST", urlStr.latin1(), 0, 0, accept,
111 INTERNET_FLAG_KEEP_CONNECTION |
112 INTERNET_FLAG_FORMS_SUBMIT |
113 INTERNET_FLAG_RELOAD | INTERNET_FLAG_NO_CACHE_WRITE,
114 (DWORD_PTR)job->d->m_jobId);
115 if (urlHandle == INVALID_HANDLE_VALUE) {
116 InternetCloseHandle(job->d->m_resourceHandle);
120 } else if (!job->d->m_secondaryHandle) {
121 assert(job->method() == "POST");
122 job->d->m_secondaryHandle = HINTERNET(dwResult);
124 // Need to actually send the request now.
125 String headers = "Content-Type: application/x-www-form-urlencoded\n";
126 headers += "Referer: ";
127 headers += job->d->m_postReferrer;
129 String formData = job->postData().flattenToString();
130 INTERNET_BUFFERSA buffers;
131 memset(&buffers, 0, sizeof(buffers));
132 buffers.dwStructSize = sizeof(INTERNET_BUFFERSA);
133 buffers.lpcszHeader = headers.latin1();
134 buffers.dwHeadersLength = headers.length();
135 buffers.dwBufferTotal = formData.length();
137 job->d->m_bytesRemainingToWrite = formData.length();
138 job->d->m_formDataString = (char*)malloc(formData.length());
139 job->d->m_formDataLength = formData.length();
140 strncpy(job->d->m_formDataString, formData.latin1(), formData.length());
141 job->d->m_writing = true;
142 HttpSendRequestExA(job->d->m_secondaryHandle, &buffers, 0, 0, (DWORD_PTR)job->d->m_jobId);
144 } else if (internetStatus == INTERNET_STATUS_REQUEST_COMPLETE) {
145 if (job->d->m_writing) {
147 InternetWriteFile(job->d->m_secondaryHandle,
148 job->d->m_formDataString + (job->d->m_formDataLength - job->d->m_bytesRemainingToWrite),
149 job->d->m_bytesRemainingToWrite,
151 job->d->m_bytesRemainingToWrite -= bytesWritten;
152 if (!job->d->m_bytesRemainingToWrite) {
154 job->d->m_writing = false;
155 HttpEndRequest(job->d->m_secondaryHandle, 0, 0, (DWORD_PTR)job->d->m_jobId);
156 free(job->d->m_formDataString);
157 job->d->m_formDataString = 0;
162 HINTERNET handle = (job->method() == "POST") ? job->d->m_secondaryHandle : job->d->m_resourceHandle;
165 static const int bufferSize = 32768;
166 char buffer[bufferSize];
167 INTERNET_BUFFERSA buffers;
168 buffers.dwStructSize = sizeof(INTERNET_BUFFERSA);
169 buffers.lpvBuffer = buffer;
170 buffers.dwBufferLength = bufferSize;
172 bool receivedAnyData = false;
173 while ((ok = InternetReadFileExA(handle, &buffers, IRF_NO_WAIT, (DWORD_PTR)job)) && buffers.dwBufferLength) {
174 if (!receivedAnyData) {
175 receivedAnyData = true;
176 job->client()->receivedResponse(job, 0);
178 job->client()->receivedData(job, buffer, buffers.dwBufferLength);
179 buffers.dwBufferLength = bufferSize;
182 PlatformDataStruct platformData;
183 platformData.errorString = 0;
184 platformData.error = 0;
185 platformData.loaded = ok;
188 int error = GetLastError();
189 if (error == ERROR_IO_PENDING)
192 DWORD errorStringChars = 0;
193 if (!InternetGetLastResponseInfo(&platformData.error, 0, &errorStringChars)) {
194 if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
195 platformData.errorString = new TCHAR[errorStringChars];
196 InternetGetLastResponseInfo(&platformData.error, platformData.errorString, &errorStringChars);
199 _RPTF1(_CRT_WARN, "Load error: %i\n", error);
203 if (job->d->m_secondaryHandle)
204 InternetCloseHandle(job->d->m_secondaryHandle);
205 InternetCloseHandle(job->d->m_resourceHandle);
207 job->client()->receivedAllData(job, &platformData);
208 job->client()->receivedAllData(job);
212 return DefWindowProc(hWnd, message, wParam, lParam);
216 static void initializeOffScreenResourceLoaderWindow()
218 if (transferJobWindowHandle)
222 memset(&wcex, 0, sizeof(WNDCLASSEX));
223 wcex.cbSize = sizeof(WNDCLASSEX);
224 wcex.lpfnWndProc = ResourceLoaderWndProc;
225 wcex.hInstance = Widget::instanceHandle;
226 wcex.lpszClassName = kResourceLoaderWindowClassName;
227 RegisterClassEx(&wcex);
229 transferJobWindowHandle = CreateWindow(kResourceLoaderWindowClassName, 0, 0, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
230 HWND_MESSAGE, 0, Widget::instanceHandle, 0);
231 loadStatusMessage = RegisterWindowMessage(L"com.apple.WebKit.ResourceLoaderLoadStatus");
234 ResourceLoaderInternal::~ResourceLoaderInternal()
236 if (m_fileHandle != INVALID_HANDLE_VALUE)
237 CloseHandle(m_fileHandle);
240 ResourceLoader::~ResourceLoader()
243 removeFromOutstandingJobs(d->m_jobId);
246 static void __stdcall transferJobStatusCallback(HINTERNET internetHandle, DWORD_PTR timerId, DWORD internetStatus, LPVOID statusInformation, DWORD statusInformationLength)
248 switch (internetStatus) {
249 case INTERNET_STATUS_HANDLE_CREATED:
250 case INTERNET_STATUS_REQUEST_COMPLETE:
251 JobLoadStatus* jobLoadStatus = new JobLoadStatus;
252 jobLoadStatus->internetStatus = internetStatus;
253 jobLoadStatus->dwResult = LPINTERNET_ASYNC_RESULT(statusInformation)->dwResult;
254 PostMessage(transferJobWindowHandle, loadStatusMessage, (WPARAM)timerId, (LPARAM)jobLoadStatus);
258 bool ResourceLoader::start(DocLoader* docLoader)
260 if (d->URL.isLocalFile()) {
261 DeprecatedString path = d->URL.path();
262 // windows does not enjoy a leading slash on paths
265 d->m_fileHandle = CreateFileA(path.ascii(), GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
267 // FIXME: perhaps this error should be reported asynchronously for
269 if (d->m_fileHandle == INVALID_HANDLE_VALUE) {
274 d->m_fileLoadTimer.startOneShot(0.0);
277 static HINTERNET internetHandle = 0;
278 if (!internetHandle) {
279 String userAgentStr = docLoader->frame()->userAgent() + String("", 1);
280 LPCWSTR userAgent = reinterpret_cast<const WCHAR*>(userAgentStr.characters());
281 // leak the Internet for now
282 internetHandle = InternetOpen(userAgent, INTERNET_OPEN_TYPE_PRECONFIG, 0, 0, INTERNET_FLAG_ASYNC);
284 if (!internetHandle) {
288 static INTERNET_STATUS_CALLBACK callbackHandle = InternetSetStatusCallback(internetHandle, transferJobStatusCallback);
290 initializeOffScreenResourceLoaderWindow();
291 d->m_jobId = addToOutstandingJobs(this);
293 // For form posting, we can't use InternetOpenURL. We have to use InternetConnect followed by
296 String referrer = docLoader->frame()->referrer();
297 if (method() == "POST") {
298 d->m_postReferrer = referrer;
299 DeprecatedString host = d->URL.host();
301 urlHandle = InternetConnectA(internetHandle, host.ascii(), d->URL.port(), 0, 0,
302 INTERNET_SERVICE_HTTP, 0, (DWORD_PTR)d->m_jobId);
304 DeprecatedString urlStr = d->URL.url();
305 int fragmentIndex = urlStr.find('#');
306 if (fragmentIndex != -1)
307 urlStr = urlStr.left(fragmentIndex);
309 if (!referrer.isEmpty())
310 headers += String("Referer: ") + referrer + "\r\n";
312 urlHandle = InternetOpenUrlA(internetHandle, urlStr.ascii(), headers.latin1(), headers.length(),
313 INTERNET_FLAG_KEEP_CONNECTION, (DWORD_PTR)d->m_jobId);
316 if (urlHandle == INVALID_HANDLE_VALUE) {
320 d->m_threadId = GetCurrentThreadId();
326 void ResourceLoader::fileLoadTimer(Timer<ResourceLoader>* timer)
332 const int bufferSize = 8192;
333 char buffer[bufferSize];
334 result = ReadFile(d->m_fileHandle, &buffer, bufferSize, &bytesRead, NULL);
335 if (result && bytesRead)
336 d->client->receivedData(this, buffer, bytesRead);
337 // Check for end of file.
338 } while (result && bytesRead);
340 // FIXME: handle errors better
342 CloseHandle(d->m_fileHandle);
343 d->m_fileHandle = INVALID_HANDLE_VALUE;
345 PlatformDataStruct platformData;
346 platformData.errorString = 0;
347 platformData.error = 0;
348 platformData.loaded = TRUE;
350 d->client->receivedAllData(this, &platformData);
351 d->client->receivedAllData(this);
354 void ResourceLoader::cancel()
356 if (d->m_resourceHandle)
357 InternetCloseHandle(d->m_resourceHandle);
359 d->m_fileLoadTimer.stop();
361 PlatformDataStruct platformData;
362 platformData.errorString = 0;
363 platformData.error = 0;
364 platformData.loaded = FALSE;
366 d->client->receivedAllData(this, &platformData);
367 d->client->receivedAllData(this);
369 if (!d->m_resourceHandle)
370 // Async load canceled before we have a handle -- mark ourselves as in error, to be deleted later.
374 } // namespace WebCore