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 /** 32 * @constructor 33 * @extends {WebInspector.Object} 34 */ 35 WebInspector.NetworkManager = function() 36 { 37 WebInspector.Object.call(this); 38 this._dispatcher = new WebInspector.NetworkDispatcher(this); 39 if (WebInspector.settings.cacheDisabled.get()) 40 NetworkAgent.setCacheDisabled(true); 41 42 NetworkAgent.enable(); 43 44 WebInspector.settings.cacheDisabled.addChangeListener(this._cacheDisabledSettingChanged, this); 45 } 46 47 WebInspector.NetworkManager.EventTypes = { 48 RequestStarted: "RequestStarted", 49 RequestUpdated: "RequestUpdated", 50 RequestFinished: "RequestFinished", 51 RequestUpdateDropped: "RequestUpdateDropped" 52 } 53 54 WebInspector.NetworkManager._MIMETypes = { 55 "text/html": {"document": true}, 56 "text/xml": {"document": true}, 57 "text/plain": {"document": true}, 58 "application/xhtml+xml": {"document": true}, 59 "text/css": {"stylesheet": true}, 60 "text/xsl": {"stylesheet": true}, 61 "image/jpg": {"image": true}, 62 "image/jpeg": {"image": true}, 63 "image/pjpeg": {"image": true}, 64 "image/png": {"image": true}, 65 "image/gif": {"image": true}, 66 "image/bmp": {"image": true}, 67 "image/svg+xml": {"image": true, "font": true, "document": true}, 68 "image/vnd.microsoft.icon": {"image": true}, 69 "image/webp": {"image": true}, 70 "image/x-icon": {"image": true}, 71 "image/x-xbitmap": {"image": true}, 72 "font/ttf": {"font": true}, 73 "font/otf": {"font": true}, 74 "font/woff": {"font": true}, 75 "font/woff2": {"font": true}, 76 "font/truetype": {"font": true}, 77 "font/opentype": {"font": true}, 78 "application/octet-stream": {"font": true, "image": true}, 79 "application/font-woff": {"font": true}, 80 "application/x-font-woff": {"font": true}, 81 "application/x-font-type1": {"font": true}, 82 "application/x-font-ttf": {"font": true}, 83 "application/x-truetype-font": {"font": true}, 84 "text/javascript": {"script": true}, 85 "text/ecmascript": {"script": true}, 86 "application/javascript": {"script": true}, 87 "application/ecmascript": {"script": true}, 88 "application/x-javascript": {"script": true}, 89 "application/json": {"script": true}, 90 "text/javascript1.1": {"script": true}, 91 "text/javascript1.2": {"script": true}, 92 "text/javascript1.3": {"script": true}, 93 "text/jscript": {"script": true}, 94 "text/livescript": {"script": true}, 95 } 96 97 WebInspector.NetworkManager.prototype = { 98 /** 99 * @param {string} url 100 * @return {WebInspector.NetworkRequest} 101 */ 102 inflightRequestForURL: function(url) 103 { 104 return this._dispatcher._inflightRequestsByURL[url]; 105 }, 106 107 /** 108 * @param {WebInspector.Event} event 109 */ 110 _cacheDisabledSettingChanged: function(event) 111 { 112 var enabled = /** @type {boolean} */ (event.data); 113 NetworkAgent.setCacheDisabled(enabled); 114 }, 115 116 __proto__: WebInspector.Object.prototype 117 } 118 119 /** 120 * @constructor 121 * @implements {NetworkAgent.Dispatcher} 122 */ 123 WebInspector.NetworkDispatcher = function(manager) 124 { 125 this._manager = manager; 126 this._inflightRequestsById = {}; 127 this._inflightRequestsByURL = {}; 128 InspectorBackend.registerNetworkDispatcher(this); 129 } 130 131 WebInspector.NetworkDispatcher.prototype = { 132 /** 133 * @param {NetworkAgent.Headers} headersMap 134 * @return {!Array.<!WebInspector.NetworkRequest.NameValue>} 135 */ 136 _headersMapToHeadersArray: function(headersMap) 137 { 138 var result = []; 139 for (var name in headersMap) { 140 var values = headersMap[name].split("\n"); 141 for (var i = 0; i < values.length; ++i) 142 result.push({name: name, value: values[i]}); 143 } 144 return result; 145 }, 146 147 /** 148 * @param {WebInspector.NetworkRequest} networkRequest 149 * @param {NetworkAgent.Request} request 150 */ 151 _updateNetworkRequestWithRequest: function(networkRequest, request) 152 { 153 networkRequest.requestMethod = request.method; 154 networkRequest.requestHeaders = this._headersMapToHeadersArray(request.headers); 155 networkRequest.requestFormData = request.postData; 156 }, 157 158 /** 159 * @param {WebInspector.NetworkRequest} networkRequest 160 * @param {NetworkAgent.Response=} response 161 */ 162 _updateNetworkRequestWithResponse: function(networkRequest, response) 163 { 164 if (!response) 165 return; 166 167 if (response.url && networkRequest.url !== response.url) 168 networkRequest.url = response.url; 169 networkRequest.mimeType = response.mimeType; 170 networkRequest.statusCode = response.status; 171 networkRequest.statusText = response.statusText; 172 networkRequest.responseHeaders = this._headersMapToHeadersArray(response.headers); 173 if (response.headersText) 174 networkRequest.responseHeadersText = response.headersText; 175 if (response.requestHeaders) 176 networkRequest.requestHeaders = this._headersMapToHeadersArray(response.requestHeaders); 177 if (response.requestHeadersText) 178 networkRequest.requestHeadersText = response.requestHeadersText; 179 180 networkRequest.connectionReused = response.connectionReused; 181 networkRequest.connectionId = response.connectionId; 182 183 if (response.fromDiskCache) 184 networkRequest.cached = true; 185 else 186 networkRequest.timing = response.timing; 187 188 if (!this._mimeTypeIsConsistentWithType(networkRequest)) { 189 WebInspector.console.addMessage(WebInspector.ConsoleMessage.create(WebInspector.ConsoleMessage.MessageSource.Network, 190 WebInspector.ConsoleMessage.MessageLevel.Log, 191 WebInspector.UIString("Resource interpreted as %s but transferred with MIME type %s: \"%s\".", networkRequest.type.title(), networkRequest.mimeType, networkRequest.url), 192 WebInspector.ConsoleMessage.MessageType.Log, 193 "", 194 0, 195 0, 196 1, 197 [], 198 null, 199 networkRequest.requestId)); 200 } 201 }, 202 203 /** 204 * @param {WebInspector.NetworkRequest} networkRequest 205 * @return {boolean} 206 */ 207 _mimeTypeIsConsistentWithType: function(networkRequest) 208 { 209 // If status is an error, content is likely to be of an inconsistent type, 210 // as it's going to be an error message. We do not want to emit a warning 211 // for this, though, as this will already be reported as resource loading failure. 212 // Also, if a URL like http://localhost/wiki/load.php?debug=true&lang=en produces text/css and gets reloaded, 213 // it is 304 Not Modified and its guessed mime-type is text/php, which is wrong. 214 // Don't check for mime-types in 304-resources. 215 if (networkRequest.hasErrorStatusCode() || networkRequest.statusCode === 304 || networkRequest.statusCode === 204) 216 return true; 217 218 if (typeof networkRequest.type === "undefined" 219 || networkRequest.type === WebInspector.resourceTypes.Other 220 || networkRequest.type === WebInspector.resourceTypes.XHR 221 || networkRequest.type === WebInspector.resourceTypes.WebSocket) 222 return true; 223 224 if (!networkRequest.mimeType) 225 return true; // Might be not known for cached resources with null responses. 226 227 if (networkRequest.mimeType in WebInspector.NetworkManager._MIMETypes) 228 return networkRequest.type.name() in WebInspector.NetworkManager._MIMETypes[networkRequest.mimeType]; 229 230 return false; 231 }, 232 233 /** 234 * @param {WebInspector.NetworkRequest} networkRequest 235 * @param {?NetworkAgent.CachedResource} cachedResource 236 */ 237 _updateNetworkRequestWithCachedResource: function(networkRequest, cachedResource) 238 { 239 networkRequest.type = WebInspector.resourceTypes[cachedResource.type]; 240 networkRequest.resourceSize = cachedResource.bodySize; 241 this._updateNetworkRequestWithResponse(networkRequest, cachedResource.response); 242 }, 243 244 /** 245 * @param {NetworkAgent.Response} response 246 * @return {boolean} 247 */ 248 _isNull: function(response) 249 { 250 if (!response) 251 return true; 252 return !response.status && !response.mimeType && (!response.headers || !Object.keys(response.headers).length); 253 }, 254 255 /** 256 * @param {NetworkAgent.RequestId} requestId 257 * @param {NetworkAgent.FrameId} frameId 258 * @param {NetworkAgent.LoaderId} loaderId 259 * @param {string} documentURL 260 * @param {NetworkAgent.Request} request 261 * @param {NetworkAgent.Timestamp} time 262 * @param {NetworkAgent.Initiator} initiator 263 * @param {NetworkAgent.Response=} redirectResponse 264 */ 265 requestWillBeSent: function(requestId, frameId, loaderId, documentURL, request, time, initiator, redirectResponse) 266 { 267 var networkRequest = this._inflightRequestsById[requestId]; 268 if (networkRequest) { 269 // FIXME: move this check to the backend. 270 if (!redirectResponse) 271 return; 272 this.responseReceived(requestId, frameId, loaderId, time, PageAgent.ResourceType.Other, redirectResponse); 273 networkRequest = this._appendRedirect(requestId, time, request.url); 274 } else 275 networkRequest = this._createNetworkRequest(requestId, frameId, loaderId, request.url, documentURL, initiator); 276 networkRequest.hasNetworkData = true; 277 this._updateNetworkRequestWithRequest(networkRequest, request); 278 networkRequest.startTime = time; 279 280 this._startNetworkRequest(networkRequest); 281 }, 282 283 /** 284 * @param {NetworkAgent.RequestId} requestId 285 */ 286 requestServedFromCache: function(requestId) 287 { 288 var networkRequest = this._inflightRequestsById[requestId]; 289 if (!networkRequest) 290 return; 291 292 networkRequest.cached = true; 293 }, 294 295 /** 296 * @param {NetworkAgent.RequestId} requestId 297 * @param {NetworkAgent.FrameId} frameId 298 * @param {NetworkAgent.LoaderId} loaderId 299 * @param {NetworkAgent.Timestamp} time 300 * @param {PageAgent.ResourceType} resourceType 301 * @param {NetworkAgent.Response} response 302 */ 303 responseReceived: function(requestId, frameId, loaderId, time, resourceType, response) 304 { 305 // FIXME: move this check to the backend. 306 if (this._isNull(response)) 307 return; 308 309 var networkRequest = this._inflightRequestsById[requestId]; 310 if (!networkRequest) { 311 // We missed the requestWillBeSent. 312 var eventData = {}; 313 eventData.url = response.url; 314 eventData.frameId = frameId; 315 eventData.loaderId = loaderId; 316 eventData.resourceType = resourceType; 317 eventData.mimeType = response.mimeType; 318 this._manager.dispatchEventToListeners(WebInspector.NetworkManager.EventTypes.RequestUpdateDropped, eventData); 319 return; 320 } 321 322 networkRequest.responseReceivedTime = time; 323 networkRequest.type = WebInspector.resourceTypes[resourceType]; 324 325 this._updateNetworkRequestWithResponse(networkRequest, response); 326 327 this._updateNetworkRequest(networkRequest); 328 }, 329 330 /** 331 * @param {NetworkAgent.RequestId} requestId 332 * @param {NetworkAgent.Timestamp} time 333 * @param {number} dataLength 334 * @param {number} encodedDataLength 335 */ 336 dataReceived: function(requestId, time, dataLength, encodedDataLength) 337 { 338 var networkRequest = this._inflightRequestsById[requestId]; 339 if (!networkRequest) 340 return; 341 342 networkRequest.resourceSize += dataLength; 343 if (encodedDataLength != -1) 344 networkRequest.increaseTransferSize(encodedDataLength); 345 networkRequest.endTime = time; 346 347 this._updateNetworkRequest(networkRequest); 348 }, 349 350 /** 351 * @param {NetworkAgent.RequestId} requestId 352 * @param {NetworkAgent.Timestamp} finishTime 353 */ 354 loadingFinished: function(requestId, finishTime) 355 { 356 var networkRequest = this._inflightRequestsById[requestId]; 357 if (!networkRequest) 358 return; 359 this._finishNetworkRequest(networkRequest, finishTime); 360 }, 361 362 /** 363 * @param {NetworkAgent.RequestId} requestId 364 * @param {NetworkAgent.Timestamp} time 365 * @param {string} localizedDescription 366 * @param {boolean=} canceled 367 */ 368 loadingFailed: function(requestId, time, localizedDescription, canceled) 369 { 370 var networkRequest = this._inflightRequestsById[requestId]; 371 if (!networkRequest) 372 return; 373 374 networkRequest.failed = true; 375 networkRequest.canceled = canceled; 376 networkRequest.localizedFailDescription = localizedDescription; 377 this._finishNetworkRequest(networkRequest, time); 378 }, 379 380 /** 381 * @param {NetworkAgent.RequestId} requestId 382 * @param {NetworkAgent.FrameId} frameId 383 * @param {NetworkAgent.LoaderId} loaderId 384 * @param {string} documentURL 385 * @param {NetworkAgent.Timestamp} time 386 * @param {NetworkAgent.Initiator} initiator 387 * @param {NetworkAgent.CachedResource} cachedResource 388 */ 389 requestServedFromMemoryCache: function(requestId, frameId, loaderId, documentURL, time, initiator, cachedResource) 390 { 391 var networkRequest = this._createNetworkRequest(requestId, frameId, loaderId, cachedResource.url, documentURL, initiator); 392 this._updateNetworkRequestWithCachedResource(networkRequest, cachedResource); 393 networkRequest.cached = true; 394 networkRequest.requestMethod = "GET"; 395 this._startNetworkRequest(networkRequest); 396 networkRequest.startTime = networkRequest.responseReceivedTime = time; 397 this._finishNetworkRequest(networkRequest, time); 398 }, 399 400 /** 401 * @param {NetworkAgent.RequestId} requestId 402 * @param {string} requestURL 403 */ 404 webSocketCreated: function(requestId, requestURL) 405 { 406 var networkRequest = new WebInspector.NetworkRequest(requestId, requestURL, "", "", ""); 407 networkRequest.type = WebInspector.resourceTypes.WebSocket; 408 this._startNetworkRequest(networkRequest); 409 }, 410 411 /** 412 * @param {NetworkAgent.RequestId} requestId 413 * @param {NetworkAgent.Timestamp} time 414 * @param {NetworkAgent.WebSocketRequest} request 415 */ 416 webSocketWillSendHandshakeRequest: function(requestId, time, request) 417 { 418 var networkRequest = this._inflightRequestsById[requestId]; 419 if (!networkRequest) 420 return; 421 422 networkRequest.requestMethod = "GET"; 423 networkRequest.requestHeaders = this._headersMapToHeadersArray(request.headers); 424 networkRequest.startTime = time; 425 426 this._updateNetworkRequest(networkRequest); 427 }, 428 429 /** 430 * @param {NetworkAgent.RequestId} requestId 431 * @param {NetworkAgent.Timestamp} time 432 * @param {NetworkAgent.WebSocketResponse} response 433 */ 434 webSocketHandshakeResponseReceived: function(requestId, time, response) 435 { 436 var networkRequest = this._inflightRequestsById[requestId]; 437 if (!networkRequest) 438 return; 439 440 networkRequest.statusCode = response.status; 441 networkRequest.statusText = response.statusText; 442 networkRequest.responseHeaders = this._headersMapToHeadersArray(response.headers); 443 networkRequest.responseReceivedTime = time; 444 445 this._updateNetworkRequest(networkRequest); 446 }, 447 448 /** 449 * @param {NetworkAgent.RequestId} requestId 450 * @param {NetworkAgent.Timestamp} time 451 * @param {NetworkAgent.WebSocketFrame} response 452 */ 453 webSocketFrameReceived: function(requestId, time, response) 454 { 455 var networkRequest = this._inflightRequestsById[requestId]; 456 if (!networkRequest) 457 return; 458 459 networkRequest.addFrame(response, time); 460 networkRequest.responseReceivedTime = time; 461 462 this._updateNetworkRequest(networkRequest); 463 }, 464 465 /** 466 * @param {NetworkAgent.RequestId} requestId 467 * @param {NetworkAgent.Timestamp} time 468 * @param {NetworkAgent.WebSocketFrame} response 469 */ 470 webSocketFrameSent: function(requestId, time, response) 471 { 472 var networkRequest = this._inflightRequestsById[requestId]; 473 if (!networkRequest) 474 return; 475 476 networkRequest.addFrame(response, time, true); 477 networkRequest.responseReceivedTime = time; 478 479 this._updateNetworkRequest(networkRequest); 480 }, 481 482 /** 483 * @param {NetworkAgent.RequestId} requestId 484 * @param {NetworkAgent.Timestamp} time 485 * @param {string} errorMessage 486 */ 487 webSocketFrameError: function(requestId, time, errorMessage) 488 { 489 var networkRequest = this._inflightRequestsById[requestId]; 490 if (!networkRequest) 491 return; 492 493 networkRequest.addFrameError(errorMessage, time); 494 networkRequest.responseReceivedTime = time; 495 496 this._updateNetworkRequest(networkRequest); 497 }, 498 499 /** 500 * @param {NetworkAgent.RequestId} requestId 501 * @param {NetworkAgent.Timestamp} time 502 */ 503 webSocketClosed: function(requestId, time) 504 { 505 var networkRequest = this._inflightRequestsById[requestId]; 506 if (!networkRequest) 507 return; 508 this._finishNetworkRequest(networkRequest, time); 509 }, 510 511 /** 512 * @param {NetworkAgent.RequestId} requestId 513 * @param {NetworkAgent.Timestamp} time 514 * @param {string} redirectURL 515 * @return {WebInspector.NetworkRequest} 516 */ 517 _appendRedirect: function(requestId, time, redirectURL) 518 { 519 var originalNetworkRequest = this._inflightRequestsById[requestId]; 520 var previousRedirects = originalNetworkRequest.redirects || []; 521 originalNetworkRequest.requestId = "redirected:" + requestId + "." + previousRedirects.length; 522 delete originalNetworkRequest.redirects; 523 if (previousRedirects.length > 0) 524 originalNetworkRequest.redirectSource = previousRedirects[previousRedirects.length - 1]; 525 this._finishNetworkRequest(originalNetworkRequest, time); 526 var newNetworkRequest = this._createNetworkRequest(requestId, originalNetworkRequest.frameId, originalNetworkRequest.loaderId, 527 redirectURL, originalNetworkRequest.documentURL, originalNetworkRequest.initiator); 528 newNetworkRequest.redirects = previousRedirects.concat(originalNetworkRequest); 529 return newNetworkRequest; 530 }, 531 532 /** 533 * @param {WebInspector.NetworkRequest} networkRequest 534 */ 535 _startNetworkRequest: function(networkRequest) 536 { 537 this._inflightRequestsById[networkRequest.requestId] = networkRequest; 538 this._inflightRequestsByURL[networkRequest.url] = networkRequest; 539 this._dispatchEventToListeners(WebInspector.NetworkManager.EventTypes.RequestStarted, networkRequest); 540 }, 541 542 /** 543 * @param {WebInspector.NetworkRequest} networkRequest 544 */ 545 _updateNetworkRequest: function(networkRequest) 546 { 547 this._dispatchEventToListeners(WebInspector.NetworkManager.EventTypes.RequestUpdated, networkRequest); 548 }, 549 550 /** 551 * @param {WebInspector.NetworkRequest} networkRequest 552 * @param {NetworkAgent.Timestamp} finishTime 553 */ 554 _finishNetworkRequest: function(networkRequest, finishTime) 555 { 556 networkRequest.endTime = finishTime; 557 networkRequest.finished = true; 558 this._dispatchEventToListeners(WebInspector.NetworkManager.EventTypes.RequestFinished, networkRequest); 559 delete this._inflightRequestsById[networkRequest.requestId]; 560 delete this._inflightRequestsByURL[networkRequest.url]; 561 }, 562 563 /** 564 * @param {string} eventType 565 * @param {WebInspector.NetworkRequest} networkRequest 566 */ 567 _dispatchEventToListeners: function(eventType, networkRequest) 568 { 569 this._manager.dispatchEventToListeners(eventType, networkRequest); 570 }, 571 572 /** 573 * @param {NetworkAgent.RequestId} requestId 574 * @param {string} frameId 575 * @param {NetworkAgent.LoaderId} loaderId 576 * @param {string} url 577 * @param {string} documentURL 578 * @param {NetworkAgent.Initiator} initiator 579 */ 580 _createNetworkRequest: function(requestId, frameId, loaderId, url, documentURL, initiator) 581 { 582 var networkRequest = new WebInspector.NetworkRequest(requestId, url, documentURL, frameId, loaderId); 583 networkRequest.initiator = initiator; 584 return networkRequest; 585 } 586 } 587 588 /** 589 * @type {?WebInspector.NetworkManager} 590 */ 591 WebInspector.networkManager = null; 592