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