• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2007, 2008 Apple Inc. All rights reserved.
3  * Copyright (C) 2008 Matt Lilek <webkit@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 
InspectorResource(unsigned long identifier,DocumentLoader * loader,const KURL & requestURL)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 
~InspectorResource()69 InspectorResource::~InspectorResource()
70 {
71 }
72 
appendRedirect(unsigned long identifier,const KURL & redirectURL)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 
createCached(unsigned long identifier,DocumentLoader * loader,const CachedResource * cachedResource)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 
updateRequest(const ResourceRequest & request)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 
updateResponse(const ResourceResponse & response)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 
populateHeadersObject(ScriptObject * object,const HTTPHeaderMap & headers)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 
updateScriptObject(InspectorFrontend * frontend)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 
releaseScriptObject(InspectorFrontend * frontend,bool callRemoveResource)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 
cachedResource() const229 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 
cachedResourceType() const241 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 
type() const265 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 
setXMLHttpResponseText(const ScriptString & data)284 void InspectorResource::setXMLHttpResponseText(const ScriptString& data)
285 {
286     m_xmlHttpResponseText = data;
287     m_changes.set(TypeChange);
288 }
289 
sourceString() const290 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 
resourceData(String * textEncodingName) const306 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 
startTiming()332 void InspectorResource::startTiming()
333 {
334     m_startTime = currentTime();
335     m_changes.set(TimingChange);
336 }
337 
markResponseReceivedTime()338 void InspectorResource::markResponseReceivedTime()
339 {
340     m_responseReceivedTime = currentTime();
341     m_changes.set(TimingChange);
342 }
343 
endTiming()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 
markDOMContentEventTime()352 void InspectorResource::markDOMContentEventTime()
353 {
354     m_domContentEventTime = currentTime();
355     m_changes.set(TimingChange);
356 }
357 
markLoadEventTime()358 void InspectorResource::markLoadEventTime()
359 {
360     m_loadEventTime = currentTime();
361     m_changes.set(TimingChange);
362 }
363 
markFailed()364 void InspectorResource::markFailed()
365 {
366     m_failed = true;
367     m_changes.set(CompletionChange);
368 }
369 
addLength(int lengthReceived)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