Bug 1283712 - Part 11.1: Show notes in devtools console. r=nchevobbe
authorTooru Fujisawa <arai_a@mac.com>
Wed, 15 Feb 2017 23:53:07 +0900
changeset 343095 4ce13b03d9562b51debd438eeb46c33dd3b4c448
parent 343094 231c25dff4ce02cca0ada64fa31be62a30e864fe
child 343096 2b6d22e44e92b60efd7ee9238011323b72d1edfb
push id31369
push userkwierso@gmail.com
push dateThu, 16 Feb 2017 00:18:40 +0000
treeherdermozilla-central@e9b926463f9e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnchevobbe
bugs1283712
milestone54.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1283712 - Part 11.1: Show notes in devtools console. r=nchevobbe
devtools/client/webconsole/new-console-output/components/message-types/evaluation-result.js
devtools/client/webconsole/new-console-output/components/message-types/page-error.js
devtools/client/webconsole/new-console-output/components/message.js
devtools/client/webconsole/new-console-output/selectors/messages.js
devtools/client/webconsole/new-console-output/types.js
devtools/client/webconsole/new-console-output/utils/messages.js
devtools/server/actors/webconsole.js
--- a/devtools/client/webconsole/new-console-output/components/message-types/evaluation-result.js
+++ b/devtools/client/webconsole/new-console-output/components/message-types/evaluation-result.js
@@ -31,16 +31,17 @@ function EvaluationResult(props) {
     source,
     type,
     level,
     id: messageId,
     exceptionDocURL,
     frame,
     timeStamp,
     parameters,
+    notes,
   } = message;
 
   let messageBody;
   if (message.messageText) {
     messageBody = message.messageText;
   } else {
     messageBody = GripMessageBody({grip: parameters, serviceContainer});
   }
@@ -56,13 +57,14 @@ function EvaluationResult(props) {
     messageBody,
     messageId,
     scrollToMessage: props.autoscroll,
     serviceContainer,
     exceptionDocURL,
     frame,
     timeStamp,
     parameters,
+    notes,
   };
   return Message(childProps);
 }
 
 module.exports = EvaluationResult;
--- a/devtools/client/webconsole/new-console-output/components/message-types/page-error.js
+++ b/devtools/client/webconsole/new-console-output/components/message-types/page-error.js
@@ -40,16 +40,17 @@ function PageError(props) {
     type,
     level,
     messageText: messageBody,
     repeat,
     stacktrace,
     frame,
     exceptionDocURL,
     timeStamp,
+    notes,
   } = message;
 
   const childProps = {
     dispatch,
     messageId,
     open,
     collapsible: Array.isArray(stacktrace),
     source,
@@ -59,13 +60,14 @@ function PageError(props) {
     indent,
     messageBody,
     repeat,
     frame,
     stacktrace,
     serviceContainer,
     exceptionDocURL,
     timeStamp,
+    notes,
   };
   return Message(childProps);
 }
 
 module.exports = PageError;
--- a/devtools/client/webconsole/new-console-output/components/message.js
+++ b/devtools/client/webconsole/new-console-output/components/message.js
@@ -51,16 +51,20 @@ const Message = createClass({
       emitNewMessage: PropTypes.func.isRequired,
       onViewSourceInDebugger: PropTypes.func.isRequired,
       onViewSourceInScratchpad: PropTypes.func.isRequired,
       onViewSourceInStyleEditor: PropTypes.func.isRequired,
       openContextMenu: PropTypes.func.isRequired,
       openLink: PropTypes.func.isRequired,
       sourceMapService: PropTypes.any,
     }),
+    notes: PropTypes.arrayOf(PropTypes.shape({
+      messageBody: PropTypes.string.isRequired,
+      frame: PropTypes.any,
+    })),
   },
 
   getDefaultProps: function () {
     return {
       indent: 0
     };
   },
 
@@ -107,16 +111,17 @@ const Message = createClass({
       topLevelClasses,
       messageBody,
       frame,
       stacktrace,
       serviceContainer,
       dispatch,
       exceptionDocURL,
       timeStamp = Date.now(),
+      notes,
     } = this.props;
 
     topLevelClasses.push("message", source, type, level);
     if (open) {
       topLevelClasses.push("open");
     }
 
     const timestampEl = dom.span({
@@ -149,16 +154,39 @@ const Message = createClass({
             dispatch(actions.messageClose(messageId));
           } else {
             dispatch(actions.messageOpen(messageId));
           }
         },
       });
     }
 
