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 42 static inline wstring wstringFromBSTR(BSTR str) 43 { 44 return wstring(str, ::SysStringLen(str)); 45 } 46 47 static inline wstring wstringFromInt(int i) 48 { 49 wostringstream ss; 50 ss << i; 51 return ss.str(); 52 } 53 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 62 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 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 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 121 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 174 ResourceLoadDelegate::ResourceLoadDelegate() 175 : m_refCount(1) 176 { 177 } 178 179 ResourceLoadDelegate::~ResourceLoadDelegate() 180 { 181 } 182 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 199 ULONG STDMETHODCALLTYPE ResourceLoadDelegate::AddRef(void) 200 { 201 return ++m_refCount; 202 } 203 204 ULONG STDMETHODCALLTYPE ResourceLoadDelegate::Release(void) 205 { 206 ULONG newRef = --m_refCount; 207 if (!newRef) 208 delete(this); 209 210 return newRef; 211 } 212 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 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 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 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 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 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 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