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