2 * Copyright (C) 2011 Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
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
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.
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.
33 * @extends {WebInspector.Object}
35 WebInspector.NetworkManager = function()
37 WebInspector.Object.call(this);
38 this._dispatcher = new WebInspector.NetworkDispatcher(this);
39 if (WebInspector.settings.cacheDisabled.get())
40 NetworkAgent.setCacheDisabled(true);
42 NetworkAgent.enable();
44 WebInspector.settings.cacheDisabled.addChangeListener(this._cacheDisabledSettingChanged, this);
47 WebInspector.NetworkManager.EventTypes = {
48 RequestStarted: "RequestStarted",
49 RequestUpdated: "RequestUpdated",
50 RequestFinished: "RequestFinished",
51 RequestUpdateDropped: "RequestUpdateDropped"
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},
97 WebInspector.NetworkManager.prototype = {
100 * @return {!WebInspector.NetworkRequest}
102 inflightRequestForURL: function(url)
104 return this._dispatcher._inflightRequestsByURL[url];
108 * @param {!WebInspector.Event} event
110 _cacheDisabledSettingChanged: function(event)
112 var enabled = /** @type {boolean} */ (event.data);
113 NetworkAgent.setCacheDisabled(enabled);
116 __proto__: WebInspector.Object.prototype
121 * @implements {NetworkAgent.Dispatcher}
123 WebInspector.NetworkDispatcher = function(manager)
125 this._manager = manager;
126 this._inflightRequestsById = {};
127 this._inflightRequestsByURL = {};
128 InspectorBackend.registerNetworkDispatcher(this);
131 WebInspector.NetworkDispatcher.prototype = {
133 * @param {!NetworkAgent.Headers} headersMap
134 * @return {!Array.<!WebInspector.NetworkRequest.NameValue>}
136 _headersMapToHeadersArray: function(headersMap)
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]});
148 * @param {!WebInspector.NetworkRequest} networkRequest
149 * @param {!NetworkAgent.Request} request
151 _updateNetworkRequestWithRequest: function(networkRequest, request)
153 networkRequest.requestMethod = request.method;
154 networkRequest.setRequestHeaders(this._headersMapToHeadersArray(request.headers));
155 networkRequest.requestFormData = request.postData;
159 * @param {!WebInspector.NetworkRequest} networkRequest
160 * @param {!NetworkAgent.Response=} response
162 _updateNetworkRequestWithResponse: function(networkRequest, response)
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.setRequestHeaders(this._headersMapToHeadersArray(response.requestHeaders));
177 networkRequest.setRequestHeadersText(response.requestHeadersText || "");
180 networkRequest.connectionReused = response.connectionReused;
181 networkRequest.connectionId = response.connectionId;
183 if (response.fromDiskCache)
184 networkRequest.cached = true;
186 networkRequest.timing = response.timing;
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,
199 networkRequest.requestId));
204 * @param {!WebInspector.NetworkRequest} networkRequest
207 _mimeTypeIsConsistentWithType: function(networkRequest)
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)
218 if (typeof networkRequest.type === "undefined"
219 || networkRequest.type === WebInspector.resourceTypes.Other
220 || networkRequest.type === WebInspector.resourceTypes.XHR
221 || networkRequest.type === WebInspector.resourceTypes.WebSocket)
224 if (!networkRequest.mimeType)
225 return true; // Might be not known for cached resources with null responses.
227 if (networkRequest.mimeType in WebInspector.NetworkManager._MIMETypes)
228 return networkRequest.type.name() in WebInspector.NetworkManager._MIMETypes[networkRequest.mimeType];
234 * @param {!NetworkAgent.Response} response
237 _isNull: function(response)
241 return !response.status && !response.mimeType && (!response.headers || !Object.keys(response.headers).length);
245 * @param {!NetworkAgent.RequestId} requestId
246 * @param {!PageAgent.FrameId} frameId
247 * @param {!NetworkAgent.LoaderId} loaderId
248 * @param {string} documentURL
249 * @param {!NetworkAgent.Request} request
250 * @param {!NetworkAgent.Timestamp} time
251 * @param {!NetworkAgent.Initiator} initiator
252 * @param {!NetworkAgent.Response=} redirectResponse
254 requestWillBeSent: function(requestId, frameId, loaderId, documentURL, request, time, initiator, redirectResponse)
256 var networkRequest = this._inflightRequestsById[requestId];
257 if (networkRequest) {
258 // FIXME: move this check to the backend.
259 if (!redirectResponse)
261 this.responseReceived(requestId, frameId, loaderId, time, PageAgent.ResourceType.Other, redirectResponse);
262 networkRequest = this._appendRedirect(requestId, time, request.url);
264 networkRequest = this._createNetworkRequest(requestId, frameId, loaderId, request.url, documentURL, initiator);
265 networkRequest.hasNetworkData = true;
266 this._updateNetworkRequestWithRequest(networkRequest, request);
267 networkRequest.startTime = time;
269 this._startNetworkRequest(networkRequest);
273 * @param {!NetworkAgent.RequestId} requestId
275 requestServedFromCache: function(requestId)
277 var networkRequest = this._inflightRequestsById[requestId];
281 networkRequest.cached = true;
285 * @param {!NetworkAgent.RequestId} requestId
286 * @param {!PageAgent.FrameId} frameId
287 * @param {!NetworkAgent.LoaderId} loaderId
288 * @param {!NetworkAgent.Timestamp} time
289 * @param {!PageAgent.ResourceType} resourceType
290 * @param {!NetworkAgent.Response} response
292 responseReceived: function(requestId, frameId, loaderId, time, resourceType, response)
294 // FIXME: move this check to the backend.
295 if (this._isNull(response))
298 var networkRequest = this._inflightRequestsById[requestId];
299 if (!networkRequest) {
300 // We missed the requestWillBeSent.
302 eventData.url = response.url;
303 eventData.frameId = frameId;
304 eventData.loaderId = loaderId;
305 eventData.resourceType = resourceType;
306 eventData.mimeType = response.mimeType;
307 this._manager.dispatchEventToListeners(WebInspector.NetworkManager.EventTypes.RequestUpdateDropped, eventData);
311 networkRequest.responseReceivedTime = time;
312 networkRequest.type = WebInspector.resourceTypes[resourceType];
314 this._updateNetworkRequestWithResponse(networkRequest, response);
316 this._updateNetworkRequest(networkRequest);
320 * @param {!NetworkAgent.RequestId} requestId
321 * @param {!NetworkAgent.Timestamp} time
322 * @param {number} dataLength
323 * @param {number} encodedDataLength
325 dataReceived: function(requestId, time, dataLength, encodedDataLength)
327 var networkRequest = this._inflightRequestsById[requestId];
331 networkRequest.resourceSize += dataLength;
332 if (encodedDataLength != -1)
333 networkRequest.increaseTransferSize(encodedDataLength);
334 networkRequest.endTime = time;
336 this._updateNetworkRequest(networkRequest);
340 * @param {!NetworkAgent.RequestId} requestId
341 * @param {!NetworkAgent.Timestamp} finishTime
343 loadingFinished: function(requestId, finishTime)
345 var networkRequest = this._inflightRequestsById[requestId];
348 this._finishNetworkRequest(networkRequest, finishTime);
352 * @param {!NetworkAgent.RequestId} requestId
353 * @param {!NetworkAgent.Timestamp} time
354 * @param {string} localizedDescription
355 * @param {boolean=} canceled
357 loadingFailed: function(requestId, time, localizedDescription, canceled)
359 var networkRequest = this._inflightRequestsById[requestId];
363 networkRequest.failed = true;
364 networkRequest.canceled = canceled;
365 networkRequest.localizedFailDescription = localizedDescription;
366 this._finishNetworkRequest(networkRequest, time);
370 * @param {!NetworkAgent.RequestId} requestId
371 * @param {string} requestURL
373 webSocketCreated: function(requestId, requestURL)
375 var networkRequest = new WebInspector.NetworkRequest(requestId, requestURL, "", "", "");
376 networkRequest.type = WebInspector.resourceTypes.WebSocket;
377 this._startNetworkRequest(networkRequest);
381 * @param {!NetworkAgent.RequestId} requestId
382 * @param {!NetworkAgent.Timestamp} time
383 * @param {!NetworkAgent.WebSocketRequest} request
385 webSocketWillSendHandshakeRequest: function(requestId, time, request)
387 var networkRequest = this._inflightRequestsById[requestId];
391 networkRequest.requestMethod = "GET";
392 networkRequest.setRequestHeaders(this._headersMapToHeadersArray(request.headers));
393 networkRequest.startTime = time;
395 this._updateNetworkRequest(networkRequest);
399 * @param {!NetworkAgent.RequestId} requestId
400 * @param {!NetworkAgent.Timestamp} time
401 * @param {!NetworkAgent.WebSocketResponse} response
403 webSocketHandshakeResponseReceived: function(requestId, time, response)
405 var networkRequest = this._inflightRequestsById[requestId];
409 networkRequest.statusCode = response.status;
410 networkRequest.statusText = response.statusText;
411 networkRequest.responseHeaders = this._headersMapToHeadersArray(response.headers);
412 networkRequest.responseReceivedTime = time;
414 this._updateNetworkRequest(networkRequest);
418 * @param {!NetworkAgent.RequestId} requestId
419 * @param {!NetworkAgent.Timestamp} time
420 * @param {!NetworkAgent.WebSocketFrame} response
422 webSocketFrameReceived: function(requestId, time, response)
424 var networkRequest = this._inflightRequestsById[requestId];
428 networkRequest.addFrame(response, time);
429 networkRequest.responseReceivedTime = time;
431 this._updateNetworkRequest(networkRequest);
435 * @param {!NetworkAgent.RequestId} requestId
436 * @param {!NetworkAgent.Timestamp} time
437 * @param {!NetworkAgent.WebSocketFrame} response
439 webSocketFrameSent: function(requestId, time, response)
441 var networkRequest = this._inflightRequestsById[requestId];
445 networkRequest.addFrame(response, time, true);
446 networkRequest.responseReceivedTime = time;
448 this._updateNetworkRequest(networkRequest);
452 * @param {!NetworkAgent.RequestId} requestId
453 * @param {!NetworkAgent.Timestamp} time
454 * @param {string} errorMessage
456 webSocketFrameError: function(requestId, time, errorMessage)
458 var networkRequest = this._inflightRequestsById[requestId];
462 networkRequest.addFrameError(errorMessage, time);
463 networkRequest.responseReceivedTime = time;
465 this._updateNetworkRequest(networkRequest);
469 * @param {!NetworkAgent.RequestId} requestId
470 * @param {!NetworkAgent.Timestamp} time
472 webSocketClosed: function(requestId, time)
474 var networkRequest = this._inflightRequestsById[requestId];
477 this._finishNetworkRequest(networkRequest, time);
481 * @param {!NetworkAgent.RequestId} requestId
482 * @param {!NetworkAgent.Timestamp} time
483 * @param {string} redirectURL
484 * @return {!WebInspector.NetworkRequest}
486 _appendRedirect: function(requestId, time, redirectURL)
488 var originalNetworkRequest = this._inflightRequestsById[requestId];
489 var previousRedirects = originalNetworkRequest.redirects || [];
490 originalNetworkRequest.requestId = "redirected:" + requestId + "." + previousRedirects.length;
491 delete originalNetworkRequest.redirects;
492 if (previousRedirects.length > 0)
493 originalNetworkRequest.redirectSource = previousRedirects[previousRedirects.length - 1];
494 this._finishNetworkRequest(originalNetworkRequest, time);
495 var newNetworkRequest = this._createNetworkRequest(requestId, originalNetworkRequest.frameId, originalNetworkRequest.loaderId,
496 redirectURL, originalNetworkRequest.documentURL, originalNetworkRequest.initiator);
497 newNetworkRequest.redirects = previousRedirects.concat(originalNetworkRequest);
498 return newNetworkRequest;
502 * @param {!WebInspector.NetworkRequest} networkRequest
504 _startNetworkRequest: function(networkRequest)
506 this._inflightRequestsById[networkRequest.requestId] = networkRequest;
507 this._inflightRequestsByURL[networkRequest.url] = networkRequest;
508 this._dispatchEventToListeners(WebInspector.NetworkManager.EventTypes.RequestStarted, networkRequest);
512 * @param {!WebInspector.NetworkRequest} networkRequest
514 _updateNetworkRequest: function(networkRequest)
516 this._dispatchEventToListeners(WebInspector.NetworkManager.EventTypes.RequestUpdated, networkRequest);
520 * @param {!WebInspector.NetworkRequest} networkRequest
521 * @param {!NetworkAgent.Timestamp} finishTime
523 _finishNetworkRequest: function(networkRequest, finishTime)
525 networkRequest.endTime = finishTime;
526 networkRequest.finished = true;
527 this._dispatchEventToListeners(WebInspector.NetworkManager.EventTypes.RequestFinished, networkRequest);
528 delete this._inflightRequestsById[networkRequest.requestId];
529 delete this._inflightRequestsByURL[networkRequest.url];
533 * @param {string} eventType
534 * @param {!WebInspector.NetworkRequest} networkRequest
536 _dispatchEventToListeners: function(eventType, networkRequest)
538 this._manager.dispatchEventToListeners(eventType, networkRequest);
542 * @param {!NetworkAgent.RequestId} requestId
543 * @param {string} frameId
544 * @param {!NetworkAgent.LoaderId} loaderId
545 * @param {string} url
546 * @param {string} documentURL
547 * @param {!NetworkAgent.Initiator} initiator
549 _createNetworkRequest: function(requestId, frameId, loaderId, url, documentURL, initiator)
551 var networkRequest = new WebInspector.NetworkRequest(requestId, url, documentURL, frameId, loaderId);
552 networkRequest.initiator = initiator;
553 return networkRequest;
558 * @type {!WebInspector.NetworkManager}
560 WebInspector.networkManager;