5bbd360122d41fa034c5f115f59e6943a55b696a
[WebKit-https.git] / Source / WebCore / inspector / InspectorResourceAgent.cpp
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 "Base64.h"
37 #include "CachedResource.h"
38 #include "CachedResourceLoader.h"
39 #include "Document.h"
40 #include "DocumentLoader.h"
41 #include "EventsCollector.h"
42 #include "Frame.h"
43 #include "FrameLoader.h"
44 #include "HTMLFrameOwnerElement.h"
45 #include "HTMLNames.h"
46 #include "HTTPHeaderMap.h"
47 #include "InspectorFrontend.h"
48 #include "InspectorFrontendChannel.h"
49 #include "InspectorFrontendProxy.h"
50 #include "InspectorState.h"
51 #include "InspectorValues.h"
52 #include "InstrumentingAgents.h"
53 #include "KURL.h"
54 #include "MemoryCache.h"
55 #include "Page.h"
56 #include "ProgressTracker.h"
57 #include "ResourceError.h"
58 #include "ResourceRequest.h"
59 #include "ResourceResponse.h"
60 #include "ScriptCallStack.h"
61 #include "ScriptCallStackFactory.h"
62 #include "SharedBuffer.h"
63 #include "TextEncoding.h"
64 #include "WebSocketHandshakeRequest.h"
65 #include "WebSocketHandshakeResponse.h"
66
67 #include <wtf/CurrentTime.h>
68 #include <wtf/HexNumber.h>
69 #include <wtf/ListHashSet.h>
70 #include <wtf/RefPtr.h>
71 #include <wtf/text/StringBuilder.h>
72
73 namespace WebCore {
74
75 namespace ResourceAgentState {
76 static const char resourceAgentEnabled[] = "resourceAgentEnabled";
77 static const char extraRequestHeaders[] = "extraRequestHeaders";
78 }
79
80 namespace ResourceType {
81 static const char document[] = "Document";
82 static const char stylesheet[] = "Stylesheet";
83 static const char image[] = "Image";
84 static const char font[] = "Font";
85 static const char script[] = "Script";
86 static const char xhr[] = "XHR";
87 static const char websocket[] = "WebSocket";
88 static const char other[] = "Other";
89 }
90
91 void InspectorResourceAgent::setFrontend(InspectorFrontend* frontend)
92 {
93     m_frontend = frontend->network();
94     if (backgroundEventsCollectionEnabled()) {
95         // Insert Message Proxy in receiver chain.
96         InspectorFrontendChannel* client = m_frontend->getInspectorFrontendChannel();
97         m_inspectorFrontendProxy->setInspectorFrontendChannel(client);
98         m_frontend->setInspectorFrontendChannel(m_inspectorFrontendProxy.get());
99         m_eventsCollector->sendCollectedEvents(client);
100     }
101 }
102
103 void InspectorResourceAgent::clearFrontend()
104 {
105     if (backgroundEventsCollectionEnabled()) {
106         m_inspectorFrontendProxy->setInspectorFrontendChannel(0);
107         m_frontend = m_mockFrontend.get();
108     } else
109         m_frontend = 0;
110
111     m_userAgentOverride = "";
112     disable(0);
113 }
114
115 void InspectorResourceAgent::restore()
116 {
117     if (m_state->getBoolean(ResourceAgentState::resourceAgentEnabled))
118         enable();
119 }
120
121 void InspectorResourceAgent::resourceContent(ErrorString* errorString, Frame* frame, const KURL& url, String* result)
122 {
123     if (!frame) {
124         *errorString = "No frame to get resource content for";
125         return;
126     }
127
128     String textEncodingName;
129     RefPtr<SharedBuffer> buffer = InspectorResourceAgent::resourceData(frame, url, &textEncodingName);
130
131     if (buffer) {
132         TextEncoding encoding(textEncodingName);
133         if (!encoding.isValid())
134             encoding = WindowsLatin1Encoding();
135         *result = encoding.decode(buffer->data(), buffer->size());
136         return;
137     }
138     *errorString = "No resource with given URL found"; 
139 }
140
141 void InspectorResourceAgent::resourceContentBase64(ErrorString* errorString, Frame* frame, const KURL& url, String* result)
142 {
143     String textEncodingName;
144     RefPtr<SharedBuffer> data = InspectorResourceAgent::resourceData(frame, url, &textEncodingName);
145     if (!data) {
146         *result = String();
147         *errorString = "No resource with given URL found"; 
148         return;
149     }
150
151     *result = base64Encode(data->data(), data->size());
152 }
153
154 PassRefPtr<SharedBuffer> InspectorResourceAgent::resourceData(Frame* frame, const KURL& url, String* textEncodingName)
155 {
156     FrameLoader* frameLoader = frame->loader();
157     DocumentLoader* loader = frameLoader->documentLoader();
158     if (equalIgnoringFragmentIdentifier(url, loader->url())) {
159         *textEncodingName = frame->document()->inputEncoding();
160         return frameLoader->documentLoader()->mainResourceData();
161     }
162
163     CachedResource* cachedResource = InspectorResourceAgent::cachedResource(frame, url);
164     if (!cachedResource)
165         return 0;
166
167     // Zero-sized resources don't have data at all -- so fake the empty buffer, insted of indicating error by returning 0.
168     if (!cachedResource->encodedSize())
169         return SharedBuffer::create();
170
171     if (cachedResource->isPurgeable()) {
172         // If the resource is purgeable then make it unpurgeable to get
173         // get its data. This might fail, in which case we return an
174         // empty String.
175         // FIXME: should we do something else in the case of a purged
176         // resource that informs the user why there is no data in the
177         // inspector?
178         if (!cachedResource->makePurgeable(false))
179             return 0;
180     }
181
182     *textEncodingName = cachedResource->encoding();
183     return cachedResource->data();
184 }
185
186 CachedResource* InspectorResourceAgent::cachedResource(Frame* frame, const KURL& url)
187 {
188     CachedResource* cachedResource = frame->document()->cachedResourceLoader()->cachedResource(url);
189     if (!cachedResource)
190         cachedResource = memoryCache()->resourceForURL(url);
191     return cachedResource;
192 }
193
194 static PassRefPtr<InspectorObject> buildObjectForHeaders(const HTTPHeaderMap& headers)
195 {
196     RefPtr<InspectorObject> headersObject = InspectorObject::create();
197     HTTPHeaderMap::const_iterator end = headers.end();
198     for (HTTPHeaderMap::const_iterator it = headers.begin(); it != end; ++it)
199         headersObject->setString(it->first.string(), it->second);
200     return headersObject;
201 }
202
203 static PassRefPtr<InspectorObject> buildObjectForTiming(const ResourceLoadTiming& timing)
204 {
205     RefPtr<InspectorObject> timingObject = InspectorObject::create();
206     timingObject->setNumber("requestTime", timing.requestTime);
207     timingObject->setNumber("proxyStart", timing.proxyStart);
208     timingObject->setNumber("proxyEnd", timing.proxyEnd);
209     timingObject->setNumber("dnsStart", timing.dnsStart);
210     timingObject->setNumber("dnsEnd", timing.dnsEnd);
211     timingObject->setNumber("connectStart", timing.connectStart);
212     timingObject->setNumber("connectEnd", timing.connectEnd);
213     timingObject->setNumber("sslStart", timing.sslStart);
214     timingObject->setNumber("sslEnd", timing.sslEnd);
215     timingObject->setNumber("sendStart", timing.sendStart);
216     timingObject->setNumber("sendEnd", timing.sendEnd);
217     timingObject->setNumber("receiveHeadersEnd", timing.receiveHeadersEnd);
218     return timingObject;
219 }
220
221 static PassRefPtr<InspectorObject> buildObjectForResourceRequest(const ResourceRequest& request)
222 {
223     RefPtr<InspectorObject> requestObject = InspectorObject::create();
224     requestObject->setString("url", request.url().string());
225     requestObject->setString("method", request.httpMethod());
226     requestObject->setObject("headers", buildObjectForHeaders(request.httpHeaderFields()));
227     if (request.httpBody() && !request.httpBody()->isEmpty())
228         requestObject->setString("postData", request.httpBody()->flattenToString());
229     return requestObject;
230 }
231
232 static PassRefPtr<InspectorObject> buildObjectForResourceResponse(const ResourceResponse& response)
233 {
234     if (response.isNull())
235         return 0;
236
237     RefPtr<InspectorObject> responseObject = InspectorObject::create();
238     responseObject->setNumber("status", response.resourceLoadInfo() ? response.resourceLoadInfo()->httpStatusCode : response.httpStatusCode());
239     responseObject->setString("statusText", response.resourceLoadInfo() ? response.resourceLoadInfo()->httpStatusText : response.httpStatusText());
240
241     responseObject->setString("mimeType", response.mimeType());
242     responseObject->setBoolean("connectionReused", response.connectionReused());
243     responseObject->setNumber("connectionID", response.connectionID());
244     responseObject->setBoolean("fromDiskCache", response.wasCached());
245     if (response.resourceLoadTiming())
246         responseObject->setObject("timing", buildObjectForTiming(*response.resourceLoadTiming()));
247
248     if (response.resourceLoadInfo()) {
249         responseObject->setObject("headers", buildObjectForHeaders(response.resourceLoadInfo()->responseHeaders));
250         if (!response.resourceLoadInfo()->responseHeadersText.isEmpty())
251             responseObject->setString("headersText", response.resourceLoadInfo()->responseHeadersText);
252
253         responseObject->setObject("requestHeaders", buildObjectForHeaders(response.resourceLoadInfo()->requestHeaders));
254         if (!response.resourceLoadInfo()->requestHeadersText.isEmpty())
255             responseObject->setString("requestHeadersText", response.resourceLoadInfo()->requestHeadersText);
256     } else
257         responseObject->setObject("headers", buildObjectForHeaders(response.httpHeaderFields()));
258
259     return responseObject;
260 }
261
262 static String pointerAsId(void* pointer)
263 {
264     unsigned long long address = reinterpret_cast<uintptr_t>(pointer);
265     // We want 0 to be "", so that JavaScript checks for if (frameId) worked.
266     return String::format("%.0llX", address);
267 }
268
269 static String cachedResourceTypeString(const CachedResource& cachedResource)
270 {
271     switch (cachedResource.type()) {
272     case CachedResource::ImageResource:
273         return ResourceType::image;
274     case CachedResource::FontResource:
275         return ResourceType::font;
276     case CachedResource::CSSStyleSheet:
277         // Fall through.
278 #if ENABLE(XSLT)
279     case CachedResource::XSLStyleSheet:
280 #endif
281         return ResourceType::stylesheet;
282     case CachedResource::Script:
283         return ResourceType::script;
284     default:
285         break;
286     }
287     return ResourceType::other;
288 }
289
290 static PassRefPtr<InspectorObject> buildObjectForCachedResource(const CachedResource& cachedResource)
291 {
292     RefPtr<InspectorObject> resourceObject = InspectorObject::create();
293     resourceObject->setString("url", cachedResource.url());
294     resourceObject->setString("type", cachedResourceTypeString(cachedResource));
295     resourceObject->setNumber("bodySize", cachedResource.encodedSize());
296     RefPtr<InspectorObject> resourceResponse = buildObjectForResourceResponse(cachedResource.response());
297     if (resourceResponse)
298         resourceObject->setObject("response", resourceResponse);
299     return resourceObject;
300 }
301
302 InspectorResourceAgent::~InspectorResourceAgent()
303 {
304     if (m_state->getBoolean(ResourceAgentState::resourceAgentEnabled)) {
305         ErrorString error;
306         disable(&error);
307     }
308     ASSERT(!m_instrumentingAgents->inspectorResourceAgent());
309 }
310
311 void InspectorResourceAgent::willSendRequest(unsigned long identifier, DocumentLoader* loader, ResourceRequest& request, const ResourceResponse& redirectResponse)
312 {
313     RefPtr<InspectorObject> headers = m_state->getObject(ResourceAgentState::extraRequestHeaders);
314
315     if (headers) {
316         InspectorObject::const_iterator end = headers->end();
317         for (InspectorObject::const_iterator it = headers->begin(); it != end; ++it) {
318             String value;
319             if (it->second->asString(&value))
320                 request.setHTTPHeaderField(it->first, value);
321         }
322     }
323
324     request.setReportLoadTiming(true);
325     request.setReportRawHeaders(true);
326
327     RefPtr<ScriptCallStack> callStack = createScriptCallStack(ScriptCallStack::maxCallStackSizeToCapture, true);
328     RefPtr<InspectorArray> callStackValue;
329     if (callStack)
330         callStackValue = callStack->buildInspectorArray();
331     else
332         callStackValue = InspectorArray::create();
333     m_frontend->requestWillBeSent(static_cast<int>(identifier), pointerAsId(loader->frame()), pointerAsId(loader), loader->url().string(), buildObjectForResourceRequest(request), currentTime(), callStackValue, buildObjectForResourceResponse(redirectResponse));
334 }
335
336 void InspectorResourceAgent::markResourceAsCached(unsigned long identifier)
337 {
338     m_frontend->resourceMarkedAsCached(static_cast<int>(identifier));
339 }
340
341 void InspectorResourceAgent::didReceiveResponse(unsigned long identifier, DocumentLoader* loader, const ResourceResponse& response)
342 {
343     RefPtr<InspectorObject> resourceResponse = buildObjectForResourceResponse(response);
344     String type = "Other";
345     long cachedResourceSize = 0;
346
347     if (loader) {
348         CachedResource* cachedResource = InspectorResourceAgent::cachedResource(loader->frame(), response.url());
349         if (cachedResource) {
350             type = cachedResourceTypeString(*cachedResource);
351             cachedResourceSize = cachedResource->encodedSize();
352             // Use mime type from cached resource in case the one in response is empty.
353             if (response.mimeType().isEmpty())
354                 resourceResponse->setString("mimeType", cachedResource->response().mimeType());
355         }
356         if (equalIgnoringFragmentIdentifier(response.url(), loader->frameLoader()->iconURL()))
357             type = ResourceType::image;
358         else if (equalIgnoringFragmentIdentifier(response.url(), loader->url()) && type == "Other")
359             type = ResourceType::document;
360     }
361     m_frontend->responseReceived(static_cast<int>(identifier), currentTime(), type, resourceResponse);
362     // If we revalidated the resource and got Not modified, send content length following didReceiveResponse
363     // as there will be no calls to didReceiveContentLength from the network stack.
364     if (cachedResourceSize && response.httpStatusCode() == 304)
365         didReceiveContentLength(identifier, cachedResourceSize, 0);
366 }
367
368 void InspectorResourceAgent::didReceiveContentLength(unsigned long identifier, int dataLength, int encodedDataLength)
369 {
370     m_frontend->dataReceived(static_cast<int>(identifier), currentTime(), dataLength, encodedDataLength);
371 }
372
373 void InspectorResourceAgent::didFinishLoading(unsigned long identifier, double finishTime)
374 {
375     if (!finishTime)
376         finishTime = currentTime();
377
378     m_frontend->loadingFinished(static_cast<int>(identifier), finishTime);
379 }
380
381 void InspectorResourceAgent::didFailLoading(unsigned long identifier, const ResourceError& error)
382 {
383     m_frontend->loadingFailed(static_cast<int>(identifier), currentTime(), error.localizedDescription(), error.isCancellation());
384 }
385
386 void InspectorResourceAgent::didLoadResourceFromMemoryCache(DocumentLoader* loader, const CachedResource* resource)
387 {
388     m_frontend->resourceLoadedFromMemoryCache(pointerAsId(loader->frame()), pointerAsId(loader), loader->url().string(), currentTime(), buildObjectForCachedResource(*resource));
389 }
390
391 void InspectorResourceAgent::setInitialScriptContent(unsigned long identifier, const String& sourceString)
392 {
393     m_frontend->initialContentSet(static_cast<int>(identifier), sourceString, ResourceType::script);
394 }
395
396 void InspectorResourceAgent::setInitialXHRContent(unsigned long identifier, const String& sourceString)
397 {
398     m_frontend->initialContentSet(static_cast<int>(identifier), sourceString, ResourceType::xhr);
399 }
400
401 void InspectorResourceAgent::domContentEventFired()
402 {
403     m_frontend->domContentEventFired(currentTime());
404 }
405
406 void InspectorResourceAgent::loadEventFired()
407 {
408     m_frontend->loadEventFired(currentTime());
409 }
410
411 static PassRefPtr<InspectorObject> buildObjectForFrame(Frame* frame)
412 {
413     RefPtr<InspectorObject> frameObject = InspectorObject::create();
414     frameObject->setString("id", pointerAsId(frame));
415     frameObject->setString("parentId", pointerAsId(frame->tree()->parent()));
416     if (frame->ownerElement()) {
417         String name = frame->ownerElement()->getAttribute(HTMLNames::nameAttr);
418         if (name.isEmpty())
419             name = frame->ownerElement()->getAttribute(HTMLNames::idAttr);
420         frameObject->setString("name", name);
421     }
422     frameObject->setString("url", frame->document()->url().string());
423     frameObject->setString("loaderId", pointerAsId(frame->loader()->documentLoader()));
424
425     return frameObject;
426 }
427
428 static PassRefPtr<InspectorObject> buildObjectForFrameTree(Frame* frame)
429 {
430     RefPtr<InspectorObject> result = InspectorObject::create();
431     RefPtr<InspectorObject> frameObject = buildObjectForFrame(frame);
432     result->setObject("frame", frameObject);
433
434     RefPtr<InspectorArray> subresources = InspectorArray::create();
435     result->setArray("resources", subresources);
436     const CachedResourceLoader::DocumentResourceMap& allResources = frame->document()->cachedResourceLoader()->allCachedResources();
437     CachedResourceLoader::DocumentResourceMap::const_iterator end = allResources.end();
438     for (CachedResourceLoader::DocumentResourceMap::const_iterator it = allResources.begin(); it != end; ++it) {
439         CachedResource* cachedResource = it->second.get();
440         RefPtr<InspectorObject> resourceObject = InspectorObject::create();
441         resourceObject->setString("url", cachedResource->url());
442         resourceObject->setString("type", cachedResourceTypeString(*cachedResource));
443         subresources->pushValue(resourceObject);
444     }
445
446     RefPtr<InspectorArray> childrenArray;
447     for (Frame* child = frame->tree()->firstChild(); child; child = child->tree()->nextSibling()) {
448         if (!childrenArray) {
449             childrenArray = InspectorArray::create();
450             result->setArray("childFrames", childrenArray);
451         }
452         childrenArray->pushObject(buildObjectForFrameTree(child));
453     }
454     return result;
455 }
456
457 void InspectorResourceAgent::didCommitLoad(DocumentLoader* loader)
458 {
459     m_frontend->frameNavigated(buildObjectForFrame(loader->frame()), pointerAsId(loader));
460 }
461
462 void InspectorResourceAgent::frameDetachedFromParent(Frame* frame)
463 {
464     m_frontend->frameDetached(pointerAsId(frame));
465 }
466
467 void InspectorResourceAgent::applyUserAgentOverride(String* userAgent)
468 {
469     if (!m_userAgentOverride.isEmpty())
470         *userAgent = m_userAgentOverride;
471 }
472
473 #if ENABLE(WEB_SOCKETS)
474
475 // FIXME: More this into the front-end?
476 // Create human-readable binary representation, like "01:23:45:67:89:AB:CD:EF".
477 static String createReadableStringFromBinary(const unsigned char* value, size_t length)
478 {
479     ASSERT(length > 0);
480     StringBuilder builder;
481     builder.reserveCapacity(length * 3 - 1);
482     for (size_t i = 0; i < length; ++i) {
483         if (i > 0)
484             builder.append(':');
485         appendByteAsHex(value[i], builder);
486     }
487     return builder.toString();
488 }
489
490 void InspectorResourceAgent::didCreateWebSocket(unsigned long identifier, const KURL& requestURL)
491 {
492     m_frontend->webSocketCreated(static_cast<int>(identifier), requestURL.string());
493 }
494
495 void InspectorResourceAgent::willSendWebSocketHandshakeRequest(unsigned long identifier, const WebSocketHandshakeRequest& request)
496 {
497     RefPtr<InspectorObject> requestObject = InspectorObject::create();
498     requestObject->setObject("headers", buildObjectForHeaders(request.headerFields()));
499     requestObject->setString("requestKey3", createReadableStringFromBinary(request.key3().value, sizeof(request.key3().value)));
500     m_frontend->webSocketWillSendHandshakeRequest(static_cast<int>(identifier), currentTime(), requestObject);
501 }
502
503 void InspectorResourceAgent::didReceiveWebSocketHandshakeResponse(unsigned long identifier, const WebSocketHandshakeResponse& response)
504 {
505     RefPtr<InspectorObject> responseObject = InspectorObject::create();
506     responseObject->setNumber("status", response.statusCode());
507     responseObject->setString("statusText", response.statusText());
508     responseObject->setObject("headers", buildObjectForHeaders(response.headerFields()));
509     responseObject->setString("challengeResponse", createReadableStringFromBinary(response.challengeResponse().value, sizeof(response.challengeResponse().value)));
510     m_frontend->webSocketHandshakeResponseReceived(static_cast<int>(identifier), currentTime(), responseObject);
511 }
512
513 void InspectorResourceAgent::didCloseWebSocket(unsigned long identifier)
514 {
515     m_frontend->webSocketClosed(static_cast<int>(identifier), currentTime());
516 }
517 #endif // ENABLE(WEB_SOCKETS)
518
519 Frame* InspectorResourceAgent::frameForId(const String& frameId)
520 {
521     Frame* mainFrame = m_page->mainFrame();
522     for (Frame* frame = mainFrame; frame; frame = frame->tree()->traverseNext(mainFrame)) {
523         if (pointerAsId(frame) == frameId)
524             return frame;
525     }
526     return 0;
527 }
528
529 bool InspectorResourceAgent::backgroundEventsCollectionEnabled()
530 {
531     // FIXME (https://bugs.webkit.org/show_bug.cgi?id=58652)
532     // Add here condition to enable background events collection.
533     // Now this function is disable.
534     return false;
535 }
536
537 void InspectorResourceAgent::enable(ErrorString*)
538 {
539     enable();
540 }
541
542 void InspectorResourceAgent::enable()
543 {
544     if (!m_frontend)
545         return;
546     m_state->setBoolean(ResourceAgentState::resourceAgentEnabled, true);
547     m_instrumentingAgents->setInspectorResourceAgent(this);
548 }
549
550 void InspectorResourceAgent::disable(ErrorString*)
551 {
552     m_state->setBoolean(ResourceAgentState::resourceAgentEnabled, false);
553     m_instrumentingAgents->setInspectorResourceAgent(0);
554 }
555
556 void InspectorResourceAgent::getCachedResources(ErrorString*, RefPtr<InspectorObject>* object)
557 {
558     *object = buildObjectForFrameTree(m_page->mainFrame());
559 }
560
561 void InspectorResourceAgent::getResourceContent(ErrorString* errorString, const String& frameId, const String& url, const bool* const optionalBase64Encode, String* content)
562 {
563     Frame* frame = frameForId(frameId);
564     if (!frame) {
565         *errorString = "No frame for given id found";
566         return;
567     }
568     if (optionalBase64Encode ? *optionalBase64Encode : false)
569         InspectorResourceAgent::resourceContentBase64(errorString, frame, KURL(ParsedURLString, url), content);
570     else
571         InspectorResourceAgent::resourceContent(errorString, frame, KURL(ParsedURLString, url), content);
572 }
573
574 void InspectorResourceAgent::setUserAgentOverride(ErrorString*, const String& userAgent)
575 {
576     m_userAgentOverride = userAgent;
577 }
578
579 void InspectorResourceAgent::setExtraHeaders(ErrorString*, PassRefPtr<InspectorObject> headers)
580 {
581     m_state->setObject(ResourceAgentState::extraRequestHeaders, headers);
582 }
583
584 InspectorResourceAgent::InspectorResourceAgent(InstrumentingAgents* instrumentingAgents, Page* page, InspectorState* state)
585     : m_instrumentingAgents(instrumentingAgents)
586     , m_page(page)
587     , m_state(state)
588 {
589     if (backgroundEventsCollectionEnabled()) {
590         m_eventsCollector = new EventsCollector();
591         m_inspectorFrontendProxy = new InspectorFrontendProxy(m_eventsCollector.get());
592         // Create mock frontend, so we can collect network events.
593         m_mockFrontend = new InspectorFrontend::Network(m_inspectorFrontendProxy.get());
594         m_frontend = m_mockFrontend.get();
595         enable();
596     } else
597         m_frontend = 0;
598 }
599
600 } // namespace WebCore
601
602 #endif // ENABLE(INSPECTOR)