• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
wstringFromBSTR(BSTR str)42 static inline wstring wstringFromBSTR(BSTR str)
43 {
44     return wstring(str, ::SysStringLen(str));
45 }
46 
wstringFromInt(int i)47 static inline wstring wstringFromInt(int i)
48 {
49     wostringstream ss;
50     ss << i;
51     return ss.str();
52 }
53 
BSTRFromString(const string & str)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 
descriptionSuitableForTestResult(unsigned long identifier) const62 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 
descriptionSuitableForTestResult(IWebURLRequest * request)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 
descriptionSuitableForTestResult(IWebURLResponse * response)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 
descriptionSuitableForTestResult(IWebError * error,unsigned long identifier) const121 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 
ResourceLoadDelegate()174 ResourceLoadDelegate::ResourceLoadDelegate()
175     : m_refCount(1)
176 {
177 }
178 
~ResourceLoadDelegate()179 ResourceLoadDelegate::~ResourceLoadDelegate()
180 {
181 }
182 
QueryInterface(REFIID riid,void ** ppvObject)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 
AddRef(void)199 ULONG STDMETHODCALLTYPE ResourceLoadDelegate::AddRef(void)
200 {
201     return ++m_refCount;
202 }
203 
Release(void)204 ULONG STDMETHODCALLTYPE ResourceLoadDelegate::Release(void)
205 {
206     ULONG newRef = --m_refCount;
207     if (!newRef)
208         delete(this);
209 
210     return newRef;
211 }
212 
identifierForInitialRequest(IWebView * webView,IWebURLRequest * request,IWebDataSource * dataSource,unsigned long identifier)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 
removeIdentifierForRequest(IWebView * webView,unsigned long identifier)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 
willSendRequest(IWebView * webView,unsigned long identifier,IWebURLRequest * request,IWebURLResponse * redirectResponse,IWebDataSource * dataSource,IWebURLRequest ** newRequest)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 
didReceiveAuthenticationChallenge(IWebView * webView,unsigned long identifier,IWebURLAuthenticationChallenge * challenge,IWebDataSource * dataSource)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 
didReceiveResponse(IWebView * webView,unsigned long identifier,IWebURLResponse * response,IWebDataSource * dataSource)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 
didFinishLoadingFromDataSource(IWebView * webView,unsigned long identifier,IWebDataSource * dataSource)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 
didFailLoadingWithError(IWebView * webView,unsigned long identifier,IWebError * error,IWebDataSource * dataSource)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