/* * Copyright (C) 2007 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" #include "ResourceLoadDelegate.h" #include "DumpRenderTree.h" #include "LayoutTestController.h" #include <WebKit/WebKitCOMAPI.h> #include <comutil.h> #include <sstream> #include <tchar.h> #include <wtf/Vector.h> using namespace std; static inline wstring wstringFromBSTR(BSTR str) { return wstring(str, ::SysStringLen(str)); } static inline wstring wstringFromInt(int i) { wostringstream ss; ss << i; return ss.str(); } static inline BSTR BSTRFromString(const string& str) { int length = ::MultiByteToWideChar(CP_UTF8, 0, str.c_str(), str.length(), 0, 0); BSTR result = ::SysAllocStringLen(0, length); ::MultiByteToWideChar(CP_UTF8, 0, str.c_str(), str.length(), result, length); return result; } wstring ResourceLoadDelegate::descriptionSuitableForTestResult(unsigned long identifier) const { IdentifierMap::const_iterator it = m_urlMap.find(identifier); if (it == m_urlMap.end()) return L"<unknown>"; return urlSuitableForTestResult(it->second); } wstring ResourceLoadDelegate::descriptionSuitableForTestResult(IWebURLRequest* request) { if (!request) return L"(null)"; BSTR urlBSTR; if (FAILED(request->URL(&urlBSTR))) return wstring(); wstring url = urlSuitableForTestResult(wstringFromBSTR(urlBSTR)); ::SysFreeString(urlBSTR); BSTR mainDocumentURLBSTR; if (FAILED(request->mainDocumentURL(&mainDocumentURLBSTR))) return wstring(); wstring mainDocumentURL = urlSuitableForTestResult(wstringFromBSTR(mainDocumentURLBSTR)); ::SysFreeString(mainDocumentURLBSTR); BSTR httpMethodBSTR; if (FAILED(request->HTTPMethod(&httpMethodBSTR))) return wstring(); wstring httpMethod = wstringFromBSTR(httpMethodBSTR); ::SysFreeString(httpMethodBSTR); return L"<NSURLRequest URL " + url + L", main document URL " + mainDocumentURL + L", http method " + httpMethod + L">"; } wstring ResourceLoadDelegate::descriptionSuitableForTestResult(IWebURLResponse* response) { if (!response) return L"(null)"; BSTR urlBSTR; if (FAILED(response->URL(&urlBSTR))) return wstring(); wstring url = urlSuitableForTestResult(wstringFromBSTR(urlBSTR)); ::SysFreeString(urlBSTR); int statusCode = 0; COMPtr<IWebHTTPURLResponse> httpResponse; if (response && SUCCEEDED(response->QueryInterface(&httpResponse))) httpResponse->statusCode(&statusCode); return L"<NSURLResponse " + url + L", http status code " + wstringFromInt(statusCode) + L">"; } wstring ResourceLoadDelegate::descriptionSuitableForTestResult(IWebError* error, unsigned long identifier) const { wstring result = L"<NSError "; BSTR domainSTR; if (FAILED(error->domain(&domainSTR))) return wstring(); wstring domain = wstringFromBSTR(domainSTR); ::SysFreeString(domainSTR); int code; if (FAILED(error->code(&code))) return wstring(); if (domain == L"CFURLErrorDomain") { domain = L"NSURLErrorDomain"; // Convert kCFURLErrorUnknown to NSURLErrorUnknown if (code == -998) code = -1; } else if (domain == L"kCFErrorDomainWinSock") { domain = L"NSURLErrorDomain"; // Convert the winsock error code to an NSURLError code. if (code == WSAEADDRNOTAVAIL) code = -1004; // NSURLErrorCannotConnectToHose; } result += L"domain " + domain; result += L", code " + wstringFromInt(code); BSTR failingURLSTR; if (FAILED(error->failingURL(&failingURLSTR))) return wstring(); wstring failingURL; // If the error doesn't have a failing URL, we fake one by using the URL the resource had // at creation time. This seems to work fine for now. // See <rdar://problem/5064234> CFErrors should have failingURL key. if (failingURLSTR) failingURL = wstringFromBSTR(failingURLSTR); else failingURL = descriptionSuitableForTestResult(identifier); ::SysFreeString(failingURLSTR); result += L", failing URL \"" + urlSuitableForTestResult(failingURL) + L"\">"; return result; } ResourceLoadDelegate::ResourceLoadDelegate() : m_refCount(1) { } ResourceLoadDelegate::~ResourceLoadDelegate() { } HRESULT STDMETHODCALLTYPE ResourceLoadDelegate::QueryInterface(REFIID riid, void** ppvObject) { *ppvObject = 0; if (IsEqualGUID(riid, IID_IUnknown)) *ppvObject = static_cast<IWebResourceLoadDelegate*>(this); else if (IsEqualGUID(riid, IID_IWebResourceLoadDelegate)) *ppvObject = static_cast<IWebResourceLoadDelegate*>(this); else if (IsEqualGUID(riid, IID_IWebResourceLoadDelegatePrivate2)) *ppvObject = static_cast<IWebResourceLoadDelegatePrivate2*>(this); else return E_NOINTERFACE; AddRef(); return S_OK; } ULONG STDMETHODCALLTYPE ResourceLoadDelegate::AddRef(void) { return ++m_refCount; } ULONG STDMETHODCALLTYPE ResourceLoadDelegate::Release(void) { ULONG newRef = --m_refCount; if (!newRef) delete(this); return newRef; } HRESULT STDMETHODCALLTYPE ResourceLoadDelegate::identifierForInitialRequest( /* [in] */ IWebView* webView, /* [in] */ IWebURLRequest* request, /* [in] */ IWebDataSource* dataSource, /* [in] */ unsigned long identifier) { if (!done && gLayoutTestController->dumpResourceLoadCallbacks()) { BSTR urlStr; if (FAILED(request->URL(&urlStr))) return E_FAIL; ASSERT(!urlMap().contains(identifier)); urlMap().set(identifier, wstringFromBSTR(urlStr)); } return S_OK; } HRESULT STDMETHODCALLTYPE ResourceLoadDelegate::removeIdentifierForRequest( /* [in] */ IWebView* webView, /* [in] */ unsigned long identifier) { urlMap().remove(identifier); return S_OK; } HRESULT STDMETHODCALLTYPE ResourceLoadDelegate::willSendRequest( /* [in] */ IWebView* webView, /* [in] */ unsigned long identifier, /* [in] */ IWebURLRequest* request, /* [in] */ IWebURLResponse* redirectResponse, /* [in] */ IWebDataSource* dataSource, /* [retval][out] */ IWebURLRequest **newRequest) { if (!done && gLayoutTestController->dumpResourceLoadCallbacks()) { printf("%S - willSendRequest %S redirectResponse %S\n", descriptionSuitableForTestResult(identifier).c_str(), descriptionSuitableForTestResult(request).c_str(), descriptionSuitableForTestResult(redirectResponse).c_str()); } if (!done && !gLayoutTestController->deferMainResourceDataLoad()) { COMPtr<IWebDataSourcePrivate> dataSourcePrivate(Query, dataSource); if (!dataSourcePrivate) return E_FAIL; dataSourcePrivate->setDeferMainResourceDataLoad(FALSE); } if (!done && gLayoutTestController->willSendRequestReturnsNull()) { *newRequest = 0; return S_OK; } if (!done && gLayoutTestController->willSendRequestReturnsNullOnRedirect() && redirectResponse) { printf("Returning null for this redirect\n"); *newRequest = 0; return S_OK; } IWebMutableURLRequest* requestCopy = 0; request->mutableCopy(&requestCopy); const set<string>& clearHeaders = gLayoutTestController->willSendRequestClearHeaders(); for (set<string>::const_iterator header = clearHeaders.begin(); header != clearHeaders.end(); ++header) { BSTR bstrHeader = BSTRFromString(*header); requestCopy->setValue(0, bstrHeader); SysFreeString(bstrHeader); } *newRequest = requestCopy; return S_OK; } HRESULT STDMETHODCALLTYPE ResourceLoadDelegate::didReceiveAuthenticationChallenge( /* [in] */ IWebView *webView, /* [in] */ unsigned long identifier, /* [in] */ IWebURLAuthenticationChallenge *challenge, /* [in] */ IWebDataSource *dataSource) { COMPtr<IWebURLAuthenticationChallengeSender> sender; if (!challenge || FAILED(challenge->sender(&sender))) return E_FAIL; if (!gLayoutTestController->handlesAuthenticationChallenges()) { printf("%S - didReceiveAuthenticationChallenge - Simulating cancelled authentication sheet\n", descriptionSuitableForTestResult(identifier).c_str()); sender->continueWithoutCredentialForAuthenticationChallenge(challenge); return S_OK; } const char* user = gLayoutTestController->authenticationUsername().c_str(); const char* password = gLayoutTestController->authenticationPassword().c_str(); printf("%S - didReceiveAuthenticationChallenge - Responding with %s:%s\n", descriptionSuitableForTestResult(identifier).c_str(), user, password); COMPtr<IWebURLCredential> credential; if (FAILED(WebKitCreateInstance(CLSID_WebURLCredential, 0, IID_IWebURLCredential, (void**)&credential))) return E_FAIL; credential->initWithUser(_bstr_t(user), _bstr_t(password), WebURLCredentialPersistenceForSession); sender->useCredential(credential.get(), challenge); return S_OK; } HRESULT STDMETHODCALLTYPE ResourceLoadDelegate::didReceiveResponse( /* [in] */ IWebView* webView, /* [in] */ unsigned long identifier, /* [in] */ IWebURLResponse* response, /* [in] */ IWebDataSource* dataSource) { if (!done && gLayoutTestController->dumpResourceLoadCallbacks()) { printf("%S - didReceiveResponse %S\n", descriptionSuitableForTestResult(identifier).c_str(), descriptionSuitableForTestResult(response).c_str()); } if (!done && gLayoutTestController->dumpResourceResponseMIMETypes()) { BSTR mimeTypeBSTR; if (FAILED(response->MIMEType(&mimeTypeBSTR))) E_FAIL; wstring mimeType = wstringFromBSTR(mimeTypeBSTR); ::SysFreeString(mimeTypeBSTR); BSTR urlBSTR; if (FAILED(response->URL(&urlBSTR))) E_FAIL; wstring url = wstringFromBSTR(urlBSTR); ::SysFreeString(urlBSTR); printf("%S has MIME type %S\n", lastPathComponent(url).c_str(), mimeType.c_str()); } return S_OK; } HRESULT STDMETHODCALLTYPE ResourceLoadDelegate::didFinishLoadingFromDataSource( /* [in] */ IWebView* webView, /* [in] */ unsigned long identifier, /* [in] */ IWebDataSource* dataSource) { if (!done && gLayoutTestController->dumpResourceLoadCallbacks()) { printf("%S - didFinishLoading\n", descriptionSuitableForTestResult(identifier).c_str()); } removeIdentifierForRequest(webView, identifier); return S_OK; } HRESULT STDMETHODCALLTYPE ResourceLoadDelegate::didFailLoadingWithError( /* [in] */ IWebView* webView, /* [in] */ unsigned long identifier, /* [in] */ IWebError* error, /* [in] */ IWebDataSource* dataSource) { if (!done && gLayoutTestController->dumpResourceLoadCallbacks()) { printf("%S - didFailLoadingWithError: %S\n", descriptionSuitableForTestResult(identifier).c_str(), descriptionSuitableForTestResult(error, identifier).c_str()); } removeIdentifierForRequest(webView, identifier); return S_OK; }