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 <wtf/HashMap.h>
35 #include <wtf/Vector.h>
36 #include <sstream>
37
38 using std::wstring;
39 using std::wiostream;
40
wstringFromBSTR(BSTR str)41 static inline wstring wstringFromBSTR(BSTR str)
42 {
43 return wstring(str, ::SysStringLen(str));
44 }
45
wstringFromInt(int i)46 wstring wstringFromInt(int i)
47 {
48 std::wostringstream ss;
49 ss << i;
50 return ss.str();
51 }
52
53 typedef HashMap<unsigned long, wstring> IdentifierMap;
54
urlMap()55 IdentifierMap& urlMap()
56 {
57 static IdentifierMap urlMap;
58
59 return urlMap;
60 }
61
descriptionSuitableForTestResult(unsigned long identifier)62 static wstring descriptionSuitableForTestResult(unsigned long identifier)
63 {
64 IdentifierMap::iterator it = urlMap().find(identifier);
65
66 if (it == urlMap().end())
67 return L"<unknown>";
68
69 return urlSuitableForTestResult(it->second);
70 }
71
descriptionSuitableForTestResult(IWebURLRequest * request)72 static wstring 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 static wstring 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)121 static wstring descriptionSuitableForTestResult(IWebError* error, unsigned long identifier)
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
191 return E_NOINTERFACE;
192
193 AddRef();
194 return S_OK;
195 }
196
AddRef(void)197 ULONG STDMETHODCALLTYPE ResourceLoadDelegate::AddRef(void)
198 {
199 return ++m_refCount;
200 }
201
Release(void)202 ULONG STDMETHODCALLTYPE ResourceLoadDelegate::Release(void)
203 {
204 ULONG newRef = --m_refCount;
205 if (!newRef)
206 delete(this);
207
208 return newRef;
209 }
210
identifierForInitialRequest(IWebView * webView,IWebURLRequest * request,IWebDataSource * dataSource,unsigned long identifier)211 HRESULT STDMETHODCALLTYPE ResourceLoadDelegate::identifierForInitialRequest(
212 /* [in] */ IWebView* webView,
213 /* [in] */ IWebURLRequest* request,
214 /* [in] */ IWebDataSource* dataSource,
215 /* [in] */ unsigned long identifier)
216 {
217 if (!done && gLayoutTestController->dumpResourceLoadCallbacks()) {
218 BSTR urlStr;
219 if (FAILED(request->URL(&urlStr)))
220 return E_FAIL;
221
222 urlMap().set(identifier, wstringFromBSTR(urlStr));
223 }
224
225 return S_OK;
226 }
227
willSendRequest(IWebView * webView,unsigned long identifier,IWebURLRequest * request,IWebURLResponse * redirectResponse,IWebDataSource * dataSource,IWebURLRequest ** newRequest)228 HRESULT STDMETHODCALLTYPE ResourceLoadDelegate::willSendRequest(
229 /* [in] */ IWebView* webView,
230 /* [in] */ unsigned long identifier,
231 /* [in] */ IWebURLRequest* request,
232 /* [in] */ IWebURLResponse* redirectResponse,
233 /* [in] */ IWebDataSource* dataSource,
234 /* [retval][out] */ IWebURLRequest **newRequest)
235 {
236 if (!done && gLayoutTestController->dumpResourceLoadCallbacks()) {
237 printf("%S - willSendRequest %S redirectResponse %S\n",
238 descriptionSuitableForTestResult(identifier).c_str(),
239 descriptionSuitableForTestResult(request).c_str(),
240 descriptionSuitableForTestResult(redirectResponse).c_str());
241 }
242
243 if (!done && gLayoutTestController->willSendRequestReturnsNullOnRedirect() && redirectResponse) {
244 printf("Returning null for this redirect\n");
245 *newRequest = 0;
246 return S_OK;
247 }
248
249 request->AddRef();
250 *newRequest = request;
251 return S_OK;
252 }
253
didReceiveResponse(IWebView * webView,unsigned long identifier,IWebURLResponse * response,IWebDataSource * dataSource)254 HRESULT STDMETHODCALLTYPE ResourceLoadDelegate::didReceiveResponse(
255 /* [in] */ IWebView* webView,
256 /* [in] */ unsigned long identifier,
257 /* [in] */ IWebURLResponse* response,
258 /* [in] */ IWebDataSource* dataSource)
259 {
260 if (!done && gLayoutTestController->dumpResourceLoadCallbacks()) {
261 printf("%S - didReceiveResponse %S\n",
262 descriptionSuitableForTestResult(identifier).c_str(),
263 descriptionSuitableForTestResult(response).c_str());
264 }
265 if (!done && gLayoutTestController->dumpResourceResponseMIMETypes()) {
266 BSTR mimeTypeBSTR;
267 if (FAILED(response->MIMEType(&mimeTypeBSTR)))
268 E_FAIL;
269
270 wstring mimeType = wstringFromBSTR(mimeTypeBSTR);
271 ::SysFreeString(mimeTypeBSTR);
272
273 BSTR urlBSTR;
274 if (FAILED(response->URL(&urlBSTR)))
275 E_FAIL;
276
277 wstring url = urlSuitableForTestResult(wstringFromBSTR(urlBSTR));
278 ::SysFreeString(urlBSTR);
279
280 printf("%S has MIME type %S\n", url.c_str(), mimeType.c_str());
281 }
282
283 return S_OK;
284 }
285
286
didFinishLoadingFromDataSource(IWebView * webView,unsigned long identifier,IWebDataSource * dataSource)287 HRESULT STDMETHODCALLTYPE ResourceLoadDelegate::didFinishLoadingFromDataSource(
288 /* [in] */ IWebView* webView,
289 /* [in] */ unsigned long identifier,
290 /* [in] */ IWebDataSource* dataSource)
291 {
292 if (!done && gLayoutTestController->dumpResourceLoadCallbacks()) {
293 printf("%S - didFinishLoading\n",
294 descriptionSuitableForTestResult(identifier).c_str()),
295 urlMap().remove(identifier);
296 }
297
298 return S_OK;
299 }
300
didFailLoadingWithError(IWebView * webView,unsigned long identifier,IWebError * error,IWebDataSource * dataSource)301 HRESULT STDMETHODCALLTYPE ResourceLoadDelegate::didFailLoadingWithError(
302 /* [in] */ IWebView* webView,
303 /* [in] */ unsigned long identifier,
304 /* [in] */ IWebError* error,
305 /* [in] */ IWebDataSource* dataSource)
306 {
307 if (!done && gLayoutTestController->dumpResourceLoadCallbacks()) {
308 printf("%S - didFailLoadingWithError: %S\n",
309 descriptionSuitableForTestResult(identifier).c_str(),
310 descriptionSuitableForTestResult(error, identifier).c_str());
311 urlMap().remove(identifier);
312 }
313
314 return S_OK;
315 }
316