+    let notesNodes;
+    if (notes) {
+      notes.map(note => dom.span(
+        { className: "message-flex-body error-note" },
+        dom.span({ className: "message-body devtools-monospace" },
+          "note: " + note.messageBody
+        ),
+        dom.span({ className: "message-location devtools-monospace" },
+          note.frame ? FrameView({
+            frame: note.frame,
+            onClick: serviceContainer
+              ? serviceContainer.onViewSourceInDebugger
+              : undefined,
+            showEmptyPathAsHost: true,
+            sourceMapService: serviceContainer
+              ? serviceContainer.sourceMapService
+              : undefined
+          }) : null
+        )));
+    } else {
+      notesNodes = [];
+    }
+
     const repeat = this.props.repeat ? MessageRepeat({repeat: this.props.repeat}) : null;
 
     let onFrameClick;
     if (serviceContainer && frame) {
       if (source === MESSAGE_SOURCE.CSS) {
         onFrameClick = serviceContainer.onViewSourceInStyleEditor;
       } else if (/^Scratchpad\/\d+$/.test(frame.source)) {
         onFrameClick = serviceContainer.onViewSourceInScratchpad;
@@ -207,15 +235,16 @@ const Message = createClass({
             learnMore
           ),
           " ", repeat,
           " ", location
         ),
         // Add a newline for formatting when copying to the clipboard.
         "\n",
         // If an attachment is displayed, the final newline is handled by the attachment.
-        attachment
+        attachment,
+        ...notesNodes
       )
     );
   }
 });
 
 module.exports = Message;
--- a/devtools/client/webconsole/new-console-output/selectors/messages.js
+++ b/devtools/client/webconsole/new-console-output/selectors/messages.js
@@ -129,16 +129,25 @@ function matchSearchFilters(message, fil
     )
     // Look for a match in messageText.
     || (message.messageText !== null
           && message.messageText.toLocaleLowerCase().includes(text.toLocaleLowerCase()))
     // Look for a match in parameters. Currently only checks value grips.
     || (message.parameters !== null
         && message.parameters.join("").toLocaleLowerCase()
             .includes(text.toLocaleLowerCase()))
