blob: 73f341b7e35792e335b82d636e93dc4b02eefba6 [file] [log] [blame]
/*
* Copyright (C) 2011 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* @constructor
* @extends {WebInspector.Object}
*/
WebInspector.NetworkManager = function()
{
WebInspector.Object.call(this);
this._dispatcher = new WebInspector.NetworkDispatcher(this);
if (WebInspector.settings.cacheDisabled.get())
NetworkAgent.setCacheDisabled(true);
NetworkAgent.enable();
WebInspector.settings.cacheDisabled.addChangeListener(this._cacheDisabledSettingChanged, this);
}
WebInspector.NetworkManager.EventTypes = {
RequestStarted: "RequestStarted",
RequestUpdated: "RequestUpdated",
RequestFinished: "RequestFinished",
RequestUpdateDropped: "RequestUpdateDropped"
}
WebInspector.NetworkManager._MIMETypes = {
"text/html": {"document": true},
"text/xml": {"document": true},
"text/plain": {"document": true},
"application/xhtml+xml": {"document": true},
"text/css": {"stylesheet": true},
"text/xsl": {"stylesheet": true},
"image/jpg": {"image": true},
"image/jpeg": {"image": true},
"image/pjpeg": {"image": true},
"image/png": {"image": true},
"image/gif": {"image": true},
"image/bmp": {"image": true},
"image/svg+xml": {"image": true, "font": true, "document": true},
"image/vnd.microsoft.icon": {"image": true},
"image/webp": {"image": true},
"image/x-icon": {"image": true},
"image/x-xbitmap": {"image": true},
"font/ttf": {"font": true},
"font/otf": {"font": true},
"font/woff": {"font": true},
"font/woff2": {"font": true},
"font/truetype": {"font": true},
"font/opentype": {"font": true},
"application/octet-stream": {"font": true, "image": true},
"application/font-woff": {"font": true},
"application/x-font-woff": {"font": true},
"application/x-font-type1": {"font": true},
"application/x-font-ttf": {"font": true},
"application/x-truetype-font": {"font": true},
"text/javascript": {"script": true},
"text/ecmascript": {"script": true},
"application/javascript": {"script": true},
"application/ecmascript": {"script": true},
"application/x-javascript": {"script": true},
"application/json": {"script": true},
"text/javascript1.1": {"script": true},
"text/javascript1.2": {"script": true},
"text/javascript1.3": {"script": true},
"text/jscript": {"script": true},
"text/livescript": {"script": true},
}
WebInspector.NetworkManager.prototype = {
/**
* @param {string} url
* @return {WebInspector.NetworkRequest}
*/
inflightRequestForURL: function(url)
{
return this._dispatcher._inflightRequestsByURL[url];
},
/**
* @param {WebInspector.Event} event
*/
_cacheDisabledSettingChanged: function(event)
{
var enabled = /** @type {boolean} */ (event.data);
NetworkAgent.setCacheDisabled(enabled);
},
__proto__: WebInspector.Object.prototype
}
/**
* @constructor
* @implements {NetworkAgent.Dispatcher}
*/
WebInspector.NetworkDispatcher = function(manager)
{
this._manager = manager;
this._inflightRequestsById = {};
this._inflightRequestsByURL = {};
InspectorBackend.registerNetworkDispatcher(this);
}
WebInspector.NetworkDispatcher.prototype = {
/**
* @param {NetworkAgent.Headers} headersMap
* @return {!Array.<!WebInspector.NetworkRequest.NameValue>}
*/
_headersMapToHeadersArray: function(headersMap)
{
var result = [];
for (var name in headersMap) {
var values = headersMap[name].split("\n");
for (var i = 0; i < values.length; ++i)
result.push({name: name, value: values[i]});
}
return result;
},
/**
* @param {WebInspector.NetworkRequest} networkRequest
* @param {NetworkAgent.Request} request
*/
_updateNetworkRequestWithRequest: function(networkRequest, request)
{
networkRequest.requestMethod = request.method;
networkRequest.requestHeaders = this._headersMapToHeadersArray(request.headers);
networkRequest.requestFormData = request.postData;
},
/**
* @param {WebInspector.NetworkRequest} networkRequest
* @param {NetworkAgent.Response=} response
*/
_updateNetworkRequestWithResponse: function(networkRequest, response)
{
if (!response)
return;
if (response.url && networkRequest.url !== response.url)
networkRequest.url = response.url;
networkRequest.mimeType = response.mimeType;
networkRequest.statusCode = response.status;
networkRequest.statusText = response.statusText;
networkRequest.responseHeaders = this._headersMapToHeadersArray(response.headers);
if (response.headersText)
networkRequest.responseHeadersText = response.headersText;
if (response.requestHeaders)
networkRequest.requestHeaders = this._headersMapToHeadersArray(response.requestHeaders);
if (response.requestHeadersText)
networkRequest.requestHeadersText = response.requestHeadersText;
networkRequest.connectionReused = response.connectionReused;
networkRequest.connectionId = response.connectionId;
if (response.fromDiskCache)
networkRequest.cached = true;
else
networkRequest.timing = response.timing;
if (!this._mimeTypeIsConsistentWithType(networkRequest)) {
WebInspector.console.addMessage(WebInspector.ConsoleMessage.create(WebInspector.ConsoleMessage.MessageSource.Network,
WebInspector.ConsoleMessage.MessageLevel.Log,
WebInspector.UIString("Resource interpreted as %s but transferred with MIME type %s: \"%s\".", networkRequest.type.title(), networkRequest.mimeType, networkRequest.url),
WebInspector.ConsoleMessage.MessageType.Log,
"",
0,
0,
1,
[],
null,
networkRequest.requestId));
}
},
/**
* @param {WebInspector.NetworkRequest} networkRequest
* @return {boolean}
*/
_mimeTypeIsConsistentWithType: function(networkRequest)
{
// If status is an error, content is likely to be of an inconsistent type,
// as it's going to be an error message. We do not want to emit a warning
// for this, though, as this will already be reported as resource loading failure.
// Also, if a URL like http://localhost/wiki/load.php?debug=true&lang=en produces text/css and gets reloaded,
// it is 304 Not Modified and its guessed mime-type is text/php, which is wrong.
// Don't check for mime-types in 304-resources.
if (networkRequest.hasErrorStatusCode() || networkRequest.statusCode === 304 || networkRequest.statusCode === 204)
return true;
if (typeof networkRequest.type === "undefined"
|| networkRequest.type === WebInspector.resourceTypes.Other
|| networkRequest.type === WebInspector.resourceTypes.XHR
|| networkRequest.type === WebInspector.resourceTypes.WebSocket)
return true;
if (!networkRequest.mimeType)
return true; // Might be not known for cached resources with null responses.
if (networkRequest.mimeType in WebInspector.NetworkManager._MIMETypes)
return networkRequest.type.name() in WebInspector.NetworkManager._MIMETypes[networkRequest.mimeType];
return false;
},
/**
* @param {NetworkAgent.Response} response
* @return {boolean}
*/
_isNull: function(response)
{
if (!response)
return true;
return !response.status && !response.mimeType && (!response.headers || !Object.keys(response.headers).length);
},
/**
* @param {NetworkAgent.RequestId} requestId
* @param {PageAgent.FrameId} frameId
* @param {NetworkAgent.LoaderId} loaderId
* @param {string} documentURL
* @param {NetworkAgent.Request} request
* @param {NetworkAgent.Timestamp} time
* @param {NetworkAgent.Initiator} initiator
* @param {NetworkAgent.Response=} redirectResponse
*/
requestWillBeSent: function(requestId, frameId, loaderId, documentURL, request, time, initiator, redirectResponse)
{
var networkRequest = this._inflightRequestsById[requestId];
if (networkRequest) {
// FIXME: move this check to the backend.
if (!redirectResponse)
return;
this.responseReceived(requestId, frameId, loaderId, time, PageAgent.ResourceType.Other, redirectResponse);
networkRequest = this._appendRedirect(requestId, time, request.url);
} else
networkRequest = this._createNetworkRequest(requestId, frameId, loaderId, request.url, documentURL, initiator);
networkRequest.hasNetworkData = true;
this._updateNetworkRequestWithRequest(networkRequest, request);
networkRequest.startTime = time;
this._startNetworkRequest(networkRequest);
},
/**
* @param {NetworkAgent.RequestId} requestId
*/
requestServedFromCache: function(requestId)
{
var networkRequest = this._inflightRequestsById[requestId];
if (!networkRequest)
return;
networkRequest.cached = true;
},
/**
* @param {NetworkAgent.RequestId} requestId
* @param {PageAgent.FrameId} frameId
* @param {NetworkAgent.LoaderId} loaderId
* @param {NetworkAgent.Timestamp} time
* @param {PageAgent.ResourceType} resourceType
* @param {NetworkAgent.Response} response
*/
responseReceived: function(requestId, frameId, loaderId, time, resourceType, response)
{
// FIXME: move this check to the backend.
if (this._isNull(response))
return;
var networkRequest = this._inflightRequestsById[requestId];
if (!networkRequest) {
// We missed the requestWillBeSent.
var eventData = {};
eventData.url = response.url;
eventData.frameId = frameId;
eventData.loaderId = loaderId;
eventData.resourceType = resourceType;
eventData.mimeType = response.mimeType;
this._manager.dispatchEventToListeners(WebInspector.NetworkManager.EventTypes.RequestUpdateDropped, eventData);
return;
}
networkRequest.responseReceivedTime = time;
networkRequest.type = WebInspector.resourceTypes[resourceType];
this._updateNetworkRequestWithResponse(networkRequest, response);
this._updateNetworkRequest(networkRequest);
},
/**
* @param {NetworkAgent.RequestId} requestId
* @param {NetworkAgent.Timestamp} time
* @param {number} dataLength
* @param {number} encodedDataLength
*/
dataReceived: function(requestId, time, dataLength, encodedDataLength)
{
var networkRequest = this._inflightRequestsById[requestId];
if (!networkRequest)
return;
networkRequest.resourceSize += dataLength;
if (encodedDataLength != -1)
networkRequest.increaseTransferSize(encodedDataLength);
networkRequest.endTime = time;
this._updateNetworkRequest(networkRequest);
},
/**
* @param {NetworkAgent.RequestId} requestId
* @param {NetworkAgent.Timestamp} finishTime
*/
loadingFinished: function(requestId, finishTime)
{
var networkRequest = this._inflightRequestsById[requestId];
if (!networkRequest)
return;
this._finishNetworkRequest(networkRequest, finishTime);
},
/**
* @param {NetworkAgent.RequestId} requestId
* @param {NetworkAgent.Timestamp} time
* @param {string} localizedDescription
* @param {boolean=} canceled
*/
loadingFailed: function(requestId, time, localizedDescription, canceled)
{
var networkRequest = this._inflightRequestsById[requestId];
if (!networkRequest)
return;
networkRequest.failed = true;
networkRequest.canceled = canceled;
networkRequest.localizedFailDescription = localizedDescription;
this._finishNetworkRequest(networkRequest, time);
},
/**
* @param {NetworkAgent.RequestId} requestId
* @param {string} requestURL
*/
webSocketCreated: function(requestId, requestURL)
{
var networkRequest = new WebInspector.NetworkRequest(requestId, requestURL, "", "", "");
networkRequest.type = WebInspector.resourceTypes.WebSocket;
this._startNetworkRequest(networkRequest);
},
/**
* @param {NetworkAgent.RequestId} requestId
* @param {NetworkAgent.Timestamp} time
* @param {NetworkAgent.WebSocketRequest} request
*/
webSocketWillSendHandshakeRequest: function(requestId, time, request)
{
var networkRequest = this._inflightRequestsById[requestId];
if (!networkRequest)
return;
networkRequest.requestMethod = "GET";
networkRequest.requestHeaders = this._headersMapToHeadersArray(request.headers);
networkRequest.startTime = time;
this._updateNetworkRequest(networkRequest);
},
/**
* @param {NetworkAgent.RequestId} requestId
* @param {NetworkAgent.Timestamp} time
* @param {NetworkAgent.WebSocketResponse} response
*/
webSocketHandshakeResponseReceived: function(requestId, time, response)
{
var networkRequest = this._inflightRequestsById[requestId];
if (!networkRequest)
return;
networkRequest.statusCode = response.status;
networkRequest.statusText = response.statusText;
networkRequest.responseHeaders = this._headersMapToHeadersArray(response.headers);
networkRequest.responseReceivedTime = time;
this._updateNetworkRequest(networkRequest);
},
/**
* @param {NetworkAgent.RequestId} requestId
* @param {NetworkAgent.Timestamp} time
* @param {NetworkAgent.WebSocketFrame} response
*/
webSocketFrameReceived: function(requestId, time, response)
{
var networkRequest = this._inflightRequestsById[requestId];
if (!networkRequest)
return;
networkRequest.addFrame(response, time);
networkRequest.responseReceivedTime = time;
this._updateNetworkRequest(networkRequest);
},
/**
* @param {NetworkAgent.RequestId} requestId
* @param {NetworkAgent.Timestamp} time
* @param {NetworkAgent.WebSocketFrame} response
*/
webSocketFrameSent: function(requestId, time, response)
{
var networkRequest = this._inflightRequestsById[requestId];
if (!networkRequest)
return;
networkRequest.addFrame(response, time, true);
networkRequest.responseReceivedTime = time;
this._updateNetworkRequest(networkRequest);
},
/**
* @param {NetworkAgent.RequestId} requestId
* @param {NetworkAgent.Timestamp} time
* @param {string} errorMessage
*/
webSocketFrameError: function(requestId, time, errorMessage)
{
var networkRequest = this._inflightRequestsById[requestId];
if (!networkRequest)
return;
networkRequest.addFrameError(errorMessage, time);
networkRequest.responseReceivedTime = time;
this._updateNetworkRequest(networkRequest);
},
/**
* @param {NetworkAgent.RequestId} requestId
* @param {NetworkAgent.Timestamp} time
*/
webSocketClosed: function(requestId, time)
{
var networkRequest = this._inflightRequestsById[requestId];
if (!networkRequest)
return;
this._finishNetworkRequest(networkRequest, time);
},
/**
* @param {NetworkAgent.RequestId} requestId
* @param {NetworkAgent.Timestamp} time
* @param {string} redirectURL
* @return {WebInspector.NetworkRequest}
*/
_appendRedirect: function(requestId, time, redirectURL)
{
var originalNetworkRequest = this._inflightRequestsById[requestId];
var previousRedirects = originalNetworkRequest.redirects || [];
originalNetworkRequest.requestId = "redirected:" + requestId + "." + previousRedirects.length;
delete originalNetworkRequest.redirects;
if (previousRedirects.length > 0)
originalNetworkRequest.redirectSource = previousRedirects[previousRedirects.length - 1];
this._finishNetworkRequest(originalNetworkRequest, time);
var newNetworkRequest = this._createNetworkRequest(requestId, originalNetworkRequest.frameId, originalNetworkRequest.loaderId,
redirectURL, originalNetworkRequest.documentURL, originalNetworkRequest.initiator);
newNetworkRequest.redirects = previousRedirects.concat(originalNetworkRequest);
return newNetworkRequest;
},
/**
* @param {WebInspector.NetworkRequest} networkRequest
*/
_startNetworkRequest: function(networkRequest)
{
this._inflightRequestsById[networkRequest.requestId] = networkRequest;
this._inflightRequestsByURL[networkRequest.url] = networkRequest;
this._dispatchEventToListeners(WebInspector.NetworkManager.EventTypes.RequestStarted, networkRequest);
},
/**
* @param {WebInspector.NetworkRequest} networkRequest
*/
_updateNetworkRequest: function(networkRequest)
{
this._dispatchEventToListeners(WebInspector.NetworkManager.EventTypes.RequestUpdated, networkRequest);
},
/**
* @param {WebInspector.NetworkRequest} networkRequest
* @param {NetworkAgent.Timestamp} finishTime
*/
_finishNetworkRequest: function(networkRequest, finishTime)
{
networkRequest.endTime = finishTime;
networkRequest.finished = true;
this._dispatchEventToListeners(WebInspector.NetworkManager.EventTypes.RequestFinished, networkRequest);
delete this._inflightRequestsById[networkRequest.requestId];
delete this._inflightRequestsByURL[networkRequest.url];
},
/**
* @param {string} eventType
* @param {WebInspector.NetworkRequest} networkRequest
*/
_dispatchEventToListeners: function(eventType, networkRequest)
{
this._manager.dispatchEventToListeners(eventType, networkRequest);
},
/**
* @param {NetworkAgent.RequestId} requestId
* @param {string} frameId
* @param {NetworkAgent.LoaderId} loaderId
* @param {string} url
* @param {string} documentURL
* @param {NetworkAgent.Initiator} initiator
*/
_createNetworkRequest: function(requestId, frameId, loaderId, url, documentURL, initiator)
{
var networkRequest = new WebInspector.NetworkRequest(requestId, url, documentURL, frameId, loaderId);
networkRequest.initiator = initiator;
return networkRequest;
}
}
/**
* @type {?WebInspector.NetworkManager}
*/
WebInspector.networkManager = null;