| /* |
| * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. |
| * Copyright (C) 2008 Matt Lilek <webkit@mattlilek.com> |
| * 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> InspectorResource::createCached(long long identifier, DocumentLoader* loader, const CachedResource* cachedResource) |
| { |
| PassRefPtr<InspectorResource> 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<int>(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<SharedBuffer> buffer = resourceData(&textEncodingName); |
| if (!buffer) |
| return String(); |
| |
| TextEncoding encoding(textEncodingName); |
| if (!encoding.isValid()) |
| encoding = WindowsLatin1Encoding(); |
| return encoding.decode(buffer->data(), buffer->size()); |
| } |
| |
| PassRefPtr<SharedBuffer> 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) |