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 CachedResourcesLoaded: "CachedResourcesLoaded" 49 } 50 51 WebInspector.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 310 WebInspector.ResourceTreeModel.prototype.__proto__ = WebInspector.Object.prototype; 311