initial import
[vuplus_webkit] / 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  * @constructor
33  * @extends {WebInspector.Object}
34  */
35 WebInspector.ResourceTreeModel = function(networkManager)
36 {
37     WebInspector.networkManager.addEventListener(WebInspector.NetworkManager.EventTypes.ResourceStarted, this._onResourceStarted, this);
38     WebInspector.networkManager.addEventListener(WebInspector.NetworkManager.EventTypes.ResourceUpdated, this._onResourceUpdated, this);
39     WebInspector.networkManager.addEventListener(WebInspector.NetworkManager.EventTypes.ResourceFinished, this._onResourceUpdated, this);
40
41     WebInspector.console.addEventListener(WebInspector.ConsoleModel.Events.MessageAdded, this._consoleMessageAdded, this);
42     WebInspector.console.addEventListener(WebInspector.ConsoleModel.Events.RepeatCountUpdated, this._consoleMessageAdded, this);
43     WebInspector.console.addEventListener(WebInspector.ConsoleModel.Events.ConsoleCleared, this._consoleCleared, this);
44
45     this.frontendReused();
46     InspectorBackend.registerPageDispatcher(new WebInspector.PageDispatcher(this));
47
48     this._pendingConsoleMessages = {};
49 }
50
51 WebInspector.ResourceTreeModel.EventTypes = {
52     FrameAdded: "FrameAdded",
53     FrameNavigated: "FrameNavigated",
54     FrameDetached: "FrameDetached",
55     ResourceAdded: "ResourceAdded",
56     WillLoadCachedResources: "WillLoadCachedResources",
57     CachedResourcesLoaded: "CachedResourcesLoaded",
58     DOMContentLoaded: "DOMContentLoaded",
59     OnLoad: "OnLoad",
60     InspectedURLChanged: "InspectedURLChanged"
61 }
62
63 WebInspector.ResourceTreeModel.prototype = {
64     frontendReused: function()
65     {
66         this._resourcesByURL = {};
67         this._resourcesByFrameId = {};
68         this._subframes = {};
69         this._frameIds = {};
70         delete this._cachedResourcesProcessed;
71         PageAgent.getResourceTree(this._processCachedResources.bind(this));
72     },
73
74     _processCachedResources: function(error, mainFramePayload)
75     {
76         if (error) {
77             console.error(JSON.stringify(error));
78             return;
79         }
80
81         this.dispatchEventToListeners(WebInspector.ResourceTreeModel.EventTypes.WillLoadCachedResources);
82         this._addFramesRecursively(mainFramePayload);
83         this._dispatchInspectedURLChanged(WebInspector.mainResource.url);
84         this.dispatchEventToListeners(WebInspector.ResourceTreeModel.EventTypes.CachedResourcesLoaded);
85         WebInspector.Resource.restoreRevisions();
86
87         this._cachedResourcesProcessed = true;
88     },
89
90     _dispatchInspectedURLChanged: function(url)
91     {
92         InspectorFrontendHost.inspectedURLChanged(url);
93         this.dispatchEventToListeners(WebInspector.ResourceTreeModel.EventTypes.InspectedURLChanged, url);
94     },
95
96     _addFrame: function(frame)
97     {
98         frame.parentId = frame.parentId || "";
99         this._frameIds[frame.id] = frame;
100         var subframes = this._subframes[frame.parentId];
101         if (!subframes) {
102             subframes = [];
103             this._subframes[frame.parentId] = subframes;
104         }
105
106         subframes.push(frame);
107         this.dispatchEventToListeners(WebInspector.ResourceTreeModel.EventTypes.FrameAdded, frame);
108     },
109
110     subframes: function(parentFrameId)
111     {
112         return this._subframes[parentFrameId] || [];
113     },
114
115     resources: function(frameId)
116     {
117         var result = [];
118         var resources = this._resourcesByFrameId[frameId] || {};
119         for (var url in resources)
120             result.push(resources[url]);
121         return result;
122     },
123
124     _frameNavigated: function(frame, loaderId)
125     {
126         var isMainFrame = !frame.parentId;
127
128         if (isMainFrame) {
129             this._cleanupFramesAfterNavigation(frame);
130             if (this.resourceForURL(frame.url))
131                 WebInspector.mainResource = this.resourceForURL(frame.url);
132         }
133         // Do nothing unless cached resource tree is processed - it will overwrite everything.
134         if (!this._cachedResourcesProcessed)
135             return;
136
137         // Add frame in case it is seen for the first time, otherwise, do a within-frame cleanup.
138         if (!this._frameIds[frame.id])
139             this._addFrame(frame);
140         else {
141             this._clearChildFramesAndResources(frame.id, loaderId);
142             frame.parentId = frame.parentId || "";
143             this._frameIds[frame.id] = frame;
144         }
145         // Dispatch frame navigated event to clients prior to filling it with the resources.
146         this.dispatchEventToListeners(WebInspector.ResourceTreeModel.EventTypes.FrameNavigated, { frame: frame, loaderId: loaderId, isMainFrame: isMainFrame });
147
148         // Fill frame with retained resources (the ones loaded using new loader).
149         var resourcesForFrame = this._resourcesByFrameId[frame.id];
150         if (resourcesForFrame) {
151             for (var url in resourcesForFrame)
152                 this.dispatchEventToListeners(WebInspector.ResourceTreeModel.EventTypes.ResourceAdded, resourcesForFrame[url]);
153         }
154
155         if (isMainFrame)
156             this._dispatchInspectedURLChanged(frame.url);
157     },
158
159     _cleanupFramesAfterNavigation: function(newMainFrame)
160     {
161         if (this._currentMainFrameId)
162             this._frameDetached(this._currentMainFrameId);
163         this._currentMainFrameId = newMainFrame.id;
164     },
165
166     _frameDetached: function(frameId)
167     {
168         // Do nothing unless cached resource tree is processed - it will overwrite everything.
169         if (!this._cachedResourcesProcessed)
170             return;
171
172         this._clearChildFramesAndResources(frameId, "");
173         var frame = this._frameIds[frameId];
174
175         if (frame) {
176             var siblings = this._subframes[frame.parentId];
177             if (siblings)
178                 siblings.remove(frame);
179             delete this._frameIds[frameId];
180         }
181
182         this.dispatchEventToListeners(WebInspector.ResourceTreeModel.EventTypes.FrameDetached, frameId);
183     },
184
185     _onResourceStarted: function(event)
186     {
187         if (!this._cachedResourcesProcessed)
188             return;
189         this._bindResourceURL(event.data);
190     },
191
192     _onResourceUpdated: function(event)
193     {
194         if (!this._cachedResourcesProcessed)
195             return;
196
197         var resource = event.data;
198         if (resource.failed) {
199             this._unbindResourceURL(resource);
200             return;
201         }
202
203         if (resource.type === WebInspector.Resource.Type.XHR) {
204             this._unbindResourceURL(resource);
205             return;
206         }
207
208         if (resource.finished)
209             this._addResourceToFrame(resource);
210     },
211
212     _addResourceToFrame: function(resource)
213     {
214         var frameId = resource.frameId;
215         var resourcesForFrame = this._resourcesByFrameId[frameId];
216         if (!resourcesForFrame) {
217             resourcesForFrame = {};
218             this._resourcesByFrameId[frameId] = resourcesForFrame;
219         }
220         if (resourcesForFrame[resource.url] === resource) {
221             // Already in the tree, we just got an extra update.
222             return;
223         }
224
225         resourcesForFrame[resource.url] = resource;
226         this._bindResourceURL(resource);
227         this.dispatchEventToListeners(WebInspector.ResourceTreeModel.EventTypes.ResourceAdded, resource);
228     },
229
230     forAllResources: function(callback)
231     {
232         return this._callForFrameResources("", callback);
233     },
234
235     _consoleMessageAdded: function(event)
236     {
237         var msg = event.data;
238         var resource = this.resourceForURL(msg.url);
239         if (resource)
240             this._addConsoleMessageToResource(msg, resource);
241         else
242             this._addPendingConsoleMessage(msg);
243     },
244
245     _addPendingConsoleMessage: function(msg)
246     {
247         if (!msg.url)
248             return;
249         if (!this._pendingConsoleMessages[msg.url])
250             this._pendingConsoleMessages[msg.url] = [];
251         this._pendingConsoleMessages[msg.url].push(msg);
252     },
253
254     _addPendingConsoleMessagesToResource: function(resource)
255     {
256         var messages = this._pendingConsoleMessages[resource.url];
257         if (messages) {
258             for (var i = 0; i < messages.length; i++)
259                 this._addConsoleMessageToResource(messages[i], resource);
260             delete this._pendingConsoleMessages[resource.url];
261         }
262     },
263
264     _addConsoleMessageToResource: function(msg, resource)
265     {
266         switch (msg.level) {
267         case WebInspector.ConsoleMessage.MessageLevel.Warning:
268             resource.warnings += msg.repeatDelta;
269             break;
270         case WebInspector.ConsoleMessage.MessageLevel.Error:
271             resource.errors += msg.repeatDelta;
272             break;
273         }
274         resource.addMessage(msg);
275     },
276
277     _consoleCleared: function()
278     {
279         function callback(resource)
280         {
281             resource.clearErrorsAndWarnings();
282         }
283
284         this._pendingConsoleMessages = {};
285         this.forAllResources(callback);
286     },
287
288     resourceForURL: function(url)
289     {
290         return this._resourcesByURL[url];
291     },
292
293     _bindResourceURL: function(resource)
294     {
295         this._resourcesByURL[resource.url] = resource;
296
297         this._addPendingConsoleMessagesToResource(resource);
298     },
299
300     _clearChildFramesAndResources: function(frameId, loaderToPreserveId)
301     {
302         this._clearResources(frameId, loaderToPreserveId);
303         var subframes = this._subframes[frameId];
304         for (var i = 0; subframes && i < subframes.length; ++ i) {
305             this.dispatchEventToListeners(WebInspector.ResourceTreeModel.EventTypes.FrameDetached, subframes[i].id);
306             this._clearChildFramesAndResources(subframes[i].id, loaderToPreserveId);
307         }
308         delete this._subframes[frameId];
309     },
310
311     _clearResources: function(frameId, loaderToPreserveId)
312     {
313         var resourcesForFrame = this._resourcesByFrameId[frameId];
314         if (!resourcesForFrame)
315             return;
316
317         var preservedResourcesForFrame = [];
318         for (var url in resourcesForFrame) {
319             var resource = resourcesForFrame[url];
320             if (resource.loaderId === loaderToPreserveId) {
321                 preservedResourcesForFrame[url] = resource;
322                 continue;
323             }
324             this._unbindResourceURL(resource);
325         }
326
327         delete this._resourcesByFrameId[frameId];
328         if (preservedResourcesForFrame.length) {
329             this._resourcesByFrameId[frameId] = preservedResourcesForFrame;
330         }
331     },
332
333     _callForFrameResources: function(frameId, callback)
334     {
335         var resources = this._resourcesByFrameId[frameId];
336
337         for (var url in resources) {
338             if (callback(resources[url]))
339                 return true;
340         }
341
342         var frames = this._subframes[frameId];
343         for (var i = 0; frames && i < frames.length; ++i) {
344             if (this._callForFrameResources(frames[i].id, callback))
345                 return true;
346         }
347         return false;
348     },
349
350     _unbindResourceURL: function(resource)
351     {
352         delete this._resourcesByURL[resource.url];
353     },
354
355     _addFramesRecursively: function(frameTreePayload)
356     {
357         var framePayload = frameTreePayload.frame;
358
359         // Create frame resource.
360         var frameResource = this._createResource(framePayload, framePayload.url);
361         frameResource.type = WebInspector.Resource.Type.Document;
362         frameResource.finished = true;
363
364         if (!framePayload.parentId) {
365             WebInspector.mainResource = frameResource;
366             this._currentMainFrameId = framePayload.id;
367         }
368         this._addFrame(framePayload);
369         this._addResourceToFrame(frameResource);
370
371         for (var i = 0; frameTreePayload.childFrames && i < frameTreePayload.childFrames.length; ++i)
372             this._addFramesRecursively(frameTreePayload.childFrames[i]);
373
374         if (!frameTreePayload.resources)
375             return;
376
377         // Create frame subresources.
378         for (var i = 0; i < frameTreePayload.resources.length; ++i) {
379             var subresource = frameTreePayload.resources[i];
380             var resource = this._createResource(framePayload, subresource.url);
381             resource.type = WebInspector.Resource.Type[subresource.type];
382             resource.mimeType = subresource.mimeType;
383             resource.finished = true;
384             this._addResourceToFrame(resource);
385         }
386     },
387
388     _createResource: function(frame, url)
389     {
390         var resource = new WebInspector.Resource(null, url, frame.loaderId);
391         resource.frameId = frame.id;
392         resource.documentURL = frame.url;
393         resource.mimeType = frame.mimeType;
394         return resource;
395     }
396 }
397
398 WebInspector.ResourceTreeModel.prototype.__proto__ = WebInspector.Object.prototype;
399
400 /**
401  * @constructor
402  * @implements {PageAgent.Dispatcher}
403  */
404 WebInspector.PageDispatcher = function(resourceTreeModel)
405 {
406     this._resourceTreeModel = resourceTreeModel;
407 }
408
409 WebInspector.PageDispatcher.prototype = {
410     domContentEventFired: function(time)
411     {
412         this._resourceTreeModel.dispatchEventToListeners(WebInspector.ResourceTreeModel.EventTypes.DOMContentLoaded, time);
413
414         // FIXME: the only client is HAR, fix it there.
415         WebInspector.mainResourceDOMContentTime = time;
416     },
417
418     loadEventFired: function(time)
419     {
420         this._resourceTreeModel.dispatchEventToListeners(WebInspector.ResourceTreeModel.EventTypes.OnLoad, time);
421
422         // FIXME: the only client is HAR, fix it there.
423         WebInspector.mainResourceLoadTime = time;
424     },
425
426     frameNavigated: function(frame, loaderId)
427     {
428         this._resourceTreeModel._frameNavigated(frame, loaderId);
429     },
430
431     frameDetached: function(frameId)
432     {
433         this._resourceTreeModel._frameDetached(frameId);
434     }
435 }