Home | History | Annotate | Download | only in sdk
      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.SDKModel}
     34  * @param {!WebInspector.Target} target
     35  */
     36 WebInspector.ConsoleModel = function(target)
     37 {
     38     WebInspector.SDKModel.call(this, WebInspector.ConsoleModel, target);
     39 
     40     /** @type {!Array.<!WebInspector.ConsoleMessage>} */
     41     this._messages = [];
     42     this.warnings = 0;
     43     this.errors = 0;
     44     this._consoleAgent = target.consoleAgent();
     45     target.registerConsoleDispatcher(new WebInspector.ConsoleDispatcher(this));
     46     this._enableAgent();
     47 }
     48 
     49 WebInspector.ConsoleModel.Events = {
     50     ConsoleCleared: "ConsoleCleared",
     51     MessageAdded: "MessageAdded",
     52     CommandEvaluated: "CommandEvaluated",
     53 }
     54 
     55 WebInspector.ConsoleModel.prototype = {
     56     _enableAgent: function()
     57     {
     58         if (WebInspector.settings.monitoringXHREnabled.get())
     59             this._consoleAgent.setMonitoringXHREnabled(true);
     60 
     61         this._enablingConsole = true;
     62 
     63         /**
     64          * @this {WebInspector.ConsoleModel}
     65          */
     66         function callback()
     67         {
     68             delete this._enablingConsole;
     69         }
     70         this._consoleAgent.enable(callback.bind(this));
     71     },
     72 
     73     /**
     74      * @return {boolean}
     75      */
     76     enablingConsole: function()
     77     {
     78         return !!this._enablingConsole;
     79     },
     80 
     81     /**
     82      * @param {!WebInspector.ConsoleMessage} msg
     83      */
     84     addMessage: function(msg)
     85     {
     86         if (WebInspector.NetworkManager.hasDevToolsRequestHeader(msg.request))
     87             return;
     88 
     89         msg.index = this._messages.length;
     90         this._messages.push(msg);
     91         this._incrementErrorWarningCount(msg);
     92 
     93         this.dispatchEventToListeners(WebInspector.ConsoleModel.Events.MessageAdded, msg);
     94     },
     95 
     96     /**
     97      * @param {!WebInspector.ConsoleMessage} msg
     98      */
     99     _incrementErrorWarningCount: function(msg)
    100     {
    101         switch (msg.level) {
    102             case WebInspector.ConsoleMessage.MessageLevel.Warning:
    103                 this.warnings++;
    104                 break;
    105             case WebInspector.ConsoleMessage.MessageLevel.Error:
    106                 this.errors++;
    107                 break;
    108         }
    109     },
    110 
    111     /**
    112      * @return {!Array.<!WebInspector.ConsoleMessage>}
    113      */
    114     messages: function()
    115     {
    116         return this._messages;
    117     },
    118 
    119     requestClearMessages: function()
    120     {
    121         this._consoleAgent.clearMessages();
    122         this._messagesCleared();
    123     },
    124 
    125     _messagesCleared: function()
    126     {
    127         this._messages = [];
    128         this.errors = 0;
    129         this.warnings = 0;
    130         this.dispatchEventToListeners(WebInspector.ConsoleModel.Events.ConsoleCleared);
    131     },
    132 
    133     __proto__: WebInspector.SDKModel.prototype
    134 }
    135 
    136 /**
    137  * @param {!WebInspector.ExecutionContext} executionContext
    138  * @param {string} text
    139  * @param {boolean=} useCommandLineAPI
    140  */
    141 WebInspector.ConsoleModel.evaluateCommandInConsole = function(executionContext, text, useCommandLineAPI)
    142 {
    143     useCommandLineAPI = !!useCommandLineAPI;
    144     var target = executionContext.target();
    145 
    146     var commandMessage = new WebInspector.ConsoleMessage(target, WebInspector.ConsoleMessage.MessageSource.JS, null, text, WebInspector.ConsoleMessage.MessageType.Command);
    147     commandMessage.setExecutionContextId(executionContext.id);
    148     target.consoleModel.addMessage(commandMessage);
    149 
    150     /**
    151      * @param {?WebInspector.RemoteObject} result
    152      * @param {boolean} wasThrown
    153      * @param {?RuntimeAgent.RemoteObject=} valueResult
    154      * @param {?DebuggerAgent.ExceptionDetails=} exceptionDetails
    155      * @this {WebInspector.ConsoleModel}
    156      */
    157     function printResult(result, wasThrown, valueResult, exceptionDetails)
    158     {
    159         if (!result)
    160             return;
    161 
    162         WebInspector.console.show();
    163         this.dispatchEventToListeners(WebInspector.ConsoleModel.Events.CommandEvaluated, {result: result, wasThrown: wasThrown, text: text, commandMessage: commandMessage, exceptionDetails: exceptionDetails});
    164     }
    165 
    166     executionContext.evaluate(text, "console", useCommandLineAPI, false, false, true, printResult.bind(target.consoleModel));
    167 
    168     WebInspector.userMetrics.ConsoleEvaluated.record();
    169 }
    170 
    171 
    172 /**
    173  * @constructor
    174  * @param {?WebInspector.Target} target
    175  * @param {string} source
    176  * @param {?string} level
    177  * @param {string} messageText
    178  * @param {string=} type
    179  * @param {?string=} url
    180  * @param {number=} line
    181  * @param {number=} column
    182  * @param {!NetworkAgent.RequestId=} requestId
    183  * @param {!Array.<!RuntimeAgent.RemoteObject>=} parameters
    184  * @param {!Array.<!ConsoleAgent.CallFrame>=} stackTrace
    185  * @param {number=} timestamp
    186  * @param {boolean=} isOutdated
    187  * @param {!RuntimeAgent.ExecutionContextId=} executionContextId
    188  * @param {!ConsoleAgent.AsyncStackTrace=} asyncStackTrace
    189  * @param {?string=} scriptId
    190  */
    191 WebInspector.ConsoleMessage = function(target, source, level, messageText, type, url, line, column, requestId, parameters, stackTrace, timestamp, isOutdated, executionContextId, asyncStackTrace, scriptId)
    192 {
    193     this._target = target;
    194     this.source = source;
    195     this.level = level;
    196     this.messageText = messageText;
    197     this.type = type || WebInspector.ConsoleMessage.MessageType.Log;
    198     /** @type {string|undefined} */
    199     this.url = url || undefined;
    200     /** @type {number} */
    201     this.line = line || 0;
    202     /** @type {number} */
    203     this.column = column || 0;
    204     this.parameters = parameters;
    205     /** @type {!Array.<!ConsoleAgent.CallFrame>|undefined} */
    206     this.stackTrace = stackTrace;
    207     this.timestamp = timestamp || Date.now();
    208     this.isOutdated = isOutdated;
    209     this.executionContextId = executionContextId || 0;
    210     this.asyncStackTrace = asyncStackTrace;
    211     this.scriptId = scriptId || null;
    212 
    213     this.request = requestId ? target.networkLog.requestForId(requestId) : null;
    214 
    215     if (this.request) {
    216         var initiator = this.request.initiator();
    217         if (initiator) {
    218             this.stackTrace = initiator.stackTrace || undefined;
    219             this.asyncStackTrace = initiator.asyncStackTrace;
    220             if (initiator.url) {
    221                 this.url = initiator.url;
    222                 this.line = initiator.lineNumber || 0;
    223             }
    224         }
    225     }
    226 }
    227 
    228 WebInspector.ConsoleMessage.prototype = {
    229     /**
    230      * @return {?WebInspector.Target}
    231      */
    232     target: function()
    233     {
    234         return this._target;
    235     },
    236 
    237     /**
    238      * @param {!WebInspector.ConsoleMessage} originatingMessage
    239      */
    240     setOriginatingMessage: function(originatingMessage)
    241     {
    242         this._originatingConsoleMessage = originatingMessage;
    243         this.executionContextId = originatingMessage.executionContextId;
    244     },
    245 
    246     /**
    247      * @param {!RuntimeAgent.ExecutionContextId} executionContextId
    248      */
    249     setExecutionContextId: function(executionContextId)
    250     {
    251         this.executionContextId = executionContextId;
    252     },
    253 
    254     /**
    255      * @return {?WebInspector.ConsoleMessage}
    256      */
    257     originatingMessage: function()
    258     {
    259         return this._originatingConsoleMessage;
    260     },
    261 
    262     /**
    263      * @return {boolean}
    264      */
    265     isGroupMessage: function()
    266     {
    267         return this.type === WebInspector.ConsoleMessage.MessageType.StartGroup ||
    268             this.type === WebInspector.ConsoleMessage.MessageType.StartGroupCollapsed ||
    269             this.type === WebInspector.ConsoleMessage.MessageType.EndGroup;
    270     },
    271 
    272     /**
    273      * @return {boolean}
    274      */
    275     isGroupStartMessage: function()
    276     {
    277         return this.type === WebInspector.ConsoleMessage.MessageType.StartGroup ||
    278             this.type === WebInspector.ConsoleMessage.MessageType.StartGroupCollapsed;
    279     },
    280 
    281     /**
    282      * @return {boolean}
    283      */
    284     isErrorOrWarning: function()
    285     {
    286         return (this.level === WebInspector.ConsoleMessage.MessageLevel.Warning || this.level === WebInspector.ConsoleMessage.MessageLevel.Error);
    287     },
    288 
    289     /**
    290      * @return {!WebInspector.ConsoleMessage}
    291      */
    292     clone: function()
    293     {
    294         return new WebInspector.ConsoleMessage(
    295             this.target(),
    296             this.source,
    297             this.level,
    298             this.messageText,
    299             this.type,
    300             this.url,
    301             this.line,
    302             this.column,
    303             this.request ? this.request.requestId : undefined,
    304             this.parameters,
    305             this.stackTrace,
    306             this.timestamp,
    307             this.isOutdated,
    308             this.executionContextId,
    309             this.asyncStackTrace,
    310             this.scriptId);
    311     },
    312 
    313     /**
    314      * @param {?WebInspector.ConsoleMessage} msg
    315      * @return {boolean}
    316      */
    317     isEqual: function(msg)
    318     {
    319         if (!msg)
    320             return false;
    321 
    322         if (!this._isEqualStackTraces(this.stackTrace, msg.stackTrace))
    323             return false;
    324 
    325         var asyncTrace1 = this.asyncStackTrace;
    326         var asyncTrace2 = msg.asyncStackTrace;
    327         while (asyncTrace1 || asyncTrace2) {
    328             if (!asyncTrace1 || !asyncTrace2)
    329                 return false;
    330             if (asyncTrace1.description !== asyncTrace2.description)
    331                 return false;
    332             if (!this._isEqualStackTraces(asyncTrace1.callFrames, asyncTrace2.callFrames))
    333                 return false;
    334             asyncTrace1 = asyncTrace1.asyncStackTrace;
    335             asyncTrace2 = asyncTrace2.asyncStackTrace;
    336         }
    337 
    338         if (this.parameters) {
    339             if (!msg.parameters || this.parameters.length !== msg.parameters.length)
    340                 return false;
    341 
    342             for (var i = 0; i < msg.parameters.length; ++i) {
    343                 // Never treat objects as equal - their properties might change over time.
    344                 if (this.parameters[i].type !== msg.parameters[i].type || msg.parameters[i].type === "object" || this.parameters[i].value !== msg.parameters[i].value)
    345                     return false;
    346             }
    347         }
    348 
    349         return (this.target() === msg.target())
    350             && (this.source === msg.source)
    351             && (this.type === msg.type)
    352             && (this.level === msg.level)
    353             && (this.line === msg.line)
    354             && (this.url === msg.url)
    355             && (this.messageText === msg.messageText)
    356             && (this.request === msg.request)
    357             && (this.executionContextId === msg.executionContextId)
    358             && (this.scriptId === msg.scriptId);
    359     },
    360 
    361     /**
    362      * @param {!Array.<!ConsoleAgent.CallFrame>|undefined} stackTrace1
    363      * @param {!Array.<!ConsoleAgent.CallFrame>|undefined} stackTrace2
    364      * @return {boolean}
    365      */
    366     _isEqualStackTraces: function(stackTrace1, stackTrace2)
    367     {
    368         stackTrace1 = stackTrace1 || [];
    369         stackTrace2 = stackTrace2 || [];
    370         if (stackTrace1.length !== stackTrace2.length)
    371             return false;
    372         for (var i = 0, n = stackTrace1.length; i < n; ++i) {
    373             if (stackTrace1[i].url !== stackTrace2[i].url ||
    374                 stackTrace1[i].functionName !== stackTrace2[i].functionName ||
    375                 stackTrace1[i].lineNumber !== stackTrace2[i].lineNumber ||
    376                 stackTrace1[i].columnNumber !== stackTrace2[i].columnNumber)
    377                 return false;
    378         }
    379         return true;
    380     }
    381 }
    382 
    383 // Note: Keep these constants in sync with the ones in Console.h
    384 /**
    385  * @enum {string}
    386  */
    387 WebInspector.ConsoleMessage.MessageSource = {
    388     XML: "xml",
    389     JS: "javascript",
    390     Network: "network",
    391     ConsoleAPI: "console-api",
    392     Storage: "storage",
    393     AppCache: "appcache",
    394     Rendering: "rendering",
    395     CSS: "css",
    396     Security: "security",
    397     Other: "other",
    398     Deprecation: "deprecation"
    399 }
    400 
    401 /**
    402  * @enum {string}
    403  */
    404 WebInspector.ConsoleMessage.MessageType = {
    405     Log: "log",
    406     Dir: "dir",
    407     DirXML: "dirxml",
    408     Table: "table",
    409     Trace: "trace",
    410     Clear: "clear",
    411     StartGroup: "startGroup",
    412     StartGroupCollapsed: "startGroupCollapsed",
    413     EndGroup: "endGroup",
    414     Assert: "assert",
    415     Result: "result",
    416     Profile: "profile",
    417     ProfileEnd: "profileEnd",
    418     Command: "command"
    419 }
    420 
    421 /**
    422  * @enum {string}
    423  */
    424 WebInspector.ConsoleMessage.MessageLevel = {
    425     Log: "log",
    426     Info: "info",
    427     Warning: "warning",
    428     Error: "error",
    429     Debug: "debug"
    430 };
    431 
    432 WebInspector.ConsoleMessage._messageLevelPriority = {
    433     "debug": 0,
    434     "log": 1,
    435     "info": 2,
    436     "warning": 3,
    437     "error": 4
    438 };
    439 
    440 /**
    441  * @param {!WebInspector.ConsoleMessage} a
    442  * @param {!WebInspector.ConsoleMessage} b
    443  * @return {number}
    444  */
    445 WebInspector.ConsoleMessage.messageLevelComparator = function(a, b)
    446 {
    447     return WebInspector.ConsoleMessage._messageLevelPriority[a.level] - WebInspector.ConsoleMessage._messageLevelPriority[b.level];
    448 }
    449 
    450 /**
    451  * @param {!WebInspector.ConsoleMessage} a
    452  * @param {!WebInspector.ConsoleMessage} b
    453  * @return {number}
    454  */
    455 WebInspector.ConsoleMessage.timestampComparator = function (a, b)
    456 {
    457     return a.timestamp - b.timestamp;
    458 }
    459 
    460 /**
    461  * @constructor
    462  * @implements {ConsoleAgent.Dispatcher}
    463  * @param {!WebInspector.ConsoleModel} console
    464  */
    465 WebInspector.ConsoleDispatcher = function(console)
    466 {
    467     this._console = console;
    468 }
    469 
    470 WebInspector.ConsoleDispatcher.prototype = {
    471     /**
    472      * @param {!ConsoleAgent.ConsoleMessage} payload
    473      */
    474     messageAdded: function(payload)
    475     {
    476         var consoleMessage = new WebInspector.ConsoleMessage(
    477             this._console.target(),
    478             payload.source,
    479             payload.level,
    480             payload.text,
    481             payload.type,
    482             payload.url,
    483             payload.line,
    484             payload.column,
    485             payload.networkRequestId,
    486             payload.parameters,
    487             payload.stackTrace,
    488             payload.timestamp * 1000, // Convert to ms.
    489             this._console._enablingConsole,
    490             payload.executionContextId,
    491             payload.asyncStackTrace,
    492             payload.scriptId);
    493         this._console.addMessage(consoleMessage);
    494     },
    495 
    496     /**
    497      * @param {number} count
    498      */
    499     messageRepeatCountUpdated: function(count)
    500     {
    501     },
    502 
    503     messagesCleared: function()
    504     {
    505         if (!WebInspector.settings.preserveConsoleLog.get())
    506             this._console._messagesCleared();
    507     }
    508 }
    509 
    510 /**
    511  * @constructor
    512  * @extends {WebInspector.Object}
    513  * @implements {WebInspector.TargetManager.Observer}
    514  */
    515 WebInspector.MultitargetConsoleModel = function()
    516 {
    517     WebInspector.targetManager.observeTargets(this);
    518     WebInspector.targetManager.addModelListener(WebInspector.ConsoleModel, WebInspector.ConsoleModel.Events.MessageAdded, this._consoleMessageAdded, this);
    519     WebInspector.targetManager.addModelListener(WebInspector.ConsoleModel, WebInspector.ConsoleModel.Events.CommandEvaluated, this._commandEvaluated, this);
    520 }
    521 
    522 WebInspector.MultitargetConsoleModel.prototype = {
    523     /**
    524      * @param {!WebInspector.Target} target
    525      */
    526     targetAdded: function(target)
    527     {
    528         if (!this._mainTarget) {
    529             this._mainTarget = target;
    530             target.consoleModel.addEventListener(WebInspector.ConsoleModel.Events.ConsoleCleared, this._consoleCleared, this);
    531         }
    532     },
    533 
    534     /**
    535      * @param {!WebInspector.Target} target
    536      */
    537     targetRemoved: function(target)
    538     {
    539         if (this._mainTarget === target) {
    540             delete this._mainTarget;
    541             target.consoleModel.removeEventListener(WebInspector.ConsoleModel.Events.ConsoleCleared, this._consoleCleared, this);
    542         }
    543     },
    544 
    545     /**
    546      * @return {!Array.<!WebInspector.ConsoleMessage>}
    547      */
    548     messages: function()
    549     {
    550         var targets = WebInspector.targetManager.targets();
    551         var result = [];
    552         for (var i = 0; i < targets.length; ++i)
    553             result = result.concat(targets[i].consoleModel.messages());
    554         return result;
    555     },
    556 
    557     _consoleCleared: function()
    558     {
    559         this.dispatchEventToListeners(WebInspector.ConsoleModel.Events.ConsoleCleared);
    560     },
    561 
    562     /**
    563      * @param {!WebInspector.Event} event
    564      */
    565     _consoleMessageAdded: function(event)
    566     {
    567         this.dispatchEventToListeners(WebInspector.ConsoleModel.Events.MessageAdded, event.data);
    568     },
    569 
    570     /**
    571      * @param {!WebInspector.Event} event
    572      */
    573     _commandEvaluated: function(event)
    574     {
    575         this.dispatchEventToListeners(WebInspector.ConsoleModel.Events.CommandEvaluated, event.data);
    576     },
    577 
    578     __proto__: WebInspector.Object.prototype
    579 }
    580 
    581 /**
    582  * @type {!WebInspector.MultitargetConsoleModel}
    583  */
    584 WebInspector.multitargetConsoleModel;
    585