2 * Copyright (C) 2011 Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
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
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.
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.
33 * @extends {WebInspector.Object}
35 WebInspector.ResourceTreeModel = function(networkManager)
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);
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);
45 this.frontendReused();
46 InspectorBackend.registerPageDispatcher(new WebInspector.PageDispatcher(this));
48 this._pendingConsoleMessages = {};
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",
60 InspectedURLChanged: "InspectedURLChanged"
63 WebInspector.ResourceTreeModel.prototype = {
64 frontendReused: function()
66 this._resourcesByURL = {};
67 this._resourcesByFrameId = {};
70 delete this._cachedResourcesProcessed;
71 PageAgent.getResourceTree(this._processCachedResources.bind(this));
74 _processCachedResources: function(error, mainFramePayload)
77 console.error(JSON.stringify(error));
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();
87 this._cachedResourcesProcessed = true;
90 _dispatchInspectedURLChanged: function(url)
92 InspectorFrontendHost.inspectedURLChanged(url);
93 this.dispatchEventToListeners(WebInspector.ResourceTreeModel.EventTypes.InspectedURLChanged, url);
96 _addFrame: function(frame)
98 frame.parentId = frame.parentId || "";
99 this._frameIds[frame.id] = frame;
100 var subframes = this._subframes[frame.parentId];
103 this._subframes[frame.parentId] = subframes;
106 subframes.push(frame);
107 this.dispatchEventToListeners(WebInspector.ResourceTreeModel.EventTypes.FrameAdded, frame);
110 subframes: function(parentFrameId)
112 return this._subframes[parentFrameId] || [];
115 resources: function(frameId)
118 var resources = this._resourcesByFrameId[frameId] || {};
119 for (var url in resources)
120 result.push(resources[url]);
124 _frameNavigated: function(frame, loaderId)
126 var isMainFrame = !frame.parentId;
129 this._cleanupFramesAfterNavigation(frame);
130 if (this.resourceForURL(frame.url))
131 WebInspector.mainResource = this.resourceForURL(frame.url);
133 // Do nothing unless cached resource tree is processed - it will overwrite everything.
134 if (!this._cachedResourcesProcessed)
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);
141 this._clearChildFramesAndResources(frame.id, loaderId);
142 frame.parentId = frame.parentId || "";
143 this._frameIds[frame.id] = frame;
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 });
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]);
156 this._dispatchInspectedURLChanged(frame.url);
159 _cleanupFramesAfterNavigation: function(newMainFrame)
161 if (this._currentMainFrameId)
162 this._frameDetached(this._currentMainFrameId);
163 this._currentMainFrameId = newMainFrame.id;
166 _frameDetached: function(frameId)
168 // Do nothing unless cached resource tree is processed - it will overwrite everything.
169 if (!this._cachedResourcesProcessed)
172 this._clearChildFramesAndResources(frameId, "");
173 var frame = this._frameIds[frameId];
176 var siblings = this._subframes[frame.parentId];
178 siblings.remove(frame);
179 delete this._frameIds[frameId];
182 this.dispatchEventToListeners(WebInspector.ResourceTreeModel.EventTypes.FrameDetached, frameId);
185 _onResourceStarted: function(event)
187 if (!this._cachedResourcesProcessed)
189 this._bindResourceURL(event.data);
192 _onResourceUpdated: function(event)
194 if (!this._cachedResourcesProcessed)
197 var resource = event.data;
198 if (resource.failed) {
199 this._unbindResourceURL(resource);
203 if (resource.type === WebInspector.Resource.Type.XHR) {
204 this._unbindResourceURL(resource);
208 if (resource.finished)
209 this._addResourceToFrame(resource);
212 _addResourceToFrame: function(resource)
214 var frameId = resource.frameId;
215 var resourcesForFrame = this._resourcesByFrameId[frameId];
216 if (!resourcesForFrame) {
217 resourcesForFrame = {};
218 this._resourcesByFrameId[frameId] = resourcesForFrame;
220 if (resourcesForFrame[resource.url] === resource) {
221 // Already in the tree, we just got an extra update.
225 resourcesForFrame[resource.url] = resource;
226 this._bindResourceURL(resource);
227 this.dispatchEventToListeners(WebInspector.ResourceTreeModel.EventTypes.ResourceAdded, resource);
230 forAllResources: function(callback)
232 return this._callForFrameResources("", callback);
235 _consoleMessageAdded: function(event)
237 var msg = event.data;
238 var resource = this.resourceForURL(msg.url);
240 this._addConsoleMessageToResource(msg, resource);
242 this._addPendingConsoleMessage(msg);
245 _addPendingConsoleMessage: function(msg)
249 if (!this._pendingConsoleMessages[msg.url])
250 this._pendingConsoleMessages[msg.url] = [];
251 this._pendingConsoleMessages[msg.url].push(msg);
254 _addPendingConsoleMessagesToResource: function(resource)
256 var messages = this._pendingConsoleMessages[resource.url];
258 for (var i = 0; i < messages.length; i++)
259 this._addConsoleMessageToResource(messages[i], resource);
260 delete this._pendingConsoleMessages[resource.url];
264 _addConsoleMessageToResource: function(msg, resource)
267 case WebInspector.ConsoleMessage.MessageLevel.Warning:
268 resource.warnings += msg.repeatDelta;
270 case WebInspector.ConsoleMessage.MessageLevel.Error:
271 resource.errors += msg.repeatDelta;
274 resource.addMessage(msg);
277 _consoleCleared: function()
279 function callback(resource)
281 resource.clearErrorsAndWarnings();
284 this._pendingConsoleMessages = {};
285 this.forAllResources(callback);
288 resourceForURL: function(url)
290 return this._resourcesByURL[url];
293 _bindResourceURL: function(resource)
295 this._resourcesByURL[resource.url] = resource;
297 this._addPendingConsoleMessagesToResource(resource);
300 _clearChildFramesAndResources: function(frameId, loaderToPreserveId)
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);
308 delete this._subframes[frameId];
311 _clearResources: function(frameId, loaderToPreserveId)
313 var resourcesForFrame = this._resourcesByFrameId[frameId];
314 if (!resourcesForFrame)
317 var preservedResourcesForFrame = [];
318 for (var url in resourcesForFrame) {
319 var resource = resourcesForFrame[url];
320 if (resource.loaderId === loaderToPreserveId) {
321 preservedResourcesForFrame[url] = resource;
324 this._unbindResourceURL(resource);
327 delete this._resourcesByFrameId[frameId];
328 if (preservedResourcesForFrame.length) {
329 this._resourcesByFrameId[frameId] = preservedResourcesForFrame;
333 _callForFrameResources: function(frameId, callback)
335 var resources = this._resourcesByFrameId[frameId];
337 for (var url in resources) {
338 if (callback(resources[url]))
342 var frames = this._subframes[frameId];
343 for (var i = 0; frames && i < frames.length; ++i) {
344 if (this._callForFrameResources(frames[i].id, callback))
350 _unbindResourceURL: function(resource)
352 delete this._resourcesByURL[resource.url];
355 _addFramesRecursively: function(frameTreePayload)
357 var framePayload = frameTreePayload.frame;
359 // Create frame resource.
360 var frameResource = this._createResource(framePayload, framePayload.url);
361 frameResource.type = WebInspector.Resource.Type.Document;
362 frameResource.finished = true;
364 if (!framePayload.parentId) {
365 WebInspector.mainResource = frameResource;
366 this._currentMainFrameId = framePayload.id;
368 this._addFrame(framePayload);
369 this._addResourceToFrame(frameResource);
371 for (var i = 0; frameTreePayload.childFrames && i < frameTreePayload.childFrames.length; ++i)
372 this._addFramesRecursively(frameTreePayload.childFrames[i]);
374 if (!frameTreePayload.resources)
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);
388 _createResource: function(frame, url)
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;
398 WebInspector.ResourceTreeModel.prototype.__proto__ = WebInspector.Object.prototype;
402 * @implements {PageAgent.Dispatcher}
404 WebInspector.PageDispatcher = function(resourceTreeModel)
406 this._resourceTreeModel = resourceTreeModel;
409 WebInspector.PageDispatcher.prototype = {
410 domContentEventFired: function(time)
412 this._resourceTreeModel.dispatchEventToListeners(WebInspector.ResourceTreeModel.EventTypes.DOMContentLoaded, time);
414 // FIXME: the only client is HAR, fix it there.
415 WebInspector.mainResourceDOMContentTime = time;
418 loadEventFired: function(time)
420 this._resourceTreeModel.dispatchEventToListeners(WebInspector.ResourceTreeModel.EventTypes.OnLoad, time);
422 // FIXME: the only client is HAR, fix it there.
423 WebInspector.mainResourceLoadTime = time;
426 frameNavigated: function(frame, loaderId)
428 this._resourceTreeModel._frameNavigated(frame, loaderId);
431 frameDetached: function(frameId)
433 this._resourceTreeModel._frameDetached(frameId);