Bug 1547084 Part 6 - Console changes for new control logic, r=nchevobbe.
authorBrian Hackett <bhackett1024@gmail.com>
Sun, 12 May 2019 13:20:11 -1000
changeset 536107 85b94102fa34e3f261f2d6713ba3c5b88e72e31a
parent 536106 fdd4b06edab957c38eebfaed5c80c8f0b6cc9307
child 536108 d394c173d6a4a527d6da32fd0ef8fa110a83bb6b
push id2082
push userffxbld-merge
push dateMon, 01 Jul 2019 08:34:18 +0000
treeherdermozilla-release@2fb19d0466d2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnchevobbe
bugs1547084
milestone68.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 1547084 Part 6 - Console changes for new control logic, r=nchevobbe.
devtools/client/webconsole/components/ConsoleOutput.js
devtools/client/webconsole/components/Message.js
devtools/client/webconsole/reducers/messages.js
--- a/devtools/client/webconsole/components/ConsoleOutput.js
+++ b/devtools/client/webconsole/components/ConsoleOutput.js
@@ -17,38 +17,43 @@ const {
   getVisibleMessages,
   getPausedExecutionPoint,
   getAllRepeatById,
   getAllWarningGroupsById,
   isMessageInWarningGroup,
 } = require("devtools/client/webconsole/selectors/messages");
 
 loader.lazyRequireGetter(this, "PropTypes", "devtools/client/shared/vendor/react-prop-types");
-loader.lazyRequireGetter(this, "sortBy", "devtools/client/shared/vendor/lodash", true);
 loader.lazyRequireGetter(this, "MessageContainer", "devtools/client/webconsole/components/MessageContainer", true);
