Skip to content

Instantly share code, notes, and snippets.

@robcee
Forked from mihaisucan/webconsole-output-rewrite.md
Last active December 16, 2015 13:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save robcee/5442825 to your computer and use it in GitHub Desktop.
Save robcee/5442825 to your computer and use it in GitHub Desktop.

Web Console output rewrite, bug 778766

This document describes the ongoing work for the Web Console output rewrite / reimplementation. Feedback is requested.

WORK IN PROGRESS!

Current issues

Issues currently affecting the Web Console output:

  • page navigation/reload issues:
    • add an option to clear messages on reload, bug 705921.
    • add a marker for each reload, bug 793996.
    • group / gray out / mark as obsolete output from previous reloads, bug 655700.
  • customized output for different types of objects:
  • you cannot select freely any text in the output, bugs 706755 (sub-line, character selection) and 760876 (drag-select multiple lines).
  • add stack traces to every console API call and every script error, bug 814497.
  • hard to experiment with output changes, due to an aging codebase that doesn't take into consideration today's use cases and needs. as a class of problem, this one doesn't feel consistent with the others -- what do we need to do to fix the problem? Do you have experiments in mind? Obvious ones should include SourceEditor-based output area and a pure HTML version. Others?
    • hard to add new (custom) messages using the Web Console API, bug 775607.
    • technical debt, cleanups needed:
      • Move methods from the HUD Service to the individual HeadsUpDisplay objects, bug 592523,
      • Use attributes instead of classes for category/severity, bug 628019, and
      • confusion over console node, hud box and output node, bug 643135
  • keyboard accessibility, Make clickable output in the WebConsole keyboard accessible bug 588010.
  • too much output:
    • improve filtering:
      • provide APIs for filtering existing and new messages added to output, bug 837774. current filtering logic is based on xpath and walking DOM nodes...
      • highlight category button when a new message is added, if the category is off - bug 801067.
    • allow grouping of messages based on custom logic.
    • allow expansion of grouped repeated messages, bug 704252.
    • timestamps should be expandable/collapsible, bug 722267.
  • performance issues:
    • layout is flushed by reading scrollHeight too often, bug 746870.
    • HS_regroupOutput() is too slow and irrelevant as it stands, bug 746871.
    • pruneOutputIfNecessary is too slow, bug 746872.
  • lack of polish and UX issues:
    • links can open using any mouse button, bug 653710.
    • links in errors/warnings cannot be clicked/copied, bug 663370.
    • clicking in blank areas of messages opens popups/sidebar, bug 773291.
    • better UX for all links in output, bug 776939.
    • "No value to execute" has no value, bug 835951.
    • add tooltip to the repeat bubble, bug 675487.
    • the choice of fonts need to be checked again, bugs 674422 and 696385.
    • wrong image in console.css, bug 844637.
    • display column number in addition to line numbers, bug 684096.
    • incorrect URL shown when a hash is used, bug 724224.
  • window.console API improvements and new features that depend on a more flexible output implementation:

This list is somewhat sorted by priority. Do note that keyboard accessibility and performance are overall goals that should be under our constant attention.

Suggestions to prioritize bugs are welcome.

Goals

Overall goals in no particular order:

  • improve user experience by polishing the output. We have a lot of bugs about simple issues that can be fixed to improve UX.
  • improve the output visual appearance.
  • make it easier to manage a lot of output by improving filtering, search, message grouping and more.
  • make it easier for Mozilla developers to iterate on output changes, to add new kinds of messages, and to write new tests.
  • add specialized output for different kinds of objects, to improve the overall console usability.
  • improve accessibility.
  • improve performance.

TODO 1: Any goals we should add/remove? Are they too vague/lofty? Should we be more explicit about what we want to fix in this list of goals?

TODO 2: Any non-goals?

We haven't decided whether or not we're going to replace the XUL RichItemList with some other format, HTML or SourceEditor. We should be up front about that because it will dictate how we deal with all of the other bugs on this list.

What about being explicit about Modularizing the ConsoleOutput so it can be retargeted or reused?

The new Web Console output API

The HUDService.jsm file will continue to be the Firefox and toolbox glue for the Web Console user interface. This file should continue to be kept as lightweight as possible.

All output will be HTML to make it easier to integrate other pieces of output that come from outside of the Web Console.

Changes in the webconsole.js file

The webconsole.js and webconsole.xul files will continue to hold the user interface implementation for the Web and Browser Consoles. I would like the XUL file to continue to hold the basic console UI as opposed to having to create each DOM element from JavaScript during initialization.

For webconsole.js I would like to see it become much lighter. It is currently bloated and it includes a lot of the older Web Console's arcane legacy code.

The WebConsoleFrame and JSTerm objects will continue to exist, while almost everything related to output will move into ConsoleOutput.jsm.

The JSTerm input and autocomplete code will be refactored later, after the output work. That will be a simpler and quicker endeavour.

Do we want to write down a plan here for JSTerm input and autocomplete as well?

