1 /*
2 * Copyright (C) 2007 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 *
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
14 * its contributors may be used to endorse or promote products derived
15 * from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include "config.h"
30 #include "ResourceLoadDelegate.h"
31
32 #include "DumpRenderTree.h"
33 #include "LayoutTestController.h"
34 #include <WebKit/WebKitCOMAPI.h>
35 #include <comutil.h>
36 #include <sstream>
37 #include <tchar.h>
38 #include <wtf/Vector.h>
39
40 using namespace std;
41
wstringFromBSTR(BSTR str)42 static inline wstring wstringFromBSTR(BSTR str)
43 {
44 return wstring(str, ::SysStringLen(str));
45 }
46
wstringFromInt(int i)47 static inline wstring wstringFromInt(int i)
48 {
49 wostringstream ss;
50 ss << i;
51 return ss.str();
52 }
53
BSTRFromString(const string & str)54 static inline BSTR BSTRFromString(const string& str)
55 {
56 int length = ::MultiByteToWideChar(CP_UTF8, 0, str.c_str(), str.length(), 0, 0);
57 BSTR result = ::SysAllocStringLen(0, length);
58 ::MultiByteToWideChar(CP_UTF8, 0, str.c_str(), str.length(), result, length);
59 return result;
60 }
61
descriptionSuitableForTestResult(unsigned long identifier) const62 wstring ResourceLoadDelegate::descriptionSuitableForTestResult(unsigned long identifier) const
63 {
64 IdentifierMap::const_iterator it = m_urlMap.find(identifier);
65
66 if (it == m_urlMap.end())
67 return L"<unknown>";
68
69 return urlSuitableForTestResult(it->second);
70 }
71
descriptionSuitableForTestResult(IWebURLRequest * request)72 wstring ResourceLoadDelegate::descriptionSuitableForTestResult(IWebURLRequest* request)
73 {
74 if (!request)
75 return L"(null)";
76
77 BSTR urlBSTR;
78 if (FAILED(request->URL(&urlBSTR)))
79 return wstring();
80
81 wstring url = urlSuitableForTestResult(wstringFromBSTR(urlBSTR));
82 ::SysFreeString(urlBSTR);
83
84 BSTR mainDocumentURLBSTR;
85 if (FAILED(request->mainDocumentURL(&mainDocumentURLBSTR)))
86 return wstring();
87
88 wstring mainDocumentURL = urlSuitableForTestResult(wstringFromBSTR(mainDocumentURLBSTR));
89 ::SysFreeString(mainDocumentURLBSTR);
90
91 BSTR httpMethodBSTR;
92 if (FAILED(request->HTTPMethod(&httpMethodBSTR)))
93 return wstring();
94
95 wstring httpMethod = wstringFromBSTR(httpMethodBSTR);
96 ::SysFreeString(httpMethodBSTR);
97
98 return L"<NSURLRequest URL " + url + L", main document URL " + mainDocumentURL + L", http method " + httpMethod + L">";
99 }
100
descriptionSuitableForTestResult(IWebURLResponse * response)101 wstring ResourceLoadDelegate::descriptionSuitableForTestResult(IWebURLResponse* response)
102 {
103 if (!response)
104 return L"(null)";
105
106 BSTR urlBSTR;
107 if (FAILED(response->URL(&urlBSTR)))
108 return wstring();
109
110 wstring url = urlSuitableForTestResult(wstringFromBSTR(urlBSTR));
111 ::SysFreeString(urlBSTR);
112
113 int statusCode = 0;
114 COMPtr<IWebHTTPURLResponse> httpResponse;
115 if (response && SUCCEEDED(response->QueryInterface(&httpResponse)))
116 httpResponse->statusCode(&statusCode);
117
118 return L"<NSURLResponse " + url + L", http status code " + wstringFromInt(statusCode) + L">";
119 }
120
descriptionSuitableForTestResult(IWebError * error,unsigned long identifier) const121 wstring ResourceLoadDelegate::descriptionSuitableForTestResult(IWebError* error, unsigned long identifier) const
122 {
123 wstring result = L"<NSError ";
124
125 BSTR domainSTR;
126 if (FAILED(error->domain(&domainSTR)))
127 return wstring();
128
129 wstring domain = wstringFromBSTR(domainSTR);
130 ::SysFreeString(domainSTR);
131
132 int code;
133 if (FAILED(error->code(&code)))
134 return wstring();
135
136 if (domain == L"CFURLErrorDomain") {
137 domain = L"NSURLErrorDomain";
138
139 // Convert kCFURLErrorUnknown to NSURLErrorUnknown
140 if (code == -998)
141 code = -1;
142 } else if (domain == L"kCFErrorDomainWinSock") {
143 domain = L"NSURLErrorDomain";
144
145 // Convert the winsock error code to an NSURLError code.
146 if (code == WSAEADDRNOTAVAIL)
147 code = -1004; // NSURLErrorCannotConnectToHose;
148 }
149
150 result += L"domain " + domain;
151 result += L", code " + wstringFromInt(code);
152
153 BSTR failingURLSTR;
154 if (FAILED(error->failingURL(&failingURLSTR)))
155 return wstring();
156
157 wstring failingURL;
158
159 // If the error doesn't have a failing URL, we fake one by using the URL the resource had
160 // at creation time. This seems to work fine for now.
161 // See <rdar://problem/5064234> CFErrors should have failingURL key.
162 if (failingURLSTR)
163 failingURL = wstringFromBSTR(failingURLSTR);
164 else
165 failingURL = descriptionSuitableForTestResult(identifier);
166
167 ::SysFreeString(failingURLSTR);
168
169 result += L", failing URL \"" + urlSuitableForTestResult(failingURL) + L"\">";
170
171 return result;
172 }
173
ResourceLoadDelegate()174 ResourceLoadDelegate::ResourceLoadDelegate()
175 : m_refCount(1)
176 {
177 }
178
~ResourceLoadDelegate()179 ResourceLoadDelegate::~ResourceLoadDelegate()
180 {
181 }
182
QueryInterface(REFIID riid,void ** ppvObject)183 HRESULT STDMETHODCALLTYPE ResourceLoadDelegate::QueryInterface(REFIID riid, void** ppvObject)
184 {
185 *ppvObject = 0;
186 if (IsEqualGUID(riid, IID_IUnknown))
187 *ppvObject = static_cast<IWebResourceLoadDelegate*>(this);
188 else if (IsEqualGUID(riid, IID_IWebResourceLoadDelegate))
189 *ppvObject = static_cast<IWebResourceLoadDelegate*>(this);
190 else if (IsEqualGUID(riid, IID_IWebResourceLoadDelegatePrivate2))
191 *ppvObject = static_cast<IWebResourceLoadDelegatePrivate2*>(this);
192 else
193 return E_NOINTERFACE;
194
195 AddRef();
196 return S_OK;
197 }
198
AddRef(void)199 ULONG STDMETHODCALLTYPE ResourceLoadDelegate::AddRef(void)
200 {
201 return ++m_refCount;
202 }
203
Release(void)204 ULONG STDMETHODCALLTYPE ResourceLoadDelegate::Release(void)
205 {
206 ULONG newRef = --m_refCount;
207 if (!newRef)
208 delete(this);
209
210 return newRef;
211 }
212
identifierForInitialRequest(IWebView * webView,IWebURLRequest * request,IWebDataSource * dataSource,unsigned long identifier)213 HRESULT STDMETHODCALLTYPE ResourceLoadDelegate::identifierForInitialRequest(
214 /* [in] */ IWebView* webView,
215 /* [in] */ IWebURLRequest* request,
216 /* [in] */ IWebDataSource* dataSource,
217 /* [in] */ unsigned long identifier)
218 {
219 if (!done && gLayoutTestController->dumpResourceLoadCallbacks()) {
220 BSTR urlStr;
221 if (FAILED(request->URL(&urlStr)))
222 return E_FAIL;
223
224 ASSERT(!urlMap().contains(identifier));
225 urlMap().set(identifier, wstringFromBSTR(urlStr));
226 }
227
228 return S_OK;
229 }
230
removeIdentifierForRequest(IWebView * webView,unsigned long identifier)231 HRESULT STDMETHODCALLTYPE ResourceLoadDelegate::removeIdentifierForRequest(
232 /* [in] */ IWebView* webView,
233 /* [in] */ unsigned long identifier)
234 {
235 urlMap().remove(identifier);
236
237 return S_OK;
238 }
239
willSendRequest(IWebView * webView,unsigned long identifier,IWebURLRequest * request,IWebURLResponse * redirectResponse,IWebDataSource * dataSource,IWebURLRequest ** newRequest)240 HRESULT STDMETHODCALLTYPE ResourceLoadDelegate::willSendRequest(
241 /* [in] */ IWebView* webView,
242 /* [in] */ unsigned long identifier,
243 /* [in] */ IWebURLRequest* request,
244 /* [in] */ IWebURLResponse* redirectResponse,
245 /* [in] */ IWebDataSource* dataSource,
246 /* [retval][out] */ IWebURLRequest **newRequest)
247 {
248 if (!done && gLayoutTestController->dumpResourceLoadCallbacks()) {
249 printf("%S - willSendRequest %S redirectResponse %S\n",
250 descriptionSuitableForTestResult(identifier).c_str(),
251 descriptionSuitableForTestResult(request).c_str(),
252 descriptionSuitableForTestResult(redirectResponse).c_str());
253 }
254
255 if (!done && !gLayoutTestController->deferMainResourceDataLoad()) {
256 COMPtr<IWebDataSourcePrivate> dataSourcePrivate(Query, dataSource);
257 if (!dataSourcePrivate)
258 return E_FAIL;
259 dataSourcePrivate->setDeferMainResourceDataLoad(FALSE);
260 }
261
262 if (!done && gLayoutTestController->willSendRequestReturnsNull()) {
263 *newRequest = 0;
264 return S_OK;
265 }
266
267 if (!done && gLayoutTestController->willSendRequestReturnsNullOnRedirect() && redirectResponse) {
268 printf("Returning null for this redirect\n");
269 *newRequest = 0;
270 return S_OK;
271 }
272
273 IWebMutableURLRequest* requestCopy = 0;
274 request->mutableCopy(&requestCopy);
275 const set<string>& clearHeaders = gLayoutTestController->willSendRequestClearHeaders();
276 for (set<string>::const_iterator header = clearHeaders.begin(); header != clearHeaders.end(); ++header) {
277 BSTR bstrHeader = BSTRFromString(*header);
278 requestCopy->setValue(0, bstrHeader);
279 SysFreeString(bstrHeader);
280 }
281
282 *newRequest = requestCopy;
283 return S_OK;
284 }
285
didReceiveAuthenticationChallenge(IWebView * webView,unsigned long identifier,IWebURLAuthenticationChallenge * challenge,IWebDataSource * dataSource)286 HRESULT STDMETHODCALLTYPE ResourceLoadDelegate::didReceiveAuthenticationChallenge(
287 /* [in] */ IWebView *webView,
288 /* [in] */ unsigned long identifier,
289 /* [in] */ IWebURLAuthenticationChallenge *challenge,
290 /* [in] */ IWebDataSource *dataSource)
291 {
292 COMPtr<IWebURLAuthenticationChallengeSender> sender;
293 if (!challenge || FAILED(challenge->sender(&sender)))
294 return E_FAIL;
295
296 if (!gLayoutTestController->handlesAuthenticationChallenges()) {
297 printf("%S - didReceiveAuthenticationChallenge - Simulating cancelled authentication sheet\n", descriptionSuitableForTestResult(identifier).c_str());
298 sender->continueWithoutCredentialForAuthenticationChallenge(challenge);
299 return S_OK;
300 }
301
302 const char* user = gLayoutTestController->authenticationUsername().c_str();
303 const char* password = gLayoutTestController->authenticationPassword().c_str();
304
305 printf("%S - didReceiveAuthenticationChallenge - Responding with %s:%s\n", descriptionSuitableForTestResult(identifier).c_str(), user, password);
306
307 COMPtr<IWebURLCredential> credential;
308 if (FAILED(WebKitCreateInstance(CLSID_WebURLCredential, 0, IID_IWebURLCredential, (void**)&credential)))
309 return E_FAIL;
310 credential->initWithUser(_bstr_t(user), _bstr_t(password), WebURLCredentialPersistenceForSession);
311
312 sender->useCredential(credential.get(), challenge);
313 return S_OK;
314 }
315
didReceiveResponse(IWebView * webView,unsigned long identifier,IWebURLResponse * response,IWebDataSource * dataSource)316 HRESULT STDMETHODCALLTYPE ResourceLoadDelegate::didReceiveResponse(
317 /* [in] */ IWebView* webView,
318 /* [in] */ unsigned long identifier,
319 /* [in] */ IWebURLResponse* response,
320 /* [in] */ IWebDataSource* dataSource)
321 {
322 if (!done && gLayoutTestController->dumpResourceLoadCallbacks()) {
323 printf("%S - didReceiveResponse %S\n",
324 descriptionSuitableForTestResult(identifier).c_str(),
325 descriptionSuitableForTestResult(response).c_str());
326 }
327 if (!done && gLayoutTestController->dumpResourceResponseMIMETypes()) {
328 BSTR mimeTypeBSTR;
329 if (FAILED(response->MIMEType(&mimeTypeBSTR)))
330 E_FAIL;
331
332 wstring mimeType = wstringFromBSTR(mimeTypeBSTR);
333 ::SysFreeString(mimeTypeBSTR);
334
335 BSTR urlBSTR;
336 if (FAILED(response->URL(&urlBSTR)))
337 E_FAIL;
338
339 wstring url = wstringFromBSTR(urlBSTR);
340 ::SysFreeString(urlBSTR);
341
342 printf("%S has MIME type %S\n", lastPathComponent(url).c_str(), mimeType.c_str());
343 }
344
345 return S_OK;
346 }
347
348
didFinishLoadingFromDataSource(IWebView * webView,unsigned long identifier,IWebDataSource * dataSource)349 HRESULT STDMETHODCALLTYPE ResourceLoadDelegate::didFinishLoadingFromDataSource(
350 /* [in] */ IWebView* webView,
351 /* [in] */ unsigned long identifier,
352 /* [in] */ IWebDataSource* dataSource)
353 {
354 if (!done && gLayoutTestController->dumpResourceLoadCallbacks()) {
355 printf("%S - didFinishLoading\n",
356 descriptionSuitableForTestResult(identifier).c_str());
357 }
358
359 removeIdentifierForRequest(webView, identifier);
360
361 return S_OK;
362 }
363
didFailLoadingWithError(IWebView * webView,unsigned long identifier,IWebError * error,IWebDataSource * dataSource)364 HRESULT STDMETHODCALLTYPE ResourceLoadDelegate::didFailLoadingWithError(
365 /* [in] */ IWebView* webView,
366 /* [in] */ unsigned long identifier,
367 /* [in] */ IWebError* error,
368 /* [in] */ IWebDataSource* dataSource)
369 {
370 if (!done && gLayoutTestController->dumpResourceLoadCallbacks()) {
371 printf("%S - didFailLoadingWithError: %S\n",
372 descriptionSuitableForTestResult(identifier).c_str(),
373 descriptionSuitableForTestResult(error, identifier).c_str());
374 }
375
376 removeIdentifierForRequest(webView, identifier);
377
378 return S_OK;
379 }
380