2010-10-14 Pavel Feldman <pfeldman@chromium.org>
[WebKit-https.git] / WebCore / inspector / InspectorResource.cpp
1 /*
2  * Copyright (C) 2007, 2008 Apple Inc. All rights reserved.
3  * Copyright (C) 2008 Matt Lilek <webkit@mattlilek.com>
4  * Copyright (C) 2009 Google Inc. All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1.  Redistributions of source code must retain the above copyright
11  *     notice, this list of conditions and the following disclaimer.
12  * 2.  Redistributions in binary form must reproduce the above copyright
13  *     notice, this list of conditions and the following disclaimer in the
14  *     documentation and/or other materials provided with the distribution.
15  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
16  *     its contributors may be used to endorse or promote products derived
17  *     from this software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
20  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
23  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30
31 #include "config.h"
32 #include "InspectorResource.h"
33
34 #if ENABLE(INSPECTOR)
35
36 #include "Base64.h"
37 #include "Cache.h"
38 #include "CachedResource.h"
39 #include "CachedResourceLoader.h"
40 #include "DocumentLoader.h"
41 #include "Frame.h"
42 #include "InspectorFrontend.h"
43 #include "InspectorResourceAgent.h"
44 #include "InspectorValues.h"
45 #include "ResourceLoadTiming.h"
46 #include "ResourceRequest.h"
47 #include "ResourceResponse.h"
48 #include "TextEncoding.h"
49 #include "WebSocketHandshakeRequest.h"
50 #include "WebSocketHandshakeResponse.h"
51
52 #include <wtf/Assertions.h>
53 #include <wtf/text/StringBuffer.h>
54
55 namespace WebCore {
56
57 #if ENABLE(WEB_SOCKETS)
58 // Create human-readable binary representation, like "01:23:45:67:89:AB:CD:EF".
59 static String createReadableStringFromBinary(const unsigned char* value, size_t length)
60 {
61     ASSERT(length > 0);
62     static const char hexDigits[17] = "0123456789ABCDEF";
63     size_t bufferSize = length * 3 - 1;
64     StringBuffer buffer(bufferSize);
65     size_t index = 0;
66     for (size_t i = 0; i < length; ++i) {
67         if (i > 0)
68             buffer[index++] = ':';
69         buffer[index++] = hexDigits[value[i] >> 4];
70         buffer[index++] = hexDigits[value[i] & 0xF];
71     }
72     ASSERT(index == bufferSize);
73     return String::adopt(buffer);
74 }
75 #endif
76
77 InspectorResource::InspectorResource(unsigned long identifier, DocumentLoader* loader, const KURL& requestURL)
78     : m_identifier(identifier)
79     , m_loader(loader)
80     , m_frame(loader ? loader->frame() : 0)
81     , m_requestURL(requestURL)
82     , m_expectedContentLength(0)
83     , m_cached(false)
84     , m_finished(false)
85     , m_failed(false)
86     , m_length(0)
87     , m_responseStatusCode(0)
88     , m_startTime(-1.0)
89     , m_responseReceivedTime(-1.0)
90     , m_endTime(-1.0)
91     , m_loadEventTime(-1.0)
92     , m_domContentEventTime(-1.0)
93     , m_connectionID(0)
94     , m_connectionReused(false)
95     , m_isMainResource(false)
96 #if ENABLE(WEB_SOCKETS)
97     , m_isWebSocket(false)
98 #endif
99 {
100 }
101
102 InspectorResource::~InspectorResource()
103 {
104 }
105
106 PassRefPtr<InspectorResource> InspectorResource::appendRedirect(unsigned long identifier, const KURL& redirectURL)
107 {
108     // Last redirect is always a container of all previous ones. Pass this container here.
109     RefPtr<InspectorResource> redirect = InspectorResource::create(m_identifier, m_loader.get(), redirectURL);
110     redirect->m_redirects = m_redirects;
111     redirect->m_redirects.append(this);
112     redirect->m_changes.set(RedirectsChange);
113
114     m_identifier = identifier;
115     // Re-send request info with new id.
116     m_changes.set(RequestChange);
117     m_redirects.clear();
118     return redirect;
119 }
120
121 PassRefPtr<InspectorResource> InspectorResource::create(unsigned long identifier, DocumentLoader* loader, const KURL& requestURL)
122 {
123     ASSERT(loader);
124     return adoptRef(new InspectorResource(identifier, loader, requestURL));
125 }
126
127 PassRefPtr<InspectorResource> InspectorResource::createCached(unsigned long identifier, DocumentLoader* loader, const CachedResource* cachedResource)
128 {
129     PassRefPtr<InspectorResource> resource = create(identifier, loader, KURL(ParsedURLString, cachedResource->url()));
130
131     resource->m_finished = true;
132
133     resource->updateResponse(cachedResource->response());
134
135     resource->m_length = cachedResource->encodedSize();
136     resource->m_cached = true;
137     resource->m_startTime = currentTime();
138     resource->m_responseReceivedTime = resource->m_startTime;
139     resource->m_endTime = resource->m_startTime;
140
141     resource->m_changes.setAll();
142
143     return resource;
144 }
145
146 #if ENABLE(WEB_SOCKETS)
147 PassRefPtr<InspectorResource> InspectorResource::createWebSocket(unsigned long identifier, const KURL& requestURL, const KURL& documentURL)
148 {
149     RefPtr<InspectorResource> resource = adoptRef(new InspectorResource(identifier, 0, requestURL));
150     resource->markWebSocket();
151     resource->m_documentURL = documentURL;
152     return resource.release();
153 }
154 #endif
155
156 void InspectorResource::updateRequest(const ResourceRequest& request)
157 {
158     m_requestHeaderFields = request.httpHeaderFields();
159     m_requestMethod = request.httpMethod();
160     if (request.httpBody() && !request.httpBody()->isEmpty())
161         m_requestFormData = request.httpBody()->flattenToString();
162
163     m_changes.set(RequestChange);
164 }
165
166 void InspectorResource::markAsCached()
167 {
168     m_cached = true;
169 }
170
171 void InspectorResource::updateResponse(const ResourceResponse& response)
172 {
173     m_expectedContentLength = response.expectedContentLength();
174     m_mimeType = response.mimeType();
175     if (m_mimeType.isEmpty() && response.httpStatusCode() == 304) {
176         CachedResource* cachedResource = cache()->resourceForURL(response.url().string());
177         if (cachedResource)
178             m_mimeType = cachedResource->response().mimeType();
179     }
180     if (ResourceRawHeaders* headers = response.resourceRawHeaders().get()) {
181         m_requestHeaderFields = headers->requestHeaders;
182         m_responseHeaderFields = headers->responseHeaders;
183         m_changes.set(RequestChange);
184     } else
185         m_responseHeaderFields = response.httpHeaderFields();
186     m_responseStatusCode = response.httpStatusCode();
187     m_responseStatusText = response.httpStatusText();
188     m_suggestedFilename = response.suggestedFilename();
189
190     m_connectionID = response.connectionID();
191     m_connectionReused = response.connectionReused();
192     m_loadTiming = response.resourceLoadTiming();
193     m_cached = m_cached || response.wasCached();
194
195     if (!m_cached && m_loadTiming && m_loadTiming->requestTime)
196         m_responseReceivedTime = m_loadTiming->requestTime + m_loadTiming->receiveHeadersEnd / 1000.0;
197     else
198         m_responseReceivedTime = currentTime();
199
200     m_changes.set(TimingChange);
201     m_changes.set(ResponseChange);
202     m_changes.set(TypeChange);
203 }
204
205 #if ENABLE(WEB_SOCKETS)
206 void InspectorResource::updateWebSocketRequest(const WebSocketHandshakeRequest& request)
207 {
208     m_requestHeaderFields = request.headerFields();
209     m_requestMethod = "GET"; // Currently we always use "GET" to request handshake.
210     m_webSocketRequestKey3.set(new WebSocketHandshakeRequest::Key3(request.key3()));
211     m_changes.set(RequestChange);
212     m_changes.set(TypeChange);
213 }
214
215 void InspectorResource::updateWebSocketResponse(const WebSocketHandshakeResponse& response)
216 {
217     m_responseStatusCode = response.statusCode();
218     m_responseStatusText = response.statusText();
219     m_responseHeaderFields = response.headerFields();
220     m_webSocketChallengeResponse.set(new WebSocketHandshakeResponse::ChallengeResponse(response.challengeResponse()));
221     m_changes.set(ResponseChange);
222     m_changes.set(TypeChange);
223 }
224 #endif // ENABLE(WEB_SOCKETS)
225
226 static PassRefPtr<InspectorObject> buildHeadersObject(const HTTPHeaderMap& headers)
227 {
228     RefPtr<InspectorObject> object = InspectorObject::create();
229     HTTPHeaderMap::const_iterator end = headers.end();
230     for (HTTPHeaderMap::const_iterator it = headers.begin(); it != end; ++it) {
231         object->setString(it->first.string(), it->second);
232     }
233     return object;
234 }
235
236 static PassRefPtr<InspectorObject> buildObjectForTiming(ResourceLoadTiming* timing)
237 {
238     RefPtr<InspectorObject> jsonObject = InspectorObject::create();
239     jsonObject->setNumber("requestTime", timing->requestTime);
240     jsonObject->setNumber("proxyStart", timing->proxyStart);
241     jsonObject->setNumber("proxyEnd", timing->proxyEnd);
242     jsonObject->setNumber("dnsStart", timing->dnsStart);
243     jsonObject->setNumber("dnsEnd", timing->dnsEnd);
244     jsonObject->setNumber("connectStart", timing->connectStart);
245     jsonObject->setNumber("connectEnd", timing->connectEnd);
246     jsonObject->setNumber("sslStart", timing->sslStart);
247     jsonObject->setNumber("sslEnd", timing->sslEnd);
248     jsonObject->setNumber("sendStart", timing->sendStart);
249     jsonObject->setNumber("sendEnd", timing->sendEnd);
250     jsonObject->setNumber("receiveHeadersEnd", timing->receiveHeadersEnd);
251     return jsonObject;
252 }
253
254
255 void InspectorResource::updateScriptObject(InspectorFrontend* frontend)
256 {
257     if (m_changes.hasChange(NoChange))
258         return;
259
260     RefPtr<InspectorObject> jsonObject = InspectorObject::create();
261     jsonObject->setNumber("id", m_identifier);
262     if (m_changes.hasChange(RequestChange)) {
263         if (m_frame)
264             m_documentURL = m_frame->document()->url();
265         jsonObject->setString("url", m_requestURL.string());
266         jsonObject->setString("documentURL", m_documentURL.string());
267         jsonObject->setString("host", m_requestURL.host());
268         jsonObject->setString("path", m_requestURL.path());
269         jsonObject->setString("lastPathComponent", m_requestURL.lastPathComponent());
270         RefPtr<InspectorObject> requestHeaders = buildHeadersObject(m_requestHeaderFields);
271         jsonObject->setObject("requestHeaders", requestHeaders);
272         jsonObject->setBoolean("mainResource", m_isMainResource);
273         jsonObject->setString("requestMethod", m_requestMethod);
274         jsonObject->setString("requestFormData", m_requestFormData);
275         jsonObject->setBoolean("didRequestChange", true);
276 #if ENABLE(WEB_SOCKETS)
277         if (m_webSocketRequestKey3)
278             jsonObject->setString("webSocketRequestKey3", createReadableStringFromBinary(m_webSocketRequestKey3->value, sizeof(m_webSocketRequestKey3->value)));
279 #endif
280     }
281
282     if (m_changes.hasChange(ResponseChange)) {
283         jsonObject->setString("mimeType", m_mimeType);
284         jsonObject->setString("suggestedFilename", m_suggestedFilename);
285         jsonObject->setNumber("expectedContentLength", m_expectedContentLength);
286         jsonObject->setNumber("statusCode", m_responseStatusCode);
287         jsonObject->setString("statusText", m_responseStatusText);
288         RefPtr<InspectorObject> responseHeaders = buildHeadersObject(m_responseHeaderFields);
289         jsonObject->setObject("responseHeaders", responseHeaders);
290         jsonObject->setNumber("connectionID", m_connectionID);
291         jsonObject->setBoolean("connectionReused", m_connectionReused);
292         jsonObject->setBoolean("cached", m_cached);
293         if (m_loadTiming && !m_cached)
294             jsonObject->setObject("timing", buildObjectForTiming(m_loadTiming.get()));
295 #if ENABLE(WEB_SOCKETS)
296         if (m_webSocketChallengeResponse)
297             jsonObject->setString("webSocketChallengeResponse", createReadableStringFromBinary(m_webSocketChallengeResponse->value, sizeof(m_webSocketChallengeResponse->value)));
298 #endif
299         jsonObject->setBoolean("didResponseChange", true);
300     }
301
302     if (m_changes.hasChange(TypeChange)) {
303         jsonObject->setNumber("type", static_cast<int>(type()));
304         jsonObject->setBoolean("didTypeChange", true);
305     }
306
307     if (m_changes.hasChange(LengthChange)) {
308         jsonObject->setNumber("resourceSize", m_length);
309         jsonObject->setBoolean("didLengthChange", true);
310     }
311
312     if (m_changes.hasChange(CompletionChange)) {
313         jsonObject->setBoolean("failed", m_failed);
314         jsonObject->setBoolean("finished", m_finished);
315         jsonObject->setBoolean("didCompletionChange", true);
316     }
317
318     if (m_changes.hasChange(TimingChange)) {
319         if (m_startTime > 0)
320             jsonObject->setNumber("startTime", m_startTime);
321         if (m_responseReceivedTime > 0)
322             jsonObject->setNumber("responseReceivedTime", m_responseReceivedTime);
323         if (m_endTime > 0)
324             jsonObject->setNumber("endTime", m_endTime);
325         if (m_loadEventTime > 0)
326             jsonObject->setNumber("loadEventTime", m_loadEventTime);
327         if (m_domContentEventTime > 0)
328             jsonObject->setNumber("domContentEventTime", m_domContentEventTime);
329         jsonObject->setBoolean("didTimingChange", true);
330     }
331
332     if (m_changes.hasChange(RedirectsChange)) {
333         for (size_t i = 0; i < m_redirects.size(); ++i)
334             m_redirects[i]->updateScriptObject(frontend);
335     }
336
337     frontend->updateResource(jsonObject);
338     m_changes.clearAll();
339 }
340
341 void InspectorResource::releaseScriptObject(InspectorFrontend* frontend)
342 {
343     m_changes.setAll();
344
345     for (size_t i = 0; i < m_redirects.size(); ++i)
346         m_redirects[i]->releaseScriptObject(frontend);
347
348     if (frontend)
349         frontend->removeResource(m_identifier);
350 }
351
352 InspectorResource::Type InspectorResource::type() const
353 {
354     if (!m_overrideContent.isNull())
355         return m_overrideContentType;
356
357 #if ENABLE(WEB_SOCKETS)
358     if (m_isWebSocket)
359         return WebSocket;
360 #endif
361
362     ASSERT(m_loader);
363
364     if (m_loader->frameLoader() && equalIgnoringFragmentIdentifier(m_requestURL, m_loader->frameLoader()->iconURL()))
365         return Image;
366
367     if (!m_frame)
368         return Other;
369
370     InspectorResource::Type resourceType = InspectorResourceAgent::cachedResourceType(m_frame->document(), m_requestURL);
371     if (equalIgnoringFragmentIdentifier(m_requestURL, m_loader->requestURL()) && resourceType == Other)
372         return Doc;
373
374     return resourceType;
375 }
376
377 void InspectorResource::setOverrideContent(const String& data, Type type)
378 {
379     m_overrideContent = data;
380     m_overrideContentType = type;
381     m_changes.set(TypeChange);
382 }
383
384 String InspectorResource::sourceString() const
385 {
386     if (!m_overrideContent.isNull())
387         return String(m_overrideContent);
388
389     String result;
390     if (!InspectorResourceAgent::resourceContent(m_frame->document(), m_requestURL, &result))
391         return String();
392     return result;
393 }
394
395 String InspectorResource::sourceBytes() const
396 {
397     Vector<char> out;
398     if (!m_overrideContent.isNull()) {
399         Vector<char> data;
400         String overrideContent = m_overrideContent;
401         data.append(overrideContent.characters(), overrideContent.length());
402         base64Encode(data, out);
403         return String(out.data(), out.size());
404     }
405
406     String result;
407     if (!InspectorResourceAgent::resourceContentBase64(m_frame->document(), m_requestURL, &result))
408         return String();
409     return result;
410 }
411
412 void InspectorResource::startTiming()
413 {
414     m_startTime = currentTime();
415     m_changes.set(TimingChange);
416 }
417
418 void InspectorResource::endTiming(double actualEndTime)
419 {
420     if (actualEndTime) {
421         m_endTime = actualEndTime;
422         // In case of fast load (or in case of cached resources), endTime on network stack
423         // can be less then m_responseReceivedTime measured in WebCore. Normalize it here,
424         // prefer actualEndTime to m_responseReceivedTime.
425         if (m_endTime < m_responseReceivedTime)
426             m_responseReceivedTime = m_endTime;
427     } else
428         m_endTime = currentTime();
429
430     m_finished = true;
431     m_changes.set(TimingChange);
432     m_changes.set(CompletionChange);
433 }
434
435 void InspectorResource::markDOMContentEventTime()
436 {
437     m_domContentEventTime = currentTime();
438     m_changes.set(TimingChange);
439 }
440
441 void InspectorResource::markLoadEventTime()
442 {
443     m_loadEventTime = currentTime();
444     m_changes.set(TimingChange);
445 }
446
447 void InspectorResource::markFailed()
448 {
449     m_failed = true;
450     m_changes.set(CompletionChange);
451 }
452
453 void InspectorResource::addLength(int lengthReceived)
454 {
455     m_length += lengthReceived;
456     m_changes.set(LengthChange);
457
458     // Update load time, otherwise the resource will
459     // have start time == end time and  0 load duration
460     // until its loading is completed.
461     m_endTime = currentTime();
462     m_changes.set(TimingChange);
463 }
464
465 } // namespace WebCore
466
467 #endif // ENABLE(INSPECTOR)