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