c0dd6916e56927a821475f80160861bee2def8d9
[WebKit-https.git] / Source / WebCore / inspector / front-end / ResourceTreeModel.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 WebInspector.ResourceTreeModel = function(networkManager)
33 {
34     WebInspector.networkManager.addEventListener(WebInspector.NetworkManager.EventTypes.ResourceStarted, this._onResourceStarted, this);
35     WebInspector.networkManager.addEventListener(WebInspector.NetworkManager.EventTypes.ResourceUpdated, this._onResourceUpdated, this);
36     WebInspector.networkManager.addEventListener(WebInspector.NetworkManager.EventTypes.ResourceFinished, this._onResourceUpdated, this);
37     WebInspector.networkManager.addEventListener(WebInspector.NetworkManager.EventTypes.FrameDetached, this._onFrameDetachedFromParent, this);
38     WebInspector.networkManager.addEventListener(WebInspector.NetworkManager.EventTypes.FrameCommittedLoad, this._onCommitLoad, this);
39
40     this.frontendReused();
41 }
42
43 WebInspector.ResourceTreeModel.EventTypes = {
44     FrameAdded: "FrameAdded",
45     FrameNavigated: "FrameNavigated",
46     FrameDetached: "FrameDetached",
47     ResourceAdded: "ResourceAdded",
48     CachedResourcesLoaded: "CachedResourcesLoaded"
49 }
50
51 WebInspector.ResourceTreeModel.prototype = {
52     frontendReused: function()
53     {
54         this._resourcesByURL = {};
55         this._resourcesByFrameId = {};
56         this._subframes = {};
57         NetworkAgent.getCachedResources(this._processCachedResources.bind(this));
58     },
59
60     _processCachedResources: function(error, mainFramePayload)
61     {
62         if (error)
63             return;
64
65         this.dispatchEventToListeners(WebInspector.ResourceTreeModel.EventTypes.FrameNavigated, 0);
66
67         WebInspector.mainResource = this._addFramesRecursively(mainFramePayload);
68         this._cachedResourcesProcessed = true;
69
70         this.dispatchEventToListeners(WebInspector.ResourceTreeModel.EventTypes.CachedResourcesLoaded);
71     },
72
73     _addOrUpdateFrame: function(frame)
74     {
75         this.dispatchEventToListeners(WebInspector.ResourceTreeModel.EventTypes.FrameAdded, frame);
76
77         var subframes = this._subframes[frame.parentId];
78         if (!subframes) {
79             subframes = [];
80             this._subframes[frame.parentId || 0] = subframes;
81         }
82         subframes.push(frame);
83     },
84
85     frames: function(parentFrameId)
86     {
87         return this._subframes[parentFrameId] || [];
88     },
89
90     subframes: function(parentFrameId)
91     {
92         return this._subframes[parentFrameId] || [];
93     },
94
95     resources: function(frameId)
96     {
97         var result = [];
98         var resources = this._resourcesByFrameId[frameId] || {};
99         for (var url in resources)
100             result.push(resources[url]);
101         return result;
102     },
103
104     _onCommitLoad: function(event)
105     {
106         if (!this._cachedResourcesProcessed)
107             return;
108
109         var frame = event.data.frame;
110         var loaderId = event.data.loaderId;
111         var isMainFrame = !frame.parentId;
112
113         // frame.parentId === 0 is when main frame navigation happens.
114         this._clearChildFramesAndResources(isMainFrame ? 0 : frame.id, loaderId);
115
116         this._addOrUpdateFrame(frame);
117
118         var resourcesForFrame = this._resourcesByFrameId[frame.id];
119         if (resourcesForFrame) {
120             for (var url in resourcesForFrame)
121                 this.dispatchEventToListeners(WebInspector.ResourceTreeModel.EventTypes.ResourceAdded, resourcesForFrame[url]);
122         }
123
124         if (isMainFrame && this.resourceForURL(frame.url))
125             WebInspector.mainResource = this.resourceForURL(frame.url);
126
127         if (isMainFrame)
128             WebInspector.Resource.clearRevisionHistory();
129     },
130
131     _onFrameDetachedFromParent: function(event)
132     {
133         if (!this._cachedResourcesProcessed)
134             return;
135
136         var frameId = event.data;
137         this._clearChildFramesAndResources(frameId, 0);
138         this.dispatchEventToListeners(WebInspector.ResourceTreeModel.EventTypes.FrameDetached, frameId);
139     },
140
141     _onResourceStarted: function(event)
142     {
143         if (!this._cachedResourcesProcessed)
144             return;
145         this._bindResourceURL(event.data);
146     },
147
148     _onResourceUpdated: function(event)
149     {
150         if (!this._cachedResourcesProcessed)
151             return;
152         this._addResourceToFrame(event.data);
153     },
154
155     _addResourceToFrame: function(resource)
156     {
157         var frameId = resource.frameId;
158         var resourcesForFrame = this._resourcesByFrameId[frameId];
159         if (!resourcesForFrame) {
160             resourcesForFrame = {};
161             this._resourcesByFrameId[frameId] = resourcesForFrame;
162         }
163         if (resourcesForFrame[resource.url] === resource) {
164             // Already in the tree, we just got an extra update.
165             return;
166         }
167
168         resourcesForFrame[resource.url] = resource;
169         this._bindResourceURL(resource);
170         this.dispatchEventToListeners(WebInspector.ResourceTreeModel.EventTypes.ResourceAdded, resource);
171     },
172
173     forAllResources: function(callback)
174     {
175         this._callForFrameResources(0, callback);
176     },
177
178     addConsoleMessage: function(msg)
179     {
180         var resource = this.resourceForURL(msg.url);
181         if (!resource)
182             return;
183
184         switch (msg.level) {
185         case WebInspector.ConsoleMessage.MessageLevel.Warning:
186             resource.warnings += msg.repeatDelta;
187             break;
188         case WebInspector.ConsoleMessage.MessageLevel.Error:
189             resource.errors += msg.repeatDelta;
190             break;
191         }
192
193         var view = WebInspector.ResourceView.resourceViewForResource(resource);
194         if (view.addMessage && msg.isErrorOrWarning() && msg.message)
195             view.addMessage(msg);
196     },
197
198     clearConsoleMessages: function()
199     {
200         function callback(resource)
201         {
202             resource.clearErrorsAndWarnings();
203         }
204         this.forAllResources(callback);
205     },
206
207     resourceForURL: function(url)
208     {
209         return this._resourcesByURL[url];
210     },
211
212     _bindResourceURL: function(resource)
213     {
214         this._resourcesByURL[resource.url] = resource;
215     },
216
217     _clearChildFramesAndResources: function(frameId, loaderToPreserveId)
218     {
219         this.dispatchEventToListeners(WebInspector.ResourceTreeModel.EventTypes.FrameNavigated, frameId);
220
221         this._clearResources(frameId, loaderToPreserveId);
222         var subframes = this._subframes[frameId];
223         for (var i = 0; subframes && i < subframes.length; ++ i) {
224             this.dispatchEventToListeners(WebInspector.ResourceTreeModel.EventTypes.FrameRemoved, subframes[i].id);
225             this._clearChildFramesAndResources(subframes[i].id, loaderToPreserveId);
226         }
227         delete this._subframes[frameId];
228     },
229
230     _clearResources: function(frameId, loaderToPreserveId)
231     {
232         var resourcesForFrame = this._resourcesByFrameId[frameId];
233         if (!resourcesForFrame)
234             return;
235
236         var preservedResourcesForFrame = [];
237         for (var url in resourcesForFrame) {
238             var resource = resourcesForFrame[url];
239             if (resource.loaderId === loaderToPreserveId) {
240                 preservedResourcesForFrame[url] = resource;
241                 continue;
242             }
243             this._unbindResourceURL(resource);
244         }
245
246         delete this._resourcesByFrameId[frameId];
247         if (preservedResourcesForFrame.length) {
248             this._resourcesByFrameId[frameId] = preservedResourcesForFrame;
249         }
250     },
251
252     _callForFrameResources: function(frameId, callback)
253     {
254         var resources = this._resourcesByFrameId[frameId];
255
256         for (var url in resources) {
257             if (callback(resources[url]))
258                 return true;
259         }
260         
261         var frames = this._subframes[frameId];
262         for (var i = 0; frames && i < frames.length; ++i) {
263             if (this._callForFrameResources(frames[i].id, callback))
264                 return true;
265         }
266         return false;
267     },
268
269     _unbindResourceURL: function(resource)
270     {
271         delete this._resourcesByURL[resource.url];
272     },
273
274     _addFramesRecursively: function(frameTreePayload)
275     {
276         var framePayload = frameTreePayload.frame;
277
278         // Create frame resource.
279         var frameResource = this._createResource(framePayload, framePayload.url);
280         frameResource.type = WebInspector.Resource.Type.Document;
281         frameResource.finished = true;
282
283         this._addOrUpdateFrame(framePayload);
284         this._addResourceToFrame(frameResource);
285
286         for (var i = 0; frameTreePayload.childFrames && i < frameTreePayload.childFrames.length; ++i)
287             this._addFramesRecursively(frameTreePayload.childFrames[i]);
288
289         if (!frameTreePayload.resources)
290             return;
291
292         // Create frame subresources.
293         for (var i = 0; i < frameTreePayload.resources.length; ++i) {
294             var subresource = frameTreePayload.resources[i];
295             var resource = this._createResource(framePayload, subresource.url);
296             resource.type = WebInspector.Resource.Type[subresource.type];
297             resource.finished = true;
298             this._addResourceToFrame(resource);
299         }
300         return frameResource;
301     },
302
303     _createResource: function(frame, url)
304     {
305         var resource = new WebInspector.Resource(null, url, frame.loaderId);
306         resource.frameId = frame.id;
307         resource.documentURL = frame.url;
308         return resource;
309     }
310 }
311
312 WebInspector.ResourceTreeModel.prototype.__proto__ = WebInspector.Object.prototype;