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