We will add an output property to the WebConsoleFrame object which will hold an instance of ConsoleOutput.

Filters and message taxonomy

We will keep setFilterState() and getFilterState(): the Web Console UI will continue to manage filter UI (the buttons) and state. We currently have a rather confusing mix of things: categories, severities (log levels) and class names.

I propose that we do not impose any strict set of filter categories, severities, etc. I think we should have each button associated to a flag name / pref key that is either on or off. Messages will have their own filter logic which can check for any of these flags. This should allow us flexibility in terms of UI: we can later change those filter buttons and menus as we want as long as we set the right filter flags. We can add/remove flags freely - it's up to the messages we implement if they react to them or not.

When the filters state change via API calls or due to UI interaction we will invoke ConsoleOutput.onFilterStateChanged() or ConsoleOutput.onFilterTextChanged(). Then it is all up to the ConsoleOutput to do the message filtering.

Instead of calling the onFilterStateChanged()/onFilterTextChanged() methods on ConsoleOutput we could emit events. Should we do that? ConsoleOutput could listen to these events on the owning WebConsole instance and react to such changes.

Once all messages are filtered ConsoleOutput should emit a messages-filtered event.

Should we include which messages? Do we care?

The flags-based approach should allow us to have the same message belonging to multiple "categories" and any severity. We can also output messages that do not belong to any category or severity (as is the case with JavaScript evaluation).

Log limits: currently we have log limits per categories and we prune messages per category. We also have undocumented preferences allowing users to change the log limit for each category. In my opinion this is over the top. I believe we should simplify to a single log limit that limits the number of messages we display, overall. This would help us simplify output queues and pruning. We can also have one documented preference for this log limit.

Is there agreement with this change?

New module: ConsoleOutput.jsm

The new ConsoleOutput.jsm would export a reusable component for building something like the Web Console output. This file will contain the following objects:

  • ConsoleOutput

    Output manager: how to add new messages, how output is queued, pruning, filtering, removal of messages and scrolling.

  • Messages.BaseMessage

    Base object from which all of the other message types inherit. This will include minimal API to get a usable message.

  • Messages.Simple

    This is going to be the simplest type of message you can output to the console: it will support HTML content, a message source link, basic filtering, along with the option of assigning the message a category and a severity. To display the message source we will use the MessageSource widget. The MessageTimestamp widget will be used to display the timestamp.

  • Messages.JavaScriptInput

    Will be used for displaying JavaScript input that is evaluated. The JavaScriptSnippet widget will be used to syntax highlight user's input.

  • Messages.JavaScriptOutput

    Will be used for displaying JavaScript input that is evaluated. Initial implementation will use the JSObject widget to make the result available for inspection. For DOM elements we will use Widgets.DOMElement.

  • Messages.NetworkRequest

  • Messages.ReloadMarker

    Will be used for bug 793996. The Separator widget will be used here.

  • Messages.ConsoleGeneric

    Will be used for almost all of the window.console API messages. JavaScript objects and DOM elements will be rendered with the appropriate widgets (JSObject and DOMElement).

  • Messages.ConsoleDir

    This is for console.dir() messages. The VariablesView widget will be used for displaying an inline VariablesView instance.

  • Messages.ConsoleTrace

    This is for console.trace() messages. The Stacktrace widget will be used here, with nicer UI than now, to fix bug 790309.

  • Messages.ConsoleGroup

  • Messages.ScriptError

  • Messages.StyleError

  • Widgets.BaseWidget

    Every widget inherits from this one.

  • Widgets.Separator

  • Widgets.MessageSource used for displaying the message source for most types of messages.

  • Widgets.MessageTimestamp used to display the message timestamp.

  • Widgets.JavaScriptSnippet

  • Widgets.CSSSnippet

    Reusable widgets for displaying JavaScript and CSS with syntax highlighting. We can probably reuse parts from CodeMirror when that lands.

  • Widgets.Stacktrace will be used for displaying stacktraces for console.trace(). Once we fix bug 814497 we will also use it for window.console API messages and for script errors.

  • Widgets.JSObject

    Widget that displays an ObjectActor and allows users to inspect the object using the VariablesView in a sidebar.

  • Widgets.DOMElement

    Similar to the JSObject widget but for DOM elements. The object will be rendered as an HTML tag: <tag id="foo" class="class names">. This will fix bug 682033.

  • Widgets.VariablesView

    Reusable widget for messages that need to display an inline VariablesView.

  • Utils

    Holds common static methods we need throughout the code.

Each message object is meant to handle its own state - render itself, filter, destroy, etc. The current Web Console output implementation is far too cumbersome in this regard: we have one big createMessageNode() function that renders all kinds of messages. There's a similar situation with message filtering and removal.

Widgets are meant to be reusable bits of UI within message objects. For example the separator is reusable for the reload marker. The JSObject widget is reusable for all of the console messages that need to display an inline object (an ObjectActor) that you can inspect in the sidebar with VariablesView.

TODO: this section is probably almost complete.

The ConsoleOutput object