+ChromeUtils.defineModuleGetter(this, "pointPrecedes", "resource://devtools/shared/execution-point-utils.js");
 
 const {
   MESSAGE_TYPE,
 } = require("devtools/client/webconsole/constants");
 const {
   getInitialMessageCountForViewport,
 } = require("devtools/client/webconsole/utils/messages.js");
 
 function getClosestMessage(visibleMessages, messages, executionPoint) {
   if (!executionPoint || !visibleMessages) {
     return null;
   }
 
-  const { progress } = executionPoint;
-  const getProgress = m => m && m.executionPoint && m.executionPoint.progress;
-
-  return sortBy(
-    visibleMessages.map(id => messages.get(id)),
-    m => Math.abs(progress - getProgress(m))
-  )[0];
+  const messageList = visibleMessages.map(id => messages.get(id));
+  const precedingMessages = messageList.filter(m => {
+    return m && m.executionPoint && pointPrecedes(m.executionPoint, executionPoint);
+  });
+  if (precedingMessages.length != 0) {
+    return precedingMessages.sort((a, b) => {
+      return pointPrecedes(a.executionPoint, b.executionPoint);
+    })[0];
+  }
+  return messageList.filter(m => m && m.executionPoint).sort((a, b) => {
+    return pointPrecedes(b.executionPoint, a.executionPoint);
+  })[0];
 }
 
 class ConsoleOutput extends Component {
   static get propTypes() {
     return {
       initialized: PropTypes.bool.isRequired,
       messages: PropTypes.object.isRequired,
       messagesUi: PropTypes.array.isRequired,
--- a/devtools/client/webconsole/components/Message.js
+++ b/devtools/client/webconsole/components/Message.js
@@ -15,16 +15,17 @@ const { MESSAGE_SOURCE, MESSAGE_TYPE } =
 const { MessageIndent } = require("devtools/client/webconsole/components/MessageIndent");
 const MessageIcon = require("devtools/client/webconsole/components/MessageIcon");
 const FrameView = createFactory(require("devtools/client/shared/components/Frame"));
 
 loader.lazyRequireGetter(this, "CollapseButton", "devtools/client/webconsole/components/CollapseButton");
 loader.lazyRequireGetter(this, "MessageRepeat", "devtools/client/webconsole/components/MessageRepeat");
 loader.lazyRequireGetter(this, "PropTypes", "devtools/client/shared/vendor/react-prop-types");
 loader.lazyRequireGetter(this, "SmartTrace", "devtools/client/shared/components/SmartTrace");
+ChromeUtils.defineModuleGetter(this, "pointPrecedes", "resource://devtools/shared/execution-point-utils.js");
 
 class Message extends Component {
   static get propTypes() {
     return {
       open: PropTypes.bool,
       collapsible: PropTypes.bool,
       collapseTitle: PropTypes.string,
       onToggle: PropTypes.func,
@@ -35,22 +36,18 @@ class Message extends Component {
       inWarningGroup: PropTypes.bool,
       topLevelClasses: PropTypes.array.isRequired,
       messageBody: PropTypes.any.isRequired,
       repeat: PropTypes.any,
       frame: PropTypes.any,
       attachment: PropTypes.any,
       stacktrace: PropTypes.any,
       messageId: PropTypes.string,
-      executionPoint: PropTypes.shape({
-        progress: PropTypes.number,
-      }),
-      pausedExecutionPoint: PropTypes.shape({
-        progress: PropTypes.number,
-      }),
+      executionPoint: PropTypes.object,
+      pausedExecutionPoint: PropTypes.object,
       scrollToMessage: PropTypes.bool,
       exceptionDocURL: PropTypes.string,
       request: PropTypes.object,
       dispatch: PropTypes.func,
       timeStamp: PropTypes.number,
       timestampsVisible: PropTypes.bool.isRequired,
       serviceContainer: PropTypes.shape({
         emitNewMessage: PropTypes.func.isRequired,
@@ -190,17 +187,17 @@ class Message extends Component {
       topLevelClasses.push("open");
     }
 
     if (isPaused) {
       topLevelClasses.push("paused");
 
       if (pausedExecutionPoint
         && executionPoint
-        && pausedExecutionPoint.progress < executionPoint.progress) {
+        && !pointPrecedes(executionPoint, pausedExecutionPoint)) {
         topLevelClasses.push("paused-before");
       }
     }
 
     let timestampEl;
     if (timestampsVisible === true) {
       timestampEl = dom.span({
         className: "timestamp devtools-monospace",
--- a/devtools/client/webconsole/reducers/messages.js
+++ b/devtools/client/webconsole/reducers/messages.js
@@ -20,33 +20,32 @@ const {
 
 loader.lazyRequireGetter(this, "getGripPreviewItems", "devtools/client/shared/components/reps/reps", true);
 loader.lazyRequireGetter(this, "getUnicodeUrlPath", "devtools/client/shared/unicode-url", true);
 loader.lazyRequireGetter(this, "getSourceNames", "devtools/client/shared/source-utils", true);
 loader.lazyRequireGetter(this, "createWarningGroupMessage", "devtools/client/webconsole/utils/messages", true);
 loader.lazyRequireGetter(this, "isWarningGroup", "devtools/client/webconsole/utils/messages", true);
 loader.lazyRequireGetter(this, "getWarningGroupType", "devtools/client/webconsole/utils/messages", true);
 loader.lazyRequireGetter(this, "getParentWarningGroupMessageId", "devtools/client/webconsole/utils/messages", true);
+ChromeUtils.defineModuleGetter(this, "pointPrecedes", "resource://devtools/shared/execution-point-utils.js");
 
 const {
   UPDATE_REQUEST,
 } = require("devtools/client/netmonitor/src/constants");
 
 const {
   processNetworkUpdates,
 } = require("devtools/client/netmonitor/src/utils/request-utils");
 
 const MessageState = overrides => Object.freeze(Object.assign({
   // List of all the messages added to the console.
   messagesById: new Map(),
   // List of additional data associated with messages (populated async or on-demand at a
   // later time after the message is received).
   messagesPayloadById: new Map(),
-  // When recording or replaying, all progress values in messagesById.
-  replayProgressMessages: new Set(),
   // Array of the visible messages.
   visibleMessages: [],
   // Object for the filtered messages.
   filteredMessagesCount: getDefaultFiltersCounter(),
   // List of the message ids which are opened.
   messagesUiById: [],
   // Map of the form {messageId : tableData}, which represent the data passed
   // as an argument in console.table calls.
@@ -65,35 +64,38 @@ const MessageState = overrides => Object
   removedActors: [],
   // Map of the form {messageId : numberOfRepeat}
   repeatById: {},
   // Map of the form {messageId : networkInformation}
   // `networkInformation` holds request, response, totalTime, ...
   networkMessagesUpdateById: {},
   // Set of logpoint IDs that have been removed
   removedLogpointIds: new Set(),
+  // Any execution point we are currently paused at, when replaying.
   pausedExecutionPoint: null,
+  // Whether any messages with execution points have been seen.
+  hasExecutionPoints: false,
 }, overrides));
 
 function cloneState(state) {
   return {
     messagesById: new Map(state.messagesById),
-    replayProgressMessages: new Set(state.replayProgressMessages),
     visibleMessages: [...state.visibleMessages],
     filteredMessagesCount: {...state.filteredMessagesCount},
     messagesUiById: [...state.messagesUiById],
     messagesPayloadById: new Map(state.messagesPayloadById),
     messagesTableDataById: new Map(state.messagesTableDataById),
     groupsById: new Map(state.groupsById),
     currentGroup: state.currentGroup,
     removedActors: [...state.removedActors],
     repeatById: {...state.repeatById},
     networkMessagesUpdateById: {...state.networkMessagesUpdateById},
     removedLogpointIds: new Set(state.removedLogpointIds),
     pausedExecutionPoint: state.pausedExecutionPoint,
+    hasExecutionPoints: state.hasExecutionPoints,
     warningGroupsById: new Map(state.warningGroupsById),
   };
 }
 
 /**
  * Add a console message to the state.
  *
  * @param {ConsoleMessage} newMessage: The message to add to the state.
@@ -101,38 +103,26 @@ function cloneState(state) {
  * @param {FiltersState} filtersState: The filters state.
  * @param {PrefsState} prefsState: The preferences state.
  * @param {UiState} uiState: The ui state.
  * @returns {MessageState} a new messages state.
  */
 function addMessage(newMessage, state, filtersState, prefsState, uiState) {
   const {
     messagesById,
-    replayProgressMessages,
     groupsById,
     currentGroup,
     repeatById,
   } = state;
 
   if (newMessage.type === constants.MESSAGE_TYPE.NULL_MESSAGE) {
     // When the message has a NULL type, we don't add it.
     return state;
   }
 
-  if (newMessage.executionPoint && !newMessage.logpointId) {
-    // When replaying old behaviors in a tab, we might see the same messages
-    // multiple times. Ignore duplicate messages with the same progress values.
-    // We don't need to do this for logpoint messages, which will only arrive once.
-    const progress = newMessage.executionPoint.progress;
-    if (replayProgressMessages.has(progress)) {
-      return state;
-    }
-    state.replayProgressMessages.add(progress);
-  }
-
   // After messages with a given logpoint ID have been removed, ignore all
   // future messages with that ID.
   if (newMessage.logpointId &&
       state.removedLogpointIds &&
       state.removedLogpointIds.has(newMessage.logpointId)) {
     return state;
   }
 
@@ -158,16 +148,20 @@ function addMessage(newMessage, state, f
   const parentGroups = getParentGroups(currentGroup, groupsById);
   if (!isWarningGroup(newMessage)) {
     newMessage.groupId = currentGroup;
     newMessage.indent = parentGroups.length;
   }
 
   ensureExecutionPoint(state, newMessage);
 
+  if (newMessage.executionPoint) {
+    state.hasExecutionPoints = true;
+  }
+
   // Check if the current message could be placed in a Warning Group.
   // This needs to be done before setting the new message in messagesById so we have a
   // proper message.
   const warningGroupType = getWarningGroupType(newMessage);
 
   // If the preference for warning grouping is true, and the new message could be in a
   // warning group.
   if (prefsState.groupWarnings && warningGroupType !== null) {
@@ -1184,17 +1178,17 @@ function getDefaultFiltersCounter() {
 // if other messages with real execution points appear later.
 function ensureExecutionPoint(state, newMessage) {
   if (newMessage.executionPoint) {
     return;
   }
 
   // Add a lastExecutionPoint property which will place this message immediately
   // after the last visible one when sorting.
-  let point = { progress: 0 }, messageCount = 1;
+  let point = { checkpoint: 0, progress: 0 }, messageCount = 1;
   if (state.visibleMessages.length) {
     const lastId = state.visibleMessages[state.visibleMessages.length - 1];
     const lastMessage = state.messagesById.get(lastId);
     if (lastMessage.executionPoint) {
       point = lastMessage.executionPoint;
     } else {
       point = lastMessage.lastExecutionPoint.point;
       messageCount = lastMessage.lastExecutionPoint.messageCount + 1;
@@ -1213,42 +1207,29 @@ function messageCountSinceLastExecutionP
   return message.lastExecutionPoint ? message.lastExecutionPoint.messageCount : 0;
 }
 
 function maybeSortVisibleMessages(state) {
   // When using log points while replaying, messages can be added out of order
   // with respect to how they originally executed. Use the execution point
   // information in the messages to sort visible messages according to how
   // they originally executed. This isn't necessary if we haven't seen any
-  // messages with progress counters, as either we aren't replaying or haven't
+  // messages with execution points, as either we aren't replaying or haven't
   // seen any messages yet.
-  if (state.replayProgressMessages.size) {
+  if (state.hasExecutionPoints) {
     state.visibleMessages.sort((a, b) => {
       const pointA = messageExecutionPoint(state, a);
       const pointB = messageExecutionPoint(state, b);
-      if (pointA.progress != pointB.progress) {
-        return pointA.progress > pointB.progress;
-      }
-      // Execution points without a progress counter predate execution points
-      // with one, i.e. a console.log() call (which bumps the progress value)
-      // predates the code that runs afterward.
-      if ("frameIndex" in pointA != "frameIndex" in pointB) {
-        return "frameIndex" in pointA;
+      if (pointPrecedes(pointB, pointA)) {
+        return true;
+      } else if (pointPrecedes(pointA, pointB)) {
+        return false;
       }
-      // Deeper frames predate shallower frames, if the progress counter is the
-      // same. We bump the progress counter when pushing frames, but not when
-      // popping them.
-      if (pointA.frameIndex != pointB.frameIndex) {
-        return pointA.frameIndex < pointB.frameIndex;
-      }
-      // Earlier script locations predate later script locations.
-      if (pointA.offset != pointB.offset) {
-        return pointA.offset > pointB.offset;
-      }
-      // When messages don't have their own execution point, they can still be
+
+      // When messages have the same execution point, they can still be
       // distinguished by the number of messages since the last one which did
       // have an execution point.
       const countA = messageCountSinceLastExecutionPoint(state, a);
       const countB = messageCountSinceLastExecutionPoint(state, b);
       return countA > countB;
     });
   }
 }