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