| /* |
| * Copyright (C) 2010 Google Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: |
| * |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * * Redistributions in binary form must reproduce the above |
| * copyright notice, this list of conditions and the following disclaimer |
| * in the documentation and/or other materials provided with the |
| * distribution. |
| * * Neither the name of Google Inc. nor the names of its |
| * contributors may be used to endorse or promote products derived from |
| * this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "config.h" |
| #include "InspectorResourceAgent.h" |
| |
| #if ENABLE(INSPECTOR) |
| |
| #include "Base64.h" |
| #include "MemoryCache.h" |
| #include "CachedResource.h" |
| #include "CachedResourceLoader.h" |
| #include "Document.h" |
| #include "DocumentLoader.h" |
| #include "Frame.h" |
| #include "FrameLoader.h" |
| #include "HTMLFrameOwnerElement.h" |
| #include "HTMLNames.h" |
| #include "HTTPHeaderMap.h" |
| #include "InspectorFrontend.h" |
| #include "InspectorValues.h" |
| #include "KURL.h" |
| #include "Page.h" |
| #include "ProgressTracker.h" |
| #include "ResourceError.h" |
| #include "ResourceRequest.h" |
| #include "ResourceResponse.h" |
| #include "ScriptCallStack.h" |
| #include "ScriptCallStackFactory.h" |
| #include "SharedBuffer.h" |
| #include "TextEncoding.h" |
| #include "WebSocketHandshakeRequest.h" |
| #include "WebSocketHandshakeResponse.h" |
| |
| #include <wtf/CurrentTime.h> |
| #include <wtf/ListHashSet.h> |
| #include <wtf/RefPtr.h> |
| #include <wtf/text/StringBuffer.h> |
| |
| namespace WebCore { |
| |
| bool InspectorResourceAgent::resourceContent(Frame* frame, const KURL& url, String* result) |
| { |
| if (!frame) |
| return false; |
| |
| String textEncodingName; |
| RefPtr<SharedBuffer> buffer = InspectorResourceAgent::resourceData(frame, url, &textEncodingName); |
| |
| if (buffer) { |
| TextEncoding encoding(textEncodingName); |
| if (!encoding.isValid()) |
| encoding = WindowsLatin1Encoding(); |
| *result = encoding.decode(buffer->data(), buffer->size()); |
| return true; |
| } |
| |
| return false; |
| } |
| |
| bool InspectorResourceAgent::resourceContentBase64(Frame* frame, const KURL& url, String* result) |
| { |
| Vector<char> out; |
| String textEncodingName; |
| RefPtr<SharedBuffer> data = InspectorResourceAgent::resourceData(frame, url, &textEncodingName); |
| if (!data) { |
| *result = String(); |
| return false; |
| } |
| |
| base64Encode(data->buffer(), out); |
| *result = String(out.data(), out.size()); |
| return true; |
| } |
| |
| PassRefPtr<SharedBuffer> InspectorResourceAgent::resourceData(Frame* frame, const KURL& url, String* textEncodingName) |
| { |
| FrameLoader* frameLoader = frame->loader(); |
| DocumentLoader* loader = frameLoader->documentLoader(); |
| if (equalIgnoringFragmentIdentifier(url, loader->url())) { |
| *textEncodingName = frame->document()->inputEncoding(); |
| return frameLoader->documentLoader()->mainResourceData(); |
| } |
| |
| CachedResource* cachedResource = InspectorResourceAgent::cachedResource(frame, url); |
| if (!cachedResource) |
| return 0; |
| |
| if (cachedResource->isPurgeable()) { |
| // If the resource is purgeable then make it unpurgeable to get |
| // get its data. This might fail, in which case we return an |
| // empty String. |
| // FIXME: should we do something else in the case of a purged |
| // resource that informs the user why there is no data in the |
| // inspector? |
| if (!cachedResource->makePurgeable(false)) |
| return 0; |
| } |
| |
| *textEncodingName = cachedResource->encoding(); |
| return cachedResource->data(); |
| } |
| |
| CachedResource* InspectorResourceAgent::cachedResource(Frame* frame, const KURL& url) |
| { |
| CachedResource* cachedResource = frame->document()->cachedResourceLoader()->cachedResource(url); |
| if (!cachedResource) |
| cachedResource = cache()->resourceForURL(url); |
| return cachedResource; |
| } |
| |
| static PassRefPtr<InspectorObject> buildObjectForHeaders(const HTTPHeaderMap& headers) |
| { |
| RefPtr<InspectorObject> headersObject = InspectorObject::create(); |
| HTTPHeaderMap::const_iterator end = headers.end(); |
| for (HTTPHeaderMap::const_iterator it = headers.begin(); it != end; ++it) |
| headersObject->setString(it->first.string(), it->second); |
| return headersObject; |
| } |
| |
| static PassRefPtr<InspectorObject> buildObjectForTiming(const ResourceLoadTiming& timing) |
| { |
| RefPtr<InspectorObject> timingObject = InspectorObject::create(); |
| timingObject->setNumber("requestTime", timing.requestTime); |
| timingObject->setNumber("proxyStart", timing.proxyStart); |
| timingObject->setNumber("proxyEnd", timing.proxyEnd); |
| timingObject->setNumber("dnsStart", timing.dnsStart); |
| timingObject->setNumber("dnsEnd", timing.dnsEnd); |
| timingObject->setNumber("connectStart", timing.connectStart); |
| timingObject->setNumber("connectEnd", timing.connectEnd); |
| timingObject->setNumber("sslStart", timing.sslStart); |
| timingObject->setNumber("sslEnd", timing.sslEnd); |
| timingObject->setNumber("sendStart", timing.sendStart); |
| timingObject->setNumber("sendEnd", timing.sendEnd); |
| timingObject->setNumber("receiveHeadersEnd", timing.receiveHeadersEnd); |
| return timingObject; |
| } |
| |
| static PassRefPtr<InspectorObject> buildObjectForResourceRequest(const ResourceRequest& request) |
| { |
| RefPtr<InspectorObject> requestObject = InspectorObject::create(); |
| requestObject->setString("url", request.url().string()); |
| requestObject->setString("httpMethod", request.httpMethod()); |
| requestObject->setObject("httpHeaderFields", buildObjectForHeaders(request.httpHeaderFields())); |
| if (request.httpBody() && !request.httpBody()->isEmpty()) |
| requestObject->setString("requestFormData", request.httpBody()->flattenToString()); |
| return requestObject; |
| } |
| |
| static PassRefPtr<InspectorObject> buildObjectForResourceResponse(const ResourceResponse& response) |
| { |
| RefPtr<InspectorObject> responseObject = InspectorObject::create(); |
| if (response.isNull()) { |
| responseObject->setBoolean("isNull", true); |
| return responseObject; |
| } |
| responseObject->setString("url", response.url().string()); |
| responseObject->setString("mimeType", response.mimeType()); |
| responseObject->setNumber("expectedContentLength", response.expectedContentLength()); |
| responseObject->setString("textEncodingName", response.textEncodingName()); |
| responseObject->setString("suggestedFilename", response.suggestedFilename()); |
| responseObject->setNumber("httpStatusCode", response.httpStatusCode()); |
| responseObject->setString("httpStatusText", response.httpStatusText()); |
| responseObject->setObject("httpHeaderFields", buildObjectForHeaders(response.httpHeaderFields())); |
| responseObject->setBoolean("connectionReused", response.connectionReused()); |
| responseObject->setNumber("connectionID", response.connectionID()); |
| responseObject->setBoolean("wasCached", response.wasCached()); |
| if (response.resourceLoadTiming()) |
| responseObject->setObject("timing", buildObjectForTiming(*response.resourceLoadTiming())); |
| if (response.resourceLoadInfo()) { |
| RefPtr<InspectorObject> loadInfoObject = InspectorObject::create(); |
| loadInfoObject->setNumber("httpStatusCode", response.resourceLoadInfo()->httpStatusCode); |
| loadInfoObject->setString("httpStatusText", response.resourceLoadInfo()->httpStatusText); |
| loadInfoObject->setObject("requestHeaders", buildObjectForHeaders(response.resourceLoadInfo()->requestHeaders)); |
| loadInfoObject->setObject("responseHeaders", buildObjectForHeaders(response.resourceLoadInfo()->responseHeaders)); |
| responseObject->setObject("loadInfo", loadInfoObject); |
| } |
| return responseObject; |
| } |
| |
| static unsigned long frameId(Frame* frame) |
| { |
| return reinterpret_cast<uintptr_t>(frame); |
| } |
| |
| static PassRefPtr<InspectorObject> buildObjectForDocumentLoader(DocumentLoader* loader) |
| { |
| RefPtr<InspectorObject> documentLoaderObject = InspectorObject::create(); |
| documentLoaderObject->setNumber("frameId", frameId(loader->frame())); |
| documentLoaderObject->setNumber("loaderId", reinterpret_cast<uintptr_t>(loader)); |
| documentLoaderObject->setString("url", loader->requestURL().string()); |
| return documentLoaderObject; |
| } |
| |
| static PassRefPtr<InspectorObject> buildObjectForFrameResource(Frame* frame) |
| { |
| FrameLoader* frameLoader = frame->loader(); |
| DocumentLoader* loader = frameLoader->documentLoader(); |
| |
| RefPtr<InspectorObject> resourceObject = InspectorObject::create(); |
| resourceObject->setString("url", loader->url().string()); |
| resourceObject->setObject("loader", buildObjectForDocumentLoader(loader)); |
| resourceObject->setObject("request", buildObjectForResourceRequest(loader->request())); |
| resourceObject->setObject("response", buildObjectForResourceResponse(loader->response())); |
| return resourceObject; |
| } |
| |
| static String cachedResourceTypeString(const CachedResource& cachedResource) |
| { |
| switch (cachedResource.type()) { |
| case CachedResource::ImageResource: |
| return "Image"; |
| case CachedResource::FontResource: |
| return "Font"; |
| case CachedResource::CSSStyleSheet: |
| // Fall through. |
| #if ENABLE(XSLT) |
| case CachedResource::XSLStyleSheet: |
| #endif |
| return "Stylesheet"; |
| case CachedResource::Script: |
| return "Script"; |
| default: |
| break; |
| } |
| return "Other"; |
| } |
| |
| static PassRefPtr<InspectorObject> buildObjectForCachedResource(DocumentLoader* loader, const CachedResource& cachedResource) |
| { |
| RefPtr<InspectorObject> resourceObject = InspectorObject::create(); |
| resourceObject->setString("url", cachedResource.url()); |
| resourceObject->setString("type", cachedResourceTypeString(cachedResource)); |
| resourceObject->setNumber("encodedSize", cachedResource.encodedSize()); |
| resourceObject->setObject("response", buildObjectForResourceResponse(cachedResource.response())); |
| resourceObject->setObject("loader", buildObjectForDocumentLoader(loader)); |
| return resourceObject; |
| } |
| |
| static void populateObjectWithFrameResources(Frame* frame, PassRefPtr<InspectorObject> frameResources) |
| { |
| frameResources->setObject("resource", buildObjectForFrameResource(frame)); |
| RefPtr<InspectorArray> subresources = InspectorArray::create(); |
| frameResources->setArray("subresources", subresources); |
| |
| const CachedResourceLoader::DocumentResourceMap& allResources = frame->document()->cachedResourceLoader()->allCachedResources(); |
| CachedResourceLoader::DocumentResourceMap::const_iterator end = allResources.end(); |
| for (CachedResourceLoader::DocumentResourceMap::const_iterator it = allResources.begin(); it != end; ++it) { |
| CachedResource* cachedResource = it->second.get(); |
| RefPtr<InspectorObject> cachedResourceObject = buildObjectForCachedResource(frame->loader()->documentLoader(), *cachedResource); |
| subresources->pushValue(cachedResourceObject); |
| } |
| } |
| |
| InspectorResourceAgent::~InspectorResourceAgent() |
| { |
| } |
| |
| void InspectorResourceAgent::identifierForInitialRequest(unsigned long identifier, const KURL& url, DocumentLoader* loader) |
| { |
| RefPtr<InspectorObject> loaderObject = buildObjectForDocumentLoader(loader); |
| RefPtr<ScriptCallStack> callStack = createScriptCallStack(ScriptCallStack::maxCallStackSizeToCapture, true); |
| RefPtr<InspectorValue> callStackValue; |
| if (callStack) |
| callStackValue = callStack->buildInspectorObject(); |
| else |
| callStackValue = InspectorValue::null(); |
| m_frontend->identifierForInitialRequest(identifier, url.string(), loaderObject, callStackValue); |
| } |
| |
| void InspectorResourceAgent::willSendRequest(unsigned long identifier, ResourceRequest& request, const ResourceResponse& redirectResponse) |
| { |
| m_frontend->willSendRequest(identifier, currentTime(), buildObjectForResourceRequest(request), buildObjectForResourceResponse(redirectResponse)); |
| } |
| |
| void InspectorResourceAgent::markResourceAsCached(unsigned long identifier) |
| { |
| m_frontend->markResourceAsCached(identifier); |
| } |
| |
| void InspectorResourceAgent::didReceiveResponse(unsigned long identifier, DocumentLoader* loader, const ResourceResponse& response) |
| { |
| RefPtr<InspectorObject> resourceResponse = buildObjectForResourceResponse(response); |
| String type = "Other"; |
| if (loader) { |
| if (equalIgnoringFragmentIdentifier(response.url(), loader->frameLoader()->iconURL())) |
| type = "Image"; |
| else { |
| CachedResource* cachedResource = InspectorResourceAgent::cachedResource(loader->frame(), response.url()); |
| if (cachedResource) |
| type = cachedResourceTypeString(*cachedResource); |
| |
| if (equalIgnoringFragmentIdentifier(response.url(), loader->url()) && type == "Other") |
| type = "Document"; |
| |
| // Use mime type from cached resource in case the one in response is empty. |
| if (response.mimeType().isEmpty() && cachedResource) |
| resourceResponse->setString("mimeType", cachedResource->response().mimeType()); |
| } |
| } |
| m_frontend->didReceiveResponse(identifier, currentTime(), type, resourceResponse); |
| } |
| |
| void InspectorResourceAgent::didReceiveContentLength(unsigned long identifier, int lengthReceived) |
| { |
| m_frontend->didReceiveContentLength(identifier, currentTime(), lengthReceived); |
| } |
| |
| void InspectorResourceAgent::didFinishLoading(unsigned long identifier, double finishTime) |
| { |
| if (!finishTime) |
| finishTime = currentTime(); |
| |
| m_frontend->didFinishLoading(identifier, finishTime); |
| } |
| |
| void InspectorResourceAgent::didFailLoading(unsigned long identifier, const ResourceError& error) |
| { |
| m_frontend->didFailLoading(identifier, currentTime(), error.localizedDescription()); |
| } |
| |
| void InspectorResourceAgent::didLoadResourceFromMemoryCache(DocumentLoader* loader, const CachedResource* resource) |
| { |
| m_frontend->didLoadResourceFromMemoryCache(currentTime(), buildObjectForCachedResource(loader, *resource)); |
| } |
| |
| void InspectorResourceAgent::setInitialContent(unsigned long identifier, const String& sourceString, const String& type) |
| { |
| m_frontend->setInitialContent(identifier, sourceString, type); |
| } |
| |
| static PassRefPtr<InspectorObject> buildObjectForFrame(Frame* frame) |
| { |
| RefPtr<InspectorObject> frameObject = InspectorObject::create(); |
| frameObject->setNumber("id", frameId(frame)); |
| frameObject->setNumber("parentId", frameId(frame->tree()->parent())); |
| if (frame->ownerElement()) { |
| String name = frame->ownerElement()->getAttribute(HTMLNames::nameAttr); |
| if (name.isEmpty()) |
| name = frame->ownerElement()->getAttribute(HTMLNames::idAttr); |
| frameObject->setString("name", name); |
| } |
| frameObject->setString("url", frame->loader()->url().string()); |
| return frameObject; |
| } |
| |
| static PassRefPtr<InspectorObject> buildObjectForFrameTree(Frame* frame, bool dumpResources) |
| { |
| RefPtr<InspectorObject> frameObject = buildObjectForFrame(frame); |
| |
| if (dumpResources) |
| populateObjectWithFrameResources(frame, frameObject); |
| RefPtr<InspectorArray> childrenArray; |
| for (Frame* child = frame->tree()->firstChild(); child; child = child->tree()->nextSibling()) { |
| if (!childrenArray) { |
| childrenArray = InspectorArray::create(); |
| frameObject->setArray("children", childrenArray); |
| } |
| childrenArray->pushObject(buildObjectForFrameTree(child, dumpResources)); |
| } |
| return frameObject; |
| } |
| |
| void InspectorResourceAgent::didCommitLoad(DocumentLoader* loader) |
| { |
| m_frontend->didCommitLoadForFrame(buildObjectForFrame(loader->frame()), buildObjectForDocumentLoader(loader)); |
| } |
| |
| void InspectorResourceAgent::frameDetachedFromParent(Frame* frame) |
| { |
| m_frontend->frameDetachedFromParent(frameId(frame)); |
| } |
| |
| #if ENABLE(WEB_SOCKETS) |
| |
| // FIXME: More this into the front-end? |
| // Create human-readable binary representation, like "01:23:45:67:89:AB:CD:EF". |
| static String createReadableStringFromBinary(const unsigned char* value, size_t length) |
| { |
| ASSERT(length > 0); |
| static const char hexDigits[17] = "0123456789ABCDEF"; |
| size_t bufferSize = length * 3 - 1; |
| StringBuffer buffer(bufferSize); |
| size_t index = 0; |
| for (size_t i = 0; i < length; ++i) { |
| if (i > 0) |
| buffer[index++] = ':'; |
| buffer[index++] = hexDigits[value[i] >> 4]; |
| buffer[index++] = hexDigits[value[i] & 0xF]; |
| } |
| ASSERT(index == bufferSize); |
| return String::adopt(buffer); |
| } |
| |
| void InspectorResourceAgent::didCreateWebSocket(unsigned long identifier, const KURL& requestURL) |
| { |
| m_frontend->didCreateWebSocket(identifier, requestURL.string()); |
| } |
| |
| void InspectorResourceAgent::willSendWebSocketHandshakeRequest(unsigned long identifier, const WebSocketHandshakeRequest& request) |
| { |
| RefPtr<InspectorObject> requestObject = InspectorObject::create(); |
| requestObject->setObject("webSocketHeaderFields", buildObjectForHeaders(request.headerFields())); |
| requestObject->setString("webSocketRequestKey3", createReadableStringFromBinary(request.key3().value, sizeof(request.key3().value))); |
| m_frontend->willSendWebSocketHandshakeRequest(identifier, currentTime(), requestObject); |
| } |
| |
| void InspectorResourceAgent::didReceiveWebSocketHandshakeResponse(unsigned long identifier, const WebSocketHandshakeResponse& response) |
| { |
| RefPtr<InspectorObject> responseObject = InspectorObject::create(); |
| responseObject->setNumber("statusCode", response.statusCode()); |
| responseObject->setString("statusText", response.statusText()); |
| responseObject->setObject("webSocketHeaderFields", buildObjectForHeaders(response.headerFields())); |
| responseObject->setString("webSocketChallengeResponse", createReadableStringFromBinary(response.challengeResponse().value, sizeof(response.challengeResponse().value))); |
| m_frontend->didReceiveWebSocketHandshakeResponse(identifier, currentTime(), responseObject); |
| } |
| |
| void InspectorResourceAgent::didCloseWebSocket(unsigned long identifier) |
| { |
| m_frontend->didCloseWebSocket(identifier, currentTime()); |
| } |
| #endif // ENABLE(WEB_SOCKETS) |
| |
| Frame* InspectorResourceAgent::frameForId(unsigned long frameId) |
| { |
| Frame* mainFrame = m_page->mainFrame(); |
| for (Frame* frame = mainFrame; frame; frame = frame->tree()->traverseNext(mainFrame)) { |
| if (reinterpret_cast<uintptr_t>(frame) == frameId) |
| return frame; |
| } |
| return 0; |
| } |
| |
| void InspectorResourceAgent::cachedResources(RefPtr<InspectorObject>* object) |
| { |
| *object = buildObjectForFrameTree(m_page->mainFrame(), true); |
| } |
| |
| void InspectorResourceAgent::resourceContent(unsigned long id, const String& url, bool base64Encode, String* content) |
| { |
| for (Frame* frame = m_page->mainFrame(); frame; frame = frame->tree()->traverseNext(m_page->mainFrame())) { |
| if (frameId(frame) != id) |
| continue; |
| if (base64Encode) |
| InspectorResourceAgent::resourceContentBase64(frame, KURL(ParsedURLString, url), content); |
| else |
| InspectorResourceAgent::resourceContent(frame, KURL(ParsedURLString, url), content); |
| break; |
| } |
| } |
| |
| InspectorResourceAgent::InspectorResourceAgent(Page* page, InspectorFrontend* frontend) |
| : m_page(page) |
| , m_frontend(frontend) |
| { |
| } |
| |
| } // namespace WebCore |
| |
| #endif // ENABLE(INSPECTOR) |