/* This Source Code Form is subject to the terms of the Mozilla Public *License,v.2.0.IfacopyoftheMPLwasnotdistributedwiththis
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
// This is just a reentrancy guard. Because findMessagesVirtualized mucks // around with the scroll position, if we do something like // let promise1 = findMessagesVirtualized(...); // let promise2 = findMessagesVirtualized(...); // await promise1; // await promise2; // then the two calls will end up messing up each other's expected scroll // position, at which point they could get stuck. This lets us throw an // error when that happens.
let gInFindMessagesVirtualized = false; // And this lets us get a little more information in the error - it just holds // the stack of the prior call.
let gFindMessagesVirtualizedStack = null;
/** *Findmultiplemessagesintheoutput,scrollingthroughtheoutputfromtop *tobottominordertomakesurethemessagesareactuallyrendered. * *@paramobjectoptions *@paramobjectoptions.hud *Thewebconsole. *@paramoptions.text[optional] *Asubstringthatcanbefoundinthemessage. *@paramoptions.typeSelector *Apartofselectorforthemessage,tospecifythemessagetype. *@paramoptions.expectedCount[optional] *Thenumberofmessagestoget.Thisletsusstopscrollingearlyif *wefindthatnumberofmessages. *@return{Array}allofthemessagenodesintheconsoleoutputmatchingthe *providedfilters.IfexpectedCountisgreaterthan1,orequalto-1, *someofthesemaybestalefromhavingbeenscrolledoutofview.
*/
async function findMessagesVirtualizedByType({
hud,
text,
typeSelector,
expectedCount,
}) { if (!typeSelector) { thrownew Error("typeSelector parameter is required");
} if (!typeSelector.startsWith(".")) { thrownew Error("typeSelector should start with a dot e.g. `.result`");
}
function getMessageIndex(message) { return getVisibleMessageIds().indexOf(
message.getAttribute("data-message-id")
);
}
function getNextMessageId(prevMessage) { const visible = getVisibleMessageIds();
let index = 0; if (prevMessage) { const lastId = prevMessage.getAttribute("data-message-id");
index = visible.indexOf(lastId); if (index === -1) { thrownew Error(
`Tried to get next message ID for message that doesn't exist. Last seen ID: ${lastId}, all visible ids: [${visible.join( ", "
)}]`
);
}
} if (index + 1 >= visible.length) { returnnull;
} return visible[index + 1];
}
if (gInFindMessagesVirtualized) { thrownew Error(
`findMessagesVirtualized was re-entered somehow. This is not allowed. Other stack: [${gFindMessagesVirtualizedStack}]`
);
} try {
gInFindMessagesVirtualized = true;
gFindMessagesVirtualizedStack = new Error().stack; // The console output will automatically scroll to the bottom of the // scrollport in certain circumstances. Because we need to scroll the // output to find all messages, we need to disable this. This attribute // controls that.
scrollport.setAttribute("disable-autoscroll", "");
// This array is here purely for debugging purposes. We collect the indices // of every element we see in order to validate that we don't have any gaps // in the list. const allIndices = [];
const allElements = []; const seenIds = new Set();
let lastItem = null; while (true) { if (scrollport.scrollHeight > scrollport.clientHeight) { if (!lastItem && scrollport.scrollTop != 0) { // For simplicity's sake, we always start from the top of the output.
scrollport.scrollTop = 0;
} elseif (!lastItem && scrollport.scrollTop == 0) { // We want to make sure that we actually change the scroll position // here, because we're going to wait for an update below regardless, // just to flush out any changes that may have just happened. If we // don't do this, and there were no changes before this function was // called, then we'll just hang on the promise below.
scrollport.scrollTop = 1;
} else { // This is the core of the loop. Scroll down to the bottom of the // current scrollport, wait until we see the element after the last // one we've seen, and then harvest the messages that are displayed.
scrollport.scrollTop = scrollport.scrollTop + scrollport.clientHeight;
}
// Wait for something to happen in the output before checking for our // expected next message.
await new Promise(resolve =>
hud.ui.once("lazy-message-list-updated-or-noop", resolve)
);
// After a scroll, we typically expect to get an updated list of // elements. However, we have some slack at the top of the list, // because we draw elements above and below the actual scrollport to // avoid white flashes when async scrolling. const scrollTarget = scrollport.scrollTop + scrollport.clientHeight;
scrollport.scrollTop = scrollTarget;
await new Promise(resolve =>
hud.ui.once("lazy-message-list-updated-or-noop", resolve)
); returnfalse;
});
} catch (e) { thrownew Error(
`Failed waiting for next message ID (${getNextMessageId(
lastItem
)}) Visible messages: [${[
...scrollport.querySelectorAll(".message"),
].map(el => el.getAttribute("data-message-id"))}]`
);
}
}
const bottomPlaceholder = scrollport.querySelector( ".lazy-message-list-bottom"
); if (!bottomPlaceholder) { // When there are no messages in the output, there is also no // top/bottom placeholder. There's nothing more to do at this point, // so break and return. break;
}
lastItem = bottomPlaceholder.previousSibling;
// This chunk is just validating that we have no gaps in our output so // far. const indices = [...scrollport.querySelectorAll("[data-message-id]")]
.filter(
el => el !== scrollport.firstChild && el !== scrollport.lastChild
)
.map(el => getMessageIndex(el));
allIndices.push(...indices);
allIndices.sort((lhs, rhs) => lhs - rhs); for (let i = 1; i < allIndices.length; i++) { if (
allIndices[i] != allIndices[i - 1] &&
allIndices[i] != allIndices[i - 1] + 1
) { thrownew Error(
`Gap detected in virtualized webconsole output between ${
allIndices[i - 1]
} and ${allIndices[i]}. Indices: ${allIndices.join(",")}`
);
}
}
const messages = scrollport.querySelectorAll(selector); const filtered = [...messages].filter(
el => // Core user filters:
el.textContent.includes(text) &&
(!messageId || el.getAttribute("data-message-id") === messageId) && // Make sure we don't collect duplicate messages:
!seenIds.has(el.getAttribute("data-message-id"))
);
allElements.push(...filtered); for (const message of filtered) {
seenIds.add(message.getAttribute("data-message-id"));
}
// If the bottom placeholder has 0 height, it means we've scrolled to the // bottom and output all the items. if (bottomPlaceholder.getBoundingClientRect().height == 0) { break;
}
await waitForTime(0);
}
// Finally, we get the map of message IDs to indices within the output, and // sort the message nodes according to that index. They can come in out of // order for a number of reasons (we continue rendering any messages that // have been expanded, and we always render the topmost and bottommost // messages for a11y reasons.) const idsToIndices = getVisibleMessageMap();
allElements.sort(
(lhs, rhs) =>
idsToIndices.get(lhs.getAttribute("data-message-id")) -
idsToIndices.get(rhs.getAttribute("data-message-id"))
); return allElements;
} finally {
scrollport.removeAttribute("disable-autoscroll");
gInFindMessagesVirtualized = false;
gFindMessagesVirtualizedStack = null;
}
}
/** *Findmultiplemessageswithgivenmessagetypeintheoutput. * *@paramobjecthud *Thewebconsole. *@paramstringtext *Asubstringthatcanbefoundinthemessage. *@paramstringtypeSelector *Apartofselectorforthemessage,tospecifythemessagetype. *@return{Array}Thenodescorrespondingthefoundmessages
*/ function findMessagesByType(hud, text, typeSelector) { if (!typeSelector) { thrownew Error("typeSelector parameter is required");
} if (!typeSelector.startsWith(".")) { thrownew Error("typeSelector should start with a dot e.g. `.result`");
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.