+    // Look for a match in notes.
+    || (Array.isArray(message.notes) && message.notes.some(note =>
+          // Look for a match in location.
+          isTextInFrame(text, note.frame)
+          // Look for a match in messageBody.
+          || (note.messageBody !== null
+                && note.messageBody.toLocaleLowerCase()
+                     .includes(text.toLocaleLowerCase()))
+        ))
   );
 }
 
 function isTextInFrame(text, frame) {
   if (!frame) {
     return false;
   }
   // @TODO Change this to Object.values once it's supported in Node's version of V8
--- a/devtools/client/webconsole/new-console-output/types.js
+++ b/devtools/client/webconsole/new-console-output/types.js
@@ -34,16 +34,17 @@ exports.ConsoleMessage = Immutable.Recor
   parameters: null,
   repeat: 1,
   repeatId: null,
   stacktrace: null,
   frame: null,
   groupId: null,
   exceptionDocURL: null,
   userProvidedStyles: null,
+  notes: null,
 });
 
 exports.NetworkEventMessage = Immutable.Record({
   id: null,
   actor: null,
   level: MESSAGE_LEVEL.LOG,
   isXHR: false,
   request: null,
--- a/devtools/client/webconsole/new-console-output/utils/messages.js
+++ b/devtools/client/webconsole/new-console-output/utils/messages.js
@@ -165,17 +165,18 @@ function transformPacket(packet) {
       return new ConsoleMessage({
         source: messageSource,
         type: MESSAGE_TYPE.LOG,
         level,
         messageText: pageError.errorMessage,
         stacktrace: pageError.stacktrace ? pageError.stacktrace : null,
         frame,
         exceptionDocURL: pageError.exceptionDocURL,
-        timeStamp: pageError.timeStamp
+        timeStamp: pageError.timeStamp,
+        notes: pageError.notes,
       });
     }
 
     case "networkEvent": {
       let { networkEvent } = packet;
 
       return new NetworkEventMessage({
         actor: networkEvent.actor,
@@ -190,28 +191,30 @@ function transformPacket(packet) {
     case "evaluationResult":
     default: {
       let {
         exceptionMessage: messageText,
         exceptionDocURL,
         frame,
         result: parameters,
         timestamp: timeStamp,
+        notes,
       } = packet;
 
       const level = messageText ? MESSAGE_LEVEL.ERROR : MESSAGE_LEVEL.LOG;
       return new ConsoleMessage({
         source: MESSAGE_SOURCE.JAVASCRIPT,
         type: MESSAGE_TYPE.RESULT,
         level,
         messageText,
         parameters,
         exceptionDocURL,
         frame,
         timeStamp,
+        notes,
       });
     }
   }
 }
 
 // Helpers
 function getRepeatId(message) {
   message = message.toJS();
--- a/devtools/server/actors/webconsole.js
+++ b/devtools/server/actors/webconsole.js
@@ -894,17 +894,18 @@ WebConsoleActor.prototype =
       selectedNodeActor: aRequest.selectedNodeActor,
       selectedObjectActor: aRequest.selectedObjectActor,
     };
 
     let evalInfo = this.evalWithDebugger(input, evalOptions);
     let evalResult = evalInfo.result;
     let helperResult = evalInfo.helperResult;
 
-    let result, errorDocURL, errorMessage, errorGrip = null, frame = null;
+    let result, errorDocURL, errorMessage, errorNotes = null, errorGrip = null,
+        frame = null;
     if (evalResult) {
       if ("return" in evalResult) {
         result = evalResult.return;
       } else if ("yield" in evalResult) {
         result = evalResult.yield;
       } else if ("throw" in evalResult) {
         let error = evalResult.throw;
 
@@ -949,16 +950,33 @@ WebConsoleActor.prototype =
             // Set frame only if we have line/column numbers.
             frame = {
               source: "debugger eval code",
               line,
               column
             };
           }
         } catch (ex) {}
+
+        try {
+          let notes = error.errorNotes;
+          if (notes && notes.length) {
+            errorNotes = [];
+            for (let note of notes) {
+              errorNotes.push({
+                messageBody: this._createStringGrip(note.message),
+                frame: {
+                  source: note.fileName,
+                  line: note.lineNumber,
+                  column: note.columnNumber,
+                }
+              });
+            }
+          }
+        } catch (ex) {}
       }
     }
 
     // If a value is encountered that the debugger server doesn't support yet,
     // the console should remain functional.
     let resultGrip;
     try {
       resultGrip = this.createValueGrip(result);
@@ -973,16 +991,17 @@ WebConsoleActor.prototype =
       input: input,
       result: resultGrip,
       timestamp: timestamp,
       exception: errorGrip,
       exceptionMessage: this._createStringGrip(errorMessage),
       exceptionDocURL: errorDocURL,
       frame,
       helperResult: helperResult,
+      notes: errorNotes,
     };
   },
 
   /**
    * The Autocomplete request handler.
    *
    * @param object aRequest
    *        The request message - what input to autocomplete.
@@ -1506,33 +1525,51 @@ WebConsoleActor.prototype =
         s = s.parent;
       }
     }
     let lineText = aPageError.sourceLine;
     if (lineText && lineText.length > DebuggerServer.LONG_STRING_INITIAL_LENGTH) {
       lineText = lineText.substr(0, DebuggerServer.LONG_STRING_INITIAL_LENGTH);
     }
 
+    let notesArray = null;
+    let notes = aPageError.notes;
+    if (notes && notes.length) {
+      notesArray = [];
+      for (let i = 0, len = notes.length; i < len; i++) {
+        let note = notes.queryElementAt(i, Ci.nsIScriptErrorNote);
+        notesArray.push({
+          messageBody: this._createStringGrip(note.errorMessage),
+          frame: {
+            source: note.sourceName,
+            line: note.lineNumber,
+            column: note.columnNumber,
+          }
+        });
+      }
+    }
+
     return {
       errorMessage: this._createStringGrip(aPageError.errorMessage),
       errorMessageName: aPageError.errorMessageName,
       exceptionDocURL: ErrorDocs.GetURL(aPageError),
       sourceName: aPageError.sourceName,
       lineText: lineText,
       lineNumber: aPageError.lineNumber,
       columnNumber: aPageError.columnNumber,
       category: aPageError.category,
       timeStamp: aPageError.timeStamp,
       warning: !!(aPageError.flags & aPageError.warningFlag),
       error: !!(aPageError.flags & aPageError.errorFlag),
       exception: !!(aPageError.flags & aPageError.exceptionFlag),
       strict: !!(aPageError.flags & aPageError.strictFlag),
       info: !!(aPageError.flags & aPageError.infoFlag),
       private: aPageError.isFromPrivateWindow,
-      stacktrace: stack
+      stacktrace: stack,
+      notes: notesArray,
     };
   },
 
   /**
    * Handler for window.console API calls received from the ConsoleAPIListener.
    * This method sends the object to the remote Web Console client.
    *
    * @see ConsoleAPIListener