1/* 2 * Copyright (C) 2012 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 * @param {!WebInspector.Workspace} workspace 34 */ 35WebInspector.PresentationConsoleMessageHelper = function(workspace) 36{ 37 this._workspace = workspace; 38 39 /** @type {!Object.<string, !Array.<!WebInspector.ConsoleMessage>>} */ 40 this._pendingConsoleMessages = {}; 41 42 /** @type {!Array.<!WebInspector.PresentationConsoleMessage>} */ 43 this._presentationConsoleMessages = []; 44 45 /** @type {!Map.<!WebInspector.UISourceCode, !Array.<!WebInspector.PresentationConsoleMessage>>} */ 46 this._uiSourceCodeToMessages = new Map(); 47 48 /** @type {!Map.<!WebInspector.UISourceCode, !WebInspector.Object>} */ 49 this._uiSourceCodeToEventTarget = new Map(); 50 51 workspace.addEventListener(WebInspector.Workspace.Events.UISourceCodeRemoved, this._uiSourceCodeRemoved, this); 52 workspace.addEventListener(WebInspector.Workspace.Events.ProjectRemoved, this._projectRemoved, this); 53 WebInspector.multitargetConsoleModel.addEventListener(WebInspector.ConsoleModel.Events.ConsoleCleared, this._consoleCleared, this); 54 WebInspector.multitargetConsoleModel.addEventListener(WebInspector.ConsoleModel.Events.MessageAdded, this._onConsoleMessageAdded, this); 55 WebInspector.multitargetConsoleModel.messages().forEach(this._consoleMessageAdded, this); 56 WebInspector.targetManager.addModelListener(WebInspector.DebuggerModel, WebInspector.DebuggerModel.Events.ParsedScriptSource, this._parsedScriptSource, this); 57 WebInspector.targetManager.addModelListener(WebInspector.DebuggerModel, WebInspector.DebuggerModel.Events.FailedToParseScriptSource, this._parsedScriptSource, this); 58 WebInspector.targetManager.addModelListener(WebInspector.DebuggerModel, WebInspector.DebuggerModel.Events.GlobalObjectCleared, this._debuggerReset, this); 59} 60 61/** 62 * @enum {string} 63 */ 64WebInspector.PresentationConsoleMessageHelper.Events = { 65 ConsoleMessageAdded: "ConsoleMessageAdded", 66 ConsoleMessageRemoved: "ConsoleMessageRemoved", 67 ConsoleMessagesCleared: "ConsoleMessagesCleared", 68} 69 70WebInspector.PresentationConsoleMessageHelper.prototype = { 71 /** 72 * @param {!WebInspector.PresentationConsoleMessageHelper.Events} eventType 73 * @param {!WebInspector.UISourceCode} uiSourceCode 74 * @param {function(!WebInspector.Event)} listener 75 * @param {!Object=} thisObject 76 */ 77 addConsoleMessageEventListener: function(eventType, uiSourceCode, listener, thisObject) 78 { 79 var target = this._uiSourceCodeToEventTarget.get(uiSourceCode); 80 if (!target) { 81 target = new WebInspector.Object(); 82 this._uiSourceCodeToEventTarget.set(uiSourceCode, target); 83 } 84 target.addEventListener(eventType, listener, thisObject); 85 }, 86 87 /** 88 * @param {!WebInspector.PresentationConsoleMessageHelper.Events} eventType 89 * @param {!WebInspector.UISourceCode} uiSourceCode 90 * @param {function(!WebInspector.Event)} listener 91 * @param {!Object=} thisObject 92 */ 93 removeConsoleMessageEventListener: function(eventType, uiSourceCode, listener, thisObject) 94 { 95 var target = this._uiSourceCodeToEventTarget.get(uiSourceCode); 96 if (!target) 97 return; 98 target.removeEventListener(eventType, listener, thisObject); 99 }, 100 101 /** 102 * @param {!WebInspector.UISourceCode} uiSourceCode 103 * @return {!Array.<!WebInspector.PresentationConsoleMessage>} 104 */ 105 consoleMessages: function(uiSourceCode) 106 { 107 return this._uiSourceCodeToMessages.get(uiSourceCode) || []; 108 }, 109 110 /** 111 * @param {!WebInspector.PresentationConsoleMessageHelper.Events} eventType 112 * @param {!WebInspector.UISourceCode} uiSourceCode 113 * @param {!WebInspector.PresentationConsoleMessage=} message 114 */ 115 _dispatchConsoleEvent: function(eventType, uiSourceCode, message) 116 { 117 var target = this._uiSourceCodeToEventTarget.get(uiSourceCode); 118 if (!target) 119 return; 120 target.dispatchEventToListeners(eventType, message); 121 }, 122 123 /** 124 * @param {!WebInspector.Event} event 125 */ 126 _uiSourceCodeRemoved: function(event) 127 { 128 var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (event.data); 129 this._uiSourceCodeToEventTarget.remove(uiSourceCode); 130 this._uiSourceCodeToMessages.remove(uiSourceCode); 131 }, 132 133 /** 134 * @param {!WebInspector.Event} event 135 */ 136 _projectRemoved: function(event) 137 { 138 var project = /** @type {!WebInspector.Project} */ (event.data); 139 var uiSourceCodes = project.uiSourceCodes(); 140 for (var i = 0; i < uiSourceCodes.length; ++i) { 141 this._uiSourceCodeToEventTarget.remove(uiSourceCodes[i]); 142 this._uiSourceCodeToMessages.remove(uiSourceCodes[i]); 143 } 144 }, 145 146 /** 147 * @param {!WebInspector.Event} event 148 */ 149 _onConsoleMessageAdded: function(event) 150 { 151 var message = /** @type {!WebInspector.ConsoleMessage} */ (event.data); 152 this._consoleMessageAdded(message) 153 }, 154 155 /** 156 * @param {!WebInspector.ConsoleMessage} message 157 */ 158 _consoleMessageAdded: function(message) 159 { 160 if (!message.url || !message.isErrorOrWarning()) 161 return; 162 163 var rawLocation = this._rawLocation(message); 164 if (rawLocation) 165 this._addConsoleMessageToScript(message, rawLocation); 166 else 167 this._addPendingConsoleMessage(message); 168 }, 169 170 /** 171 * @param {!WebInspector.ConsoleMessage} message 172 * @return {?WebInspector.DebuggerModel.Location} 173 */ 174 _rawLocation: function(message) 175 { 176 // FIXME(62725): stack trace line/column numbers are one-based. 177 var lineNumber = message.stackTrace ? message.stackTrace[0].lineNumber - 1 : message.line - 1; 178 var columnNumber = message.stackTrace && message.stackTrace[0].columnNumber ? message.stackTrace[0].columnNumber - 1 : 0; 179 if (message.scriptId) 180 return message.target().debuggerModel.createRawLocationByScriptId(message.scriptId, message.url || "", lineNumber, columnNumber); 181 return message.target().debuggerModel.createRawLocationByURL(message.url || "", lineNumber, columnNumber); 182 }, 183 184 /** 185 * @param {!WebInspector.ConsoleMessage} message 186 * @param {!WebInspector.DebuggerModel.Location} rawLocation 187 */ 188 _addConsoleMessageToScript: function(message, rawLocation) 189 { 190 this._presentationConsoleMessages.push(new WebInspector.PresentationConsoleMessage(message, rawLocation)); 191 }, 192 193 /** 194 * @param {!WebInspector.ConsoleMessage} message 195 */ 196 _addPendingConsoleMessage: function(message) 197 { 198 if (!message.url) 199 return; 200 if (!this._pendingConsoleMessages[message.url]) 201 this._pendingConsoleMessages[message.url] = []; 202 this._pendingConsoleMessages[message.url].push(message); 203 }, 204 205 /** 206 * @param {!WebInspector.Event} event 207 */ 208 _parsedScriptSource: function(event) 209 { 210 var script = /** @type {!WebInspector.Script} */ (event.data); 211 212 var messages = this._pendingConsoleMessages[script.sourceURL]; 213 if (!messages) 214 return; 215 216 var pendingMessages = []; 217 for (var i = 0; i < messages.length; i++) { 218 var message = messages[i]; 219 var rawLocation = this._rawLocation(message); 220 if (script.target() === message.target() && script.scriptId === rawLocation.scriptId) 221 this._addConsoleMessageToScript(message, rawLocation); 222 else 223 pendingMessages.push(message); 224 } 225 226 if (pendingMessages.length) 227 this._pendingConsoleMessages[script.sourceURL] = pendingMessages; 228 else 229 delete this._pendingConsoleMessages[script.sourceURL]; 230 }, 231 232 /** 233 * @param {!WebInspector.PresentationConsoleMessage} message 234 */ 235 _presentationConsoleMessageAdded: function(message) 236 { 237 var uiSourceCode = message._uiLocation.uiSourceCode; 238 var messages = this._uiSourceCodeToMessages.get(uiSourceCode); 239 if (!messages) { 240 messages = []; 241 this._uiSourceCodeToMessages.set(uiSourceCode, messages); 242 } 243 messages.push(message); 244 this._dispatchConsoleEvent(WebInspector.PresentationConsoleMessageHelper.Events.ConsoleMessageAdded, uiSourceCode, message); 245 }, 246 247 /** 248 * @param {!WebInspector.PresentationConsoleMessage} message 249 */ 250 _presentationConsoleMessageRemoved: function(message) 251 { 252 var uiSourceCode = message._uiLocation.uiSourceCode; 253 var messages = this._uiSourceCodeToMessages.get(uiSourceCode); 254 if (!messages) 255 return; 256 messages.remove(message); 257 this._dispatchConsoleEvent(WebInspector.PresentationConsoleMessageHelper.Events.ConsoleMessageRemoved, uiSourceCode, message); 258 }, 259 260 _consoleCleared: function() 261 { 262 this._pendingConsoleMessages = {}; 263 for (var i = 0; i < this._presentationConsoleMessages.length; ++i) 264 this._presentationConsoleMessages[i].dispose(); 265 this._presentationConsoleMessages = []; 266 var targets = this._uiSourceCodeToEventTarget.values(); 267 for (var i = 0; i < targets.length; ++i) 268 targets[i].dispatchEventToListeners(WebInspector.PresentationConsoleMessageHelper.Events.ConsoleMessagesCleared); 269 this._uiSourceCodeToMessages.clear(); 270 }, 271 272 _debuggerReset: function() 273 { 274 this._pendingConsoleMessages = {}; 275 this._presentationConsoleMessages = []; 276 } 277} 278 279/** 280 * @constructor 281 * @param {!WebInspector.ConsoleMessage} message 282 * @param {!WebInspector.DebuggerModel.Location} rawLocation 283 */ 284WebInspector.PresentationConsoleMessage = function(message, rawLocation) 285{ 286 this.originalMessage = message; 287 this._liveLocation = WebInspector.debuggerWorkspaceBinding.createLiveLocation(rawLocation, this._updateLocation.bind(this)); 288} 289 290WebInspector.PresentationConsoleMessage.prototype = { 291 /** 292 * @param {!WebInspector.UILocation} uiLocation 293 */ 294 _updateLocation: function(uiLocation) 295 { 296 if (this._uiLocation) 297 WebInspector.presentationConsoleMessageHelper._presentationConsoleMessageRemoved(this); 298 this._uiLocation = uiLocation; 299 WebInspector.presentationConsoleMessageHelper._presentationConsoleMessageAdded(this); 300 }, 301 302 get lineNumber() 303 { 304 return this._uiLocation.lineNumber; 305 }, 306 307 dispose: function() 308 { 309 this._liveLocation.dispose(); 310 } 311} 312 313/** @type {!WebInspector.PresentationConsoleMessageHelper} */ 314WebInspector.presentationConsoleMessageHelper;