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