/* * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. * Copyright (C) 2008 Matt Lilek * Copyright (C) 2009 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: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. 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. * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 "InspectorResource.h" #if ENABLE(INSPECTOR) #include "Cache.h" #include "CachedResource.h" #include "DocLoader.h" #include "DocumentLoader.h" #include "Frame.h" #include "InspectorFrontend.h" #include "ResourceRequest.h" #include "ResourceResponse.h" #include "TextEncoding.h" #include "ScriptObject.h" namespace WebCore { InspectorResource::InspectorResource(long long identifier, DocumentLoader* loader) : m_identifier(identifier) , m_loader(loader) , m_frame(loader->frame()) , m_scriptObjectCreated(false) , m_expectedContentLength(0) , m_cached(false) , m_finished(false) , m_failed(false) , m_length(0) , m_responseStatusCode(0) , m_startTime(-1.0) , m_responseReceivedTime(-1.0) , m_endTime(-1.0) , m_loadEventTime(-1.0) , m_domContentEventTime(-1.0) , m_isMainResource(false) { } InspectorResource::~InspectorResource() { } PassRefPtr InspectorResource::createCached(long long identifier, DocumentLoader* loader, const CachedResource* cachedResource) { PassRefPtr resource = create(identifier, loader); resource->m_finished = true; resource->m_requestURL = KURL(ParsedURLString, cachedResource->url()); resource->updateResponse(cachedResource->response()); resource->m_length = cachedResource->encodedSize(); resource->m_cached = true; resource->m_startTime = currentTime(); resource->m_responseReceivedTime = resource->m_startTime; resource->m_endTime = resource->m_startTime; resource->m_changes.setAll(); return resource; } void InspectorResource::updateRequest(const ResourceRequest& request) { m_requestHeaderFields = request.httpHeaderFields(); m_requestURL = request.url(); m_requestMethod = request.httpMethod(); if (request.httpBody() && !request.httpBody()->isEmpty()) m_requestFormData = request.httpBody()->flattenToString(); m_changes.set(RequestChange); } void InspectorResource::updateResponse(const ResourceResponse& response) { m_expectedContentLength = response.expectedContentLength(); m_mimeType = response.mimeType(); m_responseHeaderFields = response.httpHeaderFields(); m_responseStatusCode = response.httpStatusCode(); m_suggestedFilename = response.suggestedFilename(); m_changes.set(ResponseChange); m_changes.set(TypeChange); } static void populateHeadersObject(ScriptObject* object, const HTTPHeaderMap& headers) { HTTPHeaderMap::const_iterator end = headers.end(); for (HTTPHeaderMap::const_iterator it = headers.begin(); it != end; ++it) { object->set(it->first.string(), it->second); } } void InspectorResource::createScriptObject(InspectorFrontend* frontend) { if (!m_scriptObjectCreated) { ScriptObject jsonObject = frontend->newScriptObject(); ScriptObject requestHeaders = frontend->newScriptObject(); populateHeadersObject(&requestHeaders, m_requestHeaderFields); jsonObject.set("requestHeaders", requestHeaders); jsonObject.set("requestURL", requestURL()); jsonObject.set("host", m_requestURL.host()); jsonObject.set("path", m_requestURL.path()); jsonObject.set("lastPathComponent", m_requestURL.lastPathComponent()); jsonObject.set("isMainResource", m_isMainResource); jsonObject.set("cached", m_cached); jsonObject.set("requestMethod", m_requestMethod); jsonObject.set("requestFormData", m_requestFormData); if (!frontend->addResource(m_identifier, jsonObject)) return; m_scriptObjectCreated = true; m_changes.clear(RequestChange); } updateScriptObject(frontend); } void InspectorResource::updateScriptObject(InspectorFrontend* frontend) { if (!m_scriptObjectCreated) return; if (m_changes.hasChange(NoChange)) return; ScriptObject jsonObject = frontend->newScriptObject(); if (m_changes.hasChange(RequestChange)) { jsonObject.set("url", requestURL()); jsonObject.set("domain", m_requestURL.host()); jsonObject.set("path", m_requestURL.path()); jsonObject.set("lastPathComponent", m_requestURL.lastPathComponent()); ScriptObject requestHeaders = frontend->newScriptObject(); populateHeadersObject(&requestHeaders, m_requestHeaderFields); jsonObject.set("requestHeaders", requestHeaders); jsonObject.set("mainResource", m_isMainResource); jsonObject.set("requestMethod", m_requestMethod); jsonObject.set("requestFormData", m_requestFormData); jsonObject.set("didRequestChange", true); } if (m_changes.hasChange(ResponseChange)) { jsonObject.set("mimeType", m_mimeType); jsonObject.set("suggestedFilename", m_suggestedFilename); jsonObject.set("expectedContentLength", m_expectedContentLength); jsonObject.set("statusCode", m_responseStatusCode); jsonObject.set("suggestedFilename", m_suggestedFilename); ScriptObject responseHeaders = frontend->newScriptObject(); populateHeadersObject(&responseHeaders, m_responseHeaderFields); jsonObject.set("responseHeaders", responseHeaders); jsonObject.set("didResponseChange", true); } if (m_changes.hasChange(TypeChange)) { jsonObject.set("type", static_cast(type())); jsonObject.set("didTypeChange", true); } if (m_changes.hasChange(LengthChange)) { jsonObject.set("contentLength", m_length); jsonObject.set("didLengthChange", true); } if (m_changes.hasChange(CompletionChange)) { jsonObject.set("failed", m_failed); jsonObject.set("finished", m_finished); jsonObject.set("didCompletionChange", true); } if (m_changes.hasChange(TimingChange)) { if (m_startTime > 0) jsonObject.set("startTime", m_startTime); if (m_responseReceivedTime > 0) jsonObject.set("responseReceivedTime", m_responseReceivedTime); if (m_endTime > 0) jsonObject.set("endTime", m_endTime); if (m_loadEventTime > 0) jsonObject.set("loadEventTime", m_loadEventTime); if (m_domContentEventTime > 0) jsonObject.set("domContentEventTime", m_domContentEventTime); jsonObject.set("didTimingChange", true); } if (!frontend->updateResource(m_identifier, jsonObject)) return; m_changes.clearAll(); } void InspectorResource::releaseScriptObject(InspectorFrontend* frontend, bool callRemoveResource) { if (!m_scriptObjectCreated) return; m_scriptObjectCreated = false; m_changes.setAll(); if (!callRemoveResource) return; frontend->removeResource(m_identifier); } CachedResource* InspectorResource::cachedResource() const { // Try hard to find a corresponding CachedResource. During preloading, DocLoader may not have the resource in document resources set yet, // but Inspector will already try to fetch data that is only available via CachedResource (and it won't update once the resource is added, // because m_changes will not have the appropriate bits set). const String& url = requestURL(); CachedResource* cachedResource = m_frame->document()->docLoader()->cachedResource(url); if (!cachedResource) cachedResource = cache()->resourceForURL(url); return cachedResource; } InspectorResource::Type InspectorResource::type() const { if (!m_xmlHttpResponseText.isNull()) return XHR; if (m_requestURL == m_loader->requestURL()) return Doc; if (m_loader->frameLoader() && m_requestURL == m_loader->frameLoader()->iconURL()) return Image; CachedResource* cachedResource = this->cachedResource(); if (!cachedResource) return Other; switch (cachedResource->type()) { case CachedResource::ImageResource: return Image; case CachedResource::FontResource: return Font; case CachedResource::CSSStyleSheet: #if ENABLE(XSLT) case CachedResource::XSLStyleSheet: #endif return Stylesheet; case CachedResource::Script: return Script; default: return Other; } } void InspectorResource::setXMLHttpResponseText(const ScriptString& data) { m_xmlHttpResponseText = data; m_changes.set(TypeChange); } String InspectorResource::sourceString() const { if (!m_xmlHttpResponseText.isNull()) return String(m_xmlHttpResponseText); String textEncodingName; RefPtr buffer = resourceData(&textEncodingName); if (!buffer) return String(); TextEncoding encoding(textEncodingName); if (!encoding.isValid()) encoding = WindowsLatin1Encoding(); return encoding.decode(buffer->data(), buffer->size()); } PassRefPtr InspectorResource::resourceData(String* textEncodingName) const { if (m_requestURL == m_loader->requestURL()) { *textEncodingName = m_frame->document()->inputEncoding(); return m_loader->mainResourceData(); } CachedResource* cachedResource = this->cachedResource(); 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(); } void InspectorResource::startTiming() { m_startTime = currentTime(); m_changes.set(TimingChange); } void InspectorResource::markResponseReceivedTime() { m_responseReceivedTime = currentTime(); m_changes.set(TimingChange); } void InspectorResource::endTiming() { m_endTime = currentTime(); m_finished = true; m_changes.set(TimingChange); m_changes.set(CompletionChange); } void InspectorResource::markDOMContentEventTime() { m_domContentEventTime = currentTime(); m_changes.set(TimingChange); } void InspectorResource::markLoadEventTime() { m_loadEventTime = currentTime(); m_changes.set(TimingChange); } void InspectorResource::markFailed() { m_failed = true; m_changes.set(CompletionChange); } void InspectorResource::addLength(int lengthReceived) { m_length += lengthReceived; m_changes.set(LengthChange); } } // namespace WebCore #endif // ENABLE(INSPECTOR)