blob: 96d50d9aac17b24559113b51d9feb553dd91bd15 [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.
*/
/**
* @unrestricted
*/
SDK.NetworkManager = class extends SDK.SDKModel {
/**
* @param {!SDK.Target} target
*/
constructor(target) {
super(target);
this._dispatcher = new SDK.NetworkDispatcher(this);
this._networkAgent = target.networkAgent();
target.registerNetworkDispatcher(this._dispatcher);
if (Common.moduleSetting('cacheDisabled').get())
this._networkAgent.setCacheDisabled(true);
// Limit buffer when talking to a remote device.
if (Runtime.queryParam('remoteFrontend') || Runtime.queryParam('ws'))
this._networkAgent.enable(10000000, 5000000);
else
this._networkAgent.enable();
this._bypassServiceWorkerSetting = Common.settings.createSetting('bypassServiceWorker', false);
if (this._bypassServiceWorkerSetting.get())
this._bypassServiceWorkerChanged();
this._bypassServiceWorkerSetting.addChangeListener(this._bypassServiceWorkerChanged, this);
Common.moduleSetting('cacheDisabled').addChangeListener(this._cacheDisabledSettingChanged, this);
}
/**
* @param {!SDK.NetworkRequest} request
* @return {?SDK.NetworkManager}
*/
static forRequest(request) {
return request[SDK.NetworkManager._networkManagerForRequestSymbol];
}
/**
* @param {!SDK.NetworkRequest} request
* @return {boolean}
*/
static canReplayRequest(request) {
return !!request[SDK.NetworkManager._networkManagerForRequestSymbol] &&
request.resourceType() === Common.resourceTypes.XHR;
}
/**
* @param {!SDK.NetworkRequest} request
*/
static replayRequest(request) {
var manager = request[SDK.NetworkManager._networkManagerForRequestSymbol];
if (!manager)
return;
manager._networkAgent.replayXHR(request.requestId());
}
/**
* @param {!SDK.NetworkRequest} request
* @return {!Promise<!SDK.NetworkRequest.ContentData>}
*/
static async requestContentData(request) {
if (request.resourceType() === Common.resourceTypes.WebSocket)
return {error: 'Content for WebSockets is currently not supported', content: null, encoded: false};
if (!request.finished)
await request.once(SDK.NetworkRequest.Events.FinishedLoading);
var manager = SDK.NetworkManager.forRequest(request);
if (!manager)
return {error: 'No network manager for request', content: null, encoded: false};
var response = await manager._networkAgent.invoke_getResponseBody({requestId: request.requestId()});
var error = response[Protocol.Error] || null;
return {error: error, content: error ? null : response.body, encoded: response.base64Encoded};
}
/**
* @param {!SDK.NetworkManager.Conditions} conditions
* @return {!Protocol.Network.ConnectionType}
* TODO(allada): this belongs to NetworkConditionsSelector, which should hardcode/guess it.
*/
static _connectionType(conditions) {
if (!conditions.download && !conditions.upload)
return Protocol.Network.ConnectionType.None;
var types = SDK.NetworkManager._connectionTypes;
if (!types) {
SDK.NetworkManager._connectionTypes = [];
types = SDK.NetworkManager._connectionTypes;
types.push(['2g', Protocol.Network.ConnectionType.Cellular2g]);
types.push(['3g', Protocol.Network.ConnectionType.Cellular3g]);
types.push(['4g', Protocol.Network.ConnectionType.Cellular4g]);
types.push(['bluetooth', Protocol.Network.ConnectionType.Bluetooth]);
types.push(['wifi', Protocol.Network.ConnectionType.Wifi]);
types.push(['wimax', Protocol.Network.ConnectionType.Wimax]);
}
for (var type of types) {
if (conditions.title.toLowerCase().indexOf(type[0]) !== -1)
return type[1];
}
return Protocol.Network.ConnectionType.Other;
}
/**
* @param {string} url
* @return {!SDK.NetworkRequest}
*/
inflightRequestForURL(url) {
return this._dispatcher._inflightRequestsByURL[url];
}
/**
* @param {!Common.Event} event
*/
_cacheDisabledSettingChanged(event) {
var enabled = /** @type {boolean} */ (event.data);
this._networkAgent.setCacheDisabled(enabled);
}
/**
* @override
*/
dispose() {
Common.moduleSetting('cacheDisabled').removeChangeListener(this._cacheDisabledSettingChanged, this);
}
_bypassServiceWorkerChanged() {
this._networkAgent.setBypassServiceWorker(this._bypassServiceWorkerSetting.get());
}
};
SDK.SDKModel.register(SDK.NetworkManager, SDK.Target.Capability.Network, true);
/** @enum {symbol} */
SDK.NetworkManager.Events = {
RequestStarted: Symbol('RequestStarted'),
RequestUpdated: Symbol('RequestUpdated'),
RequestFinished: Symbol('RequestFinished'),
RequestUpdateDropped: Symbol('RequestUpdateDropped'),
ResponseReceived: Symbol('ResponseReceived'),
MessageGenerated: Symbol('MessageGenerated'),
RequestRedirected: Symbol('RequestRedirected'),
};
/** @typedef {{message: string, requestId: string, warning: boolean}} */
SDK.NetworkManager.Message;
SDK.NetworkManager._MIMETypes = {
'text/html': {'document': true},
'text/xml': {'document': true},
'text/plain': {'document': true},
'application/xhtml+xml': {'document': true},
'image/svg+xml': {'document': true},
'text/css': {'stylesheet': true},
'text/xsl': {'stylesheet': true},
'text/vtt': {'texttrack': true},
};
/** @typedef {{download: number, upload: number, latency: number, title: string}} */
SDK.NetworkManager.Conditions;
/** @type {!SDK.NetworkManager.Conditions} */
SDK.NetworkManager.NoThrottlingConditions = {
title: Common.UIString('No throttling'),
download: -1,
upload: -1,
latency: 0
};
/** @type {!SDK.NetworkManager.Conditions} */
SDK.NetworkManager.OfflineConditions = {
title: Common.UIString('Offline'),
download: 0,
upload: 0,
latency: 0
};
/** @typedef {{url: string, enabled: boolean}} */
SDK.NetworkManager.BlockedPattern;
SDK.NetworkManager._networkManagerForRequestSymbol = Symbol('NetworkManager');
/**
* @implements {Protocol.NetworkDispatcher}
* @unrestricted
*/
SDK.NetworkDispatcher = class {
/**
* @param {!SDK.NetworkManager} manager
*/
constructor(manager) {
this._manager = manager;
/** @type {!Object<!Protocol.Network.RequestId, !SDK.NetworkRequest>} */
this._inflightRequestsById = {};
/** @type {!Object<string, !SDK.NetworkRequest>} */
this._inflightRequestsByURL = {};
}
/**
* @param {!Protocol.Network.Headers} headersMap
* @return {!Array.<!SDK.NetworkRequest.NameValue>}
*/
_headersMapToHeadersArray(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 {!SDK.NetworkRequest} networkRequest
* @param {!Protocol.Network.Request} request
*/
_updateNetworkRequestWithRequest(networkRequest, request) {
networkRequest.requestMethod = request.method;
networkRequest.setRequestHeaders(this._headersMapToHeadersArray(request.headers));
networkRequest.requestFormData = request.postData;
networkRequest.setInitialPriority(request.initialPriority);
networkRequest.mixedContentType = request.mixedContentType || Protocol.Network.RequestMixedContentType.None;
networkRequest.setReferrerPolicy(request.referrerPolicy);
}
/**
* @param {!SDK.NetworkRequest} networkRequest
* @param {!Protocol.Network.Response=} response
*/
_updateNetworkRequestWithResponse(networkRequest, response) {
if (response.url && networkRequest.url() !== response.url)
networkRequest.setUrl(response.url);
networkRequest.mimeType = response.mimeType;
networkRequest.statusCode = response.status;
networkRequest.statusText = response.statusText;
networkRequest.responseHeaders = this._headersMapToHeadersArray(response.headers);
if (response.encodedDataLength >= 0)
networkRequest.setTransferSize(response.encodedDataLength);
if (response.headersText)
networkRequest.responseHeadersText = response.headersText;
if (response.requestHeaders) {
networkRequest.setRequestHeaders(this._headersMapToHeadersArray(response.requestHeaders));
networkRequest.setRequestHeadersText(response.requestHeadersText || '');
}
networkRequest.connectionReused = response.connectionReused;
networkRequest.connectionId = String(response.connectionId);
if (response.remoteIPAddress)
networkRequest.setRemoteAddress(response.remoteIPAddress, response.remotePort || -1);
if (response.fromServiceWorker)
networkRequest.fetchedViaServiceWorker = true;
if (response.fromDiskCache)
networkRequest.setFromDiskCache();
networkRequest.timing = response.timing;
networkRequest.protocol = response.protocol;
networkRequest.setSecurityState(response.securityState);
if (!this._mimeTypeIsConsistentWithType(networkRequest)) {
var message = Common.UIString(
'Resource interpreted as %s but transferred with MIME type %s: "%s".', networkRequest.resourceType().title(),
networkRequest.mimeType, networkRequest.url());
this._manager.dispatchEventToListeners(
SDK.NetworkManager.Events.MessageGenerated,
{message: message, requestId: networkRequest.requestId(), warning: true});
}
if (response.securityDetails)
networkRequest.setSecurityDetails(response.securityDetails);
}
/**
* @param {!SDK.NetworkRequest} networkRequest
* @return {boolean}
*/
_mimeTypeIsConsistentWithType(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;
var resourceType = networkRequest.resourceType();
if (resourceType !== Common.resourceTypes.Stylesheet && resourceType !== Common.resourceTypes.Document &&
resourceType !== Common.resourceTypes.TextTrack)
return true;
if (!networkRequest.mimeType)
return true; // Might be not known for cached resources with null responses.
if (networkRequest.mimeType in SDK.NetworkManager._MIMETypes)
return resourceType.name() in SDK.NetworkManager._MIMETypes[networkRequest.mimeType];
return false;
}
/**
* @override
* @param {!Protocol.Network.RequestId} requestId
* @param {!Protocol.Network.ResourcePriority} newPriority
* @param {!Protocol.Network.Timestamp} timestamp
*/
resourceChangedPriority(requestId, newPriority, timestamp) {
var networkRequest = this._inflightRequestsById[requestId];
if (networkRequest)
networkRequest.setPriority(newPriority);
}
/**
* @override
* @param {!Protocol.Network.RequestId} requestId
* @param {!Protocol.Network.LoaderId} loaderId
* @param {string} documentURL
* @param {!Protocol.Network.Request} request
* @param {!Protocol.Network.Timestamp} time
* @param {!Protocol.Network.Timestamp} wallTime
* @param {!Protocol.Network.Initiator} initiator
* @param {!Protocol.Network.Response=} redirectResponse
* @param {!Protocol.Page.ResourceType=} resourceType
* @param {!Protocol.Page.FrameId=} frameId
*/
requestWillBeSent(
requestId, loaderId, documentURL, request, time, wallTime, initiator, redirectResponse, resourceType, frameId) {
var networkRequest = this._inflightRequestsById[requestId];
if (networkRequest) {
// FIXME: move this check to the backend.
if (!redirectResponse)
return;
this.responseReceived(requestId, loaderId, time, Protocol.Page.ResourceType.Other, redirectResponse, frameId);
networkRequest = this._appendRedirect(requestId, time, request.url);
this._manager.dispatchEventToListeners(SDK.NetworkManager.Events.RequestRedirected, networkRequest);
} else {
networkRequest =
this._createNetworkRequest(requestId, frameId || '', loaderId, request.url, documentURL, initiator);
}
networkRequest.hasNetworkData = true;
this._updateNetworkRequestWithRequest(networkRequest, request);
networkRequest.setIssueTime(time, wallTime);
networkRequest.setResourceType(
resourceType ? Common.resourceTypes[resourceType] : Protocol.Page.ResourceType.Other);
this._startNetworkRequest(networkRequest);
}
/**
* @override
* @param {!Protocol.Network.RequestId} requestId
*/
requestServedFromCache(requestId) {
var networkRequest = this._inflightRequestsById[requestId];
if (!networkRequest)
return;
networkRequest.setFromMemoryCache();
}
/**
* @override
* @param {!Protocol.Network.RequestId} requestId
* @param {!Protocol.Network.LoaderId} loaderId
* @param {!Protocol.Network.Timestamp} time
* @param {!Protocol.Page.ResourceType} resourceType
* @param {!Protocol.Network.Response} response
* @param {!Protocol.Page.FrameId=} frameId
*/
responseReceived(requestId, loaderId, time, resourceType, response, frameId) {
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;
var lastModifiedHeader = response.headers['last-modified'];
eventData.lastModified = lastModifiedHeader ? new Date(lastModifiedHeader) : null;
this._manager.dispatchEventToListeners(SDK.NetworkManager.Events.RequestUpdateDropped, eventData);
return;
}
networkRequest.responseReceivedTime = time;
networkRequest.setResourceType(Common.resourceTypes[resourceType]);
// net::ParsedCookie::kMaxCookieSize = 4096 (net/cookies/parsed_cookie.h)
if ('Set-Cookie' in response.headers && response.headers['Set-Cookie'].length > 4096) {
var message = Common.UIString(
'Set-Cookie header is ignored in response from url: %s. Cookie length should be less than or equal to 4096 characters.',
response.url);
this._manager.dispatchEventToListeners(
SDK.NetworkManager.Events.MessageGenerated, {message: message, requestId: requestId, warning: true});
}
this._updateNetworkRequestWithResponse(networkRequest, response);
this._updateNetworkRequest(networkRequest);
this._manager.dispatchEventToListeners(SDK.NetworkManager.Events.ResponseReceived, networkRequest);
}
/**
* @override
* @param {!Protocol.Network.RequestId} requestId
* @param {!Protocol.Network.Timestamp} time
* @param {number} dataLength
* @param {number} encodedDataLength
*/
dataReceived(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);
}
/**
* @override
* @param {!Protocol.Network.RequestId} requestId
* @param {!Protocol.Network.Timestamp} finishTime
* @param {number} encodedDataLength
*/
loadingFinished(requestId, finishTime, encodedDataLength) {
var networkRequest = this._inflightRequestsById[requestId];
if (!networkRequest)
return;
this._finishNetworkRequest(networkRequest, finishTime, encodedDataLength);
}
/**
* @override
* @param {!Protocol.Network.RequestId} requestId
* @param {!Protocol.Network.Timestamp} time
* @param {!Protocol.Page.ResourceType} resourceType
* @param {string} localizedDescription
* @param {boolean=} canceled
* @param {!Protocol.Network.BlockedReason=} blockedReason
*/
loadingFailed(requestId, time, resourceType, localizedDescription, canceled, blockedReason) {
var networkRequest = this._inflightRequestsById[requestId];
if (!networkRequest)
return;
networkRequest.failed = true;
networkRequest.setResourceType(Common.resourceTypes[resourceType]);
networkRequest.canceled = !!canceled;
if (blockedReason) {
networkRequest.setBlockedReason(blockedReason);
if (blockedReason === Protocol.Network.BlockedReason.Inspector) {
var message = Common.UIString('Request was blocked by DevTools: "%s".', networkRequest.url());
this._manager.dispatchEventToListeners(
SDK.NetworkManager.Events.MessageGenerated, {message: message, requestId: requestId, warning: true});
}
}
networkRequest.localizedFailDescription = localizedDescription;
this._finishNetworkRequest(networkRequest, time, -1);
}
/**
* @override
* @param {!Protocol.Network.RequestId} requestId
* @param {string} requestURL
* @param {!Protocol.Network.Initiator=} initiator
*/
webSocketCreated(requestId, requestURL, initiator) {
var networkRequest = new SDK.NetworkRequest(requestId, requestURL, '', '', '', initiator || null);
networkRequest[SDK.NetworkManager._networkManagerForRequestSymbol] = this._manager;
networkRequest.setResourceType(Common.resourceTypes.WebSocket);
this._startNetworkRequest(networkRequest);
}
/**
* @override
* @param {!Protocol.Network.RequestId} requestId
* @param {!Protocol.Network.Timestamp} time
* @param {!Protocol.Network.Timestamp} wallTime
* @param {!Protocol.Network.WebSocketRequest} request
*/
webSocketWillSendHandshakeRequest(requestId, time, wallTime, request) {
var networkRequest = this._inflightRequestsById[requestId];
if (!networkRequest)
return;
networkRequest.requestMethod = 'GET';
networkRequest.setRequestHeaders(this._headersMapToHeadersArray(request.headers));
networkRequest.setIssueTime(time, wallTime);
this._updateNetworkRequest(networkRequest);
}
/**
* @override
* @param {!Protocol.Network.RequestId} requestId
* @param {!Protocol.Network.Timestamp} time
* @param {!Protocol.Network.WebSocketResponse} response
*/
webSocketHandshakeResponseReceived(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.responseHeadersText = response.headersText || '';
if (response.requestHeaders)
networkRequest.setRequestHeaders(this._headersMapToHeadersArray(response.requestHeaders));
if (response.requestHeadersText)
networkRequest.setRequestHeadersText(response.requestHeadersText);
networkRequest.responseReceivedTime = time;
networkRequest.protocol = 'websocket';
this._updateNetworkRequest(networkRequest);
}
/**
* @override
* @param {!Protocol.Network.RequestId} requestId
* @param {!Protocol.Network.Timestamp} time
* @param {!Protocol.Network.WebSocketFrame} response
*/
webSocketFrameReceived(requestId, time, response) {
var networkRequest = this._inflightRequestsById[requestId];
if (!networkRequest)
return;
networkRequest.addFrame(response, time, false);
networkRequest.responseReceivedTime = time;
this._updateNetworkRequest(networkRequest);
}
/**
* @override
* @param {!Protocol.Network.RequestId} requestId
* @param {!Protocol.Network.Timestamp} time
* @param {!Protocol.Network.WebSocketFrame} response
*/
webSocketFrameSent(requestId, time, response) {
var networkRequest = this._inflightRequestsById[requestId];
if (!networkRequest)
return;
networkRequest.addFrame(response, time, true);
networkRequest.responseReceivedTime = time;
this._updateNetworkRequest(networkRequest);
}
/**
* @override
* @param {!Protocol.Network.RequestId} requestId
* @param {!Protocol.Network.Timestamp} time
* @param {string} errorMessage
*/
webSocketFrameError(requestId, time, errorMessage) {
var networkRequest = this._inflightRequestsById[requestId];
if (!networkRequest)
return;
networkRequest.addFrameError(errorMessage, time);
networkRequest.responseReceivedTime = time;
this._updateNetworkRequest(networkRequest);
}
/**
* @override
* @param {!Protocol.Network.RequestId} requestId
* @param {!Protocol.Network.Timestamp} time
*/
webSocketClosed(requestId, time) {
var networkRequest = this._inflightRequestsById[requestId];
if (!networkRequest)
return;
this._finishNetworkRequest(networkRequest, time, -1);
}
/**
* @override
* @param {!Protocol.Network.RequestId} requestId
* @param {!Protocol.Network.Timestamp} time
* @param {string} eventName
* @param {string} eventId
* @param {string} data
*/
eventSourceMessageReceived(requestId, time, eventName, eventId, data) {
var networkRequest = this._inflightRequestsById[requestId];
if (!networkRequest)
return;
networkRequest.addEventSourceMessage(time, eventName, eventId, data);
}
/**
* @override
* @param {!Protocol.Network.RequestId} requestId
* @param {!Protocol.Network.Request} request
* @param {string} resourceType
* @param {!Protocol.Network.Headers=} redirectHeaders
* @param {number=} redirectStatusCode
* @param {string=} redirectUrl
*/
requestIntercepted(requestId, request, resourceType, redirectHeaders, redirectStatusCode, redirectUrl) {
// Stub implementation. Event not currently used by the frontend.
}
/**
* @param {!Protocol.Network.RequestId} requestId
* @param {!Protocol.Network.Timestamp} time
* @param {string} redirectURL
* @return {!SDK.NetworkRequest}
*/
_appendRedirect(requestId, time, redirectURL) {
var originalNetworkRequest = this._inflightRequestsById[requestId];
var redirectCount = 0;
for (var redirect = originalNetworkRequest.redirectSource(); redirect; redirect = redirect.redirectSource())
redirectCount++;
originalNetworkRequest.setRequestId(requestId + ':redirected.' + redirectCount);
this._finishNetworkRequest(originalNetworkRequest, time, -1);
var newNetworkRequest = this._createNetworkRequest(
requestId, originalNetworkRequest.frameId, originalNetworkRequest.loaderId, redirectURL,
originalNetworkRequest.documentURL, originalNetworkRequest.initiator());
newNetworkRequest.setRedirectSource(originalNetworkRequest);
return newNetworkRequest;
}
/**
* @param {!SDK.NetworkRequest} networkRequest
*/
_startNetworkRequest(networkRequest) {
this._inflightRequestsById[networkRequest.requestId()] = networkRequest;
this._inflightRequestsByURL[networkRequest.url()] = networkRequest;
this._manager.dispatchEventToListeners(SDK.NetworkManager.Events.RequestStarted, networkRequest);
}
/**
* @param {!SDK.NetworkRequest} networkRequest
*/
_updateNetworkRequest(networkRequest) {
this._manager.dispatchEventToListeners(SDK.NetworkManager.Events.RequestUpdated, networkRequest);
}
/**
* @param {!SDK.NetworkRequest} networkRequest
* @param {!Protocol.Network.Timestamp} finishTime
* @param {number} encodedDataLength
*/
_finishNetworkRequest(networkRequest, finishTime, encodedDataLength) {
networkRequest.endTime = finishTime;
networkRequest.finished = true;
if (encodedDataLength >= 0)
networkRequest.setTransferSize(encodedDataLength);
this._manager.dispatchEventToListeners(SDK.NetworkManager.Events.RequestFinished, networkRequest);
delete this._inflightRequestsById[networkRequest.requestId()];
delete this._inflightRequestsByURL[networkRequest.url()];
if (Common.moduleSetting('monitoringXHREnabled').get() &&
networkRequest.resourceType().category() === Common.resourceCategories.XHR) {
var message = Common.UIString(
(networkRequest.failed || networkRequest.hasErrorStatusCode()) ? '%s failed loading: %s "%s".' :
'%s finished loading: %s "%s".',
networkRequest.resourceType().title(), networkRequest.requestMethod, networkRequest.url());
this._manager.dispatchEventToListeners(
SDK.NetworkManager.Events.MessageGenerated,
{message: message, requestId: networkRequest.requestId(), warning: false});
}
}
/**
* @param {!Protocol.Network.RequestId} requestId
* @param {string} frameId
* @param {!Protocol.Network.LoaderId} loaderId
* @param {string} url
* @param {string} documentURL
* @param {?Protocol.Network.Initiator} initiator
*/
_createNetworkRequest(requestId, frameId, loaderId, url, documentURL, initiator) {
var request = new SDK.NetworkRequest(requestId, url, documentURL, frameId, loaderId, initiator);
request[SDK.NetworkManager._networkManagerForRequestSymbol] = this._manager;
return request;
}
};
/**
* @implements {SDK.TargetManager.Observer}
* @unrestricted
*/
SDK.MultitargetNetworkManager = class extends Common.Object {
constructor() {
super();
this._userAgentOverride = '';
/** @type {!Set<!Protocol.NetworkAgent>} */
this._agents = new Set();
/** @type {!SDK.NetworkManager.Conditions} */
this._networkConditions = SDK.NetworkManager.NoThrottlingConditions;
this._blockingEnabledSetting = Common.moduleSetting('requestBlockingEnabled');
this._blockedPatternsSetting = Common.settings.createSetting('networkBlockedPatterns', []);
this._effectiveBlockedURLs = [];
this._updateBlockedPatterns();
SDK.targetManager.observeTargets(this, SDK.Target.Capability.Network);
}
/**
* @param {string} uaString
* @return {string}
*/
static patchUserAgentWithChromeVersion(uaString) {
// Patches Chrome/CriOS version from user agent ("1.2.3.4" when user agent is: "Chrome/1.2.3.4").
var chromeRegex = new RegExp('(?:^|\\W)Chrome/(\\S+)');
var chromeMatch = navigator.userAgent.match(chromeRegex);
if (chromeMatch && chromeMatch.length > 1)
return String.sprintf(uaString, chromeMatch[1]);
return uaString;
}
/**
* @override
* @param {!SDK.Target} target
*/
targetAdded(target) {
var networkAgent = target.networkAgent();
if (this._extraHeaders)
networkAgent.setExtraHTTPHeaders(this._extraHeaders);
if (this._currentUserAgent())
networkAgent.setUserAgentOverride(this._currentUserAgent());
if (this._effectiveBlockedURLs.length)
networkAgent.setBlockedURLs(this._effectiveBlockedURLs);
this._agents.add(networkAgent);
if (this.isThrottling())
this._updateNetworkConditions(networkAgent);
}
/**
* @override
* @param {!SDK.Target} target
*/
targetRemoved(target) {
this._agents.delete(target.networkAgent());
}
/**
* @return {boolean}
*/
isThrottling() {
return this._networkConditions.download >= 0 || this._networkConditions.upload >= 0 ||
this._networkConditions.latency > 0;
}
/**
* @return {boolean}
*/
isOffline() {
return !this._networkConditions.download && !this._networkConditions.upload;
}
/**
* @param {!SDK.NetworkManager.Conditions} conditions
*/
setNetworkConditions(conditions) {
this._networkConditions = conditions;
for (var agent of this._agents)
this._updateNetworkConditions(agent);
this.dispatchEventToListeners(SDK.MultitargetNetworkManager.Events.ConditionsChanged);
}
/**
* @return {!SDK.NetworkManager.Conditions}
*/
networkConditions() {
return this._networkConditions;
}
/**
* @param {!Protocol.NetworkAgent} networkAgent
*/
_updateNetworkConditions(networkAgent) {
var conditions = this._networkConditions;
if (!this.isThrottling()) {
networkAgent.emulateNetworkConditions(false, 0, 0, 0);
} else {
networkAgent.emulateNetworkConditions(
this.isOffline(), conditions.latency, conditions.download < 0 ? 0 : conditions.download,
conditions.upload < 0 ? 0 : conditions.upload, SDK.NetworkManager._connectionType(conditions));
}
}
/**
* @param {!Protocol.Network.Headers} headers
*/
setExtraHTTPHeaders(headers) {
this._extraHeaders = headers;
for (var agent of this._agents)
agent.setExtraHTTPHeaders(this._extraHeaders);
}
/**
* @return {string}
*/
_currentUserAgent() {
return this._customUserAgent ? this._customUserAgent : this._userAgentOverride;
}
_updateUserAgentOverride() {
var userAgent = this._currentUserAgent();
for (var agent of this._agents)
agent.setUserAgentOverride(userAgent);
}
/**
* @param {string} userAgent
*/
setUserAgentOverride(userAgent) {
if (this._userAgentOverride === userAgent)
return;
this._userAgentOverride = userAgent;
if (!this._customUserAgent)
this._updateUserAgentOverride();
this.dispatchEventToListeners(SDK.MultitargetNetworkManager.Events.UserAgentChanged);
}
/**
* @return {string}
*/
userAgentOverride() {
return this._userAgentOverride;
}
/**
* @param {string} userAgent
*/
setCustomUserAgentOverride(userAgent) {
this._customUserAgent = userAgent;
this._updateUserAgentOverride();
}
/**
* @return {!Array<!SDK.NetworkManager.BlockedPattern>}
*/
blockedPatterns() {
return this._blockedPatternsSetting.get().slice();
}
/**
* @return {boolean}
*/
blockingEnabled() {
return this._blockingEnabledSetting.get();
}
/**
* @return {boolean}
*/
isBlocking() {
return !!this._effectiveBlockedURLs.length;
}
/**
* @param {!Array<!SDK.NetworkManager.BlockedPattern>} patterns
*/
setBlockedPatterns(patterns) {
this._blockedPatternsSetting.set(patterns);
this._updateBlockedPatterns();
this.dispatchEventToListeners(SDK.MultitargetNetworkManager.Events.BlockedPatternsChanged);
}
/**
* @param {boolean} enabled
*/
setBlockingEnabled(enabled) {
if (this._blockingEnabledSetting.get() === enabled)
return;
this._blockingEnabledSetting.set(enabled);
this._updateBlockedPatterns();
this.dispatchEventToListeners(SDK.MultitargetNetworkManager.Events.BlockedPatternsChanged);
}
_updateBlockedPatterns() {
var urls = [];
if (this._blockingEnabledSetting.get()) {
for (var pattern of this._blockedPatternsSetting.get()) {
if (pattern.enabled)
urls.push(pattern.url);
}
}
if (!urls.length && !this._effectiveBlockedURLs.length)
return;
this._effectiveBlockedURLs = urls;
for (var agent of this._agents)
agent.setBlockedURLs(this._effectiveBlockedURLs);
}
clearBrowserCache() {
for (var agent of this._agents)
agent.clearBrowserCache();
}
clearBrowserCookies() {
for (var agent of this._agents)
agent.clearBrowserCookies();
}
/**
* @param {string} origin
* @return {!Promise<!Array<string>>}
*/
getCertificate(origin) {
var target = SDK.targetManager.mainTarget();
return target.networkAgent().getCertificate(origin).then(certificate => certificate || []);
}
/**
* @param {string} url
* @param {function(number, !Object.<string, string>, string)} callback
*/
loadResource(url, callback) {
var headers = {};
var currentUserAgent = this._currentUserAgent();
if (currentUserAgent)
headers['User-Agent'] = currentUserAgent;
if (Common.moduleSetting('cacheDisabled').get())
headers['Cache-Control'] = 'no-cache';
Host.ResourceLoader.load(url, headers, callback);
}
};
/** @enum {symbol} */
SDK.MultitargetNetworkManager.Events = {
BlockedPatternsChanged: Symbol('BlockedPatternsChanged'),
ConditionsChanged: Symbol('ConditionsChanged'),
UserAgentChanged: Symbol('UserAgentChanged')
};
/**
* @type {!SDK.MultitargetNetworkManager}
*/
SDK.multitargetNetworkManager;