• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "core/inspector/InspectorResourceAgent.h"
33 
34 #include "bindings/v8/ExceptionStatePlaceholder.h"
35 #include "bindings/v8/ScriptCallStackFactory.h"
36 #include "core/FetchInitiatorTypeNames.h"
37 #include "core/dom/Document.h"
38 #include "core/dom/ScriptableDocumentParser.h"
39 #include "core/fetch/FetchInitiatorInfo.h"
40 #include "core/fetch/MemoryCache.h"
41 #include "core/fetch/Resource.h"
42 #include "core/fetch/ResourceFetcher.h"
43 #include "core/fetch/ResourceLoader.h"
44 #include "core/frame/LocalFrame.h"
45 #include "core/inspector/IdentifiersFactory.h"
46 #include "core/inspector/InspectorOverlay.h"
47 #include "core/inspector/InspectorPageAgent.h"
48 #include "core/inspector/InspectorState.h"
49 #include "core/inspector/InstrumentingAgents.h"
50 #include "core/inspector/NetworkResourcesData.h"
51 #include "core/inspector/ScriptCallStack.h"
52 #include "core/loader/DocumentLoader.h"
53 #include "core/loader/DocumentThreadableLoader.h"
54 #include "core/loader/FrameLoader.h"
55 #include "core/loader/ThreadableLoader.h"
56 #include "core/loader/ThreadableLoaderClient.h"
57 #include "core/page/Page.h"
58 #include "core/xml/XMLHttpRequest.h"
59 #include "platform/JSONValues.h"
60 #include "platform/network/HTTPHeaderMap.h"
61 #include "platform/network/ResourceError.h"
62 #include "platform/network/ResourceRequest.h"
63 #include "platform/network/ResourceResponse.h"
64 #include "platform/network/WebSocketHandshakeRequest.h"
65 #include "platform/network/WebSocketHandshakeResponse.h"
66 #include "platform/weborigin/KURL.h"
67 #include "wtf/CurrentTime.h"
68 #include "wtf/RefPtr.h"
69 
70 typedef WebCore::InspectorBackendDispatcher::NetworkCommandHandler::LoadResourceForFrontendCallback LoadResourceForFrontendCallback;
71 
72 namespace WebCore {
73 
74 namespace ResourceAgentState {
75 static const char resourceAgentEnabled[] = "resourceAgentEnabled";
76 static const char extraRequestHeaders[] = "extraRequestHeaders";
77 static const char cacheDisabled[] = "cacheDisabled";
78 static const char userAgentOverride[] = "userAgentOverride";
79 }
80 
81 namespace {
82 
83 // Keep in sync with kDevToolsRequestInitiator defined in devtools_network_controller.cc
84 const char kDevToolsRequestInitiator[] = "X-DevTools-Request-Initiator";
85 const char kDevToolsEmulateNetworkConditionsClientId[] = "X-DevTools-Emulate-Network-Conditions-Client-Id";
86 
buildObjectForHeaders(const HTTPHeaderMap & headers)87 static PassRefPtr<JSONObject> buildObjectForHeaders(const HTTPHeaderMap& headers)
88 {
89     RefPtr<JSONObject> headersObject = JSONObject::create();
90     HTTPHeaderMap::const_iterator end = headers.end();
91     for (HTTPHeaderMap::const_iterator it = headers.begin(); it != end; ++it)
92         headersObject->setString(it->key.string(), it->value);
93     return headersObject;
94 }
95 
96 class InspectorThreadableLoaderClient FINAL : public ThreadableLoaderClient {
97     WTF_MAKE_NONCOPYABLE(InspectorThreadableLoaderClient);
98 public:
InspectorThreadableLoaderClient(PassRefPtr<LoadResourceForFrontendCallback> callback)99     InspectorThreadableLoaderClient(PassRefPtr<LoadResourceForFrontendCallback> callback)
100         : m_callback(callback)
101         , m_statusCode(0) { }
102 
~InspectorThreadableLoaderClient()103     virtual ~InspectorThreadableLoaderClient() { }
104 
didReceiveResponse(unsigned long identifier,const ResourceResponse & response)105     virtual void didReceiveResponse(unsigned long identifier, const ResourceResponse& response) OVERRIDE
106     {
107         WTF::TextEncoding textEncoding(response.textEncodingName());
108         bool useDetector = false;
109         if (!textEncoding.isValid()) {
110             textEncoding = UTF8Encoding();
111             useDetector = true;
112         }
113         m_decoder = TextResourceDecoder::create("text/plain", textEncoding, useDetector);
114         m_statusCode = response.httpStatusCode();
115         m_responseHeaders = response.httpHeaderFields();
116     }
117 
didReceiveData(const char * data,int dataLength)118     virtual void didReceiveData(const char* data, int dataLength) OVERRIDE
119     {
120         if (!dataLength)
121             return;
122 
123         if (dataLength == -1)
124             dataLength = strlen(data);
125 
126         m_responseText = m_responseText.concatenateWith(m_decoder->decode(data, dataLength));
127     }
128 
didFinishLoading(unsigned long,double)129     virtual void didFinishLoading(unsigned long /*identifier*/, double /*finishTime*/) OVERRIDE
130     {
131         if (m_decoder)
132             m_responseText = m_responseText.concatenateWith(m_decoder->flush());
133         m_callback->sendSuccess(m_statusCode, buildObjectForHeaders(m_responseHeaders), m_responseText.flattenToString());
134         dispose();
135     }
136 
didFail(const ResourceError &)137     virtual void didFail(const ResourceError&) OVERRIDE
138     {
139         m_callback->sendFailure("Loading resource for inspector failed");
140         dispose();
141     }
142 
didFailRedirectCheck()143     virtual void didFailRedirectCheck() OVERRIDE
144     {
145         m_callback->sendFailure("Loading resource for inspector failed redirect check");
146         dispose();
147     }
148 
didFailLoaderCreation()149     void didFailLoaderCreation()
150     {
151         m_callback->sendFailure("Couldn't create a loader");
152         dispose();
153     }
154 
setLoader(PassRefPtr<ThreadableLoader> loader)155     void setLoader(PassRefPtr<ThreadableLoader> loader)
156     {
157         m_loader = loader;
158     }
159 
160 private:
dispose()161     void dispose()
162     {
163         m_loader = nullptr;
164         delete this;
165     }
166 
167     RefPtr<LoadResourceForFrontendCallback> m_callback;
168     RefPtr<ThreadableLoader> m_loader;
169     OwnPtr<TextResourceDecoder> m_decoder;
170     ScriptString m_responseText;
171     int m_statusCode;
172     HTTPHeaderMap m_responseHeaders;
173 };
174 
urlWithoutFragment(const KURL & url)175 KURL urlWithoutFragment(const KURL& url)
176 {
177     KURL result = url;
178     result.removeFragmentIdentifier();
179     return result;
180 }
181 
182 } // namespace
183 
setFrontend(InspectorFrontend * frontend)184 void InspectorResourceAgent::setFrontend(InspectorFrontend* frontend)
185 {
186     m_frontend = frontend->network();
187 }
188 
clearFrontend()189 void InspectorResourceAgent::clearFrontend()
190 {
191     m_frontend = 0;
192     ErrorString error;
193     disable(&error);
194 }
195 
restore()196 void InspectorResourceAgent::restore()
197 {
198     if (m_state->getBoolean(ResourceAgentState::resourceAgentEnabled))
199         enable();
200 }
201 
buildObjectForTiming(const ResourceLoadTiming & timing,DocumentLoader * loader)202 static PassRefPtr<TypeBuilder::Network::ResourceTiming> buildObjectForTiming(const ResourceLoadTiming& timing, DocumentLoader* loader)
203 {
204     return TypeBuilder::Network::ResourceTiming::create()
205         .setRequestTime(loader->timing()->monotonicTimeToPseudoWallTime(timing.requestTime))
206         .setProxyStart(timing.calculateMillisecondDelta(timing.proxyStart))
207         .setProxyEnd(timing.calculateMillisecondDelta(timing.proxyEnd))
208         .setDnsStart(timing.calculateMillisecondDelta(timing.dnsStart))
209         .setDnsEnd(timing.calculateMillisecondDelta(timing.dnsEnd))
210         .setConnectStart(timing.calculateMillisecondDelta(timing.connectStart))
211         .setConnectEnd(timing.calculateMillisecondDelta(timing.connectEnd))
212         .setSslStart(timing.calculateMillisecondDelta(timing.sslStart))
213         .setSslEnd(timing.calculateMillisecondDelta(timing.sslEnd))
214         .setSendStart(timing.calculateMillisecondDelta(timing.sendStart))
215         .setSendEnd(timing.calculateMillisecondDelta(timing.sendEnd))
216         .setReceiveHeadersEnd(timing.calculateMillisecondDelta(timing.receiveHeadersEnd))
217         .release();
218 }
219 
buildObjectForResourceRequest(const ResourceRequest & request)220 static PassRefPtr<TypeBuilder::Network::Request> buildObjectForResourceRequest(const ResourceRequest& request)
221 {
222     RefPtr<TypeBuilder::Network::Request> requestObject = TypeBuilder::Network::Request::create()
223         .setUrl(urlWithoutFragment(request.url()).string())
224         .setMethod(request.httpMethod())
225         .setHeaders(buildObjectForHeaders(request.httpHeaderFields()));
226     if (request.httpBody() && !request.httpBody()->isEmpty()) {
227         Vector<char> bytes;
228         request.httpBody()->flatten(bytes);
229         requestObject->setPostData(String::fromUTF8WithLatin1Fallback(bytes.data(), bytes.size()));
230     }
231     return requestObject;
232 }
233 
buildObjectForResourceResponse(const ResourceResponse & response,DocumentLoader * loader)234 static PassRefPtr<TypeBuilder::Network::Response> buildObjectForResourceResponse(const ResourceResponse& response, DocumentLoader* loader)
235 {
236     if (response.isNull())
237         return nullptr;
238 
239     double status;
240     String statusText;
241     if (response.resourceLoadInfo() && response.resourceLoadInfo()->httpStatusCode) {
242         status = response.resourceLoadInfo()->httpStatusCode;
243         statusText = response.resourceLoadInfo()->httpStatusText;
244     } else {
245         status = response.httpStatusCode();
246         statusText = response.httpStatusText();
247     }
248     RefPtr<JSONObject> headers;
249     if (response.resourceLoadInfo() && response.resourceLoadInfo()->responseHeaders.size())
250         headers = buildObjectForHeaders(response.resourceLoadInfo()->responseHeaders);
251     else
252         headers = buildObjectForHeaders(response.httpHeaderFields());
253 
254     int64_t encodedDataLength = response.resourceLoadInfo() ? response.resourceLoadInfo()->encodedDataLength : -1;
255 
256     RefPtr<TypeBuilder::Network::Response> responseObject = TypeBuilder::Network::Response::create()
257         .setUrl(urlWithoutFragment(response.url()).string())
258         .setStatus(status)
259         .setStatusText(statusText)
260         .setHeaders(headers)
261         .setMimeType(response.mimeType())
262         .setConnectionReused(response.connectionReused())
263         .setConnectionId(response.connectionID())
264         .setEncodedDataLength(encodedDataLength);
265 
266     responseObject->setFromDiskCache(response.wasCached());
267     if (response.resourceLoadTiming())
268         responseObject->setTiming(buildObjectForTiming(*response.resourceLoadTiming(), loader));
269 
270     if (response.resourceLoadInfo()) {
271         if (!response.resourceLoadInfo()->responseHeadersText.isEmpty())
272             responseObject->setHeadersText(response.resourceLoadInfo()->responseHeadersText);
273         if (response.resourceLoadInfo()->requestHeaders.size())
274             responseObject->setRequestHeaders(buildObjectForHeaders(response.resourceLoadInfo()->requestHeaders));
275         if (!response.resourceLoadInfo()->requestHeadersText.isEmpty())
276             responseObject->setRequestHeadersText(response.resourceLoadInfo()->requestHeadersText);
277     }
278 
279     AtomicString remoteIPAddress = response.remoteIPAddress();
280     if (!remoteIPAddress.isEmpty()) {
281         responseObject->setRemoteIPAddress(remoteIPAddress);
282         responseObject->setRemotePort(response.remotePort());
283     }
284 
285     return responseObject;
286 }
287 
~InspectorResourceAgent()288 InspectorResourceAgent::~InspectorResourceAgent()
289 {
290     if (m_state->getBoolean(ResourceAgentState::resourceAgentEnabled)) {
291         ErrorString error;
292         disable(&error);
293     }
294     ASSERT(!m_instrumentingAgents->inspectorResourceAgent());
295 }
296 
willSendRequest(unsigned long identifier,DocumentLoader * loader,ResourceRequest & request,const ResourceResponse & redirectResponse,const FetchInitiatorInfo & initiatorInfo)297 void InspectorResourceAgent::willSendRequest(unsigned long identifier, DocumentLoader* loader, ResourceRequest& request, const ResourceResponse& redirectResponse, const FetchInitiatorInfo& initiatorInfo)
298 {
299     // Ignore the request initiated internally.
300     if (initiatorInfo.name == FetchInitiatorTypeNames::internal)
301         return;
302 
303     if (!m_hostId.isEmpty())
304         request.addHTTPHeaderField(kDevToolsEmulateNetworkConditionsClientId, AtomicString(m_hostId));
305 
306     String requestId = IdentifiersFactory::requestId(identifier);
307     m_resourcesData->resourceCreated(requestId, m_pageAgent->loaderId(loader));
308 
309     RefPtr<JSONObject> headers = m_state->getObject(ResourceAgentState::extraRequestHeaders);
310 
311     if (headers) {
312         JSONObject::const_iterator end = headers->end();
313         for (JSONObject::const_iterator it = headers->begin(); it != end; ++it) {
314             String value;
315             if (it->value->asString(&value))
316                 request.setHTTPHeaderField(AtomicString(it->key), AtomicString(value));
317         }
318     }
319 
320     request.setReportRawHeaders(true);
321 
322     if (m_state->getBoolean(ResourceAgentState::cacheDisabled))
323         request.setCachePolicy(ReloadBypassingCache);
324 
325     String frameId = m_pageAgent->frameId(loader->frame());
326 
327     RefPtr<TypeBuilder::Network::Initiator> initiatorObject = buildInitiatorObject(loader->frame() ? loader->frame()->document() : 0, initiatorInfo);
328     if (initiatorInfo.name == FetchInitiatorTypeNames::document) {
329         FrameNavigationInitiatorMap::iterator it = m_frameNavigationInitiatorMap.find(frameId);
330         if (it != m_frameNavigationInitiatorMap.end())
331             initiatorObject = it->value;
332     }
333 
334     m_frontend->requestWillBeSent(requestId, frameId, m_pageAgent->loaderId(loader), urlWithoutFragment(loader->url()).string(), buildObjectForResourceRequest(request), currentTime(), initiatorObject, buildObjectForResourceResponse(redirectResponse, loader));
335 }
336 
markResourceAsCached(unsigned long identifier)337 void InspectorResourceAgent::markResourceAsCached(unsigned long identifier)
338 {
339     m_frontend->requestServedFromCache(IdentifiersFactory::requestId(identifier));
340 }
341 
isResponseEmpty(PassRefPtr<TypeBuilder::Network::Response> response)342 bool isResponseEmpty(PassRefPtr<TypeBuilder::Network::Response> response)
343 {
344     if (!response)
345         return true;
346 
347     RefPtr<JSONValue> status = response->get("status");
348     RefPtr<JSONValue> mimeType = response->get("mimeType");
349     RefPtr<JSONObject> headers = response->getObject("headers");
350 
351     return !status && !mimeType && (!headers || !headers->size());
352 }
353 
didReceiveResourceResponse(LocalFrame * frame,unsigned long identifier,DocumentLoader * loader,const ResourceResponse & response,ResourceLoader * resourceLoader)354 void InspectorResourceAgent::didReceiveResourceResponse(LocalFrame* frame, unsigned long identifier, DocumentLoader* loader, const ResourceResponse& response, ResourceLoader* resourceLoader)
355 {
356     if (!loader)
357         return;
358 
359     String requestId = IdentifiersFactory::requestId(identifier);
360     RefPtr<TypeBuilder::Network::Response> resourceResponse = buildObjectForResourceResponse(response, loader);
361 
362     bool isNotModified = response.httpStatusCode() == 304;
363 
364     Resource* cachedResource = 0;
365     if (resourceLoader && !isNotModified)
366         cachedResource = resourceLoader->cachedResource();
367     if (!cachedResource || cachedResource->type() == Resource::MainResource)
368         cachedResource = InspectorPageAgent::cachedResource(loader->frame(), response.url());
369 
370     if (cachedResource) {
371         // Use mime type from cached resource in case the one in response is empty.
372         if (resourceResponse && response.mimeType().isEmpty())
373             resourceResponse->setString(TypeBuilder::Network::Response::MimeType, cachedResource->response().mimeType());
374         m_resourcesData->addResource(requestId, cachedResource);
375     }
376 
377     InspectorPageAgent::ResourceType type = cachedResource ? InspectorPageAgent::cachedResourceType(*cachedResource) : InspectorPageAgent::OtherResource;
378     // Workaround for worker scripts that use RawResources for loading.
379     if (m_resourcesData->resourceType(requestId) == InspectorPageAgent::ScriptResource)
380         type = InspectorPageAgent::ScriptResource;
381     // Workaround for background: url() in inline style.
382     if (equalIgnoringFragmentIdentifier(response.url(), loader->url()) && !loader->isCommitted())
383         type = InspectorPageAgent::DocumentResource;
384 
385     m_resourcesData->responseReceived(requestId, m_pageAgent->frameId(loader->frame()), response);
386     m_resourcesData->setResourceType(requestId, type);
387 
388     if (!isResponseEmpty(resourceResponse))
389         m_frontend->responseReceived(requestId, m_pageAgent->frameId(loader->frame()), m_pageAgent->loaderId(loader), currentTime(), InspectorPageAgent::resourceTypeJson(type), resourceResponse);
390     // If we revalidated the resource and got Not modified, send content length following didReceiveResponse
391     // as there will be no calls to didReceiveData from the network stack.
392     if (isNotModified && cachedResource && cachedResource->encodedSize())
393         didReceiveData(frame, identifier, 0, cachedResource->encodedSize(), 0);
394 }
395 
isErrorStatusCode(int statusCode)396 static bool isErrorStatusCode(int statusCode)
397 {
398     return statusCode >= 400;
399 }
400 
didReceiveData(LocalFrame *,unsigned long identifier,const char * data,int dataLength,int encodedDataLength)401 void InspectorResourceAgent::didReceiveData(LocalFrame*, unsigned long identifier, const char* data, int dataLength, int encodedDataLength)
402 {
403     String requestId = IdentifiersFactory::requestId(identifier);
404 
405     if (data) {
406         NetworkResourcesData::ResourceData const* resourceData = m_resourcesData->data(requestId);
407         if (resourceData && (!resourceData->cachedResource() || resourceData->cachedResource()->dataBufferingPolicy() == DoNotBufferData || isErrorStatusCode(resourceData->httpStatusCode())))
408             m_resourcesData->maybeAddResourceData(requestId, data, dataLength);
409     }
410 
411     m_frontend->dataReceived(requestId, currentTime(), dataLength, encodedDataLength);
412 }
413 
didFinishLoading(unsigned long identifier,DocumentLoader * loader,double monotonicFinishTime,int64_t encodedDataLength)414 void InspectorResourceAgent::didFinishLoading(unsigned long identifier, DocumentLoader* loader, double monotonicFinishTime, int64_t encodedDataLength)
415 {
416     double finishTime = 0.0;
417     // FIXME: Expose all of the timing details to inspector and have it calculate finishTime.
418     if (monotonicFinishTime)
419         finishTime = loader->timing()->monotonicTimeToPseudoWallTime(monotonicFinishTime);
420 
421     String requestId = IdentifiersFactory::requestId(identifier);
422     m_resourcesData->maybeDecodeDataToContent(requestId);
423     if (!finishTime)
424         finishTime = currentTime();
425     m_frontend->loadingFinished(requestId, finishTime, encodedDataLength);
426 }
427 
didReceiveCORSRedirectResponse(LocalFrame * frame,unsigned long identifier,DocumentLoader * loader,const ResourceResponse & response,ResourceLoader * resourceLoader)428 void InspectorResourceAgent::didReceiveCORSRedirectResponse(LocalFrame* frame, unsigned long identifier, DocumentLoader* loader, const ResourceResponse& response, ResourceLoader* resourceLoader)
429 {
430     // Update the response and finish loading
431     didReceiveResourceResponse(frame, identifier, loader, response, resourceLoader);
432     didFinishLoading(identifier, loader, 0, blink::WebURLLoaderClient::kUnknownEncodedDataLength);
433 }
434 
didFailLoading(unsigned long identifier,const ResourceError & error)435 void InspectorResourceAgent::didFailLoading(unsigned long identifier, const ResourceError& error)
436 {
437     String requestId = IdentifiersFactory::requestId(identifier);
438     bool canceled = error.isCancellation();
439     m_frontend->loadingFailed(requestId, currentTime(), InspectorPageAgent::resourceTypeJson(m_resourcesData->resourceType(requestId)), error.localizedDescription(), canceled ? &canceled : 0);
440 }
441 
scriptImported(unsigned long identifier,const String & sourceString)442 void InspectorResourceAgent::scriptImported(unsigned long identifier, const String& sourceString)
443 {
444     m_resourcesData->setResourceContent(IdentifiersFactory::requestId(identifier), sourceString);
445 }
446 
didReceiveScriptResponse(unsigned long identifier)447 void InspectorResourceAgent::didReceiveScriptResponse(unsigned long identifier)
448 {
449     m_resourcesData->setResourceType(IdentifiersFactory::requestId(identifier), InspectorPageAgent::ScriptResource);
450 }
451 
documentThreadableLoaderStartedLoadingForClient(unsigned long identifier,ThreadableLoaderClient * client)452 void InspectorResourceAgent::documentThreadableLoaderStartedLoadingForClient(unsigned long identifier, ThreadableLoaderClient* client)
453 {
454     if (!client)
455         return;
456 
457     PendingXHRReplayDataMap::iterator it = m_pendingXHRReplayData.find(client);
458     if (it == m_pendingXHRReplayData.end())
459         return;
460 
461     m_resourcesData->setResourceType(IdentifiersFactory::requestId(identifier), InspectorPageAgent::XHRResource);
462     XHRReplayData* xhrReplayData = it->value.get();
463     String requestId = IdentifiersFactory::requestId(identifier);
464     m_resourcesData->setXHRReplayData(requestId, xhrReplayData);
465 }
466 
willLoadXHR(XMLHttpRequest * xhr,ThreadableLoaderClient * client,const AtomicString & method,const KURL & url,bool async,FormData * formData,const HTTPHeaderMap & headers,bool includeCredentials)467 void InspectorResourceAgent::willLoadXHR(XMLHttpRequest* xhr, ThreadableLoaderClient* client, const AtomicString& method, const KURL& url, bool async, FormData* formData, const HTTPHeaderMap& headers, bool includeCredentials)
468 {
469     ASSERT(xhr);
470     RefPtr<XHRReplayData> xhrReplayData = XHRReplayData::create(xhr->executionContext(), method, urlWithoutFragment(url), async, formData, includeCredentials);
471     HTTPHeaderMap::const_iterator end = headers.end();
472     for (HTTPHeaderMap::const_iterator it = headers.begin(); it!= end; ++it)
473         xhrReplayData->addHeader(it->key, it->value);
474     m_pendingXHRReplayData.set(client, xhrReplayData);
475 }
476 
didFailXHRLoading(XMLHttpRequest *,ThreadableLoaderClient * client)477 void InspectorResourceAgent::didFailXHRLoading(XMLHttpRequest*, ThreadableLoaderClient* client)
478 {
479     m_pendingXHRReplayData.remove(client);
480 }
481 
didFinishXHRLoading(XMLHttpRequest *,ThreadableLoaderClient * client,unsigned long identifier,ScriptString sourceString,const AtomicString &,const String &,const String &,unsigned)482 void InspectorResourceAgent::didFinishXHRLoading(XMLHttpRequest*, ThreadableLoaderClient* client, unsigned long identifier, ScriptString sourceString, const AtomicString&, const String&, const String&, unsigned)
483 {
484     m_pendingXHRReplayData.remove(client);
485 }
486 
willDestroyResource(Resource * cachedResource)487 void InspectorResourceAgent::willDestroyResource(Resource* cachedResource)
488 {
489     Vector<String> requestIds = m_resourcesData->removeResource(cachedResource);
490     if (!requestIds.size())
491         return;
492 
493     String content;
494     bool base64Encoded;
495     if (!InspectorPageAgent::cachedResourceContent(cachedResource, &content, &base64Encoded))
496         return;
497     Vector<String>::iterator end = requestIds.end();
498     for (Vector<String>::iterator it = requestIds.begin(); it != end; ++it)
499         m_resourcesData->setResourceContent(*it, content, base64Encoded);
500 }
501 
applyUserAgentOverride(String * userAgent)502 void InspectorResourceAgent::applyUserAgentOverride(String* userAgent)
503 {
504     String userAgentOverride = m_state->getString(ResourceAgentState::userAgentOverride);
505     if (!userAgentOverride.isEmpty())
506         *userAgent = userAgentOverride;
507 }
508 
willRecalculateStyle(Document *)509 void InspectorResourceAgent::willRecalculateStyle(Document*)
510 {
511     m_isRecalculatingStyle = true;
512 }
513 
didRecalculateStyle(int)514 void InspectorResourceAgent::didRecalculateStyle(int)
515 {
516     m_isRecalculatingStyle = false;
517     m_styleRecalculationInitiator = nullptr;
518 }
519 
didScheduleStyleRecalculation(Document * document)520 void InspectorResourceAgent::didScheduleStyleRecalculation(Document* document)
521 {
522     if (!m_styleRecalculationInitiator)
523         m_styleRecalculationInitiator = buildInitiatorObject(document, FetchInitiatorInfo());
524 }
525 
buildInitiatorObject(Document * document,const FetchInitiatorInfo & initiatorInfo)526 PassRefPtr<TypeBuilder::Network::Initiator> InspectorResourceAgent::buildInitiatorObject(Document* document, const FetchInitiatorInfo& initiatorInfo)
527 {
528     RefPtrWillBeRawPtr<ScriptCallStack> stackTrace = createScriptCallStack(ScriptCallStack::maxCallStackSizeToCapture, true);
529     if (stackTrace && stackTrace->size() > 0) {
530         RefPtr<TypeBuilder::Network::Initiator> initiatorObject = TypeBuilder::Network::Initiator::create()
531             .setType(TypeBuilder::Network::Initiator::Type::Script);
532         initiatorObject->setStackTrace(stackTrace->buildInspectorArray());
533         return initiatorObject;
534     }
535 
536     if (document && document->scriptableDocumentParser()) {
537         RefPtr<TypeBuilder::Network::Initiator> initiatorObject = TypeBuilder::Network::Initiator::create()
538             .setType(TypeBuilder::Network::Initiator::Type::Parser);
539         initiatorObject->setUrl(urlWithoutFragment(document->url()).string());
540         if (TextPosition::belowRangePosition() != initiatorInfo.position)
541             initiatorObject->setLineNumber(initiatorInfo.position.m_line.oneBasedInt());
542         else
543             initiatorObject->setLineNumber(document->scriptableDocumentParser()->lineNumber().oneBasedInt());
544         return initiatorObject;
545     }
546 
547     if (m_isRecalculatingStyle && m_styleRecalculationInitiator)
548         return m_styleRecalculationInitiator;
549 
550     return TypeBuilder::Network::Initiator::create()
551         .setType(TypeBuilder::Network::Initiator::Type::Other)
552         .release();
553 }
554 
didCreateWebSocket(Document *,unsigned long identifier,const KURL & requestURL,const String &)555 void InspectorResourceAgent::didCreateWebSocket(Document*, unsigned long identifier, const KURL& requestURL, const String&)
556 {
557     m_frontend->webSocketCreated(IdentifiersFactory::requestId(identifier), urlWithoutFragment(requestURL).string());
558 }
559 
willSendWebSocketHandshakeRequest(Document *,unsigned long identifier,const WebSocketHandshakeRequest * request)560 void InspectorResourceAgent::willSendWebSocketHandshakeRequest(Document*, unsigned long identifier, const WebSocketHandshakeRequest* request)
561 {
562     ASSERT(request);
563     RefPtr<TypeBuilder::Network::WebSocketRequest> requestObject = TypeBuilder::Network::WebSocketRequest::create()
564         .setHeaders(buildObjectForHeaders(request->headerFields()));
565     m_frontend->webSocketWillSendHandshakeRequest(IdentifiersFactory::requestId(identifier), currentTime(), requestObject);
566 }
567 
didReceiveWebSocketHandshakeResponse(Document *,unsigned long identifier,const WebSocketHandshakeRequest * request,const WebSocketHandshakeResponse * response)568 void InspectorResourceAgent::didReceiveWebSocketHandshakeResponse(Document*, unsigned long identifier, const WebSocketHandshakeRequest* request, const WebSocketHandshakeResponse* response)
569 {
570     ASSERT(response);
571     RefPtr<TypeBuilder::Network::WebSocketResponse> responseObject = TypeBuilder::Network::WebSocketResponse::create()
572         .setStatus(response->statusCode())
573         .setStatusText(response->statusText())
574         .setHeaders(buildObjectForHeaders(response->headerFields()));
575 
576     if (!response->headersText().isEmpty())
577         responseObject->setHeadersText(response->headersText());
578     if (request) {
579         responseObject->setRequestHeaders(buildObjectForHeaders(request->headerFields()));
580         if (!request->headersText().isEmpty())
581             responseObject->setRequestHeadersText(request->headersText());
582     }
583     m_frontend->webSocketHandshakeResponseReceived(IdentifiersFactory::requestId(identifier), currentTime(), responseObject);
584 }
585 
didCloseWebSocket(Document *,unsigned long identifier)586 void InspectorResourceAgent::didCloseWebSocket(Document*, unsigned long identifier)
587 {
588     m_frontend->webSocketClosed(IdentifiersFactory::requestId(identifier), currentTime());
589 }
590 
didReceiveWebSocketFrame(unsigned long identifier,int opCode,bool masked,const char * payload,size_t payloadLength)591 void InspectorResourceAgent::didReceiveWebSocketFrame(unsigned long identifier, int opCode, bool masked, const char* payload, size_t payloadLength)
592 {
593     RefPtr<TypeBuilder::Network::WebSocketFrame> frameObject = TypeBuilder::Network::WebSocketFrame::create()
594         .setOpcode(opCode)
595         .setMask(masked)
596         .setPayloadData(String(payload, payloadLength));
597     m_frontend->webSocketFrameReceived(IdentifiersFactory::requestId(identifier), currentTime(), frameObject);
598 }
599 
didSendWebSocketFrame(unsigned long identifier,int opCode,bool masked,const char * payload,size_t payloadLength)600 void InspectorResourceAgent::didSendWebSocketFrame(unsigned long identifier, int opCode, bool masked, const char* payload, size_t payloadLength)
601 {
602     RefPtr<TypeBuilder::Network::WebSocketFrame> frameObject = TypeBuilder::Network::WebSocketFrame::create()
603         .setOpcode(opCode)
604         .setMask(masked)
605         .setPayloadData(String(payload, payloadLength));
606     m_frontend->webSocketFrameSent(IdentifiersFactory::requestId(identifier), currentTime(), frameObject);
607 }
608 
didReceiveWebSocketFrameError(unsigned long identifier,const String & errorMessage)609 void InspectorResourceAgent::didReceiveWebSocketFrameError(unsigned long identifier, const String& errorMessage)
610 {
611     m_frontend->webSocketFrameError(IdentifiersFactory::requestId(identifier), currentTime(), errorMessage);
612 }
613 
614 // called from Internals for layout test purposes.
setResourcesDataSizeLimitsFromInternals(int maximumResourcesContentSize,int maximumSingleResourceContentSize)615 void InspectorResourceAgent::setResourcesDataSizeLimitsFromInternals(int maximumResourcesContentSize, int maximumSingleResourceContentSize)
616 {
617     m_resourcesData->setResourcesDataSizeLimits(maximumResourcesContentSize, maximumSingleResourceContentSize);
618 }
619 
enable(ErrorString *)620 void InspectorResourceAgent::enable(ErrorString*)
621 {
622     enable();
623 }
624 
enable()625 void InspectorResourceAgent::enable()
626 {
627     if (!m_frontend)
628         return;
629     m_state->setBoolean(ResourceAgentState::resourceAgentEnabled, true);
630     m_instrumentingAgents->setInspectorResourceAgent(this);
631 }
632 
disable(ErrorString *)633 void InspectorResourceAgent::disable(ErrorString*)
634 {
635     m_state->setBoolean(ResourceAgentState::resourceAgentEnabled, false);
636     m_state->setString(ResourceAgentState::userAgentOverride, "");
637     m_instrumentingAgents->setInspectorResourceAgent(0);
638     m_resourcesData->clear();
639 }
640 
setUserAgentOverride(ErrorString *,const String & userAgent)641 void InspectorResourceAgent::setUserAgentOverride(ErrorString*, const String& userAgent)
642 {
643     m_state->setString(ResourceAgentState::userAgentOverride, userAgent);
644 }
645 
setExtraHTTPHeaders(ErrorString *,const RefPtr<JSONObject> & headers)646 void InspectorResourceAgent::setExtraHTTPHeaders(ErrorString*, const RefPtr<JSONObject>& headers)
647 {
648     m_state->setObject(ResourceAgentState::extraRequestHeaders, headers);
649 }
650 
getResponseBody(ErrorString * errorString,const String & requestId,String * content,bool * base64Encoded)651 void InspectorResourceAgent::getResponseBody(ErrorString* errorString, const String& requestId, String* content, bool* base64Encoded)
652 {
653     NetworkResourcesData::ResourceData const* resourceData = m_resourcesData->data(requestId);
654     if (!resourceData) {
655         *errorString = "No resource with given identifier found";
656         return;
657     }
658 
659     if (resourceData->hasContent()) {
660         *base64Encoded = resourceData->base64Encoded();
661         *content = resourceData->content();
662         return;
663     }
664 
665     if (resourceData->isContentEvicted()) {
666         *errorString = "Request content was evicted from inspector cache";
667         return;
668     }
669 
670     if (resourceData->buffer() && !resourceData->textEncodingName().isNull()) {
671         *base64Encoded = false;
672         if (InspectorPageAgent::sharedBufferContent(resourceData->buffer(), resourceData->textEncodingName(), *base64Encoded, content))
673             return;
674     }
675 
676     if (resourceData->cachedResource()) {
677         if (InspectorPageAgent::cachedResourceContent(resourceData->cachedResource(), content, base64Encoded))
678             return;
679     }
680 
681     *errorString = "No data found for resource with given identifier";
682 }
683 
replayXHR(ErrorString *,const String & requestId)684 void InspectorResourceAgent::replayXHR(ErrorString*, const String& requestId)
685 {
686     String actualRequestId = requestId;
687 
688     XHRReplayData* xhrReplayData = m_resourcesData->xhrReplayData(requestId);
689     if (!xhrReplayData)
690         return;
691 
692     ExecutionContext* executionContext = xhrReplayData->executionContext();
693     if (!executionContext) {
694         m_resourcesData->setXHRReplayData(requestId, 0);
695         return;
696     }
697 
698     RefPtrWillBeRawPtr<XMLHttpRequest> xhr = XMLHttpRequest::create(executionContext);
699 
700     Resource* cachedResource = memoryCache()->resourceForURL(xhrReplayData->url());
701     if (cachedResource)
702         memoryCache()->remove(cachedResource);
703 
704     xhr->open(xhrReplayData->method(), xhrReplayData->url(), xhrReplayData->async(), IGNORE_EXCEPTION);
705     HTTPHeaderMap::const_iterator end = xhrReplayData->headers().end();
706     for (HTTPHeaderMap::const_iterator it = xhrReplayData->headers().begin(); it!= end; ++it)
707         xhr->setRequestHeader(it->key, it->value, IGNORE_EXCEPTION);
708     xhr->sendForInspectorXHRReplay(xhrReplayData->formData(), IGNORE_EXCEPTION);
709 }
710 
canClearBrowserCache(ErrorString *,bool * result)711 void InspectorResourceAgent::canClearBrowserCache(ErrorString*, bool* result)
712 {
713     *result = true;
714 }
715 
canClearBrowserCookies(ErrorString *,bool * result)716 void InspectorResourceAgent::canClearBrowserCookies(ErrorString*, bool* result)
717 {
718     *result = true;
719 }
720 
setCacheDisabled(ErrorString *,bool cacheDisabled)721 void InspectorResourceAgent::setCacheDisabled(ErrorString*, bool cacheDisabled)
722 {
723     m_state->setBoolean(ResourceAgentState::cacheDisabled, cacheDisabled);
724     if (cacheDisabled)
725         memoryCache()->evictResources();
726     for (Frame* frame = m_pageAgent->mainFrame(); frame; frame = frame->tree().traverseNext()) {
727         if (frame->isLocalFrame())
728             toLocalFrame(frame)->document()->fetcher()->garbageCollectDocumentResources();
729     }
730 }
731 
loadResourceForFrontend(ErrorString * errorString,const String & frameId,const String & url,const RefPtr<JSONObject> * requestHeaders,PassRefPtr<LoadResourceForFrontendCallback> prpCallback)732 void InspectorResourceAgent::loadResourceForFrontend(ErrorString* errorString, const String& frameId, const String& url, const RefPtr<JSONObject>* requestHeaders, PassRefPtr<LoadResourceForFrontendCallback> prpCallback)
733 {
734     RefPtr<LoadResourceForFrontendCallback> callback = prpCallback;
735     LocalFrame* frame = m_pageAgent->assertFrame(errorString, frameId);
736     if (!frame)
737         return;
738 
739     Document* document = frame->document();
740     if (!document) {
741         *errorString = "No Document instance for the specified frame";
742         return;
743     }
744 
745     ResourceRequest request(url);
746     request.setHTTPMethod("GET");
747     request.setCachePolicy(ReloadIgnoringCacheData);
748     if (requestHeaders) {
749         for (JSONObject::iterator it = (*requestHeaders)->begin(); it != (*requestHeaders)->end(); ++it) {
750             String value;
751             bool success = it->value->asString(&value);
752             if (!success) {
753                 *errorString = "Request header \"" + it->key + "\" value is not a string";
754                 return;
755             }
756             request.addHTTPHeaderField(AtomicString(it->key), AtomicString(value));
757         }
758     }
759     request.addHTTPHeaderField(kDevToolsRequestInitiator, "frontend");
760 
761     ThreadableLoaderOptions options;
762     options.crossOriginRequestPolicy = AllowCrossOriginRequests;
763 
764     ResourceLoaderOptions resourceLoaderOptions;
765     resourceLoaderOptions.allowCredentials = AllowStoredCredentials;
766 
767     InspectorThreadableLoaderClient* inspectorThreadableLoaderClient = new InspectorThreadableLoaderClient(callback);
768     RefPtr<DocumentThreadableLoader> loader = DocumentThreadableLoader::create(*document, inspectorThreadableLoaderClient, request, options, resourceLoaderOptions);
769     if (!loader) {
770         inspectorThreadableLoaderClient->didFailLoaderCreation();
771         return;
772     }
773     loader->setDefersLoading(false);
774     if (!callback->isActive())
775         return;
776     inspectorThreadableLoaderClient->setLoader(loader.release());
777 }
778 
didCommitLoad(LocalFrame * frame,DocumentLoader * loader)779 void InspectorResourceAgent::didCommitLoad(LocalFrame* frame, DocumentLoader* loader)
780 {
781     if (loader->frame() != frame->page()->mainFrame())
782         return;
783 
784     if (m_state->getBoolean(ResourceAgentState::cacheDisabled))
785         memoryCache()->evictResources();
786 
787     m_resourcesData->clear(m_pageAgent->loaderId(loader));
788 }
789 
frameScheduledNavigation(LocalFrame * frame,double)790 void InspectorResourceAgent::frameScheduledNavigation(LocalFrame* frame, double)
791 {
792     RefPtr<TypeBuilder::Network::Initiator> initiator = buildInitiatorObject(frame->document(), FetchInitiatorInfo());
793     m_frameNavigationInitiatorMap.set(m_pageAgent->frameId(frame), initiator);
794 }
795 
frameClearedScheduledNavigation(LocalFrame * frame)796 void InspectorResourceAgent::frameClearedScheduledNavigation(LocalFrame* frame)
797 {
798     m_frameNavigationInitiatorMap.remove(m_pageAgent->frameId(frame));
799 }
800 
setHostId(const String & hostId)801 void InspectorResourceAgent::setHostId(const String& hostId)
802 {
803     m_hostId = hostId;
804 }
805 
fetchResourceContent(LocalFrame * frame,const KURL & url,String * content,bool * base64Encoded)806 bool InspectorResourceAgent::fetchResourceContent(LocalFrame* frame, const KURL& url, String* content, bool* base64Encoded)
807 {
808     // First try to fetch content from the cached resource.
809     Resource* cachedResource = frame->document()->fetcher()->cachedResource(url);
810     if (!cachedResource)
811         cachedResource = memoryCache()->resourceForURL(url);
812     if (cachedResource && InspectorPageAgent::cachedResourceContent(cachedResource, content, base64Encoded))
813         return true;
814 
815     // Then fall back to resource data.
816     Vector<NetworkResourcesData::ResourceData*> resources = m_resourcesData->resources();
817     for (Vector<NetworkResourcesData::ResourceData*>::iterator it = resources.begin(); it != resources.end(); ++it) {
818         if ((*it)->url() == url) {
819             *content = (*it)->content();
820             *base64Encoded = (*it)->base64Encoded();
821             return true;
822         }
823     }
824     return false;
825 }
826 
InspectorResourceAgent(InspectorPageAgent * pageAgent)827 InspectorResourceAgent::InspectorResourceAgent(InspectorPageAgent* pageAgent)
828     : InspectorBaseAgent<InspectorResourceAgent>("Network")
829     , m_pageAgent(pageAgent)
830     , m_frontend(0)
831     , m_resourcesData(adoptPtr(new NetworkResourcesData()))
832     , m_isRecalculatingStyle(false)
833 {
834 }
835 
836 } // namespace WebCore
837