The ConsoleOutput manages the output messages: how to add new messages, how output is queued, pruning, filtering, removal of messages and scrolling.

Proposed API:

  • constructor(aOwner, aParentElement)

    • aOwner points to the owning Web Console instance.
    • aParentElement points to the output element in the iframe where messages will be added.
  • addMessage(aMessage...)

    Allows adding multiple messages at once. Each message is added to the output queue.

  • removeMessage(aMessage...) allows the removal of messages.

  • onFilterTextChanged(aNewText)

  • onFilterStateChanged(aFilters)

    Invoked when any of the filters change. The aFilters object holds the state of all known filters. This method goes through all message objects and calls onFilterStateChanged() for them.

Available events:

  • messages-added fires whenever messages are added after there is an output update. The event object includes the list of all new messages.
  • messages-updated fires when there's an output update for messages that change their content due to user interaction. The list of all updated messages is included.
  • messages-removed fires when messages are pruned or otherwise removed.

The first two events are already partially implemented in WebConsoleFrame.

TODO: this section is incomplete.

figure out sorting.

figure out grouping.

figure out copy to clipboard.

The BaseMessage API

Each message is responsible for:

  • rendering itself when ConsoleOutput asks it to;
  • filter itself based on user's text input or based on custom flags;
  • show/hide itself when needed (for example due to filtering);
  • destroy itself when the message is removed.

The BaseMessage class is not meant to be used on its own. It has almost no logic, it cannot render itself, it has no timestamp awareness, non-working filtering capabilities, etc. All message types extend from this class or subclasses.

Methods:

  • constructor(....) each type of message has its own constructor that takes whatever it needs. For example window.console API related messages will take the console message received from the server, which includes the arguments and the rest of API call information.

    Once constructed messages don't do almost anything and they are unowned.

  • init(aOwner, aParent) is invoked by the ConsoleOutput instance once you do output.addMessage(msg). aOwner will be output and aParent will be the parent message if there's any (this is given only if this is a child message in a group). This is when the message becomes owned by a ConsoleOutput.

  • onRemoveFromQueue() is invoked only by ConsoleOutput if this message object will be pruned from the output queue before it is rendered.

  • render() is invoked when this message is displayed. It returns a DOM element that is added into the document by the parent message or by ConsoleOutput. Calling render() multiple times gives you the same element.

  • onRemove() is invoked once this message is pruned or otherwise removed after it was once rendered.

  • onFilterTextChanged(aNewText)

  • onFilterStateChanged(aFilters)

    Invoked when any of the filters change. The aFilters object holds the state of all known filters. This method returns a boolean that tells if this message should be hidden or not.

Properties:

  • owner points to the owning ConsoleOutput instance.
  • parent gives the parent group of messages (if any).
  • element gives the DOM element of this message, if it was ever rendered.
  • rendered boolean that tells if this message has been rendered.
  • visible boolean that tells if this message is visible or not.
  • textContent string that gives the full message content.
  • widgets set of widgets contained by this message.

TODO: this section is almost complete.

The Messages.Simple API

The Simple message extends the BaseMessage API making it usable for basic message output. This is the usual base class for the rest message types in the Web Console. The BaseMessage class should be used as a base only if you need a message type that has more advanced requirements related to rendering and filtering.

Methods:

  • constructor(aMessage, aOptions = {}) where aMessage is the message you want to display. Options:
    • html - (boolean) tells if aMessage is HTML or not. Defaults to false.
    • category - (string) category that this message belongs to. Defaults to no category.
    • severity - (string) severity of the message. Defaults to no severity.
    • timestamp - (number) time of message. Defaults to Date.now().
    • location object tells the message source:
      • file,
      • line,
      • column,
      • lineText.

Properties:

  • timestamp,
  • category,
  • severity,
  • location.

This message object can filter itself based on category, severity and text content. The message content is not allowed to be HTML by default to prevent potential breakage. HTML output shall be enabled on a case by case basis.

Should we make timestamp part of the BaseMessage API?

Notice that there's no explicit list of allowed categories or severities. That is left at the discretion of the implementers - UI and UX designers. This also up to each message implementation. Some types of messages are expected to force their own category/severity (for example ConsoleDir or NetworkRequest).

TODO: this section is almost complete.

The BaseWidget API

The BaseWidget class is meant to be the class that other widget classes extend from.

Methods:

  • constructor(aMessage) where aMessage is the owning message object.
  • render() is invoked when the owning message is displayed. This is also when the widget needs to be displayed. This function must return a DOM element.
  • destroy() is invoked when the owning message is removed.

Properties:

  • textContent.

TODO: almost done, perhaps.

Implementation plan

Current plan is to take bugs from the list of known issues and go through them, sorted by technical priorities. There will be no one big patch to rewrite everything. New APIs will be written and existing code will be converted to that as we fix bugs, step by step.

This section will describe the list of main bugs where work will happen to convert filtering, pruning, output queueing, and existing message types to the new APIs.

TODO: Write down the plan!

vim:set ft=markdown:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment