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