ResourceTreeModel.js revision 2daae5fd11344eaa88a0d92b0f6d65f8d2255c00
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
32WebInspector.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
43WebInspector.ResourceTreeModel.EventTypes = {
44    FrameAdded: "FrameAdded",
45    FrameNavigated: "FrameNavigated",
46    FrameDetached: "FrameDetached",
47    ResourceAdded: "ResourceAdded",
48    CachedResourcesLoaded: "CachedResourcesLoaded"
49}
50
51WebInspector.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
128    _onFrameDetachedFromParent: function(event)
129    {
130        if (!this._cachedResourcesProcessed)
131            return;
132
133        var frameId = event.data;
134        this._clearChildFramesAndResources(frameId, 0);
135        this.dispatchEventToListeners(WebInspector.ResourceTreeModel.EventTypes.FrameDetached, frameId);
136    },
137
138    _onResourceStarted: function(event)
139    {
140        if (!this._cachedResourcesProcessed)
141            return;
142        this._bindResourceURL(event.data);
143    },
144
145    _onResourceUpdated: function(event)
146    {
147        if (!this._cachedResourcesProcessed)
148            return;
149        this._addResourceToFrame(event.data);
150    },
151
152    _addResourceToFrame: function(resource)
153    {
154        var frameId = resource.frameId;
155        var resourcesForFrame = this._resourcesByFrameId[frameId];
156        if (!resourcesForFrame) {
157            resourcesForFrame = {};
158            this._resourcesByFrameId[frameId] = resourcesForFrame;
159        }
160        if (resourcesForFrame[resource.url] === resource) {
161            // Already in the tree, we just got an extra update.
162            return;
163        }
164
165        resourcesForFrame[resource.url] = resource;
166        this._bindResourceURL(resource);
167        this.dispatchEventToListeners(WebInspector.ResourceTreeModel.EventTypes.ResourceAdded, resource);
168    },
169
170    forAllResources: function(callback)
171    {
172        this._callForFrameResources(0, callback);
173    },
174
175    addConsoleMessage: function(msg)
176    {
177        var resource = this.resourceForURL(msg.url);
178        if (!resource)
179            return;
180
181        switch (msg.level) {
182        case WebInspector.ConsoleMessage.MessageLevel.Warning:
183            resource.warnings += msg.repeatDelta;
184            break;
185        case WebInspector.ConsoleMessage.MessageLevel.Error:
186            resource.errors += msg.repeatDelta;
187            break;
188        }
189
190        var view = WebInspector.ResourceView.resourceViewForResource(resource);
191        if (view.addMessage && msg.isErrorOrWarning() && msg.message)
192            view.addMessage(msg);
193    },
194
195    clearConsoleMessages: function()
196    {
197        function callback(resource)
198        {
199            resource.clearErrorsAndWarnings();
200        }
201        this.forAllResources(callback);
202    },
203
204    resourceForURL: function(url)
205    {
206        return this._resourcesByURL[url];
207    },
208
209    _bindResourceURL: function(resource)
210    {
211        this._resourcesByURL[resource.url] = resource;
212    },
213
214    _clearChildFramesAndResources: function(frameId, loaderToPreserveId)
215    {
216        this.dispatchEventToListeners(WebInspector.ResourceTreeModel.EventTypes.FrameNavigated, frameId);
217
218        this._clearResources(frameId, loaderToPreserveId);
219        var subframes = this._subframes[frameId];
220        for (var i = 0; subframes && i < subframes.length; ++ i) {
221            this.dispatchEventToListeners(WebInspector.ResourceTreeModel.EventTypes.FrameRemoved, subframes[i].id);
222            this._clearChildFramesAndResources(subframes[i].id, loaderToPreserveId);
223        }
224        delete this._subframes[frameId];
225    },
226
227    _clearResources: function(frameId, loaderToPreserveId)
228    {
229        var resourcesForFrame = this._resourcesByFrameId[frameId];
230        if (!resourcesForFrame)
231            return;
232
233        var preservedResourcesForFrame = [];
234        for (var url in resourcesForFrame) {
235            var resource = resourcesForFrame[url];
236            if (resource.loaderId === loaderToPreserveId) {
237                preservedResourcesForFrame[url] = resource;
238                continue;
239            }
240            this._unbindResourceURL(resource);
241        }
242
243        delete this._resourcesByFrameId[frameId];
244        if (preservedResourcesForFrame.length) {
245            this._resourcesByFrameId[frameId] = preservedResourcesForFrame;
246        }
247    },
248
249    _callForFrameResources: function(frameId, callback)
250    {
251        var resources = this._resourcesByFrameId[frameId];
252
253        for (var url in resources) {
254            if (callback(resources[url]))
255                return true;
256        }
257
258        var frames = this._subframes[frameId];
259        for (var i = 0; frames && i < frames.length; ++i) {
260            if (this._callForFrameResources(frames[i].id, callback))
261                return true;
262        }
263        return false;
264    },
265
266    _unbindResourceURL: function(resource)
267    {
268        delete this._resourcesByURL[resource.url];
269    },
270
271    _addFramesRecursively: function(frameTreePayload)
272    {
273        var framePayload = frameTreePayload.frame;
274
275        // Create frame resource.
276        var frameResource = this._createResource(framePayload, framePayload.url);
277        frameResource.type = WebInspector.Resource.Type.Document;
278        frameResource.finished = true;
279
280        this._addOrUpdateFrame(framePayload);
281        this._addResourceToFrame(frameResource);
282
283        for (var i = 0; frameTreePayload.childFrames && i < frameTreePayload.childFrames.length; ++i)
284            this._addFramesRecursively(frameTreePayload.childFrames[i]);
285
286        if (!frameTreePayload.resources)
287            return;
288
289        // Create frame subresources.
290        for (var i = 0; i < frameTreePayload.resources.length; ++i) {
291            var subresource = frameTreePayload.resources[i];
292            var resource = this._createResource(framePayload, subresource.url);
293            resource.type = WebInspector.Resource.Type[subresource.type];
294            resource.finished = true;
295            this._addResourceToFrame(resource);
296        }
297        return frameResource;
298    },
299
300    _createResource: function(frame, url)
301    {
302        var resource = new WebInspector.Resource(null, url);
303        resource.frameId = frame.id;
304        resource.loaderId = frame.loaderId;
305        resource.documentURL = frame.url;
306        return resource;
307    }
308}
309
310WebInspector.ResourceTreeModel.prototype.__proto__ = WebInspector.Object.prototype;
311