1 /*
2  * Copyright (C) 2011 Google Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  *     * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *     * Redistributions in binary form must reproduce the above
11  * copyright notice, this list of conditions and the following disclaimer
12  * in the documentation and/or other materials provided with the
13  * distribution.
14  *     * Neither the name of Google Inc. nor the names of its
15  * contributors may be used to endorse or promote products derived from
16  * this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30 
31 #include "config.h"
32 #include "InspectorResourceAgent.h"
33 
34 #if ENABLE(INSPECTOR)
35 
36 #include "CachedResource.h"
37 #include "CachedResourceLoader.h"
38 #include "DocumentLoader.h"
39 #include "EventsCollector.h"
40 #include "Frame.h"
41 #include "FrameLoader.h"
42 #include "HTTPHeaderMap.h"
43 #include "InspectorFrontend.h"
44 #include "InspectorFrontendChannel.h"
45 #include "InspectorFrontendProxy.h"
46 #include "InspectorPageAgent.h"
47 #include "InspectorState.h"
48 #include "InspectorValues.h"
49 #include "InstrumentingAgents.h"
50 #include "KURL.h"
51 #include "ProgressTracker.h"
52 #include "ResourceError.h"
53 #include "ResourceRequest.h"
54 #include "ResourceResponse.h"
55 #include "ScriptCallStack.h"
56 #include "ScriptCallStackFactory.h"
57 #include "WebSocketHandshakeRequest.h"
58 #include "WebSocketHandshakeResponse.h"
59 
60 #include <wtf/CurrentTime.h>
61 #include <wtf/HexNumber.h>
62 #include <wtf/ListHashSet.h>
63 #include <wtf/RefPtr.h>
64 #include <wtf/text/StringBuilder.h>
65 
66 namespace WebCore {
67 
68 namespace ResourceAgentState {
69 static const char resourceAgentEnabled[] = "resourceAgentEnabled";
70 static const char extraRequestHeaders[] = "extraRequestHeaders";
71 }
72 
setFrontend(InspectorFrontend * frontend)73 void InspectorResourceAgent::setFrontend(InspectorFrontend* frontend)
74 {
75     m_frontend = frontend->network();
76     if (backgroundEventsCollectionEnabled()) {
77         // Insert Message Proxy in receiver chain.
78         InspectorFrontendChannel* client = m_frontend->getInspectorFrontendChannel();
79         m_inspectorFrontendProxy->setInspectorFrontendChannel(client);
80         m_frontend->setInspectorFrontendChannel(m_inspectorFrontendProxy.get());
81         m_eventsCollector->sendCollectedEvents(client);
82     }
83 }
84 
clearFrontend()85 void InspectorResourceAgent::clearFrontend()
86 {
87     if (backgroundEventsCollectionEnabled()) {
88         m_inspectorFrontendProxy->setInspectorFrontendChannel(0);
89         m_frontend = m_mockFrontend.get();
90     } else
91         m_frontend = 0;
92 
93     m_userAgentOverride = "";
94     disable(0);
95 }
96 
restore()97 void InspectorResourceAgent::restore()
98 {
99     if (m_state->getBoolean(ResourceAgentState::resourceAgentEnabled))
100         enable();
101 }
102 
buildObjectForHeaders(const HTTPHeaderMap & headers)103 static PassRefPtr<InspectorObject> buildObjectForHeaders(const HTTPHeaderMap& headers)
104 {
105     RefPtr<InspectorObject> headersObject = InspectorObject::create();
106     HTTPHeaderMap::const_iterator end = headers.end();
107     for (HTTPHeaderMap::const_iterator it = headers.begin(); it != end; ++it)
108         headersObject->setString(it->first.string(), it->second);
109     return headersObject;
110 }
111 
buildObjectForTiming(const ResourceLoadTiming & timing)112 static PassRefPtr<InspectorObject> buildObjectForTiming(const ResourceLoadTiming& timing)
113 {
114     RefPtr<InspectorObject> timingObject = InspectorObject::create();
115     timingObject->setNumber("requestTime", timing.requestTime);
116     timingObject->setNumber("proxyStart", timing.proxyStart);
117     timingObject->setNumber("proxyEnd", timing.proxyEnd);
118     timingObject->setNumber("dnsStart", timing.dnsStart);
119     timingObject->setNumber("dnsEnd", timing.dnsEnd);
120     timingObject->setNumber("connectStart", timing.connectStart);
121     timingObject->setNumber("connectEnd", timing.connectEnd);
122     timingObject->setNumber("sslStart", timing.sslStart);
123     timingObject->setNumber("sslEnd", timing.sslEnd);
124     timingObject->setNumber("sendStart", timing.sendStart);
125     timingObject->setNumber("sendEnd", timing.sendEnd);
126     timingObject->setNumber("receiveHeadersEnd", timing.receiveHeadersEnd);
127     return timingObject;
128 }
129 
buildObjectForResourceRequest(const ResourceRequest & request)130 static PassRefPtr<InspectorObject> buildObjectForResourceRequest(const ResourceRequest& request)
131 {
132     RefPtr<InspectorObject> requestObject = InspectorObject::create();
133     requestObject->setString("url", request.url().string());
134     requestObject->setString("method", request.httpMethod());
135     requestObject->setObject("headers", buildObjectForHeaders(request.httpHeaderFields()));
136     if (request.httpBody() && !request.httpBody()->isEmpty())
137         requestObject->setString("postData", request.httpBody()->flattenToString());
138     return requestObject;
139 }
140 
buildObjectForResourceResponse(const ResourceResponse & response)141 static PassRefPtr<InspectorObject> buildObjectForResourceResponse(const ResourceResponse& response)
142 {
143     if (response.isNull())
144         return 0;
145 
146     RefPtr<InspectorObject> responseObject = InspectorObject::create();
147     responseObject->setNumber("status", response.resourceLoadInfo() ? response.resourceLoadInfo()->httpStatusCode : response.httpStatusCode());
148     responseObject->setString("statusText", response.resourceLoadInfo() ? response.resourceLoadInfo()->httpStatusText : response.httpStatusText());
149 
150     responseObject->setString("mimeType", response.mimeType());
151     responseObject->setBoolean("connectionReused", response.connectionReused());
152     responseObject->setNumber("connectionID", response.connectionID());
153     responseObject->setBoolean("fromDiskCache", response.wasCached());
154     if (response.resourceLoadTiming())
155         responseObject->setObject("timing", buildObjectForTiming(*response.resourceLoadTiming()));
156 
157     if (response.resourceLoadInfo()) {
158         responseObject->setObject("headers", buildObjectForHeaders(response.resourceLoadInfo()->responseHeaders));
159         if (!response.resourceLoadInfo()->responseHeadersText.isEmpty())
160             responseObject->setString("headersText", response.resourceLoadInfo()->responseHeadersText);
161 
162         responseObject->setObject("requestHeaders", buildObjectForHeaders(response.resourceLoadInfo()->requestHeaders));
163         if (!response.resourceLoadInfo()->requestHeadersText.isEmpty())
164             responseObject->setString("requestHeadersText", response.resourceLoadInfo()->requestHeadersText);
165     } else
166         responseObject->setObject("headers", buildObjectForHeaders(response.httpHeaderFields()));
167 
168     return responseObject;
169 }
170 
buildObjectForCachedResource(const CachedResource & cachedResource)171 static PassRefPtr<InspectorObject> buildObjectForCachedResource(const CachedResource& cachedResource)
172 {
173     RefPtr<InspectorObject> resourceObject = InspectorObject::create();
174     resourceObject->setString("url", cachedResource.url());
175     resourceObject->setString("type", InspectorPageAgent::cachedResourceTypeString(cachedResource));
176     resourceObject->setNumber("bodySize", cachedResource.encodedSize());
177     RefPtr<InspectorObject> resourceResponse = buildObjectForResourceResponse(cachedResource.response());
178     if (resourceResponse)
179         resourceObject->setObject("response", resourceResponse);
180     return resourceObject;
181 }
182 
~InspectorResourceAgent()183 InspectorResourceAgent::~InspectorResourceAgent()
184 {
185     if (m_state->getBoolean(ResourceAgentState::resourceAgentEnabled)) {
186         ErrorString error;
187         disable(&error);
188     }
189     ASSERT(!m_instrumentingAgents->inspectorResourceAgent());
190 }
191 
willSendRequest(unsigned long identifier,DocumentLoader * loader,ResourceRequest & request,const ResourceResponse & redirectResponse)192 void InspectorResourceAgent::willSendRequest(unsigned long identifier, DocumentLoader* loader, ResourceRequest& request, const ResourceResponse& redirectResponse)
193 {
194     RefPtr<InspectorObject> headers = m_state->getObject(ResourceAgentState::extraRequestHeaders);
195 
196     if (headers) {
197         InspectorObject::const_iterator end = headers->end();
198         for (InspectorObject::const_iterator it = headers->begin(); it != end; ++it) {
199             String value;
200             if (it->second->asString(&value))
201                 request.setHTTPHeaderField(it->first, value);
202         }
203     }
204 
205     request.setReportLoadTiming(true);
206     request.setReportRawHeaders(true);
207 
208     RefPtr<ScriptCallStack> callStack = createScriptCallStack(ScriptCallStack::maxCallStackSizeToCapture, true);
209     RefPtr<InspectorArray> callStackValue;
210     if (callStack)
211         callStackValue = callStack->buildInspectorArray();
212     else
213         callStackValue = InspectorArray::create();
214     m_frontend->requestWillBeSent(static_cast<int>(identifier), m_pageAgent->frameId(loader->frame()), m_pageAgent->loaderId(loader), loader->url().string(), buildObjectForResourceRequest(request), currentTime(), callStackValue, buildObjectForResourceResponse(redirectResponse));
215 }
216 
markResourceAsCached(unsigned long identifier)217 void InspectorResourceAgent::markResourceAsCached(unsigned long identifier)
218 {
219     m_frontend->resourceMarkedAsCached(static_cast<int>(identifier));
220 }
221 
didReceiveResponse(unsigned long identifier,DocumentLoader * loader,const ResourceResponse & response)222 void InspectorResourceAgent::didReceiveResponse(unsigned long identifier, DocumentLoader* loader, const ResourceResponse& response)
223 {
224     RefPtr<InspectorObject> resourceResponse = buildObjectForResourceResponse(response);
225     InspectorPageAgent::ResourceType type = InspectorPageAgent::OtherResource;
226     long cachedResourceSize = 0;
227 
228     if (loader) {
229         CachedResource* cachedResource = InspectorPageAgent::cachedResource(loader->frame(), response.url());
230         if (cachedResource) {
231             type = InspectorPageAgent::cachedResourceType(*cachedResource);
232             cachedResourceSize = cachedResource->encodedSize();
233             // Use mime type from cached resource in case the one in response is empty.
234             if (response.mimeType().isEmpty())
235                 resourceResponse->setString("mimeType", cachedResource->response().mimeType());
236         }
237         if (equalIgnoringFragmentIdentifier(response.url(), loader->frameLoader()->iconURL()))
238             type = InspectorPageAgent::ImageResource;
239         else if (equalIgnoringFragmentIdentifier(response.url(), loader->url()) && type == InspectorPageAgent::OtherResource)
240             type = InspectorPageAgent::DocumentResource;
241     }
242     m_frontend->responseReceived(static_cast<int>(identifier), currentTime(), InspectorPageAgent::resourceTypeString(type), resourceResponse);
243     // If we revalidated the resource and got Not modified, send content length following didReceiveResponse
244     // as there will be no calls to didReceiveContentLength from the network stack.
245     if (cachedResourceSize && response.httpStatusCode() == 304)
246         didReceiveContentLength(identifier, cachedResourceSize, 0);
247 }
248 
didReceiveContentLength(unsigned long identifier,int dataLength,int encodedDataLength)249 void InspectorResourceAgent::didReceiveContentLength(unsigned long identifier, int dataLength, int encodedDataLength)
250 {
251     m_frontend->dataReceived(static_cast<int>(identifier), currentTime(), dataLength, encodedDataLength);
252 }
253 
didFinishLoading(unsigned long identifier,double finishTime)254 void InspectorResourceAgent::didFinishLoading(unsigned long identifier, double finishTime)
255 {
256     if (!finishTime)
257         finishTime = currentTime();
258 
259     m_frontend->loadingFinished(static_cast<int>(identifier), finishTime);
260 }
261 
didFailLoading(unsigned long identifier,const ResourceError & error)262 void InspectorResourceAgent::didFailLoading(unsigned long identifier, const ResourceError& error)
263 {
264     m_frontend->loadingFailed(static_cast<int>(identifier), currentTime(), error.localizedDescription(), error.isCancellation());
265 }
266 
didLoadResourceFromMemoryCache(DocumentLoader * loader,const CachedResource * resource)267 void InspectorResourceAgent::didLoadResourceFromMemoryCache(DocumentLoader* loader, const CachedResource* resource)
268 {
269     m_frontend->resourceLoadedFromMemoryCache(m_pageAgent->frameId(loader->frame()), m_pageAgent->loaderId(loader), loader->url().string(), currentTime(), buildObjectForCachedResource(*resource));
270 }
271 
setInitialScriptContent(unsigned long identifier,const String & sourceString)272 void InspectorResourceAgent::setInitialScriptContent(unsigned long identifier, const String& sourceString)
273 {
274     m_frontend->initialContentSet(static_cast<int>(identifier), sourceString, InspectorPageAgent::resourceTypeString(InspectorPageAgent::ScriptResource));
275 }
276 
setInitialXHRContent(unsigned long identifier,const String & sourceString)277 void InspectorResourceAgent::setInitialXHRContent(unsigned long identifier, const String& sourceString)
278 {
279     m_frontend->initialContentSet(static_cast<int>(identifier), sourceString, InspectorPageAgent::resourceTypeString(InspectorPageAgent::XHRResource));
280 }
281 
applyUserAgentOverride(String * userAgent)282 void InspectorResourceAgent::applyUserAgentOverride(String* userAgent)
283 {
284     if (!m_userAgentOverride.isEmpty())
285         *userAgent = m_userAgentOverride;
286 }
287 
288 #if ENABLE(WEB_SOCKETS)
289 
290 // FIXME: More this into the front-end?
291 // Create human-readable binary representation, like "01:23:45:67:89:AB:CD:EF".
createReadableStringFromBinary(const unsigned char * value,size_t length)292 static String createReadableStringFromBinary(const unsigned char* value, size_t length)
293 {
294     ASSERT(length > 0);
295     StringBuilder builder;
296     builder.reserveCapacity(length * 3 - 1);
297     for (size_t i = 0; i < length; ++i) {
298         if (i > 0)
299             builder.append(':');
300         appendByteAsHex(value[i], builder);
301     }
302     return builder.toString();
303 }
304 
didCreateWebSocket(unsigned long identifier,const KURL & requestURL)305 void InspectorResourceAgent::didCreateWebSocket(unsigned long identifier, const KURL& requestURL)
306 {
307     m_frontend->webSocketCreated(static_cast<int>(identifier), requestURL.string());
308 }
309 
willSendWebSocketHandshakeRequest(unsigned long identifier,const WebSocketHandshakeRequest & request)310 void InspectorResourceAgent::willSendWebSocketHandshakeRequest(unsigned long identifier, const WebSocketHandshakeRequest& request)
311 {
312     RefPtr<InspectorObject> requestObject = InspectorObject::create();
313     requestObject->setObject("headers", buildObjectForHeaders(request.headerFields()));
314     requestObject->setString("requestKey3", createReadableStringFromBinary(request.key3().value, sizeof(request.key3().value)));
315     m_frontend->webSocketWillSendHandshakeRequest(static_cast<int>(identifier), currentTime(), requestObject);
316 }
317 
didReceiveWebSocketHandshakeResponse(unsigned long identifier,const WebSocketHandshakeResponse & response)318 void InspectorResourceAgent::didReceiveWebSocketHandshakeResponse(unsigned long identifier, const WebSocketHandshakeResponse& response)
319 {
320     RefPtr<InspectorObject> responseObject = InspectorObject::create();
321     responseObject->setNumber("status", response.statusCode());
322     responseObject->setString("statusText", response.statusText());
323     responseObject->setObject("headers", buildObjectForHeaders(response.headerFields()));
324     responseObject->setString("challengeResponse", createReadableStringFromBinary(response.challengeResponse().value, sizeof(response.challengeResponse().value)));
325     m_frontend->webSocketHandshakeResponseReceived(static_cast<int>(identifier), currentTime(), responseObject);
326 }
327 
didCloseWebSocket(unsigned long identifier)328 void InspectorResourceAgent::didCloseWebSocket(unsigned long identifier)
329 {
330     m_frontend->webSocketClosed(static_cast<int>(identifier), currentTime());
331 }
332 #endif // ENABLE(WEB_SOCKETS)
333 
backgroundEventsCollectionEnabled()334 bool InspectorResourceAgent::backgroundEventsCollectionEnabled()
335 {
336     // FIXME (https://bugs.webkit.org/show_bug.cgi?id=58652)
337     // Add here condition to enable background events collection.
338     // Now this function is disable.
339     return false;
340 }
341 
enable(ErrorString *)342 void InspectorResourceAgent::enable(ErrorString*)
343 {
344     enable();
345 }
346 
enable()347 void InspectorResourceAgent::enable()
348 {
349     if (!m_frontend)
350         return;
351     m_state->setBoolean(ResourceAgentState::resourceAgentEnabled, true);
352     m_instrumentingAgents->setInspectorResourceAgent(this);
353 }
354 
disable(ErrorString *)355 void InspectorResourceAgent::disable(ErrorString*)
356 {
357     m_state->setBoolean(ResourceAgentState::resourceAgentEnabled, false);
358     m_instrumentingAgents->setInspectorResourceAgent(0);
359 }
360 
setUserAgentOverride(ErrorString *,const String & userAgent)361 void InspectorResourceAgent::setUserAgentOverride(ErrorString*, const String& userAgent)
362 {
363     m_userAgentOverride = userAgent;
364 }
365 
setExtraHeaders(ErrorString *,PassRefPtr<InspectorObject> headers)366 void InspectorResourceAgent::setExtraHeaders(ErrorString*, PassRefPtr<InspectorObject> headers)
367 {
368     m_state->setObject(ResourceAgentState::extraRequestHeaders, headers);
369 }
370 
InspectorResourceAgent(InstrumentingAgents * instrumentingAgents,InspectorPageAgent * pageAgent,InspectorState * state)371 InspectorResourceAgent::InspectorResourceAgent(InstrumentingAgents* instrumentingAgents, InspectorPageAgent* pageAgent, InspectorState* state)
372     : m_instrumentingAgents(instrumentingAgents)
373     , m_pageAgent(pageAgent)
374     , m_state(state)
375 {
376     if (backgroundEventsCollectionEnabled()) {
377         m_eventsCollector = adoptPtr(new EventsCollector);
378         m_inspectorFrontendProxy = adoptPtr(new InspectorFrontendProxy(m_eventsCollector.get()));
379         // Create mock frontend, so we can collect network events.
380         m_mockFrontend = adoptPtr(new InspectorFrontend::Network(m_inspectorFrontendProxy.get()));
381         m_frontend = m_mockFrontend.get();
382         enable();
383     } else
384         m_frontend = 0;
385 }
386 
387 } // namespace WebCore
388 
389 #endif // ENABLE(INSPECTOR)
390