home *** CD-ROM | disk | FTP | other *** search
/ Computer Active Guide 2009 July / CAG7.ISO / Internetas / SafariSetup.exe / AppleApplicationSupport.msi / WebKit.resources_inspector_ConsoleView.js < prev    next >
Encoding:
Text File  |  2010-06-03  |  38.4 KB  |  1,058 lines

  1. /*
  2.  * Copyright (C) 2007, 2008 Apple Inc.  All rights reserved.
  3.  * Copyright (C) 2009 Joseph Pecoraro
  4.  *
  5.  * Redistribution and use in source and binary forms, with or without
  6.  * modification, are permitted provided that the following conditions
  7.  * are met:
  8.  *
  9.  * 1.  Redistributions of source code must retain the above copyright
  10.  *     notice, this list of conditions and the following disclaimer. 
  11.  * 2.  Redistributions in binary form must reproduce the above copyright
  12.  *     notice, this list of conditions and the following disclaimer in the
  13.  *     documentation and/or other materials provided with the distribution. 
  14.  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
  15.  *     its contributors may be used to endorse or promote products derived
  16.  *     from this software without specific prior written permission. 
  17.  *
  18.  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
  19.  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  20.  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  21.  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
  22.  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  23.  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  24.  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  25.  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  26.  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  27.  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  28.  */
  29.  
  30. const ExpressionStopCharacters = " =:[({;,!+-*/&|^<>";
  31.  
  32. WebInspector.ConsoleView = function(drawer)
  33. {
  34.     WebInspector.View.call(this, document.getElementById("console-view"));
  35.  
  36.     this.messages = [];
  37.     this.drawer = drawer;
  38.  
  39.     this.clearButton = document.getElementById("clear-console-status-bar-item");
  40.     this.clearButton.title = WebInspector.UIString("Clear console log.");
  41.     this.clearButton.addEventListener("click", this._clearButtonClicked.bind(this), false);
  42.  
  43.     this.messagesElement = document.getElementById("console-messages");
  44.     this.messagesElement.addEventListener("selectstart", this._messagesSelectStart.bind(this), false);
  45.     this.messagesElement.addEventListener("click", this._messagesClicked.bind(this), true);
  46.  
  47.     this.promptElement = document.getElementById("console-prompt");
  48.     this.promptElement.className = "source-code";
  49.     this.promptElement.addEventListener("keydown", this._promptKeyDown.bind(this), true);
  50.     this.prompt = new WebInspector.TextPrompt(this.promptElement, this.completions.bind(this), ExpressionStopCharacters + ".");
  51.     WebInspector.settings.addEventListener("loaded", this._settingsLoaded, this);
  52.  
  53.     this.topGroup = new WebInspector.ConsoleGroup(null, 0);
  54.     this.messagesElement.insertBefore(this.topGroup.element, this.promptElement);
  55.     this.groupLevel = 0;
  56.     this.currentGroup = this.topGroup;
  57.  
  58.     this.toggleConsoleButton = document.getElementById("console-status-bar-item");
  59.     this.toggleConsoleButton.title = WebInspector.UIString("Show console.");
  60.     this.toggleConsoleButton.addEventListener("click", this._toggleConsoleButtonClicked.bind(this), false);
  61.  
  62.     // Will hold the list of filter elements
  63.     this.filterBarElement = document.getElementById("console-filter");
  64.  
  65.     function createDividerElement() {
  66.         var dividerElement = document.createElement("div");
  67.         dividerElement.addStyleClass("divider");
  68.         this.filterBarElement.appendChild(dividerElement);
  69.     }
  70.  
  71.     var updateFilterHandler = this._updateFilter.bind(this);
  72.     function createFilterElement(category) {
  73.         var categoryElement = document.createElement("li");
  74.         categoryElement.category = category;
  75.         categoryElement.addStyleClass(categoryElement.category);            
  76.         categoryElement.addEventListener("click", updateFilterHandler, false);
  77.  
  78.         var label = category.toString();
  79.         categoryElement.appendChild(document.createTextNode(label));
  80.  
  81.         this.filterBarElement.appendChild(categoryElement);
  82.         return categoryElement;
  83.     }
  84.     
  85.     this.allElement = createFilterElement.call(this, "All");
  86.     createDividerElement.call(this);
  87.     this.errorElement = createFilterElement.call(this, "Errors");
  88.     this.warningElement = createFilterElement.call(this, "Warnings");
  89.     this.logElement = createFilterElement.call(this, "Logs");
  90.  
  91.     this.filter(this.allElement, false);
  92.  
  93.     this._shortcuts = {};
  94.  
  95.     var shortcut;
  96.  
  97.     shortcut = WebInspector.KeyboardShortcut.makeKey("k", WebInspector.KeyboardShortcut.Modifiers.Meta);
  98.     // This case requires a separate bound function as its isMacOnly property should not be shared among different shortcut handlers.
  99.     this._shortcuts[shortcut] = this.requestClearMessages.bind(this);
  100.     this._shortcuts[shortcut].isMacOnly = true;
  101.  
  102.     var clearConsoleHandler = this.requestClearMessages.bind(this);
  103.     shortcut = WebInspector.KeyboardShortcut.makeKey("l", WebInspector.KeyboardShortcut.Modifiers.Ctrl);
  104.     this._shortcuts[shortcut] = clearConsoleHandler;
  105.  
  106.     // Since the Context Menu for the Console View will always be the same, we can create it in
  107.     // the constructor.
  108.     this._contextMenu = new WebInspector.ContextMenu();
  109.     this._contextMenu.appendItem(WebInspector.UIString("Clear Console"), clearConsoleHandler);
  110.     this.messagesElement.addEventListener("contextmenu", this._handleContextMenuEvent.bind(this), true);
  111.     
  112.     this._customFormatters = {
  113.         "object": this._formatobject,
  114.         "array":  this._formatarray,
  115.         "node":   this._formatnode,
  116.         "string": this._formatstring
  117.     };
  118. }
  119.  
  120. WebInspector.ConsoleView.prototype = {
  121.     _settingsLoaded: function()
  122.     {
  123.         this.prompt.history = WebInspector.settings.consoleHistory;
  124.     },
  125.     
  126.     _updateFilter: function(e)
  127.     {
  128.         var isMac = WebInspector.isMac();
  129.         var selectMultiple = false;
  130.         if (isMac && e.metaKey && !e.ctrlKey && !e.altKey && !e.shiftKey)
  131.             selectMultiple = true;
  132.         if (!isMac && e.ctrlKey && !e.metaKey && !e.altKey && !e.shiftKey)
  133.             selectMultiple = true;
  134.  
  135.         this.filter(e.target, selectMultiple);
  136.     },
  137.     
  138.     filter: function(target, selectMultiple)
  139.     {
  140.         function unselectAll()
  141.         {
  142.             this.allElement.removeStyleClass("selected");
  143.             this.errorElement.removeStyleClass("selected");
  144.             this.warningElement.removeStyleClass("selected");
  145.             this.logElement.removeStyleClass("selected");
  146.             
  147.             this.messagesElement.removeStyleClass("filter-all");
  148.             this.messagesElement.removeStyleClass("filter-errors");
  149.             this.messagesElement.removeStyleClass("filter-warnings");
  150.             this.messagesElement.removeStyleClass("filter-logs");
  151.         }
  152.         
  153.         var targetFilterClass = "filter-" + target.category.toLowerCase();
  154.  
  155.         if (target.category == "All") {
  156.             if (target.hasStyleClass("selected")) {
  157.                 // We can't unselect all, so we break early here
  158.                 return;
  159.             }
  160.  
  161.             unselectAll.call(this);
  162.         } else {
  163.             // Something other than all is being selected, so we want to unselect all
  164.             if (this.allElement.hasStyleClass("selected")) {
  165.                 this.allElement.removeStyleClass("selected");
  166.                 this.messagesElement.removeStyleClass("filter-all");
  167.             }
  168.         }
  169.         
  170.         if (!selectMultiple) {
  171.             // If multiple selection is off, we want to unselect everything else
  172.             // and just select ourselves.
  173.             unselectAll.call(this);
  174.             
  175.             target.addStyleClass("selected");
  176.             this.messagesElement.addStyleClass(targetFilterClass);
  177.             
  178.             return;
  179.         }
  180.         
  181.         if (target.hasStyleClass("selected")) {
  182.             // If selectMultiple is turned on, and we were selected, we just
  183.             // want to unselect ourselves.
  184.             target.removeStyleClass("selected");
  185.             this.messagesElement.removeStyleClass(targetFilterClass);
  186.         } else {
  187.             // If selectMultiple is turned on, and we weren't selected, we just
  188.             // want to select ourselves.
  189.             target.addStyleClass("selected");
  190.             this.messagesElement.addStyleClass(targetFilterClass);
  191.         }
  192.     },
  193.     
  194.     _toggleConsoleButtonClicked: function()
  195.     {
  196.         this.drawer.visibleView = this;
  197.     },
  198.  
  199.     attach: function(mainElement, statusBarElement)
  200.     {
  201.         mainElement.appendChild(this.element);
  202.         statusBarElement.appendChild(this.clearButton);
  203.         statusBarElement.appendChild(this.filterBarElement);
  204.     },
  205.  
  206.     show: function()
  207.     {
  208.         this.toggleConsoleButton.addStyleClass("toggled-on");
  209.         this.toggleConsoleButton.title = WebInspector.UIString("Hide console.");
  210.         if (!this.prompt.isCaretInsidePrompt())
  211.             this.prompt.moveCaretToEndOfPrompt();
  212.     },
  213.  
  214.     afterShow: function()
  215.     {
  216.         WebInspector.currentFocusElement = this.promptElement;  
  217.     },
  218.  
  219.     hide: function()
  220.     {
  221.         this.toggleConsoleButton.removeStyleClass("toggled-on");
  222.         this.toggleConsoleButton.title = WebInspector.UIString("Show console.");
  223.     },
  224.  
  225.     _scheduleScrollIntoView: function()
  226.     {
  227.         if (this._scrollIntoViewTimer)
  228.             return;
  229.  
  230.         function scrollIntoView()
  231.         {
  232.             this.promptElement.scrollIntoView(false);
  233.             delete this._scrollIntoViewTimer;
  234.         }
  235.         this._scrollIntoViewTimer = setTimeout(scrollIntoView.bind(this), 20);
  236.     },
  237.  
  238.     addMessage: function(msg)
  239.     {
  240.         if (msg instanceof WebInspector.ConsoleMessage && !(msg instanceof WebInspector.ConsoleCommandResult)) {
  241.             this._incrementErrorWarningCount(msg);
  242.  
  243.             // Add message to the resource panel
  244.             if (msg.url in WebInspector.resourceURLMap) {
  245.                 msg.resource = WebInspector.resourceURLMap[msg.url];
  246.                 if (WebInspector.panels.resources)
  247.                     WebInspector.panels.resources.addMessageToResource(msg.resource, msg);
  248.             }
  249.  
  250.             this.commandSincePreviousMessage = false;
  251.             this.previousMessage = msg;
  252.         } else if (msg instanceof WebInspector.ConsoleCommand) {
  253.             if (this.previousMessage) {
  254.                 this.commandSincePreviousMessage = true;
  255.             }
  256.         }
  257.  
  258.         this.messages.push(msg);
  259.  
  260.         if (msg.type === WebInspector.ConsoleMessage.MessageType.EndGroup) {
  261.             if (this.groupLevel < 1)
  262.                 return;
  263.  
  264.             this.groupLevel--;
  265.  
  266.             this.currentGroup = this.currentGroup.parentGroup;
  267.         } else {
  268.             if (msg.type === WebInspector.ConsoleMessage.MessageType.StartGroup) {
  269.                 this.groupLevel++;
  270.  
  271.                 var group = new WebInspector.ConsoleGroup(this.currentGroup, this.groupLevel);
  272.                 this.currentGroup.messagesElement.appendChild(group.element);
  273.                 this.currentGroup = group;
  274.             }
  275.  
  276.             this.currentGroup.addMessage(msg);
  277.         }
  278.  
  279.         this._scheduleScrollIntoView();
  280.     },
  281.  
  282.     updateMessageRepeatCount: function(count)
  283.     {
  284.         var msg = this.previousMessage;
  285.         var prevRepeatCount = msg.totalRepeatCount;
  286.         
  287.         if (!this.commandSincePreviousMessage) {
  288.             msg.repeatDelta = count - prevRepeatCount;
  289.             msg.repeatCount = msg.repeatCount + msg.repeatDelta;
  290.             msg.totalRepeatCount = count;
  291.             msg._updateRepeatCount();
  292.             this._incrementErrorWarningCount(msg);
  293.         } else {
  294.             msgCopy = new WebInspector.ConsoleMessage(msg.source, msg.type, msg.level, msg.line, msg.url, msg.groupLevel, count - prevRepeatCount);
  295.             msgCopy.totalRepeatCount = count;
  296.             msgCopy.setMessageBody(msg.args);
  297.             this.addMessage(msgCopy);
  298.         }
  299.     },
  300.  
  301.     _incrementErrorWarningCount: function(msg)
  302.     {
  303.         switch (msg.level) {
  304.             case WebInspector.ConsoleMessage.MessageLevel.Warning:
  305.                 WebInspector.warnings += msg.repeatDelta;
  306.                 break;
  307.             case WebInspector.ConsoleMessage.MessageLevel.Error:
  308.                 WebInspector.errors += msg.repeatDelta;
  309.                 break;
  310.         }
  311.     },
  312.  
  313.     requestClearMessages: function()
  314.     {
  315.         InjectedScriptAccess.getDefault().clearConsoleMessages(function() {});
  316.     },
  317.  
  318.     clearMessages: function()
  319.     {
  320.         if (WebInspector.panels.resources)
  321.             WebInspector.panels.resources.clearMessages();
  322.  
  323.         this.messages = [];
  324.  
  325.         this.groupLevel = 0;
  326.         this.currentGroup = this.topGroup;
  327.         this.topGroup.messagesElement.removeChildren();
  328.  
  329.         WebInspector.errors = 0;
  330.         WebInspector.warnings = 0;
  331.  
  332.         delete this.commandSincePreviousMessage;
  333.         delete this.previousMessage;
  334.     },
  335.  
  336.     completions: function(wordRange, bestMatchOnly, completionsReadyCallback)
  337.     {
  338.         // Pass less stop characters to rangeOfWord so the range will be a more complete expression.
  339.         var expressionRange = wordRange.startContainer.rangeOfWord(wordRange.startOffset, ExpressionStopCharacters, this.promptElement, "backward");
  340.         var expressionString = expressionRange.toString();
  341.         var lastIndex = expressionString.length - 1;
  342.  
  343.         var dotNotation = (expressionString[lastIndex] === ".");
  344.         var bracketNotation = (expressionString[lastIndex] === "[");
  345.  
  346.         if (dotNotation || bracketNotation)
  347.             expressionString = expressionString.substr(0, lastIndex);
  348.  
  349.         var prefix = wordRange.toString();
  350.         if (!expressionString && !prefix)
  351.             return;
  352.  
  353.         var reportCompletions = this._reportCompletions.bind(this, bestMatchOnly, completionsReadyCallback, dotNotation, bracketNotation, prefix);
  354.         // Collect comma separated object properties for the completion.
  355.  
  356.         var includeInspectorCommandLineAPI = (!dotNotation && !bracketNotation);
  357.         var callFrameId = WebInspector.panels.scripts.selectedCallFrameId();
  358.         var injectedScriptAccess;
  359.         if (WebInspector.panels.scripts && WebInspector.panels.scripts.paused) {
  360.             var selectedCallFrame = WebInspector.panels.scripts.sidebarPanes.callstack.selectedCallFrame;
  361.             injectedScriptAccess = InjectedScriptAccess.get(selectedCallFrame.injectedScriptId);
  362.         } else
  363.             injectedScriptAccess = InjectedScriptAccess.getDefault();
  364.         injectedScriptAccess.getCompletions(expressionString, includeInspectorCommandLineAPI, callFrameId, reportCompletions);
  365.     },
  366.  
  367.     _reportCompletions: function(bestMatchOnly, completionsReadyCallback, dotNotation, bracketNotation, prefix, result, isException) {
  368.         if (isException)
  369.             return;
  370.  
  371.         if (bracketNotation) {
  372.             if (prefix.length && prefix[0] === "'")
  373.                 var quoteUsed = "'";
  374.             else
  375.                 var quoteUsed = "\"";
  376.         }
  377.  
  378.         var results = [];
  379.         var properties = Object.sortedProperties(result);
  380.  
  381.         for (var i = 0; i < properties.length; ++i) {
  382.             var property = properties[i];
  383.  
  384.             if (dotNotation && !/^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(property))
  385.                 continue;
  386.  
  387.             if (bracketNotation) {
  388.                 if (!/^[0-9]+$/.test(property))
  389.                     property = quoteUsed + property.escapeCharacters(quoteUsed + "\\") + quoteUsed;
  390.                 property += "]";
  391.             }
  392.  
  393.             if (property.length < prefix.length)
  394.                 continue;
  395.             if (property.indexOf(prefix) !== 0)
  396.                 continue;
  397.  
  398.             results.push(property);
  399.             if (bestMatchOnly)
  400.                 break;
  401.         }
  402.         completionsReadyCallback(results);
  403.     },
  404.  
  405.     _clearButtonClicked: function()
  406.     {
  407.         this.requestClearMessages();
  408.     },
  409.  
  410.     _handleContextMenuEvent: function(event)
  411.     {
  412.         if (!window.getSelection().isCollapsed) {
  413.             // If there is a selection, we want to show our normal context menu
  414.             // (with Copy, etc.), and not Clear Console.
  415.             return;
  416.         }
  417.  
  418.         this._contextMenu.show(event);
  419.     },
  420.  
  421.     _messagesSelectStart: function(event)
  422.     {
  423.         if (this._selectionTimeout)
  424.             clearTimeout(this._selectionTimeout);
  425.  
  426.         this.prompt.clearAutoComplete();
  427.  
  428.         function moveBackIfOutside()
  429.         {
  430.             delete this._selectionTimeout;
  431.             if (!this.prompt.isCaretInsidePrompt() && window.getSelection().isCollapsed)
  432.                 this.prompt.moveCaretToEndOfPrompt();
  433.             this.prompt.autoCompleteSoon();
  434.         }
  435.  
  436.         this._selectionTimeout = setTimeout(moveBackIfOutside.bind(this), 100);
  437.     },
  438.  
  439.     _messagesClicked: function(event)
  440.     {
  441.         var link = event.target.enclosingNodeOrSelfWithNodeName("a");
  442.         if (!link || !link.representedNode)
  443.             return;
  444.  
  445.         WebInspector.updateFocusedNode(link.representedNode.id);
  446.         event.stopPropagation();
  447.         event.preventDefault();
  448.     },
  449.  
  450.     _promptKeyDown: function(event)
  451.     {
  452.         if (isEnterKey(event)) {
  453.             this._enterKeyPressed(event);
  454.             return;
  455.         }
  456.  
  457.         var shortcut = WebInspector.KeyboardShortcut.makeKeyFromEvent(event);
  458.         var handler = this._shortcuts[shortcut];
  459.         if (handler) {
  460.             if (!this._shortcuts[shortcut].isMacOnly || WebInspector.isMac()) {
  461.                 handler();
  462.                 event.preventDefault();
  463.                 return;
  464.             }
  465.         }
  466.     },
  467.  
  468.     evalInInspectedWindow: function(expression, objectGroup, callback)
  469.     {
  470.         if (WebInspector.panels.scripts && WebInspector.panels.scripts.paused) {
  471.             WebInspector.panels.scripts.evaluateInSelectedCallFrame(expression, false, objectGroup, callback);
  472.             return;
  473.         }
  474.         this.doEvalInWindow(expression, objectGroup, callback);
  475.     },
  476.  
  477.     doEvalInWindow: function(expression, objectGroup, callback)
  478.     {
  479.         if (!expression) {
  480.             // There is no expression, so the completion should happen against global properties.
  481.             expression = "this";
  482.         }
  483.  
  484.         function evalCallback(result)
  485.         {
  486.             callback(result.value, result.isException);
  487.         };
  488.         InjectedScriptAccess.getDefault().evaluate(expression, objectGroup, evalCallback);
  489.     },
  490.  
  491.     _enterKeyPressed: function(event)
  492.     {
  493.         if (event.altKey)
  494.             return;
  495.  
  496.         event.preventDefault();
  497.         event.stopPropagation();
  498.  
  499.         this.prompt.clearAutoComplete(true);
  500.  
  501.         var str = this.prompt.text;
  502.         if (!str.length)
  503.             return;
  504.  
  505.         var commandMessage = new WebInspector.ConsoleCommand(str);
  506.         this.addMessage(commandMessage);
  507.  
  508.         var self = this;
  509.         function printResult(result, exception)
  510.         {
  511.             self.prompt.history.push(str);
  512.             self.prompt.historyOffset = 0;
  513.             self.prompt.text = "";
  514.  
  515.             WebInspector.settings.consoleHistory = self.prompt.history.slice(-30);
  516.  
  517.             self.addMessage(new WebInspector.ConsoleCommandResult(result, exception, commandMessage));
  518.         }
  519.         this.evalInInspectedWindow(str, "console", printResult);
  520.     },
  521.  
  522.     _format: function(output, forceObjectFormat)
  523.     {
  524.         var isProxy = (output != null && typeof output === "object");
  525.         var type = (forceObjectFormat ? "object" : Object.proxyType(output));
  526.  
  527.         var formatter = this._customFormatters[type];
  528.         if (!formatter || !isProxy) {
  529.             formatter = this._formatvalue;
  530.             output = output.description;
  531.         }
  532.  
  533.         var span = document.createElement("span");
  534.         span.className = "console-formatted-" + type + " source-code";
  535.         formatter.call(this, output, span);
  536.         return span;
  537.     },
  538.  
  539.     _formatvalue: function(val, elem)
  540.     {
  541.         elem.appendChild(document.createTextNode(val));
  542.     },
  543.  
  544.     _formatobject: function(obj, elem)
  545.     {
  546.         elem.appendChild(new WebInspector.ObjectPropertiesSection(obj, obj.description, null, true).element);
  547.     },
  548.  
  549.     _formatnode: function(object, elem)
  550.     {
  551.         function printNode(nodeId)
  552.         {
  553.             if (!nodeId)
  554.                 return;
  555.             var treeOutline = new WebInspector.ElementsTreeOutline();
  556.             treeOutline.showInElementsPanelEnabled = true;
  557.             treeOutline.rootDOMNode = WebInspector.domAgent.nodeForId(nodeId);
  558.             treeOutline.element.addStyleClass("outline-disclosure");
  559.             if (!treeOutline.children[0].hasChildren)
  560.                 treeOutline.element.addStyleClass("single-node");
  561.             elem.appendChild(treeOutline.element);
  562.         }
  563.  
  564.         InjectedScriptAccess.get(object.injectedScriptId).pushNodeToFrontend(object, printNode);
  565.     },
  566.  
  567.     _formatarray: function(arr, elem)
  568.     {
  569.         InjectedScriptAccess.get(arr.injectedScriptId).getProperties(arr, false, false, this._printArray.bind(this, elem));
  570.     },
  571.  
  572.     _formatstring: function(output, elem)
  573.     {
  574.         var span = document.createElement("span");
  575.         span.className = "console-formatted-string source-code";
  576.         span.appendChild(WebInspector.linkifyStringAsFragment(output.description));
  577.  
  578.         // Make black quotes.
  579.         elem.removeStyleClass("console-formatted-string");
  580.         elem.appendChild(document.createTextNode("\""));
  581.         elem.appendChild(span);
  582.         elem.appendChild(document.createTextNode("\""));
  583.     },
  584.  
  585.     _printArray: function(elem, properties)
  586.     {
  587.         if (!properties)
  588.             return;
  589.  
  590.         var elements = [];
  591.         for (var i = 0; i < properties.length; ++i) {
  592.             var name = properties[i].name;
  593.             if (name == parseInt(name))
  594.                 elements[name] = this._formatAsArrayEntry(properties[i].value);
  595.         }
  596.  
  597.         elem.appendChild(document.createTextNode("["));
  598.         for (var i = 0; i < elements.length; ++i) {
  599.             var element = elements[i];
  600.             if (element)
  601.                 elem.appendChild(element);
  602.             else
  603.                 elem.appendChild(document.createTextNode("undefined"))
  604.             if (i < elements.length - 1)
  605.                 elem.appendChild(document.createTextNode(", "));
  606.         }
  607.         elem.appendChild(document.createTextNode("]"));
  608.     },
  609.  
  610.     _formatAsArrayEntry: function(output)
  611.     {
  612.         var type = Object.proxyType(output);
  613.         // Prevent infinite expansion of cross-referencing arrays.
  614.         return this._format(output, type === "array");
  615.     }
  616. }
  617.  
  618. WebInspector.ConsoleView.prototype.__proto__ = WebInspector.View.prototype;
  619.  
  620. WebInspector.ConsoleMessage = function(source, type, level, line, url, groupLevel, repeatCount)
  621. {
  622.     this.source = source;
  623.     this.type = type;
  624.     this.level = level;
  625.     this.line = line;
  626.     this.url = url;
  627.     this.groupLevel = groupLevel;
  628.     this.repeatCount = repeatCount;
  629.     this.repeatDelta = repeatCount;
  630.     this.totalRepeatCount = repeatCount;
  631.     if (arguments.length > 7)
  632.         this.setMessageBody(Array.prototype.slice.call(arguments, 7));
  633. }
  634.  
  635. WebInspector.ConsoleMessage.prototype = {
  636.     setMessageBody: function(args)
  637.     {
  638.         this.args = args;
  639.         switch (this.type) {
  640.             case WebInspector.ConsoleMessage.MessageType.Trace:
  641.                 var span = document.createElement("span");
  642.                 span.className = "console-formatted-trace source-code";
  643.                 var stack = Array.prototype.slice.call(args);
  644.                 var funcNames = stack.map(function(f) {
  645.                     return f || WebInspector.UIString("(anonymous function)");
  646.                 });
  647.                 span.appendChild(document.createTextNode(funcNames.join("\n")));
  648.                 this.formattedMessage = span;
  649.                 break;
  650.             case WebInspector.ConsoleMessage.MessageType.Object:
  651.                 this.formattedMessage = this._format(["%O", args[0]]);
  652.                 break;
  653.             default:
  654.                 this.formattedMessage = this._format(args);
  655.                 break;
  656.         }
  657.  
  658.         // This is used for inline message bubbles in SourceFrames, or other plain-text representations.
  659.         this.message = this.formattedMessage.textContent;
  660.     },
  661.  
  662.     isErrorOrWarning: function()
  663.     {
  664.         return (this.level === WebInspector.ConsoleMessage.MessageLevel.Warning || this.level === WebInspector.ConsoleMessage.MessageLevel.Error);
  665.     },
  666.  
  667.     _format: function(parameters)
  668.     {
  669.         // This node is used like a Builder. Values are continually appended onto it.
  670.         var formattedResult = document.createElement("span");
  671.         if (!parameters.length)
  672.             return formattedResult;
  673.  
  674.         // Formatting code below assumes that parameters are all wrappers whereas frontend console
  675.         // API allows passing arbitrary values as messages (strings, numbers, etc.). Wrap them here.
  676.         for (var i = 0; i < parameters.length; ++i)
  677.             if (typeof parameters[i] !== "object" && typeof parameters[i] !== "function")
  678.                 parameters[i] = WebInspector.ObjectProxy.wrapPrimitiveValue(parameters[i]);
  679.  
  680.         // There can be string log and string eval result. We distinguish between them based on message type.
  681.         var shouldFormatMessage = Object.proxyType(parameters[0]) === "string" && this.type !== WebInspector.ConsoleMessage.MessageType.Result;
  682.  
  683.         // Multiple parameters with the first being a format string. Save unused substitutions.
  684.         if (shouldFormatMessage) {
  685.             // Multiple parameters with the first being a format string. Save unused substitutions.
  686.             var result = this._formatWithSubstitutionString(parameters, formattedResult);
  687.             parameters = result.unusedSubstitutions;
  688.             if (parameters.length)
  689.                 formattedResult.appendChild(document.createTextNode(" "));
  690.         }
  691.  
  692.         // Single parameter, or unused substitutions from above.
  693.         for (var i = 0; i < parameters.length; ++i) {
  694.             // Inline strings when formatting.
  695.             if (shouldFormatMessage && parameters[i].type === "string")
  696.                 formattedResult.appendChild(document.createTextNode(parameters[i].description));
  697.             else
  698.                 formattedResult.appendChild(WebInspector.console._format(parameters[i]));
  699.             if (i < parameters.length - 1)
  700.                 formattedResult.appendChild(document.createTextNode(" "));
  701.         }
  702.         return formattedResult;
  703.     },
  704.  
  705.     _formatWithSubstitutionString: function(parameters, formattedResult)
  706.     {
  707.         var formatters = {}
  708.         for (var i in String.standardFormatters)
  709.             formatters[i] = String.standardFormatters[i];
  710.  
  711.         function consoleFormatWrapper(force)
  712.         {
  713.             return function(obj) {
  714.                 return WebInspector.console._format(obj, force);
  715.             };
  716.         }
  717.  
  718.         // Firebug uses %o for formatting objects.
  719.         formatters.o = consoleFormatWrapper();
  720.         // Firebug allows both %i and %d for formatting integers.
  721.         formatters.i = formatters.d;
  722.         // Support %O to force object formatting, instead of the type-based %o formatting.
  723.         formatters.O = consoleFormatWrapper(true);
  724.  
  725.         function append(a, b)
  726.         {
  727.             if (!(b instanceof Node))
  728.                 a.appendChild(WebInspector.linkifyStringAsFragment(b.toString()));
  729.             else
  730.                 a.appendChild(b);
  731.             return a;
  732.         }
  733.  
  734.         // String.format does treat formattedResult like a Builder, result is an object.
  735.         return String.format(parameters[0].description, parameters.slice(1), formatters, formattedResult, append);
  736.     },
  737.  
  738.     toMessageElement: function()
  739.     {
  740.         if (this._element)
  741.             return this._element;
  742.  
  743.         var element = document.createElement("div");
  744.         element.message = this;
  745.         element.className = "console-message";
  746.  
  747.         this._element = element;
  748.  
  749.         switch (this.source) {
  750.             case WebInspector.ConsoleMessage.MessageSource.HTML:
  751.                 element.addStyleClass("console-html-source");
  752.                 break;
  753.             case WebInspector.ConsoleMessage.MessageSource.WML:
  754.                 element.addStyleClass("console-wml-source");
  755.                 break;
  756.             case WebInspector.ConsoleMessage.MessageSource.XML:
  757.                 element.addStyleClass("console-xml-source");
  758.                 break;
  759.             case WebInspector.ConsoleMessage.MessageSource.JS:
  760.                 element.addStyleClass("console-js-source");
  761.                 break;
  762.             case WebInspector.ConsoleMessage.MessageSource.CSS:
  763.                 element.addStyleClass("console-css-source");
  764.                 break;
  765.             case WebInspector.ConsoleMessage.MessageSource.Other:
  766.                 element.addStyleClass("console-other-source");
  767.                 break;
  768.         }
  769.  
  770.         switch (this.level) {
  771.             case WebInspector.ConsoleMessage.MessageLevel.Tip:
  772.                 element.addStyleClass("console-tip-level");
  773.                 break;
  774.             case WebInspector.ConsoleMessage.MessageLevel.Log:
  775.                 element.addStyleClass("console-log-level");
  776.                 break;
  777.             case WebInspector.ConsoleMessage.MessageLevel.Debug:
  778.                 element.addStyleClass("console-debug-level");
  779.                 break;
  780.             case WebInspector.ConsoleMessage.MessageLevel.Warning:
  781.                 element.addStyleClass("console-warning-level");
  782.                 break;
  783.             case WebInspector.ConsoleMessage.MessageLevel.Error:
  784.                 element.addStyleClass("console-error-level");
  785.                 break;
  786.         }
  787.         
  788.         if (this.type === WebInspector.ConsoleMessage.MessageType.StartGroup)
  789.             element.addStyleClass("console-group-title");
  790.  
  791.         if (this.elementsTreeOutline) {
  792.             element.addStyleClass("outline-disclosure");
  793.             element.appendChild(this.elementsTreeOutline.element);
  794.             return element;
  795.         }
  796.  
  797.         if (this.url && this.url !== "undefined") {
  798.             var urlElement = document.createElement("a");
  799.             urlElement.className = "console-message-url webkit-html-resource-link";
  800.             urlElement.href = this.url;
  801.             urlElement.lineNumber = this.line;
  802.  
  803.             if (this.source === WebInspector.ConsoleMessage.MessageSource.JS)
  804.                 urlElement.preferredPanel = "scripts";
  805.  
  806.             if (this.line > 0)
  807.                 urlElement.textContent = WebInspector.displayNameForURL(this.url) + ":" + this.line;
  808.             else
  809.                 urlElement.textContent = WebInspector.displayNameForURL(this.url);
  810.  
  811.             element.appendChild(urlElement);
  812.         }
  813.  
  814.         var messageTextElement = document.createElement("span");
  815.         messageTextElement.className = "console-message-text source-code";
  816.         if (this.type === WebInspector.ConsoleMessage.MessageType.Assert)
  817.             messageTextElement.appendChild(document.createTextNode(WebInspector.UIString("Assertion failed: ")));
  818.         messageTextElement.appendChild(this.formattedMessage);
  819.         element.appendChild(messageTextElement);
  820.  
  821.         if (this.repeatCount > 1)
  822.             this._updateRepeatCount();
  823.  
  824.         return element;
  825.     },
  826.  
  827.     _updateRepeatCount: function() {
  828.         if (!this.repeatCountElement) {
  829.             this.repeatCountElement = document.createElement("span");
  830.             this.repeatCountElement.className = "bubble";
  831.     
  832.             this._element.insertBefore(this.repeatCountElement, this._element.firstChild);
  833.             this._element.addStyleClass("repeated-message");
  834.         }
  835.         this.repeatCountElement.textContent = this.repeatCount;
  836.     },
  837.  
  838.     toString: function()
  839.     {
  840.         var sourceString;
  841.         switch (this.source) {
  842.             case WebInspector.ConsoleMessage.MessageSource.HTML:
  843.                 sourceString = "HTML";
  844.                 break;
  845.             case WebInspector.ConsoleMessage.MessageSource.WML:
  846.                 sourceString = "WML";
  847.                 break;
  848.             case WebInspector.ConsoleMessage.MessageSource.XML:
  849.                 sourceString = "XML";
  850.                 break;
  851.             case WebInspector.ConsoleMessage.MessageSource.JS:
  852.                 sourceString = "JS";
  853.                 break;
  854.             case WebInspector.ConsoleMessage.MessageSource.CSS:
  855.                 sourceString = "CSS";
  856.                 break;
  857.             case WebInspector.ConsoleMessage.MessageSource.Other:
  858.                 sourceString = "Other";
  859.                 break;
  860.         }
  861.  
  862.         var typeString;
  863.         switch (this.type) {
  864.             case WebInspector.ConsoleMessage.MessageType.Log:
  865.                 typeString = "Log";
  866.                 break;
  867.             case WebInspector.ConsoleMessage.MessageType.Object:
  868.                 typeString = "Object";
  869.                 break;
  870.             case WebInspector.ConsoleMessage.MessageType.Trace:
  871.                 typeString = "Trace";
  872.                 break;
  873.             case WebInspector.ConsoleMessage.MessageType.StartGroup:
  874.                 typeString = "Start Group";
  875.                 break;
  876.             case WebInspector.ConsoleMessage.MessageType.EndGroup:
  877.                 typeString = "End Group";
  878.                 break;
  879.             case WebInspector.ConsoleMessage.MessageType.Assert:
  880.                 typeString = "Assert";
  881.                 break;
  882.             case WebInspector.ConsoleMessage.MessageType.Result:
  883.                 typeString = "Result";
  884.                 break;
  885.         }
  886.         
  887.         var levelString;
  888.         switch (this.level) {
  889.             case WebInspector.ConsoleMessage.MessageLevel.Tip:
  890.                 levelString = "Tip";
  891.                 break;
  892.             case WebInspector.ConsoleMessage.MessageLevel.Log:
  893.                 levelString = "Log";
  894.                 break;
  895.             case WebInspector.ConsoleMessage.MessageLevel.Warning:
  896.                 levelString = "Warning";
  897.                 break;
  898.             case WebInspector.ConsoleMessage.MessageLevel.Debug:
  899.                 levelString = "Debug";
  900.                 break;
  901.             case WebInspector.ConsoleMessage.MessageLevel.Error:
  902.                 levelString = "Error";
  903.                 break;
  904.         }
  905.  
  906.         return sourceString + " " + typeString + " " + levelString + ": " + this.formattedMessage.textContent + "\n" + this.url + " line " + this.line;
  907.     },
  908.  
  909.     isEqual: function(msg, disreguardGroup)
  910.     {
  911.         if (!msg)
  912.             return false;
  913.  
  914.         var ret = (this.source == msg.source)
  915.             && (this.type == msg.type)
  916.             && (this.level == msg.level)
  917.             && (this.line == msg.line)
  918.             && (this.url == msg.url)
  919.             && (this.message == msg.message);
  920.  
  921.         return (disreguardGroup ? ret : (ret && (this.groupLevel == msg.groupLevel)));
  922.     }
  923. }
  924.  
  925. // Note: Keep these constants in sync with the ones in Console.h
  926. WebInspector.ConsoleMessage.MessageSource = {
  927.     HTML: 0,
  928.     WML: 1,
  929.     XML: 2,
  930.     JS: 3,
  931.     CSS: 4,
  932.     Other: 5
  933. }
  934.  
  935. WebInspector.ConsoleMessage.MessageType = {
  936.     Log: 0,
  937.     Object: 1,
  938.     Trace: 2,
  939.     StartGroup: 3,
  940.     EndGroup: 4,
  941.     Assert: 5,
  942.     Result: 6
  943. }
  944.  
  945. WebInspector.ConsoleMessage.MessageLevel = {
  946.     Tip: 0,
  947.     Log: 1,
  948.     Warning: 2,
  949.     Error: 3,
  950.     Debug: 4
  951. }
  952.  
  953. WebInspector.ConsoleCommand = function(command)
  954. {
  955.     this.command = command;
  956. }
  957.  
  958. WebInspector.ConsoleCommand.prototype = {
  959.     toMessageElement: function()
  960.     {
  961.         var element = document.createElement("div");
  962.         element.command = this;
  963.         element.className = "console-user-command";
  964.  
  965.         var commandTextElement = document.createElement("span");
  966.         commandTextElement.className = "console-message-text source-code";
  967.         commandTextElement.textContent = this.command;
  968.         element.appendChild(commandTextElement);
  969.  
  970.         return element;
  971.     }
  972. }
  973.  
  974. WebInspector.ConsoleTextMessage = function(text, level)
  975. {
  976.     level = level || WebInspector.ConsoleMessage.MessageLevel.Log;
  977.     WebInspector.ConsoleMessage.call(this, WebInspector.ConsoleMessage.MessageSource.JS, WebInspector.ConsoleMessage.MessageType.Log, level, 0, null, null, 1, text);
  978. }
  979.  
  980. WebInspector.ConsoleTextMessage.prototype.__proto__ = WebInspector.ConsoleMessage.prototype;
  981.  
  982. WebInspector.ConsoleCommandResult = function(result, exception, originatingCommand)
  983. {
  984.     var level = (exception ? WebInspector.ConsoleMessage.MessageLevel.Error : WebInspector.ConsoleMessage.MessageLevel.Log);
  985.     var message = result;
  986.     if (exception) {
  987.         // Distinguish between strings and errors (no need to quote latter).
  988.         message = WebInspector.ObjectProxy.wrapPrimitiveValue(result);
  989.         message.type = "error";
  990.     }
  991.     var line = (exception ? result.line : -1);
  992.     var url = (exception ? result.sourceURL : null);
  993.  
  994.     this.originatingCommand = originatingCommand;
  995.  
  996.     WebInspector.ConsoleMessage.call(this, WebInspector.ConsoleMessage.MessageSource.JS, WebInspector.ConsoleMessage.MessageType.Result, level, line, url, null, 1, message);
  997. }
  998.  
  999. WebInspector.ConsoleCommandResult.prototype = {
  1000.     toMessageElement: function()
  1001.     {
  1002.         var element = WebInspector.ConsoleMessage.prototype.toMessageElement.call(this);
  1003.         element.addStyleClass("console-user-command-result");
  1004.         return element;
  1005.     }
  1006. }
  1007.  
  1008. WebInspector.ConsoleCommandResult.prototype.__proto__ = WebInspector.ConsoleMessage.prototype;
  1009.  
  1010. WebInspector.ConsoleGroup = function(parentGroup, level)
  1011. {
  1012.     this.parentGroup = parentGroup;
  1013.     this.level = level;
  1014.  
  1015.     var element = document.createElement("div");
  1016.     element.className = "console-group";
  1017.     element.group = this;
  1018.     this.element = element;
  1019.  
  1020.     var messagesElement = document.createElement("div");
  1021.     messagesElement.className = "console-group-messages";
  1022.     element.appendChild(messagesElement);
  1023.     this.messagesElement = messagesElement;
  1024. }
  1025.  
  1026. WebInspector.ConsoleGroup.prototype = {
  1027.     addMessage: function(msg)
  1028.     {
  1029.         var element = msg.toMessageElement();
  1030.         
  1031.         if (msg.type === WebInspector.ConsoleMessage.MessageType.StartGroup) {
  1032.             this.messagesElement.parentNode.insertBefore(element, this.messagesElement);
  1033.             element.addEventListener("click", this._titleClicked.bind(this), true);
  1034.         } else
  1035.             this.messagesElement.appendChild(element);
  1036.  
  1037.         if (element.previousSibling && msg.originatingCommand && element.previousSibling.command === msg.originatingCommand)
  1038.             element.previousSibling.addStyleClass("console-adjacent-user-command-result");
  1039.     },
  1040.  
  1041.     _titleClicked: function(event)
  1042.     {
  1043.         var groupTitleElement = event.target.enclosingNodeOrSelfWithClass("console-group-title");
  1044.         if (groupTitleElement) {
  1045.             var groupElement = groupTitleElement.enclosingNodeOrSelfWithClass("console-group");
  1046.             if (groupElement)
  1047.                 if (groupElement.hasStyleClass("collapsed"))
  1048.                     groupElement.removeStyleClass("collapsed");
  1049.                 else
  1050.                     groupElement.addStyleClass("collapsed");
  1051.             groupTitleElement.scrollIntoViewIfNeeded(true);
  1052.         }
  1053.  
  1054.         event.stopPropagation();
  1055.         event.preventDefault();
  1056.     }
  1057. }
  1058.