Home | History | Annotate | Download | only in inspector
      1 /*
      2  * Copyright (C) 2007, 2008 Apple Inc. All rights reserved.
      3  * Copyright (C) 2008 Matt Lilek <webkit (at) 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 "Cache.h"
     37 #include "CachedResource.h"
     38 #include "DocLoader.h"
     39 #include "DocumentLoader.h"
     40 #include "Frame.h"
     41 #include "InspectorFrontend.h"
     42 #include "ResourceRequest.h"
     43 #include "ResourceResponse.h"
     44 #include "TextEncoding.h"
     45 #include "ScriptObject.h"
     46 
     47 namespace WebCore {
     48 
     49 InspectorResource::InspectorResource(unsigned long identifier, DocumentLoader* loader, const KURL& requestURL)
     50     : m_identifier(identifier)
     51     , m_loader(loader)
     52     , m_frame(loader->frame())
     53     , m_requestURL(requestURL)
     54     , m_expectedContentLength(0)
     55     , m_cached(false)
     56     , m_finished(false)
     57     , m_failed(false)
     58     , m_length(0)
     59     , m_responseStatusCode(0)
     60     , m_startTime(-1.0)
     61     , m_responseReceivedTime(-1.0)
     62     , m_endTime(-1.0)
     63     , m_loadEventTime(-1.0)
     64     , m_domContentEventTime(-1.0)
     65     , m_isMainResource(false)
     66 {
     67 }
     68 
     69 InspectorResource::~InspectorResource()
     70 {
     71 }
     72 
     73 PassRefPtr<InspectorResource> InspectorResource::appendRedirect(unsigned long identifier, const KURL& redirectURL)
     74 {
     75     // Last redirect is always a container of all previous ones. Pass this container here.
     76     RefPtr<InspectorResource> redirect = InspectorResource::create(m_identifier, m_loader.get(), redirectURL);
     77     redirect->m_redirects = m_redirects;
     78     redirect->m_redirects.append(this);
     79     redirect->m_changes.set(RedirectsChange);
     80 
     81     m_identifier = identifier;
     82     // Re-send request info with new id.
     83     m_changes.set(RequestChange);
     84     m_redirects.clear();
     85     return redirect;
     86 }
     87 
     88 PassRefPtr<InspectorResource> InspectorResource::createCached(unsigned long identifier, DocumentLoader* loader, const CachedResource* cachedResource)
     89 {
     90     PassRefPtr<InspectorResource> resource = create(identifier, loader, KURL(ParsedURLString, cachedResource->url()));
     91 
     92     resource->m_finished = true;
     93 
     94     resource->updateResponse(cachedResource->response());
     95 
     96     resource->m_length = cachedResource->encodedSize();
     97     resource->m_cached = true;
     98     resource->m_startTime = currentTime();
     99     resource->m_responseReceivedTime = resource->m_startTime;
    100     resource->m_endTime = resource->m_startTime;
    101 
    102     resource->m_changes.setAll();
    103 
    104     return resource;
    105 }
    106 
    107 void InspectorResource::updateRequest(const ResourceRequest& request)
    108 {
    109     m_requestHeaderFields = request.httpHeaderFields();
    110     m_requestMethod = request.httpMethod();
    111     if (request.httpBody() && !request.httpBody()->isEmpty())
    112         m_requestFormData = request.httpBody()->flattenToString();
    113 
    114     m_changes.set(RequestChange);
    115 }
    116 
    117 void InspectorResource::updateResponse(const ResourceResponse& response)
    118 {
    119     m_expectedContentLength = response.expectedContentLength();
    120     m_mimeType = response.mimeType();
    121     if (m_mimeType.isEmpty() && response.httpStatusCode() == 304) {
    122         CachedResource* cachedResource = cache()->resourceForURL(response.url().string());
    123         if (cachedResource)
    124             m_mimeType = cachedResource->response().mimeType();
    125     }
    126     m_responseHeaderFields = response.httpHeaderFields();
    127     m_responseStatusCode = response.httpStatusCode();
    128     m_suggestedFilename = response.suggestedFilename();
    129 
    130     m_changes.set(ResponseChange);
    131     m_changes.set(TypeChange);
    132 }
    133 
    134 static void populateHeadersObject(ScriptObject* object, const HTTPHeaderMap& headers)
    135 {
    136     HTTPHeaderMap::const_iterator end = headers.end();
    137     for (HTTPHeaderMap::const_iterator it = headers.begin(); it != end; ++it) {
    138         object->set(it->first.string(), it->second);
    139     }
    140 }
    141 
    142 
    143 void InspectorResource::updateScriptObject(InspectorFrontend* frontend)
    144 {
    145     if (m_changes.hasChange(NoChange))
    146         return;
    147 
    148     ScriptObject jsonObject = frontend->newScriptObject();
    149     if (m_changes.hasChange(RequestChange)) {
    150         jsonObject.set("url", m_requestURL.string());
    151         jsonObject.set("documentURL", m_frame->document()->url().string());
    152         jsonObject.set("host", m_requestURL.host());
    153         jsonObject.set("path", m_requestURL.path());
    154         jsonObject.set("lastPathComponent", m_requestURL.lastPathComponent());
    155         ScriptObject requestHeaders = frontend->newScriptObject();
    156         populateHeadersObject(&requestHeaders, m_requestHeaderFields);
    157         jsonObject.set("requestHeaders", requestHeaders);
    158         jsonObject.set("mainResource", m_isMainResource);
    159         jsonObject.set("requestMethod", m_requestMethod);
    160         jsonObject.set("requestFormData", m_requestFormData);
    161         jsonObject.set("didRequestChange", true);
    162         jsonObject.set("cached", m_cached);
    163     }
    164 
    165     if (m_changes.hasChange(ResponseChange)) {
    166         jsonObject.set("mimeType", m_mimeType);
    167         jsonObject.set("suggestedFilename", m_suggestedFilename);
    168         jsonObject.set("expectedContentLength", m_expectedContentLength);
    169         jsonObject.set("statusCode", m_responseStatusCode);
    170         jsonObject.set("suggestedFilename", m_suggestedFilename);
    171         ScriptObject responseHeaders = frontend->newScriptObject();
    172         populateHeadersObject(&responseHeaders, m_responseHeaderFields);
    173         jsonObject.set("responseHeaders", responseHeaders);
    174         jsonObject.set("didResponseChange", true);
    175     }
    176 
    177     if (m_changes.hasChange(TypeChange)) {
    178         jsonObject.set("type", static_cast<int>(type()));
    179         jsonObject.set("didTypeChange", true);
    180     }
    181 
    182     if (m_changes.hasChange(LengthChange)) {
    183         jsonObject.set("contentLength", m_length);
    184         jsonObject.set("didLengthChange", true);
    185     }
    186 
    187     if (m_changes.hasChange(CompletionChange)) {
    188         jsonObject.set("failed", m_failed);
    189         jsonObject.set("finished", m_finished);
    190         jsonObject.set("didCompletionChange", true);
    191     }
    192 
    193     if (m_changes.hasChange(TimingChange)) {
    194         if (m_startTime > 0)
    195             jsonObject.set("startTime", m_startTime);
    196         if (m_responseReceivedTime > 0)
    197             jsonObject.set("responseReceivedTime", m_responseReceivedTime);
    198         if (m_endTime > 0)
    199             jsonObject.set("endTime", m_endTime);
    200         if (m_loadEventTime > 0)
    201             jsonObject.set("loadEventTime", m_loadEventTime);
    202         if (m_domContentEventTime > 0)
    203             jsonObject.set("domContentEventTime", m_domContentEventTime);
    204         jsonObject.set("didTimingChange", true);
    205     }
    206 
    207     if (m_changes.hasChange(RedirectsChange)) {
    208         for (size_t i = 0; i < m_redirects.size(); ++i)
    209             m_redirects[i]->updateScriptObject(frontend);
    210     }
    211 
    212     if (frontend->updateResource(m_identifier, jsonObject))
    213         m_changes.clearAll();
    214 }
    215 
    216 void InspectorResource::releaseScriptObject(InspectorFrontend* frontend, bool callRemoveResource)
    217 {
    218     m_changes.setAll();
    219 
    220     for (size_t i = 0; i < m_redirects.size(); ++i)
    221         m_redirects[i]->releaseScriptObject(frontend, callRemoveResource);
    222 
    223     if (!callRemoveResource)
    224         return;
    225 
    226     frontend->removeResource(m_identifier);
    227 }
    228 
    229 CachedResource* InspectorResource::cachedResource() const
    230 {
    231     // Try hard to find a corresponding CachedResource. During preloading, DocLoader may not have the resource in document resources set yet,
    232     // but Inspector will already try to fetch data that is only available via CachedResource (and it won't update once the resource is added,
    233     // because m_changes will not have the appropriate bits set).
    234     const String& url = m_requestURL.string();
    235     CachedResource* cachedResource = m_frame->document()->docLoader()->cachedResource(url);
    236     if (!cachedResource)
    237         cachedResource = cache()->resourceForURL(url);
    238     return cachedResource;
    239 }
    240 
    241 InspectorResource::Type InspectorResource::cachedResourceType() const
    242 {
    243     CachedResource* cachedResource = this->cachedResource();
    244 
    245     if (!cachedResource)
    246         return Other;
    247 
    248     switch (cachedResource->type()) {
    249         case CachedResource::ImageResource:
    250             return Image;
    251         case CachedResource::FontResource:
    252             return Font;
    253         case CachedResource::CSSStyleSheet:
    254 #if ENABLE(XSLT)
    255         case CachedResource::XSLStyleSheet:
    256 #endif
    257             return Stylesheet;
    258         case CachedResource::Script:
    259             return Script;
    260         default:
    261             return Other;
    262     }
    263 }
    264 
    265 InspectorResource::Type InspectorResource::type() const
    266 {
    267     if (!m_xmlHttpResponseText.isNull())
    268         return XHR;
    269 
    270     if (m_requestURL == m_loader->requestURL()) {
    271         InspectorResource::Type resourceType = cachedResourceType();
    272         if (resourceType == Other)
    273             return Doc;
    274 
    275         return resourceType;
    276     }
    277 
    278     if (m_loader->frameLoader() && m_requestURL == m_loader->frameLoader()->iconURL())
    279         return Image;
    280 
    281     return cachedResourceType();
    282 }
    283 
    284 void InspectorResource::setXMLHttpResponseText(const ScriptString& data)
    285 {
    286     m_xmlHttpResponseText = data;
    287     m_changes.set(TypeChange);
    288 }
    289 
    290 String InspectorResource::sourceString() const
    291 {
    292     if (!m_xmlHttpResponseText.isNull())
    293         return String(m_xmlHttpResponseText);
    294 
    295     String textEncodingName;
    296     RefPtr<SharedBuffer> buffer = resourceData(&textEncodingName);
    297     if (!buffer)
    298         return String();
    299 
    300     TextEncoding encoding(textEncodingName);
    301     if (!encoding.isValid())
    302         encoding = WindowsLatin1Encoding();
    303     return encoding.decode(buffer->data(), buffer->size());
    304 }
    305 
    306 PassRefPtr<SharedBuffer> InspectorResource::resourceData(String* textEncodingName) const
    307 {
    308     if (m_requestURL == m_loader->requestURL()) {
    309         *textEncodingName = m_frame->document()->inputEncoding();
    310         return m_loader->mainResourceData();
    311     }
    312 
    313     CachedResource* cachedResource = this->cachedResource();
    314     if (!cachedResource)
    315         return 0;
    316 
    317     if (cachedResource->isPurgeable()) {
    318         // If the resource is purgeable then make it unpurgeable to get
    319         // get its data. This might fail, in which case we return an
    320         // empty String.
    321         // FIXME: should we do something else in the case of a purged
    322         // resource that informs the user why there is no data in the
    323         // inspector?
    324         if (!cachedResource->makePurgeable(false))
    325             return 0;
    326     }
    327 
    328     *textEncodingName = cachedResource->encoding();
    329     return cachedResource->data();
    330 }
    331 
    332 void InspectorResource::startTiming()
    333 {
    334     m_startTime = currentTime();
    335     m_changes.set(TimingChange);
    336 }
    337 
    338 void InspectorResource::markResponseReceivedTime()
    339 {
    340     m_responseReceivedTime = currentTime();
    341     m_changes.set(TimingChange);
    342 }
    343 
    344 void InspectorResource::endTiming()
    345 {
    346     m_endTime = currentTime();
    347     m_finished = true;
    348     m_changes.set(TimingChange);
    349     m_changes.set(CompletionChange);
    350 }
    351 
    352 void InspectorResource::markDOMContentEventTime()
    353 {
    354     m_domContentEventTime = currentTime();
    355     m_changes.set(TimingChange);
    356 }
    357 
    358 void InspectorResource::markLoadEventTime()
    359 {
    360     m_loadEventTime = currentTime();
    361     m_changes.set(TimingChange);
    362 }
    363 
    364 void InspectorResource::markFailed()
    365 {
    366     m_failed = true;
    367     m_changes.set(CompletionChange);
    368 }
    369 
    370 void InspectorResource::addLength(int lengthReceived)
    371 {
    372     m_length += lengthReceived;
    373     m_changes.set(LengthChange);
    374 
    375     // Update load time, otherwise the resource will
    376     // have start time == end time and  0 load duration
    377     // until its loading is completed.
    378     m_endTime = currentTime();
    379     m_changes.set(TimingChange);
    380 }
    381 
    382 } // namespace WebCore
    383 
    384 #endif // ENABLE(INSPECTOR)
    385