6a78e9dcadbcd3505ad93325ebc6c28cd230ebbf
[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 WebInspector.NetworkManager = function()
32 {
33     WebInspector.Object.call(this);
34     this._dispatcher = new WebInspector.NetworkDispatcher(this);
35     if (WebInspector.settings.cacheDisabled.get())
36         NetworkAgent.setCacheDisabled(true);
37     NetworkAgent.enable();
38     
39     WebInspector.settings.cacheDisabled.addChangeListener(this._cacheDisabledSettingChanged.bind(this));
40 }
41
42 WebInspector.NetworkManager.EventTypes = {
43     ResourceStarted: "ResourceStarted",
44     ResourceUpdated: "ResourceUpdated",
45     ResourceFinished: "ResourceFinished"
46 }
47
48 WebInspector.NetworkManager.prototype = {
49     requestContent: function(resource, callback)
50     {
51         function callbackWrapper(error, content, contentEncoded)
52         {
53             if (error)
54                 callback(null, false);
55             else
56                 callback(content, content && contentEncoded);
57         }
58         // FIXME: https://bugs.webkit.org/show_bug.cgi?id=61363 We should separate NetworkResource (NetworkPanel resource) 
59         // from ResourceRevision (ResourcesPanel/ScriptsPanel resource) and request content accordingly.
60         if (resource.requestId)
61             NetworkAgent.getResourceContent(resource.requestId, callbackWrapper);
62         else
63             PageAgent.getResourceContent(resource.frameId, resource.url, callbackWrapper);
64     },
65
66     inflightResourceForURL: function(url)
67     {
68         return this._dispatcher._inflightResourcesByURL[url];
69     },
70     
71     _cacheDisabledSettingChanged: function(event)
72     {
73         NetworkAgent.setCacheDisabled(event.data);
74     }
75 }
76
77 WebInspector.NetworkManager.prototype.__proto__ = WebInspector.Object.prototype;
78
79 WebInspector.NetworkDispatcher = function(manager)
80 {
81     this._manager = manager;
82     this._inflightResourcesById = {};
83     this._inflightResourcesByURL = {};
84     InspectorBackend.registerDomainDispatcher("Network", this);
85 }
86
87 WebInspector.NetworkDispatcher.prototype = {
88     _updateResourceWithRequest: function(resource, request)
89     {
90         resource.requestMethod = request.method;
91         resource.requestHeaders = request.headers;
92         resource.requestFormData = request.postData;
93     },
94
95     _updateResourceWithResponse: function(resource, response)
96     {
97         if (!response)
98             return;
99
100         if (response.url && resource.url !== response.url)
101             resource.url = response.url;
102         resource.mimeType = response.mimeType;
103         resource.statusCode = response.status;
104         resource.statusText = response.statusText;
105         resource.responseHeaders = response.headers;
106         if (response.headersText)
107             resource.responseHeadersText = response.headersText;
108         if (response.requestHeaders)
109             resource.requestHeaders = response.requestHeaders;
110         if (response.requestHeadersText)
111             resource.requestHeadersText = response.requestHeadersText;
112
113         resource.connectionReused = response.connectionReused;
114         resource.connectionId = response.connectionId;
115
116         if (response.fromDiskCache)
117             resource.cached = true;
118         else
119             resource.timing = response.timing;
120     },
121
122     _updateResourceWithCachedResource: function(resource, cachedResource)
123     {
124         resource.type = WebInspector.Resource.Type[cachedResource.type];
125         resource.resourceSize = cachedResource.bodySize;
126         this._updateResourceWithResponse(resource, cachedResource.response);
127     },
128
129     _isNull: function(response)
130     {
131         return response && !response.status && !response.mimeType && !Object.keys(response.headers).length;
132     },
133
134     requestWillBeSent: function(requestId, frameId, loaderId, documentURL, request, time, initiator, stackTrace, redirectResponse)
135     {
136         var resource = this._inflightResourcesById[requestId];
137         if (resource) {
138             // FIXME: move this check to the backend.
139             if (this._isNull(redirectResponse))
140                 return;
141             this.responseReceived(requestId, time, "Other", redirectResponse);
142             resource = this._appendRedirect(requestId, time, request.url);
143         } else
144             resource = this._createResource(requestId, frameId, loaderId, request.url, documentURL, initiator, stackTrace);
145         resource.hasNetworkData = true;
146         this._updateResourceWithRequest(resource, request);
147         resource.startTime = time;
148
149         this._startResource(resource);
150     },
151
152     resourceMarkedAsCached: function(requestId)
153     {
154         var resource = this._inflightResourcesById[requestId];
155         if (!resource)
156             return;
157
158         resource.cached = true;
159         this._updateResource(resource);
160     },
161
162     responseReceived: function(requestId, time, resourceType, response)
163     {
164         // FIXME: move this check to the backend.
165         if (this._isNull(response))
166             return;
167
168         var resource = this._inflightResourcesById[requestId];
169         if (!resource)
170             return;
171
172         resource.responseReceivedTime = time;
173         resource.type = WebInspector.Resource.Type[resourceType];
174
175         this._updateResourceWithResponse(resource, response);
176
177         this._updateResource(resource);
178     },
179
180     dataReceived: function(requestId, time, dataLength, encodedDataLength)
181     {
182         var resource = this._inflightResourcesById[requestId];
183         if (!resource)
184             return;
185
186         resource.resourceSize += dataLength;
187         if (encodedDataLength != -1)
188             resource.increaseTransferSize(encodedDataLength);
189         resource.endTime = time;
190
191         this._updateResource(resource);
192     },
193
194     loadingFinished: function(requestId, finishTime)
195     {
196         var resource = this._inflightResourcesById[requestId];
197         if (!resource)
198             return;
199
200         this._finishResource(resource, finishTime);
201     },
202
203     loadingFailed: function(requestId, time, localizedDescription, canceled)
204     {
205         var resource = this._inflightResourcesById[requestId];
206         if (!resource)
207             return;
208
209         resource.failed = true;
210         resource.canceled = canceled;
211         resource.localizedFailDescription = localizedDescription;
212         this._finishResource(resource, time);
213     },
214
215     resourceLoadedFromMemoryCache: function(requestId, frameId, loaderId, documentURL, time, initiator, cachedResource)
216     {
217         var resource = this._createResource(requestId, frameId, loaderId, cachedResource.url, documentURL, initiator);
218         this._updateResourceWithCachedResource(resource, cachedResource);
219         resource.cached = true;
220         resource.requestMethod = "GET";
221         this._startResource(resource);
222         resource.startTime = resource.responseReceivedTime = time;
223         this._finishResource(resource, time);
224     },
225
226     webSocketCreated: function(requestId, requestURL)
227     {
228         var resource = this._createResource(requestId, null, null, requestURL);
229         resource.type = WebInspector.Resource.Type.WebSocket;
230         this._startResource(resource);
231     },
232
233     webSocketWillSendHandshakeRequest: function(requestId, time, request)
234     {
235         var resource = this._inflightResourcesById[requestId];
236         if (!resource)
237             return;
238
239         resource.requestMethod = "GET";
240         resource.requestHeaders = request.headers;
241         resource.webSocketRequestKey3 = request.requestKey3;
242         resource.startTime = time;
243
244         this._updateResource(resource);
245     },
246
247     webSocketHandshakeResponseReceived: function(requestId, time, response)
248     {
249         var resource = this._inflightResourcesById[requestId];
250         if (!resource)
251             return;
252
253         resource.statusCode = response.status;
254         resource.statusText = response.statusText;
255         resource.responseHeaders = response.headers;
256         resource.webSocketChallengeResponse = response.challengeResponse;
257         resource.responseReceivedTime = time;
258
259         this._updateResource(resource);
260     },
261
262     webSocketClosed: function(requestId, time)
263     {
264         var resource = this._inflightResourcesById[requestId];
265         if (!resource)
266             return;
267         this._finishResource(resource, time);
268     },
269
270     _appendRedirect: function(requestId, time, redirectURL)
271     {
272         var originalResource = this._inflightResourcesById[requestId];
273         var previousRedirects = originalResource.redirects || [];
274         originalResource.requestId = "redirected:" + requestId + "." + previousRedirects.length;
275         delete originalResource.redirects;
276         this._finishResource(originalResource, time);
277         var newResource = this._createResource(requestId, originalResource.frameId, originalResource.loaderId,
278              redirectURL, originalResource.documentURL, originalResource.initiator, originalResource.stackTrace);
279         newResource.redirects = previousRedirects.concat(originalResource);
280         return newResource;
281     },
282
283     _startResource: function(resource)
284     {
285         this._inflightResourcesById[resource.requestId] = resource;
286         this._inflightResourcesByURL[resource.url] = resource;
287         this._dispatchEventToListeners(WebInspector.NetworkManager.EventTypes.ResourceStarted, resource);
288     },
289
290     _updateResource: function(resource)
291     {
292         this._dispatchEventToListeners(WebInspector.NetworkManager.EventTypes.ResourceUpdated, resource);
293     },
294
295     _finishResource: function(resource, finishTime)
296     {
297         resource.endTime = finishTime;
298         resource.finished = true;
299         this._dispatchEventToListeners(WebInspector.NetworkManager.EventTypes.ResourceFinished, resource);
300         delete this._inflightResourcesById[resource.requestId];
301         delete this._inflightResourcesByURL[resource.url];
302     },
303
304     _dispatchEventToListeners: function(eventType, resource)
305     {
306         this._manager.dispatchEventToListeners(eventType, resource);
307     },
308
309     _createResource: function(requestId, frameId, loaderId, url, documentURL, initiator, stackTrace)
310     {
311         var resource = new WebInspector.Resource(requestId, url, loaderId);
312         resource.documentURL = documentURL;
313         resource.frameId = frameId;
314         resource.initiator = initiator;
315         resource.stackTrace = stackTrace;
316         return resource;
317     }
318 }
319
320 WebInspector.NetworkLog = function()
321 {
322     this._resources = [];
323     WebInspector.networkManager.addEventListener(WebInspector.NetworkManager.EventTypes.ResourceStarted, this._onResourceStarted, this);
324     WebInspector.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.FrameNavigated, this._frameNavigated, this);
325 }
326
327 WebInspector.NetworkLog.prototype = {
328     get resources()
329     {
330         return this._resources;
331     },
332
333     _frameNavigated: function(event)
334     {
335         if (!event.data.isMainFrame)
336             return;
337         // Preserve resources from the new session.
338         var oldResources = this._resources.splice(0, this._resources.length);
339         for (var i = 0; i < oldResources.length; ++i) {
340             if (oldResources[i].loaderId === event.data.loaderId)
341                 this._resources.push(oldResources[i]);
342         }
343     },
344
345     _onResourceStarted: function(event)
346     {
347         this._resources.push(event.data);
348     }
349 }