• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 */
35WebInspector.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
47WebInspector.NetworkManager.EventTypes = {
48    RequestStarted: "RequestStarted",
49    RequestUpdated: "RequestUpdated",
50    RequestFinished: "RequestFinished",
51    RequestUpdateDropped: "RequestUpdateDropped"
52}
53
54WebInspector.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
97WebInspector.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 */
123WebInspector.NetworkDispatcher = function(manager)
124{
125    this._manager = manager;
126    this._inflightRequestsById = {};
127    this._inflightRequestsByURL = {};
128    InspectorBackend.registerNetworkDispatcher(this);
129}
130
131WebInspector.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 */
560WebInspector.networkManager;
561