Home | History | Annotate | Download | only in front_end
      1 /*
      2  * Copyright (C) 2011 Google Inc. All rights reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions are
      6  * met:
      7  *
      8  *     * Redistributions of source code must retain the above copyright
      9  * notice, this list of conditions and the following disclaimer.
     10  *     * Redistributions in binary form must reproduce the above
     11  * copyright notice, this list of conditions and the following disclaimer
     12  * in the documentation and/or other materials provided with the
     13  * distribution.
     14  *     * Neither the name of Google Inc. nor the names of its
     15  * contributors may be used to endorse or promote products derived from
     16  * this software without specific prior written permission.
     17  *
     18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     29  */
     30 
     31 /**
     32  * @constructor
     33  * @extends {WebInspector.Object}
     34  */
     35 WebInspector.NetworkManager = function()
     36 {
     37     WebInspector.Object.call(this);
     38     this._dispatcher = new WebInspector.NetworkDispatcher(this);
     39     if (WebInspector.settings.cacheDisabled.get())
     40         NetworkAgent.setCacheDisabled(true);
     41 
     42     NetworkAgent.enable();
     43 
     44     WebInspector.settings.cacheDisabled.addChangeListener(this._cacheDisabledSettingChanged, this);
     45 }
     46 
     47 WebInspector.NetworkManager.EventTypes = {
     48     RequestStarted: "RequestStarted",
     49     RequestUpdated: "RequestUpdated",
     50     RequestFinished: "RequestFinished",
     51     RequestUpdateDropped: "RequestUpdateDropped"
     52 }
     53 
     54 WebInspector.NetworkManager._MIMETypes = {
     55     "text/html":                   {"document": true},
     56     "text/xml":                    {"document": true},
     57     "text/plain":                  {"document": true},
     58     "application/xhtml+xml":       {"document": true},
     59     "text/css":                    {"stylesheet": true},
     60     "text/xsl":                    {"stylesheet": true},
     61     "image/jpg":                   {"image": true},
     62     "image/jpeg":                  {"image": true},
     63     "image/pjpeg":                 {"image": true},
     64     "image/png":                   {"image": true},
     65     "image/gif":                   {"image": true},
     66     "image/bmp":                   {"image": true},
     67     "image/svg+xml":               {"image": true, "font": true, "document": true},
     68     "image/vnd.microsoft.icon":    {"image": true},
     69     "image/webp":                  {"image": true},
     70     "image/x-icon":                {"image": true},
     71     "image/x-xbitmap":             {"image": true},
     72     "font/ttf":                    {"font": true},
     73     "font/otf":                    {"font": true},
     74     "font/woff":                   {"font": true},
     75     "font/woff2":                  {"font": true},
     76     "font/truetype":               {"font": true},
     77     "font/opentype":               {"font": true},
     78     "application/octet-stream":    {"font": true, "image": true},
     79     "application/font-woff":       {"font": true},
     80     "application/x-font-woff":     {"font": true},
     81     "application/x-font-type1":    {"font": true},
     82     "application/x-font-ttf":      {"font": true},
     83     "application/x-truetype-font": {"font": true},
     84     "text/javascript":             {"script": true},
     85     "text/ecmascript":             {"script": true},
     86     "application/javascript":      {"script": true},
     87     "application/ecmascript":      {"script": true},
     88     "application/x-javascript":    {"script": true},
     89     "application/json":            {"script": true},
     90     "text/javascript1.1":          {"script": true},
     91     "text/javascript1.2":          {"script": true},
     92     "text/javascript1.3":          {"script": true},
     93     "text/jscript":                {"script": true},
     94     "text/livescript":             {"script": true},
     95 }
     96 
     97 WebInspector.NetworkManager.prototype = {
     98     /**
     99      * @param {string} url
    100      * @return {!WebInspector.NetworkRequest}
    101      */
    102     inflightRequestForURL: function(url)
    103     {
    104         return this._dispatcher._inflightRequestsByURL[url];
    105     },
    106 
    107     /**
    108      * @param {!WebInspector.Event} event
    109      */
    110     _cacheDisabledSettingChanged: function(event)
    111     {
    112         var enabled = /** @type {boolean} */ (event.data);
    113         NetworkAgent.setCacheDisabled(enabled);
    114     },
    115 
    116     __proto__: WebInspector.Object.prototype
    117 }
    118 
    119 /**
    120  * @constructor
    121  * @implements {NetworkAgent.Dispatcher}
    122  */
    123 WebInspector.NetworkDispatcher = function(manager)
    124 {
    125     this._manager = manager;
    126     this._inflightRequestsById = {};
    127     this._inflightRequestsByURL = {};
    128     InspectorBackend.registerNetworkDispatcher(this);
    129 }
    130 
    131 WebInspector.NetworkDispatcher.prototype = {
    132     /**
    133      * @param {!NetworkAgent.Headers} headersMap
    134      * @return {!Array.<!WebInspector.NetworkRequest.NameValue>}
    135      */
    136     _headersMapToHeadersArray: function(headersMap)
    137     {
    138         var result = [];
    139         for (var name in headersMap) {
    140             var values = headersMap[name].split("\n");
    141             for (var i = 0; i < values.length; ++i)
    142                 result.push({name: name, value: values[i]});
    143         }
    144         return result;
    145     },
    146 
    147     /**
    148      * @param {!WebInspector.NetworkRequest} networkRequest
    149      * @param {!NetworkAgent.Request} request
    150      */
    151     _updateNetworkRequestWithRequest: function(networkRequest, request)
    152     {
    153         networkRequest.requestMethod = request.method;
    154         networkRequest.setRequestHeaders(this._headersMapToHeadersArray(request.headers));
    155         networkRequest.requestFormData = request.postData;
    156     },
    157 
    158     /**
    159      * @param {!WebInspector.NetworkRequest} networkRequest
    160      * @param {!NetworkAgent.Response=} response
    161      */
    162     _updateNetworkRequestWithResponse: function(networkRequest, response)
    163     {
    164         if (!response)
    165             return;
    166 
    167         if (response.url && networkRequest.url !== response.url)
    168             networkRequest.url = response.url;
    169         networkRequest.mimeType = response.mimeType;
    170         networkRequest.statusCode = response.status;
    171         networkRequest.statusText = response.statusText;
    172         networkRequest.responseHeaders = this._headersMapToHeadersArray(response.headers);
    173         if (response.headersText)
    174             networkRequest.responseHeadersText = response.headersText;
    175         if (response.requestHeaders) {
    176             networkRequest.setRequestHeaders(this._headersMapToHeadersArray(response.requestHeaders));
    177             networkRequest.setRequestHeadersText(response.requestHeadersText || "");
    178         }
    179 
    180         networkRequest.connectionReused = response.connectionReused;
    181         networkRequest.connectionId = response.connectionId;
    182 
    183         if (response.fromDiskCache)
    184             networkRequest.cached = true;
    185         else
    186             networkRequest.timing = response.timing;
    187 
    188         if (!this._mimeTypeIsConsistentWithType(networkRequest)) {
    189             WebInspector.console.addMessage(WebInspector.ConsoleMessage.create(WebInspector.ConsoleMessage.MessageSource.Network,
    190                 WebInspector.ConsoleMessage.MessageLevel.Log,
    191                 WebInspector.UIString("Resource interpreted as %s but transferred with MIME type %s: \"%s\".", networkRequest.type.title(), networkRequest.mimeType, networkRequest.url),
    192                 WebInspector.ConsoleMessage.MessageType.Log,
    193                 "",
    194                 0,
    195                 0,
    196                 1,
    197                 [],
    198                 undefined,
    199                 networkRequest.requestId));
    200         }
    201     },
    202 
    203     /**
    204      * @param {!WebInspector.NetworkRequest} networkRequest
    205      * @return {boolean}
    206      */
    207     _mimeTypeIsConsistentWithType: function(networkRequest)
    208     {
    209         // If status is an error, content is likely to be of an inconsistent type,
    210         // as it's going to be an error message. We do not want to emit a warning
    211         // for this, though, as this will already be reported as resource loading failure.
    212         // Also, if a URL like http://localhost/wiki/load.php?debug=true&lang=en produces text/css and gets reloaded,
    213         // it is 304 Not Modified and its guessed mime-type is text/php, which is wrong.
    214         // Don't check for mime-types in 304-resources.
    215         if (networkRequest.hasErrorStatusCode() || networkRequest.statusCode === 304 || networkRequest.statusCode === 204)
    216             return true;
    217 
    218         if (typeof networkRequest.type === "undefined"
    219             || networkRequest.type === WebInspector.resourceTypes.Other
    220             || networkRequest.type === WebInspector.resourceTypes.XHR
    221             || networkRequest.type === WebInspector.resourceTypes.WebSocket)
    222             return true;
    223 
    224         if (!networkRequest.mimeType)
    225             return true; // Might be not known for cached resources with null responses.
    226 
    227         if (networkRequest.mimeType in WebInspector.NetworkManager._MIMETypes)
    228             return networkRequest.type.name() in WebInspector.NetworkManager._MIMETypes[networkRequest.mimeType];
    229 
    230         return false;
    231     },
    232 
    233     /**
    234      * @param {!NetworkAgent.Response} response
    235      * @return {boolean}
    236      */
    237     _isNull: function(response)
    238     {
    239         if (!response)
    240             return true;
    241         return !response.status && !response.mimeType && (!response.headers || !Object.keys(response.headers).length);
    242     },
    243 
    244     /**
    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
    253      */
    254     requestWillBeSent: function(requestId, frameId, loaderId, documentURL, request, time, initiator, redirectResponse)
    255     {
    256         var networkRequest = this._inflightRequestsById[requestId];
    257         if (networkRequest) {
    258             // FIXME: move this check to the backend.
    259             if (!redirectResponse)
    260                 return;
    261             this.responseReceived(requestId, frameId, loaderId, time, PageAgent.ResourceType.Other, redirectResponse);
    262             networkRequest = this._appendRedirect(requestId, time, request.url);
    263         } else
    264             networkRequest = this._createNetworkRequest(requestId, frameId, loaderId, request.url, documentURL, initiator);
    265         networkRequest.hasNetworkData = true;
    266         this._updateNetworkRequestWithRequest(networkRequest, request);
    267         networkRequest.startTime = time;
    268 
    269         this._startNetworkRequest(networkRequest);
    270     },
    271 
    272     /**
    273      * @param {!NetworkAgent.RequestId} requestId
    274      */
    275     requestServedFromCache: function(requestId)
    276     {
    277         var networkRequest = this._inflightRequestsById[requestId];
    278         if (!networkRequest)
    279             return;
    280 
    281         networkRequest.cached = true;
    282     },
    283 
    284     /**
    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
    291      */
    292     responseReceived: function(requestId, frameId, loaderId, time, resourceType, response)
    293     {
    294         // FIXME: move this check to the backend.
    295         if (this._isNull(response))
    296             return;
    297 
    298         var networkRequest = this._inflightRequestsById[requestId];
    299         if (!networkRequest) {
    300             // We missed the requestWillBeSent.
    301             var eventData = {};
    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);
    308             return;
    309         }
    310 
    311         networkRequest.responseReceivedTime = time;
    312         networkRequest.type = WebInspector.resourceTypes[resourceType];
    313 
    314         this._updateNetworkRequestWithResponse(networkRequest, response);
    315 
    316         this._updateNetworkRequest(networkRequest);
    317     },
    318 
    319     /**
    320      * @param {!NetworkAgent.RequestId} requestId
    321      * @param {!NetworkAgent.Timestamp} time
    322      * @param {number} dataLength
    323      * @param {number} encodedDataLength
    324      */
    325     dataReceived: function(requestId, time, dataLength, encodedDataLength)
    326     {
    327         var networkRequest = this._inflightRequestsById[requestId];
    328         if (!networkRequest)
    329             return;
    330 
    331         networkRequest.resourceSize += dataLength;
    332         if (encodedDataLength != -1)
    333             networkRequest.increaseTransferSize(encodedDataLength);
    334         networkRequest.endTime = time;
    335 
    336         this._updateNetworkRequest(networkRequest);
    337     },
    338 
    339     /**
    340      * @param {!NetworkAgent.RequestId} requestId
    341      * @param {!NetworkAgent.Timestamp} finishTime
    342      */
    343     loadingFinished: function(requestId, finishTime)
    344     {
    345         var networkRequest = this._inflightRequestsById[requestId];
    346         if (!networkRequest)
    347             return;
    348         this._finishNetworkRequest(networkRequest, finishTime);
    349     },
    350 
    351     /**
    352      * @param {!NetworkAgent.RequestId} requestId
    353      * @param {!NetworkAgent.Timestamp} time
    354      * @param {string} localizedDescription
    355      * @param {boolean=} canceled
    356      */
    357     loadingFailed: function(requestId, time, localizedDescription, canceled)
    358     {
    359         var networkRequest = this._inflightRequestsById[requestId];
    360         if (!networkRequest)
    361             return;
    362 
    363         networkRequest.failed = true;
    364         networkRequest.canceled = canceled;
    365         networkRequest.localizedFailDescription = localizedDescription;
    366         this._finishNetworkRequest(networkRequest, time);
    367     },
    368 
    369     /**
    370      * @param {!NetworkAgent.RequestId} requestId
    371      * @param {string} requestURL
    372      */
    373     webSocketCreated: function(requestId, requestURL)
    374     {
    375         var networkRequest = new WebInspector.NetworkRequest(requestId, requestURL, "", "", "");
    376         networkRequest.type = WebInspector.resourceTypes.WebSocket;
    377         this._startNetworkRequest(networkRequest);
    378     },
    379 
    380     /**
    381      * @param {!NetworkAgent.RequestId} requestId
    382      * @param {!NetworkAgent.Timestamp} time
    383      * @param {!NetworkAgent.WebSocketRequest} request
    384      */
    385     webSocketWillSendHandshakeRequest: function(requestId, time, request)
    386     {
    387         var networkRequest = this._inflightRequestsById[requestId];
    388         if (!networkRequest)
    389             return;
    390 
    391         networkRequest.requestMethod = "GET";
    392         networkRequest.setRequestHeaders(this._headersMapToHeadersArray(request.headers));
    393         networkRequest.startTime = time;
    394 
    395         this._updateNetworkRequest(networkRequest);
    396     },
    397 
    398     /**
    399      * @param {!NetworkAgent.RequestId} requestId
    400      * @param {!NetworkAgent.Timestamp} time
    401      * @param {!NetworkAgent.WebSocketResponse} response
    402      */
    403     webSocketHandshakeResponseReceived: function(requestId, time, response)
    404     {
    405         var networkRequest = this._inflightRequestsById[requestId];
    406         if (!networkRequest)
    407             return;
    408 
    409         networkRequest.statusCode = response.status;
    410         networkRequest.statusText = response.statusText;
    411         networkRequest.responseHeaders = this._headersMapToHeadersArray(response.headers);
    412         networkRequest.responseReceivedTime = time;
    413 
    414         this._updateNetworkRequest(networkRequest);
    415     },
    416 
    417     /**
    418      * @param {!NetworkAgent.RequestId} requestId
    419      * @param {!NetworkAgent.Timestamp} time
    420      * @param {!NetworkAgent.WebSocketFrame} response
    421      */
    422     webSocketFrameReceived: function(requestId, time, response)
    423     {
    424         var networkRequest = this._inflightRequestsById[requestId];
    425         if (!networkRequest)
    426             return;
    427 
    428         networkRequest.addFrame(response, time);
    429         networkRequest.responseReceivedTime = time;
    430 
    431         this._updateNetworkRequest(networkRequest);
    432     },
    433 
    434     /**
    435      * @param {!NetworkAgent.RequestId} requestId
    436      * @param {!NetworkAgent.Timestamp} time
    437      * @param {!NetworkAgent.WebSocketFrame} response
    438      */
    439     webSocketFrameSent: function(requestId, time, response)
    440     {
    441         var networkRequest = this._inflightRequestsById[requestId];
    442         if (!networkRequest)
    443             return;
    444 
    445         networkRequest.addFrame(response, time, true);
    446         networkRequest.responseReceivedTime = time;
    447 
    448         this._updateNetworkRequest(networkRequest);
    449     },
    450 
    451     /**
    452      * @param {!NetworkAgent.RequestId} requestId
    453      * @param {!NetworkAgent.Timestamp} time
    454      * @param {string} errorMessage
    455      */
    456     webSocketFrameError: function(requestId, time, errorMessage)
    457     {
    458         var networkRequest = this._inflightRequestsById[requestId];
    459         if (!networkRequest)
    460             return;
    461 
    462         networkRequest.addFrameError(errorMessage, time);
    463         networkRequest.responseReceivedTime = time;
    464 
    465         this._updateNetworkRequest(networkRequest);
    466     },
    467 
    468     /**
    469      * @param {!NetworkAgent.RequestId} requestId
    470      * @param {!NetworkAgent.Timestamp} time
    471      */
    472     webSocketClosed: function(requestId, time)
    473     {
    474         var networkRequest = this._inflightRequestsById[requestId];
    475         if (!networkRequest)
    476             return;
    477         this._finishNetworkRequest(networkRequest, time);
    478     },
    479 
    480     /**
    481      * @param {!NetworkAgent.RequestId} requestId
    482      * @param {!NetworkAgent.Timestamp} time
    483      * @param {string} redirectURL
    484      * @return {!WebInspector.NetworkRequest}
    485      */
    486     _appendRedirect: function(requestId, time, redirectURL)
    487     {
    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;
    499     },
    500 
    501     /**
    502      * @param {!WebInspector.NetworkRequest} networkRequest
    503      */
    504     _startNetworkRequest: function(networkRequest)
    505     {
    506         this._inflightRequestsById[networkRequest.requestId] = networkRequest;
    507         this._inflightRequestsByURL[networkRequest.url] = networkRequest;
    508         this._dispatchEventToListeners(WebInspector.NetworkManager.EventTypes.RequestStarted, networkRequest);
    509     },
    510 
    511     /**
    512      * @param {!WebInspector.NetworkRequest} networkRequest
    513      */
    514     _updateNetworkRequest: function(networkRequest)
    515     {
    516         this._dispatchEventToListeners(WebInspector.NetworkManager.EventTypes.RequestUpdated, networkRequest);
    517     },
    518 
    519     /**
    520      * @param {!WebInspector.NetworkRequest} networkRequest
    521      * @param {!NetworkAgent.Timestamp} finishTime
    522      */
    523     _finishNetworkRequest: function(networkRequest, finishTime)
    524     {
    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];
    530     },
    531 
    532     /**
    533      * @param {string} eventType
    534      * @param {!WebInspector.NetworkRequest} networkRequest
    535      */
    536     _dispatchEventToListeners: function(eventType, networkRequest)
    537     {
    538         this._manager.dispatchEventToListeners(eventType, networkRequest);
    539     },
    540 
    541     /**
    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
    548      */
    549     _createNetworkRequest: function(requestId, frameId, loaderId, url, documentURL, initiator)
    550     {
    551         var networkRequest = new WebInspector.NetworkRequest(requestId, url, documentURL, frameId, loaderId);
    552         networkRequest.initiator = initiator;
    553         return networkRequest;
    554     }
    555 }
    556 
    557 /**
    558  * @type {!WebInspector.NetworkManager}
    559  */
    560 WebInspector.networkManager;
    561