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 */
35WebInspector.ConsoleModel = function()
36{
37    this.messages = [];
38    this.warnings = 0;
39    this.errors = 0;
40    this._interruptRepeatCount = false;
41    InspectorBackend.registerConsoleDispatcher(new WebInspector.ConsoleDispatcher(this));
42}
43
44WebInspector.ConsoleModel.Events = {
45    ConsoleCleared: "console-cleared",
46    MessageAdded: "console-message-added",
47    RepeatCountUpdated: "repeat-count-updated"
48}
49
50WebInspector.ConsoleModel.prototype = {
51    enableAgent: function()
52    {
53        if (WebInspector.settings.monitoringXHREnabled.get())
54            ConsoleAgent.setMonitoringXHREnabled(true);
55
56        this._enablingConsole = true;
57
58        /**
59         * @this {WebInspector.ConsoleModel}
60         */
61        function callback()
62        {
63            delete this._enablingConsole;
64        }
65        ConsoleAgent.enable(callback.bind(this));
66    },
67
68    /**
69     * @return {boolean}
70     */
71    enablingConsole: function()
72    {
73        return !!this._enablingConsole;
74    },
75
76    /**
77     * @param {!WebInspector.ConsoleMessage} msg
78     * @param {boolean=} isFromBackend
79     */
80    addMessage: function(msg, isFromBackend)
81    {
82        if (isFromBackend && WebInspector.SourceMap.hasSourceMapRequestHeader(msg.request()))
83            return;
84
85        msg.index = this.messages.length;
86        this.messages.push(msg);
87        this._incrementErrorWarningCount(msg);
88
89        if (isFromBackend)
90            this._previousMessage = msg;
91
92        this._interruptRepeatCount = !isFromBackend;
93
94        this.dispatchEventToListeners(WebInspector.ConsoleModel.Events.MessageAdded, msg);
95    },
96
97    /**
98     * @param {!WebInspector.ConsoleMessage} msg
99     */
100    _incrementErrorWarningCount: function(msg)
101    {
102        switch (msg.level) {
103            case WebInspector.ConsoleMessage.MessageLevel.Warning:
104                this.warnings += msg.repeatDelta;
105                break;
106            case WebInspector.ConsoleMessage.MessageLevel.Error:
107                this.errors += msg.repeatDelta;
108                break;
109        }
110    },
111
112    requestClearMessages: function()
113    {
114        ConsoleAgent.clearMessages();
115        this.clearMessages();
116    },
117
118    clearMessages: function()
119    {
120        this.dispatchEventToListeners(WebInspector.ConsoleModel.Events.ConsoleCleared);
121
122        this.messages = [];
123        delete this._previousMessage;
124
125        this.errors = 0;
126        this.warnings = 0;
127    },
128
129    /**
130     * @param {number} count
131     */
132    _messageRepeatCountUpdated: function(count)
133    {
134        var msg = this._previousMessage;
135        if (!msg)
136            return;
137
138        var prevRepeatCount = msg.totalRepeatCount;
139
140        if (!this._interruptRepeatCount) {
141            msg.repeatDelta = count - prevRepeatCount;
142            msg.repeatCount = msg.repeatCount + msg.repeatDelta;
143            msg.totalRepeatCount = count;
144            msg.updateRepeatCount();
145
146            this._incrementErrorWarningCount(msg);
147            this.dispatchEventToListeners(WebInspector.ConsoleModel.Events.RepeatCountUpdated, msg);
148        } else {
149            var msgCopy = msg.clone();
150            msgCopy.totalRepeatCount = count;
151            msgCopy.repeatCount = (count - prevRepeatCount) || 1;
152            msgCopy.repeatDelta = msgCopy.repeatCount;
153            this.addMessage(msgCopy, true);
154        }
155    },
156
157    __proto__: WebInspector.Object.prototype
158}
159
160/**
161 * @constructor
162 * @param {string} source
163 * @param {string} level
164 * @param {string=} url
165 * @param {number=} line
166 * @param {number=} column
167 * @param {number=} repeatCount
168 */
169WebInspector.ConsoleMessage = function(source, level, url, line, column, repeatCount)
170{
171    this.source = source;
172    this.level = level;
173    this.url = url || null;
174    this.line = line || 0;
175    this.column = column || 0;
176    this.message = "";
177
178    repeatCount = repeatCount || 1;
179    this.repeatCount = repeatCount;
180    this.repeatDelta = repeatCount;
181    this.totalRepeatCount = repeatCount;
182}
183
184WebInspector.ConsoleMessage.prototype = {
185    /**
186     * @return {boolean}
187     */
188    isErrorOrWarning: function()
189    {
190        return (this.level === WebInspector.ConsoleMessage.MessageLevel.Warning || this.level === WebInspector.ConsoleMessage.MessageLevel.Error);
191    },
192
193    updateRepeatCount: function()
194    {
195        // Implemented by concrete instances
196    },
197
198    /**
199     * @return {!WebInspector.ConsoleMessage}
200     */
201    clone: function()
202    {
203        // Implemented by concrete instances
204    },
205
206    /**
207     * @return {!WebInspector.DebuggerModel.Location}
208     */
209    location: function()
210    {
211        // Implemented by concrete instances
212    }
213}
214
215/**
216 * @param {string} source
217 * @param {string} level
218 * @param {string} message
219 * @param {string=} type
220 * @param {string=} url
221 * @param {number=} line
222 * @param {number=} column
223 * @param {number=} repeatCount
224 * @param {!Array.<!RuntimeAgent.RemoteObject>=} parameters
225 * @param {!ConsoleAgent.StackTrace=} stackTrace
226 * @param {!NetworkAgent.RequestId=} requestId
227 * @param {boolean=} isOutdated
228 * @return {!WebInspector.ConsoleMessage}
229 */
230WebInspector.ConsoleMessage.create = function(source, level, message, type, url, line, column, repeatCount, parameters, stackTrace, requestId, isOutdated)
231{
232}
233
234// Note: Keep these constants in sync with the ones in Console.h
235WebInspector.ConsoleMessage.MessageSource = {
236    XML: "xml",
237    JS: "javascript",
238    Network: "network",
239    ConsoleAPI: "console-api",
240    Storage: "storage",
241    AppCache: "appcache",
242    Rendering: "rendering",
243    CSS: "css",
244    Security: "security",
245    Other: "other",
246    Deprecation: "deprecation"
247}
248
249WebInspector.ConsoleMessage.MessageType = {
250    Log: "log",
251    Dir: "dir",
252    DirXML: "dirxml",
253    Table: "table",
254    Trace: "trace",
255    Clear: "clear",
256    StartGroup: "startGroup",
257    StartGroupCollapsed: "startGroupCollapsed",
258    EndGroup: "endGroup",
259    Assert: "assert",
260    Result: "result",
261    Profile: "profile",
262    ProfileEnd: "profileEnd",
263    Command: "command"
264}
265
266WebInspector.ConsoleMessage.MessageLevel = {
267    Log: "log",
268    Info: "info",
269    Warning: "warning",
270    Error: "error",
271    Debug: "debug"
272}
273
274
275/**
276 * @constructor
277 * @implements {ConsoleAgent.Dispatcher}
278 * @param {!WebInspector.ConsoleModel} console
279 */
280WebInspector.ConsoleDispatcher = function(console)
281{
282    this._console = console;
283}
284
285WebInspector.ConsoleDispatcher.prototype = {
286    /**
287     * @param {!ConsoleAgent.ConsoleMessage} payload
288     */
289    messageAdded: function(payload)
290    {
291        var consoleMessage = WebInspector.ConsoleMessage.create(
292            payload.source,
293            payload.level,
294            payload.text,
295            payload.type,
296            payload.url,
297            payload.line,
298            payload.column,
299            payload.repeatCount,
300            payload.parameters,
301            payload.stackTrace,
302            payload.networkRequestId,
303            this._console._enablingConsole);
304        this._console.addMessage(consoleMessage, true);
305    },
306
307    /**
308     * @param {number} count
309     */
310    messageRepeatCountUpdated: function(count)
311    {
312        this._console._messageRepeatCountUpdated(count);
313    },
314
315    messagesCleared: function()
316    {
317        if (!WebInspector.settings.preserveConsoleLog.get())
318            this._console.clearMessages();
319    }
320}
321
322/**
323 * @type {!WebInspector.ConsoleModel}
324 */
325WebInspector.console;
326