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.requestHeaders = 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.requestHeaders = this._headersMapToHeadersArray(response.requestHeaders);
    177         if (response.requestHeadersText)
    178             networkRequest.requestHeadersText = response.requestHeadersText;
    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                 null,
    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 {WebInspector.NetworkRequest} networkRequest
    235      * @param {?NetworkAgent.CachedResource} cachedResource
    236      */
    237     _updateNetworkRequestWithCachedResource: function(networkRequest, cachedResource)
    238     {
    239         networkRequest.type = WebInspector.resourceTypes[cachedResource.type];
    240         networkRequest.resourceSize = cachedResource.bodySize;
    241         this._updateNetworkRequestWithResponse(networkRequest, cachedResource.response);
    242     },
    243 
    244     /**
    245      * @param {NetworkAgent.Response} response
    246      * @return {boolean}
    247      */
    248     _isNull: function(response)
    249     {
    250         if (!response)
    251             return true;
    252         return !response.status && !response.mimeType && (!response.headers || !Object.keys(response.headers).length);
    253     },
    254 
    255     /**
    256      * @param {NetworkAgent.RequestId} requestId
    257      * @param {NetworkAgent.FrameId} frameId
    258      * @param {NetworkAgent.LoaderId} loaderId
    259      * @param {string} documentURL
    260      * @param {NetworkAgent.Request} request
    261      * @param {NetworkAgent.Timestamp} time
    262      * @param {NetworkAgent.Initiator} initiator
    263      * @param {NetworkAgent.Response=} redirectResponse
    264      */
    265     requestWillBeSent: function(requestId, frameId, loaderId, documentURL, request, time, initiator, redirectResponse)
    266     {
    267         var networkRequest = this._inflightRequestsById[requestId];
    268         if (networkRequest) {
    269             // FIXME: move this check to the backend.
    270             if (!redirectResponse)
    271                 return;
    272             this.responseReceived(requestId, frameId, loaderId, time, PageAgent.ResourceType.Other, redirectResponse);
    273             networkRequest = this._appendRedirect(requestId, time, request.url);
    274         } else
    275             networkRequest = this._createNetworkRequest(requestId, frameId, loaderId, request.url, documentURL, initiator);
    276         networkRequest.hasNetworkData = true;
    277         this._updateNetworkRequestWithRequest(networkRequest, request);
    278         networkRequest.startTime = time;
    279 
    280         this._startNetworkRequest(networkRequest);
    281     },
    282 
    283     /**
    284      * @param {NetworkAgent.RequestId} requestId
    285      */
    286     requestServedFromCache: function(requestId)
    287     {
    288         var networkRequest = this._inflightRequestsById[requestId];
    289         if (!networkRequest)
    290             return;
    291 
    292         networkRequest.cached = true;
    293     },
    294 
    295     /**
    296      * @param {NetworkAgent.RequestId} requestId
    297      * @param {NetworkAgent.FrameId} frameId
    298      * @param {NetworkAgent.LoaderId} loaderId
    299      * @param {NetworkAgent.Timestamp} time
    300      * @param {PageAgent.ResourceType} resourceType
    301      * @param {NetworkAgent.Response} response
    302      */
    303     responseReceived: function(requestId, frameId, loaderId, time, resourceType, response)
    304     {
    305         // FIXME: move this check to the backend.
    306         if (this._isNull(response))
    307             return;
    308 
    309         var networkRequest = this._inflightRequestsById[requestId];
    310         if (!networkRequest) {
    311             // We missed the requestWillBeSent.
    312             var eventData = {};
    313             eventData.url = response.url;
    314             eventData.frameId = frameId;
    315             eventData.loaderId = loaderId;
    316             eventData.resourceType = resourceType;
    317             eventData.mimeType = response.mimeType;
    318             this._manager.dispatchEventToListeners(WebInspector.NetworkManager.EventTypes.RequestUpdateDropped, eventData);
    319             return;
    320         }
    321 
    322         networkRequest.responseReceivedTime = time;
    323         networkRequest.type = WebInspector.resourceTypes[resourceType];
    324 
    325         this._updateNetworkRequestWithResponse(networkRequest, response);
    326 
    327         this._updateNetworkRequest(networkRequest);
    328     },
    329 
    330     /**
    331      * @param {NetworkAgent.RequestId} requestId
    332      * @param {NetworkAgent.Timestamp} time
    333      * @param {number} dataLength
    334      * @param {number} encodedDataLength
    335      */
    336     dataReceived: function(requestId, time, dataLength, encodedDataLength)
    337     {
    338         var networkRequest = this._inflightRequestsById[requestId];
    339         if (!networkRequest)
    340             return;
    341 
    342         networkRequest.resourceSize += dataLength;
    343         if (encodedDataLength != -1)
    344             networkRequest.increaseTransferSize(encodedDataLength);
    345         networkRequest.endTime = time;
    346 
    347         this._updateNetworkRequest(networkRequest);
    348     },
    349 
    350     /**
    351      * @param {NetworkAgent.RequestId} requestId
    352      * @param {NetworkAgent.Timestamp} finishTime
    353      */
    354     loadingFinished: function(requestId, finishTime)
    355     {
    356         var networkRequest = this._inflightRequestsById[requestId];
    357         if (!networkRequest)
    358             return;
    359         this._finishNetworkRequest(networkRequest, finishTime);
    360     },
    361 
    362     /**
    363      * @param {NetworkAgent.RequestId} requestId
    364      * @param {NetworkAgent.Timestamp} time
    365      * @param {string} localizedDescription
    366      * @param {boolean=} canceled
    367      */
    368     loadingFailed: function(requestId, time, localizedDescription, canceled)
    369     {
    370         var networkRequest = this._inflightRequestsById[requestId];
    371         if (!networkRequest)
    372             return;
    373 
    374         networkRequest.failed = true;
    375         networkRequest.canceled = canceled;
    376         networkRequest.localizedFailDescription = localizedDescription;
    377         this._finishNetworkRequest(networkRequest, time);
    378     },
    379 
    380     /**
    381      * @param {NetworkAgent.RequestId} requestId
    382      * @param {NetworkAgent.FrameId} frameId
    383      * @param {NetworkAgent.LoaderId} loaderId
    384      * @param {string} documentURL
    385      * @param {NetworkAgent.Timestamp} time
    386      * @param {NetworkAgent.Initiator} initiator
    387      * @param {NetworkAgent.CachedResource} cachedResource
    388      */
    389     requestServedFromMemoryCache: function(requestId, frameId, loaderId, documentURL, time, initiator, cachedResource)
    390     {
    391         var networkRequest = this._createNetworkRequest(requestId, frameId, loaderId, cachedResource.url, documentURL, initiator);
    392         this._updateNetworkRequestWithCachedResource(networkRequest, cachedResource);
    393         networkRequest.cached = true;
    394         networkRequest.requestMethod = "GET";
    395         this._startNetworkRequest(networkRequest);
    396         networkRequest.startTime = networkRequest.responseReceivedTime = time;
    397         this._finishNetworkRequest(networkRequest, time);
    398     },
    399 
    400     /**
    401      * @param {NetworkAgent.RequestId} requestId
    402      * @param {string} requestURL
    403      */
    404     webSocketCreated: function(requestId, requestURL)
    405     {
    406         var networkRequest = new WebInspector.NetworkRequest(requestId, requestURL, "", "", "");
    407         networkRequest.type = WebInspector.resourceTypes.WebSocket;
    408         this._startNetworkRequest(networkRequest);
    409     },
    410 
    411     /**
    412      * @param {NetworkAgent.RequestId} requestId
    413      * @param {NetworkAgent.Timestamp} time
    414      * @param {NetworkAgent.WebSocketRequest} request
    415      */
    416     webSocketWillSendHandshakeRequest: function(requestId, time, request)
    417     {
    418         var networkRequest = this._inflightRequestsById[requestId];
    419         if (!networkRequest)
    420             return;
    421 
    422         networkRequest.requestMethod = "GET";
    423         networkRequest.requestHeaders = this._headersMapToHeadersArray(request.headers);
    424         networkRequest.startTime = time;
    425 
    426         this._updateNetworkRequest(networkRequest);
    427     },
    428 
    429     /**
    430      * @param {NetworkAgent.RequestId} requestId
    431      * @param {NetworkAgent.Timestamp} time
    432      * @param {NetworkAgent.WebSocketResponse} response
    433      */
    434     webSocketHandshakeResponseReceived: function(requestId, time, response)
    435     {
    436         var networkRequest = this._inflightRequestsById[requestId];
    437         if (!networkRequest)
    438             return;
    439 
    440         networkRequest.statusCode = response.status;
    441         networkRequest.statusText = response.statusText;
    442         networkRequest.responseHeaders = this._headersMapToHeadersArray(response.headers);
    443         networkRequest.responseReceivedTime = time;
    444 
    445         this._updateNetworkRequest(networkRequest);
    446     },
    447 
    448     /**
    449      * @param {NetworkAgent.RequestId} requestId
    450      * @param {NetworkAgent.Timestamp} time
    451      * @param {NetworkAgent.WebSocketFrame} response
    452      */
    453     webSocketFrameReceived: function(requestId, time, response)
    454     {
    455         var networkRequest = this._inflightRequestsById[requestId];
    456         if (!networkRequest)
    457             return;
    458 
    459         networkRequest.addFrame(response, time);
    460         networkRequest.responseReceivedTime = time;
    461 
    462         this._updateNetworkRequest(networkRequest);
    463     },
    464 
    465     /**
    466      * @param {NetworkAgent.RequestId} requestId
    467      * @param {NetworkAgent.Timestamp} time
    468      * @param {NetworkAgent.WebSocketFrame} response
    469      */
    470     webSocketFrameSent: function(requestId, time, response)
    471     {
    472         var networkRequest = this._inflightRequestsById[requestId];
    473         if (!networkRequest)
    474             return;
    475 
    476         networkRequest.addFrame(response, time, true);
    477         networkRequest.responseReceivedTime = time;
    478 
    479         this._updateNetworkRequest(networkRequest);
    480     },
    481 
    482     /**
    483      * @param {NetworkAgent.RequestId} requestId
    484      * @param {NetworkAgent.Timestamp} time
    485      * @param {string} errorMessage
    486      */
    487     webSocketFrameError: function(requestId, time, errorMessage)
    488     {
    489         var networkRequest = this._inflightRequestsById[requestId];
    490         if (!networkRequest)
    491             return;
    492 
    493         networkRequest.addFrameError(errorMessage, time);
    494         networkRequest.responseReceivedTime = time;
    495 
    496         this._updateNetworkRequest(networkRequest);
    497     },
    498 
    499     /**
    500      * @param {NetworkAgent.RequestId} requestId
    501      * @param {NetworkAgent.Timestamp} time
    502      */
    503     webSocketClosed: function(requestId, time)
    504     {
    505         var networkRequest = this._inflightRequestsById[requestId];
    506         if (!networkRequest)
    507             return;
    508         this._finishNetworkRequest(networkRequest, time);
    509     },
    510 
    511     /**
    512      * @param {NetworkAgent.RequestId} requestId
    513      * @param {NetworkAgent.Timestamp} time
    514      * @param {string} redirectURL
    515      * @return {WebInspector.NetworkRequest}
    516      */
    517     _appendRedirect: function(requestId, time, redirectURL)
    518     {
    519         var originalNetworkRequest = this._inflightRequestsById[requestId];
    520         var previousRedirects = originalNetworkRequest.redirects || [];
    521         originalNetworkRequest.requestId = "redirected:" + requestId + "." + previousRedirects.length;
    522         delete originalNetworkRequest.redirects;
    523         if (previousRedirects.length > 0)
    524             originalNetworkRequest.redirectSource = previousRedirects[previousRedirects.length - 1];
    525         this._finishNetworkRequest(originalNetworkRequest, time);
    526         var newNetworkRequest = this._createNetworkRequest(requestId, originalNetworkRequest.frameId, originalNetworkRequest.loaderId,
    527              redirectURL, originalNetworkRequest.documentURL, originalNetworkRequest.initiator);
    528         newNetworkRequest.redirects = previousRedirects.concat(originalNetworkRequest);
    529         return newNetworkRequest;
    530     },
    531 
    532     /**
    533      * @param {WebInspector.NetworkRequest} networkRequest
    534      */
    535     _startNetworkRequest: function(networkRequest)
    536     {
    537         this._inflightRequestsById[networkRequest.requestId] = networkRequest;
    538         this._inflightRequestsByURL[networkRequest.url] = networkRequest;
    539         this._dispatchEventToListeners(WebInspector.NetworkManager.EventTypes.RequestStarted, networkRequest);
    540     },
    541 
    542     /**
    543      * @param {WebInspector.NetworkRequest} networkRequest
    544      */
    545     _updateNetworkRequest: function(networkRequest)
    546     {
    547         this._dispatchEventToListeners(WebInspector.NetworkManager.EventTypes.RequestUpdated, networkRequest);
    548     },
    549 
    550     /**
    551      * @param {WebInspector.NetworkRequest} networkRequest
    552      * @param {NetworkAgent.Timestamp} finishTime
    553      */
    554     _finishNetworkRequest: function(networkRequest, finishTime)
    555     {
    556         networkRequest.endTime = finishTime;
    557         networkRequest.finished = true;
    558         this._dispatchEventToListeners(WebInspector.NetworkManager.EventTypes.RequestFinished, networkRequest);
    559         delete this._inflightRequestsById[networkRequest.requestId];
    560         delete this._inflightRequestsByURL[networkRequest.url];
    561     },
    562 
    563     /**
    564      * @param {string} eventType
    565      * @param {WebInspector.NetworkRequest} networkRequest
    566      */
    567     _dispatchEventToListeners: function(eventType, networkRequest)
    568     {
    569         this._manager.dispatchEventToListeners(eventType, networkRequest);
    570     },
    571 
    572     /**
    573      * @param {NetworkAgent.RequestId} requestId
    574      * @param {string} frameId
    575      * @param {NetworkAgent.LoaderId} loaderId
    576      * @param {string} url
    577      * @param {string} documentURL
    578      * @param {NetworkAgent.Initiator} initiator
    579      */
    580     _createNetworkRequest: function(requestId, frameId, loaderId, url, documentURL, initiator)
    581     {
    582         var networkRequest = new WebInspector.NetworkRequest(requestId, url, documentURL, frameId, loaderId);
    583         networkRequest.initiator = initiator;
    584         return networkRequest;
    585     }
    586 }
    587 
    588 /**
    589  * @type {?WebInspector.NetworkManager}
    590  */
    591 WebInspector.networkManager = null;
    592