renderIcon() { const { level, messageId, executionPoint, serviceContainer } = this.props; if (serviceContainer.canRewind()) { return dom.span({ className: "icon", title: "Jump", "aria-live": "off", onClick: () => serviceContainer.jumpToExecutionPoint(executionPoint, messageId) }); } return MessageIcon({ level }); }
render() { const { open, collapsible, collapseTitle, source, type, level, indent, topLevelClasses, messageBody, frame, stacktrace, serviceContainer, exceptionDocURL, timeStamp = Date.now(), timestampsVisible, notes, } = this.props; topLevelClasses.push("message", source, type, level); if (open) { topLevelClasses.push("open"); } let timestampEl; if (timestampsVisible === true) { timestampEl = dom.span({ className: "timestamp devtools-monospace" }, l10n.timestampString(timeStamp)); } const icon = MessageIcon({level}); // Figure out if there is an expandable part to the message. let attachment = null; if (this.props.attachment) { attachment = this.props.attachment; } else if (stacktrace && open) { attachment = dom.div( { className: "stacktrace devtools-monospace" }, StackTrace({ stacktrace: stacktrace, onViewSourceInDebugger: serviceContainer.onViewSourceInDebugger || serviceContainer.onViewSource, onViewSourceInScratchpad: serviceContainer.onViewSourceInScratchpad || serviceContainer.onViewSource, sourceMapService: serviceContainer.sourceMapService, }) ); } // If there is an expandable part, make it collapsible. let collapse = null; if (collapsible) { collapse = CollapseButton({ open, title: collapseTitle, onClick: this.toggleMessage }); } let notesNodes; if (notes) { notesNodes = 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 || serviceContainer.onViewSource : undefined, showEmptyPathAsHost: true, sourceMapService: serviceContainer ? serviceContainer.sourceMapService : undefined }) : null ))); } else { notesNodes = []; } const repeat = this.props.repeat && this.props.repeat > 1 ? MessageRepeat({repeat: this.props.repeat}) : null; let onFrameClick; if (serviceContainer && frame) { if (source === MESSAGE_SOURCE.CSS) { onFrameClick = serviceContainer.onViewSourceInStyleEditor || serviceContainer.onViewSource; } else if (/^Scratchpad\/\d+$/.test(frame.source)) { onFrameClick = serviceContainer.onViewSourceInScratchpad || serviceContainer.onViewSource; } else { // Point everything else to debugger, if source not available, // it will fall back to view-source. onFrameClick = serviceContainer.onViewSourceInDebugger || serviceContainer.onViewSource; } } // Configure the location. const location = dom.span({ className: "message-location devtools-monospace" }, frame ? FrameView({ frame, onClick: onFrameClick, showEmptyPathAsHost: true, sourceMapService: serviceContainer ? serviceContainer.sourceMapService : undefined }) : null ); let learnMore; if (exceptionDocURL) { learnMore = dom.a({ className: "learn-more-link webconsole-learn-more-link", title: exceptionDocURL.split("?")[0], onClick: this.onLearnMoreClick, }, `[${l10n.getStr("webConsoleMoreInfoLabel")}]`); } const bodyElements = Array.isArray(messageBody) ? messageBody : [messageBody]; return dom.div({ className: topLevelClasses.join(" "), onContextMenu: this.onContextMenu, ref: node => { this.messageNode = node; }, "aria-live": type === MESSAGE_TYPE.COMMAND ? "off" : "polite" }, timestampEl, MessageIndent({indent}), icon, collapse, dom.span({ className: "message-body-wrapper" }, dom.span({ className: "message-flex-body", onClick: collapsible ? this.toggleMessage : undefined, }, // Add whitespaces for formatting when copying to the clipboard. timestampEl ? " " : null, dom.span({ className: "message-body devtools-monospace" }, ...bodyElements, learnMore ), repeat ? " " : null, 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, ...notesNodes ) ); }