2 * Copyright (C) 2011 Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
14 * * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 #include "InspectorResourceAgent.h"
37 #include "MemoryCache.h"
38 #include "CachedResource.h"
39 #include "CachedResourceLoader.h"
41 #include "DocumentLoader.h"
43 #include "FrameLoader.h"
44 #include "HTMLFrameOwnerElement.h"
45 #include "HTMLNames.h"
46 #include "HTTPHeaderMap.h"
47 #include "InspectorFrontend.h"
48 #include "InspectorState.h"
49 #include "InspectorValues.h"
52 #include "ProgressTracker.h"
53 #include "ResourceError.h"
54 #include "ResourceRequest.h"
55 #include "ResourceResponse.h"
56 #include "ScriptCallStack.h"
57 #include "ScriptCallStackFactory.h"
58 #include "SharedBuffer.h"
59 #include "TextEncoding.h"
60 #include "WebSocketHandshakeRequest.h"
61 #include "WebSocketHandshakeResponse.h"
63 #include <wtf/CurrentTime.h>
64 #include <wtf/ListHashSet.h>
65 #include <wtf/RefPtr.h>
66 #include <wtf/text/StringBuffer.h>
70 namespace ResourceAgentState {
71 static const char resourceAgentEnabled[] = "resourceAgentEnabled";
72 static const char extraRequestHeaders[] = "extraRequestHeaders";
75 PassRefPtr<InspectorResourceAgent> InspectorResourceAgent::restore(Page* page, InspectorState* state, InspectorFrontend* frontend)
77 if (state->getBoolean(ResourceAgentState::resourceAgentEnabled))
78 return create(page, state, frontend);
82 bool InspectorResourceAgent::resourceContent(Frame* frame, const KURL& url, String* result)
87 String textEncodingName;
88 RefPtr<SharedBuffer> buffer = InspectorResourceAgent::resourceData(frame, url, &textEncodingName);
91 TextEncoding encoding(textEncodingName);
92 if (!encoding.isValid())
93 encoding = WindowsLatin1Encoding();
94 *result = encoding.decode(buffer->data(), buffer->size());
101 bool InspectorResourceAgent::resourceContentBase64(Frame* frame, const KURL& url, String* result)
103 String textEncodingName;
104 RefPtr<SharedBuffer> data = InspectorResourceAgent::resourceData(frame, url, &textEncodingName);
110 *result = base64Encode(data->data(), data->size());
114 PassRefPtr<SharedBuffer> InspectorResourceAgent::resourceData(Frame* frame, const KURL& url, String* textEncodingName)
116 FrameLoader* frameLoader = frame->loader();
117 DocumentLoader* loader = frameLoader->documentLoader();
118 if (equalIgnoringFragmentIdentifier(url, loader->url())) {
119 *textEncodingName = frame->document()->inputEncoding();
120 return frameLoader->documentLoader()->mainResourceData();
123 CachedResource* cachedResource = InspectorResourceAgent::cachedResource(frame, url);
127 // Zero-sized resources don't have data at all -- so fake the empty buffer, insted of indicating error by returning 0.
128 if (!cachedResource->encodedSize())
129 return SharedBuffer::create();
131 if (cachedResource->isPurgeable()) {
132 // If the resource is purgeable then make it unpurgeable to get
133 // get its data. This might fail, in which case we return an
135 // FIXME: should we do something else in the case of a purged
136 // resource that informs the user why there is no data in the
138 if (!cachedResource->makePurgeable(false))
142 *textEncodingName = cachedResource->encoding();
143 return cachedResource->data();
146 CachedResource* InspectorResourceAgent::cachedResource(Frame* frame, const KURL& url)
148 CachedResource* cachedResource = frame->document()->cachedResourceLoader()->cachedResource(url);
150 cachedResource = memoryCache()->resourceForURL(url);
151 return cachedResource;
154 static PassRefPtr<InspectorObject> buildObjectForHeaders(const HTTPHeaderMap& headers)
156 RefPtr<InspectorObject> headersObject = InspectorObject::create();
157 HTTPHeaderMap::const_iterator end = headers.end();
158 for (HTTPHeaderMap::const_iterator it = headers.begin(); it != end; ++it)
159 headersObject->setString(it->first.string(), it->second);
160 return headersObject;
163 static PassRefPtr<InspectorObject> buildObjectForTiming(const ResourceLoadTiming& timing)
165 RefPtr<InspectorObject> timingObject = InspectorObject::create();
166 timingObject->setNumber("requestTime", timing.requestTime);
167 timingObject->setNumber("proxyStart", timing.proxyStart);
168 timingObject->setNumber("proxyEnd", timing.proxyEnd);
169 timingObject->setNumber("dnsStart", timing.dnsStart);
170 timingObject->setNumber("dnsEnd", timing.dnsEnd);
171 timingObject->setNumber("connectStart", timing.connectStart);
172 timingObject->setNumber("connectEnd", timing.connectEnd);
173 timingObject->setNumber("sslStart", timing.sslStart);
174 timingObject->setNumber("sslEnd", timing.sslEnd);
175 timingObject->setNumber("sendStart", timing.sendStart);
176 timingObject->setNumber("sendEnd", timing.sendEnd);
177 timingObject->setNumber("receiveHeadersEnd", timing.receiveHeadersEnd);
181 static PassRefPtr<InspectorObject> buildObjectForResourceRequest(const ResourceRequest& request)
183 RefPtr<InspectorObject> requestObject = InspectorObject::create();
184 requestObject->setString("url", request.url().string());
185 requestObject->setString("httpMethod", request.httpMethod());
186 requestObject->setObject("httpHeaderFields", buildObjectForHeaders(request.httpHeaderFields()));
187 if (request.httpBody() && !request.httpBody()->isEmpty())
188 requestObject->setString("requestFormData", request.httpBody()->flattenToString());
189 return requestObject;
192 static PassRefPtr<InspectorObject> buildObjectForResourceResponse(const ResourceResponse& response)
194 RefPtr<InspectorObject> responseObject = InspectorObject::create();
195 if (response.isNull()) {
196 responseObject->setBoolean("isNull", true);
197 return responseObject;
199 responseObject->setString("url", response.url().string());
200 responseObject->setString("mimeType", response.mimeType());
201 responseObject->setNumber("expectedContentLength", response.expectedContentLength());
202 responseObject->setString("textEncodingName", response.textEncodingName());
203 responseObject->setString("suggestedFilename", response.suggestedFilename());
204 responseObject->setNumber("httpStatusCode", response.httpStatusCode());
205 responseObject->setString("httpStatusText", response.httpStatusText());
206 responseObject->setObject("httpHeaderFields", buildObjectForHeaders(response.httpHeaderFields()));
207 responseObject->setBoolean("connectionReused", response.connectionReused());
208 responseObject->setNumber("connectionID", response.connectionID());
209 responseObject->setBoolean("wasCached", response.wasCached());
210 if (response.resourceLoadTiming())
211 responseObject->setObject("timing", buildObjectForTiming(*response.resourceLoadTiming()));
212 if (response.resourceLoadInfo()) {
213 RefPtr<InspectorObject> loadInfoObject = InspectorObject::create();
214 loadInfoObject->setNumber("httpStatusCode", response.resourceLoadInfo()->httpStatusCode);
215 loadInfoObject->setString("httpStatusText", response.resourceLoadInfo()->httpStatusText);
216 loadInfoObject->setObject("requestHeaders", buildObjectForHeaders(response.resourceLoadInfo()->requestHeaders));
217 loadInfoObject->setObject("responseHeaders", buildObjectForHeaders(response.resourceLoadInfo()->responseHeaders));
218 responseObject->setObject("loadInfo", loadInfoObject);
220 return responseObject;
223 static unsigned long frameId(Frame* frame)
225 return reinterpret_cast<uintptr_t>(frame);
228 static PassRefPtr<InspectorObject> buildObjectForDocumentLoader(DocumentLoader* loader)
230 RefPtr<InspectorObject> documentLoaderObject = InspectorObject::create();
231 documentLoaderObject->setNumber("frameId", frameId(loader->frame()));
232 documentLoaderObject->setNumber("loaderId", reinterpret_cast<uintptr_t>(loader));
233 documentLoaderObject->setString("url", loader->requestURL().string());
234 return documentLoaderObject;
237 static PassRefPtr<InspectorObject> buildObjectForFrameResource(Frame* frame)
239 FrameLoader* frameLoader = frame->loader();
240 DocumentLoader* loader = frameLoader->documentLoader();
242 RefPtr<InspectorObject> resourceObject = InspectorObject::create();
243 resourceObject->setString("url", loader->url().string());
244 resourceObject->setObject("loader", buildObjectForDocumentLoader(loader));
245 resourceObject->setObject("request", buildObjectForResourceRequest(loader->request()));
246 resourceObject->setObject("response", buildObjectForResourceResponse(loader->response()));
247 return resourceObject;
250 static String cachedResourceTypeString(const CachedResource& cachedResource)
252 switch (cachedResource.type()) {
253 case CachedResource::ImageResource:
255 case CachedResource::FontResource:
257 case CachedResource::CSSStyleSheet:
260 case CachedResource::XSLStyleSheet:
263 case CachedResource::Script:
271 static PassRefPtr<InspectorObject> buildObjectForCachedResource(DocumentLoader* loader, const CachedResource& cachedResource)
273 RefPtr<InspectorObject> resourceObject = InspectorObject::create();
274 resourceObject->setString("url", cachedResource.url());
275 resourceObject->setString("type", cachedResourceTypeString(cachedResource));
276 resourceObject->setNumber("encodedSize", cachedResource.encodedSize());
277 resourceObject->setObject("response", buildObjectForResourceResponse(cachedResource.response()));
278 resourceObject->setObject("loader", buildObjectForDocumentLoader(loader));
279 return resourceObject;
282 static void populateObjectWithFrameResources(Frame* frame, PassRefPtr<InspectorObject> frameResources)
284 frameResources->setObject("resource", buildObjectForFrameResource(frame));
285 RefPtr<InspectorArray> subresources = InspectorArray::create();
286 frameResources->setArray("subresources", subresources);
288 const CachedResourceLoader::DocumentResourceMap& allResources = frame->document()->cachedResourceLoader()->allCachedResources();
289 CachedResourceLoader::DocumentResourceMap::const_iterator end = allResources.end();
290 for (CachedResourceLoader::DocumentResourceMap::const_iterator it = allResources.begin(); it != end; ++it) {
291 CachedResource* cachedResource = it->second.get();
292 RefPtr<InspectorObject> cachedResourceObject = buildObjectForCachedResource(frame->loader()->documentLoader(), *cachedResource);
293 subresources->pushValue(cachedResourceObject);
297 InspectorResourceAgent::~InspectorResourceAgent()
299 m_state->setBoolean(ResourceAgentState::resourceAgentEnabled, false);
302 void InspectorResourceAgent::identifierForInitialRequest(unsigned long identifier, const KURL& url, DocumentLoader* loader)
304 RefPtr<InspectorObject> loaderObject = buildObjectForDocumentLoader(loader);
305 RefPtr<ScriptCallStack> callStack = createScriptCallStack(ScriptCallStack::maxCallStackSizeToCapture, true);
306 RefPtr<InspectorValue> callStackValue;
308 callStackValue = callStack->buildInspectorObject();
310 callStackValue = InspectorValue::null();
311 m_frontend->identifierForInitialRequest(identifier, url.string(), loaderObject, callStackValue);
314 void InspectorResourceAgent::setExtraHeaders(ErrorString*, PassRefPtr<InspectorObject> headers)
316 m_state->setObject(ResourceAgentState::extraRequestHeaders, headers);
320 void InspectorResourceAgent::willSendRequest(unsigned long identifier, ResourceRequest& request, const ResourceResponse& redirectResponse)
322 RefPtr<InspectorObject> headers = m_state->getObject(ResourceAgentState::extraRequestHeaders);
325 InspectorObject::const_iterator end = headers->end();
326 for (InspectorObject::const_iterator it = headers->begin(); it != end; ++it) {
328 if (it->second->asString(&value))
329 request.setHTTPHeaderField(it->first, value);
333 request.setReportLoadTiming(true);
334 request.setReportRawHeaders(true);
336 m_frontend->willSendRequest(identifier, currentTime(), buildObjectForResourceRequest(request), buildObjectForResourceResponse(redirectResponse));
339 void InspectorResourceAgent::markResourceAsCached(unsigned long identifier)
341 m_frontend->markResourceAsCached(identifier);
344 void InspectorResourceAgent::didReceiveResponse(unsigned long identifier, DocumentLoader* loader, const ResourceResponse& response)
346 RefPtr<InspectorObject> resourceResponse = buildObjectForResourceResponse(response);
347 String type = "Other";
348 long cachedResourceSize = 0;
351 CachedResource* cachedResource = InspectorResourceAgent::cachedResource(loader->frame(), response.url());
352 if (cachedResource) {
353 type = cachedResourceTypeString(*cachedResource);
354 cachedResourceSize = cachedResource->encodedSize();
355 // Use mime type from cached resource in case the one in response is empty.
356 if (response.mimeType().isEmpty())
357 resourceResponse->setString("mimeType", cachedResource->response().mimeType());
359 if (equalIgnoringFragmentIdentifier(response.url(), loader->frameLoader()->iconURL()))
361 else if (equalIgnoringFragmentIdentifier(response.url(), loader->url()) && type == "Other")
364 m_frontend->didReceiveResponse(identifier, currentTime(), type, resourceResponse);
365 // If we revalidated the resource and got Not modified, send content length following didReceiveResponse
366 // as there will be no calls to didReceiveContentLength from the network stack.
367 if (cachedResourceSize && response.httpStatusCode() == 304)
368 didReceiveContentLength(identifier, cachedResourceSize);
371 void InspectorResourceAgent::didReceiveContentLength(unsigned long identifier, int lengthReceived)
373 m_frontend->didReceiveContentLength(identifier, currentTime(), lengthReceived);
376 void InspectorResourceAgent::didFinishLoading(unsigned long identifier, double finishTime)
379 finishTime = currentTime();
381 m_frontend->didFinishLoading(identifier, finishTime);
384 void InspectorResourceAgent::didFailLoading(unsigned long identifier, const ResourceError& error)
386 m_frontend->didFailLoading(identifier, currentTime(), error.localizedDescription());
389 void InspectorResourceAgent::didLoadResourceFromMemoryCache(DocumentLoader* loader, const CachedResource* resource)
391 m_frontend->didLoadResourceFromMemoryCache(currentTime(), buildObjectForCachedResource(loader, *resource));
394 void InspectorResourceAgent::setInitialContent(unsigned long identifier, const String& sourceString, const String& type)
396 m_frontend->setInitialContent(identifier, sourceString, type);
399 static PassRefPtr<InspectorObject> buildObjectForFrame(Frame* frame)
401 RefPtr<InspectorObject> frameObject = InspectorObject::create();
402 frameObject->setNumber("id", frameId(frame));
403 frameObject->setNumber("parentId", frameId(frame->tree()->parent()));
404 if (frame->ownerElement()) {
405 String name = frame->ownerElement()->getAttribute(HTMLNames::nameAttr);
407 name = frame->ownerElement()->getAttribute(HTMLNames::idAttr);
408 frameObject->setString("name", name);
410 frameObject->setString("url", frame->document()->url().string());
414 static PassRefPtr<InspectorObject> buildObjectForFrameTree(Frame* frame, bool dumpResources)
416 RefPtr<InspectorObject> frameObject = buildObjectForFrame(frame);
419 populateObjectWithFrameResources(frame, frameObject);
420 RefPtr<InspectorArray> childrenArray;
421 for (Frame* child = frame->tree()->firstChild(); child; child = child->tree()->nextSibling()) {
422 if (!childrenArray) {
423 childrenArray = InspectorArray::create();
424 frameObject->setArray("children", childrenArray);
426 childrenArray->pushObject(buildObjectForFrameTree(child, dumpResources));
431 void InspectorResourceAgent::didCommitLoad(DocumentLoader* loader)
433 m_frontend->didCommitLoadForFrame(buildObjectForFrame(loader->frame()), buildObjectForDocumentLoader(loader));
436 void InspectorResourceAgent::frameDetachedFromParent(Frame* frame)
438 m_frontend->frameDetachedFromParent(frameId(frame));
441 #if ENABLE(WEB_SOCKETS)
443 // FIXME: More this into the front-end?
444 // Create human-readable binary representation, like "01:23:45:67:89:AB:CD:EF".
445 static String createReadableStringFromBinary(const unsigned char* value, size_t length)
448 static const char hexDigits[17] = "0123456789ABCDEF";
449 size_t bufferSize = length * 3 - 1;
450 StringBuffer buffer(bufferSize);
452 for (size_t i = 0; i < length; ++i) {
454 buffer[index++] = ':';
455 buffer[index++] = hexDigits[value[i] >> 4];
456 buffer[index++] = hexDigits[value[i] & 0xF];
458 ASSERT(index == bufferSize);
459 return String::adopt(buffer);
462 void InspectorResourceAgent::didCreateWebSocket(unsigned long identifier, const KURL& requestURL)
464 m_frontend->didCreateWebSocket(identifier, requestURL.string());
467 void InspectorResourceAgent::willSendWebSocketHandshakeRequest(unsigned long identifier, const WebSocketHandshakeRequest& request)
469 RefPtr<InspectorObject> requestObject = InspectorObject::create();
470 requestObject->setObject("webSocketHeaderFields", buildObjectForHeaders(request.headerFields()));
471 requestObject->setString("webSocketRequestKey3", createReadableStringFromBinary(request.key3().value, sizeof(request.key3().value)));
472 m_frontend->willSendWebSocketHandshakeRequest(identifier, currentTime(), requestObject);
475 void InspectorResourceAgent::didReceiveWebSocketHandshakeResponse(unsigned long identifier, const WebSocketHandshakeResponse& response)
477 RefPtr<InspectorObject> responseObject = InspectorObject::create();
478 responseObject->setNumber("statusCode", response.statusCode());
479 responseObject->setString("statusText", response.statusText());
480 responseObject->setObject("webSocketHeaderFields", buildObjectForHeaders(response.headerFields()));
481 responseObject->setString("webSocketChallengeResponse", createReadableStringFromBinary(response.challengeResponse().value, sizeof(response.challengeResponse().value)));
482 m_frontend->didReceiveWebSocketHandshakeResponse(identifier, currentTime(), responseObject);
485 void InspectorResourceAgent::didCloseWebSocket(unsigned long identifier)
487 m_frontend->didCloseWebSocket(identifier, currentTime());
489 #endif // ENABLE(WEB_SOCKETS)
491 Frame* InspectorResourceAgent::frameForId(unsigned long frameId)
493 Frame* mainFrame = m_page->mainFrame();
494 for (Frame* frame = mainFrame; frame; frame = frame->tree()->traverseNext(mainFrame)) {
495 if (reinterpret_cast<uintptr_t>(frame) == frameId)
501 void InspectorResourceAgent::cachedResources(ErrorString* error, RefPtr<InspectorObject>* object)
503 *object = buildObjectForFrameTree(m_page->mainFrame(), true);
506 void InspectorResourceAgent::resourceContent(ErrorString*, unsigned long frameId, const String& url, bool base64Encode, bool* success, String* content)
508 Frame* frame = frameForId(frameId);
514 *success = InspectorResourceAgent::resourceContentBase64(frame, KURL(ParsedURLString, url), content);
516 *success = InspectorResourceAgent::resourceContent(frame, KURL(ParsedURLString, url), content);
519 InspectorResourceAgent::InspectorResourceAgent(Page* page, InspectorState* state, InspectorFrontend* frontend)
522 , m_frontend(frontend)
524 m_state->setBoolean(ResourceAgentState::resourceAgentEnabled, true);
527 } // namespace WebCore
529 #endif // ENABLE(INSPECTOR)