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