Home | History | Annotate | Download | only in win
      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