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 <comutil.h> 35 #include <WebKit/WebKitCOMAPI.h> 36 #include <wtf/HashMap.h> 37 #include <wtf/Vector.h> 38 #include <sstream> 39 40 41 using std::wstring; 42 using std::wiostream; 43 44 static inline wstring wstringFromBSTR(BSTR str) 45 { 46 return wstring(str, ::SysStringLen(str)); 47 } 48 49 wstring wstringFromInt(int i) 50 { 51 std::wostringstream ss; 52 ss << i; 53 return ss.str(); 54 } 55 56 typedef HashMap<unsigned long, wstring> IdentifierMap; 57 58 IdentifierMap& urlMap() 59 { 60 static IdentifierMap urlMap; 61 62 return urlMap; 63 } 64 65 static wstring descriptionSuitableForTestResult(unsigned long identifier) 66 { 67 IdentifierMap::iterator it = urlMap().find(identifier); 68 69 if (it == urlMap().end()) 70 return L"<unknown>"; 71 72 return urlSuitableForTestResult(it->second); 73 } 74 75 static wstring descriptionSuitableForTestResult(IWebURLRequest* request) 76 { 77 if (!request) 78 return L"(null)"; 79 80 BSTR urlBSTR; 81 if (FAILED(request->URL(&urlBSTR))) 82 return wstring(); 83 84 wstring url = urlSuitableForTestResult(wstringFromBSTR(urlBSTR)); 85 ::SysFreeString(urlBSTR); 86 87 BSTR mainDocumentURLBSTR; 88 if (FAILED(request->mainDocumentURL(&mainDocumentURLBSTR))) 89 return wstring(); 90 91 wstring mainDocumentURL = urlSuitableForTestResult(wstringFromBSTR(mainDocumentURLBSTR)); 92 ::SysFreeString(mainDocumentURLBSTR); 93 94 BSTR httpMethodBSTR; 95 if (FAILED(request->HTTPMethod(&httpMethodBSTR))) 96 return wstring(); 97 98 wstring httpMethod = wstringFromBSTR(httpMethodBSTR); 99 ::SysFreeString(httpMethodBSTR); 100 101 return L"<NSURLRequest URL " + url + L", main document URL " + mainDocumentURL + L", http method " + httpMethod + L">"; 102 } 103 104 static wstring descriptionSuitableForTestResult(IWebURLResponse* response) 105 { 106 if (!response) 107 return L"(null)"; 108 109 BSTR urlBSTR; 110 if (FAILED(response->URL(&urlBSTR))) 111 return wstring(); 112 113 wstring url = urlSuitableForTestResult(wstringFromBSTR(urlBSTR)); 114 ::SysFreeString(urlBSTR); 115 116 int statusCode = 0; 117 COMPtr<IWebHTTPURLResponse> httpResponse; 118 if (response && SUCCEEDED(response->QueryInterface(&httpResponse))) 119 httpResponse->statusCode(&statusCode); 120 121 return L"<NSURLResponse " + url + L", http status code " + wstringFromInt(statusCode) + L">"; 122 } 123 124 static wstring descriptionSuitableForTestResult(IWebError* error, unsigned long identifier) 125 { 126 wstring result = L"<NSError "; 127 128 BSTR domainSTR; 129 if (FAILED(error->domain(&domainSTR))) 130 return wstring(); 131 132 wstring domain = wstringFromBSTR(domainSTR); 133 ::SysFreeString(domainSTR); 134 135 int code; 136 if (FAILED(error->code(&code))) 137 return wstring(); 138 139 if (domain == L"CFURLErrorDomain") { 140 domain = L"NSURLErrorDomain"; 141 142 // Convert kCFURLErrorUnknown to NSURLErrorUnknown 143 if (code == -998) 144 code = -1; 145 } else if (domain == L"kCFErrorDomainWinSock") { 146 domain = L"NSURLErrorDomain"; 147 148 // Convert the winsock error code to an NSURLError code. 149 if (code == WSAEADDRNOTAVAIL) 150 code = -1004; // NSURLErrorCannotConnectToHose; 151 } 152 153 result += L"domain " + domain; 154 result += L", code " + wstringFromInt(code); 155 156 BSTR failingURLSTR; 157 if (FAILED(error->failingURL(&failingURLSTR))) 158 return wstring(); 159 160 wstring failingURL; 161 162 // If the error doesn't have a failing URL, we fake one by using the URL the resource had 163 // at creation time. This seems to work fine for now. 164 // See <rdar://problem/5064234> CFErrors should have failingURL key. 165 if (failingURLSTR) 166 failingURL = wstringFromBSTR(failingURLSTR); 167 else 168 failingURL = descriptionSuitableForTestResult(identifier); 169 170 ::SysFreeString(failingURLSTR); 171 172 result += L", failing URL \"" + urlSuitableForTestResult(failingURL) + L"\">"; 173 174 return result; 175 } 176 177 ResourceLoadDelegate::ResourceLoadDelegate() 178 : m_refCount(1) 179 { 180 } 181 182 ResourceLoadDelegate::~ResourceLoadDelegate() 183 { 184 } 185 186 HRESULT STDMETHODCALLTYPE ResourceLoadDelegate::QueryInterface(REFIID riid, void** ppvObject) 187 { 188 *ppvObject = 0; 189 if (IsEqualGUID(riid, IID_IUnknown)) 190 *ppvObject = static_cast<IWebResourceLoadDelegate*>(this); 191 else if (IsEqualGUID(riid, IID_IWebResourceLoadDelegate)) 192 *ppvObject = static_cast<IWebResourceLoadDelegate*>(this); 193 else 194 return E_NOINTERFACE; 195 196 AddRef(); 197 return S_OK; 198 } 199 200 ULONG STDMETHODCALLTYPE ResourceLoadDelegate::AddRef(void) 201 { 202 return ++m_refCount; 203 } 204 205 ULONG STDMETHODCALLTYPE ResourceLoadDelegate::Release(void) 206 { 207 ULONG newRef = --m_refCount; 208 if (!newRef) 209 delete(this); 210 211 return newRef; 212 } 213 214 HRESULT STDMETHODCALLTYPE ResourceLoadDelegate::identifierForInitialRequest( 215 /* [in] */ IWebView* webView, 216 /* [in] */ IWebURLRequest* request, 217 /* [in] */ IWebDataSource* dataSource, 218 /* [in] */ unsigned long identifier) 219 { 220 if (!done && gLayoutTestController->dumpResourceLoadCallbacks()) { 221 BSTR urlStr; 222 if (FAILED(request->URL(&urlStr))) 223 return E_FAIL; 224 225 urlMap().set(identifier, wstringFromBSTR(urlStr)); 226 } 227 228 return S_OK; 229 } 230 231 HRESULT STDMETHODCALLTYPE ResourceLoadDelegate::willSendRequest( 232 /* [in] */ IWebView* webView, 233 /* [in] */ unsigned long identifier, 234 /* [in] */ IWebURLRequest* request, 235 /* [in] */ IWebURLResponse* redirectResponse, 236 /* [in] */ IWebDataSource* dataSource, 237 /* [retval][out] */ IWebURLRequest **newRequest) 238 { 239 if (!done && gLayoutTestController->dumpResourceLoadCallbacks()) { 240 printf("%S - willSendRequest %S redirectResponse %S\n", 241 descriptionSuitableForTestResult(identifier).c_str(), 242 descriptionSuitableForTestResult(request).c_str(), 243 descriptionSuitableForTestResult(redirectResponse).c_str()); 244 } 245 246 if (!done && gLayoutTestController->willSendRequestReturnsNull()) { 247 *newRequest = 0; 248 return S_OK; 249 } 250 251 if (!done && gLayoutTestController->willSendRequestReturnsNullOnRedirect() && redirectResponse) { 252 printf("Returning null for this redirect\n"); 253 *newRequest = 0; 254 return S_OK; 255 } 256 257 request->AddRef(); 258 *newRequest = request; 259 return S_OK; 260 } 261 262 HRESULT STDMETHODCALLTYPE ResourceLoadDelegate::didReceiveAuthenticationChallenge( 263 /* [in] */ IWebView *webView, 264 /* [in] */ unsigned long identifier, 265 /* [in] */ IWebURLAuthenticationChallenge *challenge, 266 /* [in] */ IWebDataSource *dataSource) 267 { 268 if (!gLayoutTestController->handlesAuthenticationChallenges()) 269 return E_FAIL; 270 271 const char* user = gLayoutTestController->authenticationUsername().c_str(); 272 const char* password = gLayoutTestController->authenticationPassword().c_str(); 273 274 printf("%S - didReceiveAuthenticationChallenge - Responding with %s:%s\n", descriptionSuitableForTestResult(identifier).c_str(), user, password); 275 276 COMPtr<IWebURLAuthenticationChallengeSender> sender; 277 if (!challenge || FAILED(challenge->sender(&sender))) 278 return E_FAIL; 279 280 COMPtr<IWebURLCredential> credential; 281 if (FAILED(WebKitCreateInstance(CLSID_WebURLCredential, 0, IID_IWebURLCredential, (void**)&credential))) 282 return E_FAIL; 283 credential->initWithUser(_bstr_t(user), _bstr_t(password), WebURLCredentialPersistenceForSession); 284 285 sender->useCredential(credential.get(), challenge); 286 return S_OK; 287 } 288 289 HRESULT STDMETHODCALLTYPE ResourceLoadDelegate::didReceiveResponse( 290 /* [in] */ IWebView* webView, 291 /* [in] */ unsigned long identifier, 292 /* [in] */ IWebURLResponse* response, 293 /* [in] */ IWebDataSource* dataSource) 294 { 295 if (!done && gLayoutTestController->dumpResourceLoadCallbacks()) { 296 printf("%S - didReceiveResponse %S\n", 297 descriptionSuitableForTestResult(identifier).c_str(), 298 descriptionSuitableForTestResult(response).c_str()); 299 } 300 if (!done && gLayoutTestController->dumpResourceResponseMIMETypes()) { 301 BSTR mimeTypeBSTR; 302 if (FAILED(response->MIMEType(&mimeTypeBSTR))) 303 E_FAIL; 304 305 wstring mimeType = wstringFromBSTR(mimeTypeBSTR); 306 ::SysFreeString(mimeTypeBSTR); 307 308 BSTR urlBSTR; 309 if (FAILED(response->URL(&urlBSTR))) 310 E_FAIL; 311 312 wstring url = urlSuitableForTestResult(wstringFromBSTR(urlBSTR)); 313 ::SysFreeString(urlBSTR); 314 315 printf("%S has MIME type %S\n", url.c_str(), mimeType.c_str()); 316 } 317 318 return S_OK; 319 } 320 321 322 HRESULT STDMETHODCALLTYPE ResourceLoadDelegate::didFinishLoadingFromDataSource( 323 /* [in] */ IWebView* webView, 324 /* [in] */ unsigned long identifier, 325 /* [in] */ IWebDataSource* dataSource) 326 { 327 if (!done && gLayoutTestController->dumpResourceLoadCallbacks()) { 328 printf("%S - didFinishLoading\n", 329 descriptionSuitableForTestResult(identifier).c_str()), 330 urlMap().remove(identifier); 331 } 332 333 return S_OK; 334 } 335 336 HRESULT STDMETHODCALLTYPE ResourceLoadDelegate::didFailLoadingWithError( 337 /* [in] */ IWebView* webView, 338 /* [in] */ unsigned long identifier, 339 /* [in] */ IWebError* error, 340 /* [in] */ IWebDataSource* dataSource) 341 { 342 if (!done && gLayoutTestController->dumpResourceLoadCallbacks()) { 343 printf("%S - didFailLoadingWithError: %S\n", 344 descriptionSuitableForTestResult(identifier).c_str(), 345 descriptionSuitableForTestResult(error, identifier).c_str()); 346 urlMap().remove(identifier); 347 } 348 349 return S_OK; 350 } 351