Bug 1511043 - Synchronize the replay timeline and console. r=nchevobbe

Tags:

Bug #: 1511043

Differential Revision: https://phabricator.services.mozilla.com/D13414
This commit is contained in:
Jason Laster 2018-11-27 17:03:46 -05:00
Родитель 36f240760d
Коммит 4c62541fea
8 изменённых файлов: 132 добавлений и 23 удалений

Просмотреть файл

@ -20,6 +20,8 @@ DevToolsModules(
'folder.svg',
'help.svg',
'javascript.svg',
'next-circle.svg',
'next.svg',
'pause.svg',
'prettyPrint.svg',
'react.svg',

Просмотреть файл

@ -0,0 +1,9 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<svg viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" fill="#0274E8">
<path d="M8,0 C3.581722,-2.705415e-16 5.41083001e-16,3.581722 0,8 C-5.41083001e-16,12.418278 3.581722,16 8,16 C12.418278,16 16,12.418278 16,8 C16,5.87826808 15.1571453,3.84343678 13.6568542,2.34314575 C12.1565632,0.842854723 10.1217319,1.2991861e-16 8,0 Z M8,15 C4.13400675,15 1,11.8659932 1,8 C1,4.13400675 4.13400675,1 8,1 C11.8659932,1 15,4.13400675 15,8 C15,9.85651543 14.2625021,11.6369928 12.9497475,12.9497475 C11.6369928,14.2625021 9.85651543,15 8,15 Z" id="Shape"></path>
<path d="M11.5,4 C11.2238576,4 11,4.22385763 11,4.5 L11,7.5 C10.9257751,7.36047643 10.8195383,7.24053164 10.69,7.15 L5.57,3.63 C5.2714387,3.432438 4.89004177,3.40955419 4.57,3.57 C4.22912746,3.732229 4.00866545,4.0725914 4,4.45 L4,11.55 C4.00294215,11.9207587 4.21077995,12.2594573 4.54,12.43 C4.68034771,12.5091766 4.83885991,12.5505276 5,12.55 C5.20390805,12.5495172 5.4027955,12.4867107 5.57,12.37 L10.69,8.82 C10.8195383,8.72946836 10.9257751,8.60952357 11,8.47 L11,11.47 C11,11.7461424 11.2238576,11.97 11.5,11.97 C11.7761424,11.97 12,11.7461424 12,11.47 L12,4.47 C11.9841101,4.20563806 11.7648386,3.99952289 11.5,4 Z M5,11.55 L5,4.45 L10.12,8 L5,11.55 Z" id="Shape"></path>
</svg>

После

Ширина:  |  Высота:  |  Размер: 1.4 KiB

Просмотреть файл

@ -0,0 +1,7 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<svg version="1.1" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
<path d="M12.4,2.1c-0.3,0-0.5,0.2-0.5,0.5v4.8c0,0-0.1-0.1-0.1-0.1l-7.4-5C3.8,1.8,3,2.2,3,3v10c0,0.8,0.8,1.3,1.4,0.8l7.4-5
c0.1,0,0.1-0.1,0.1-0.1v4.8c0,0.3,0.2,0.5,0.5,0.5s0.5-0.2,0.5-0.5v-11C12.9,2.3,12.7,2.1,12.4,2.1z M3.9,13V3l7.4,5L3.9,13z"/>
</svg>

После

Ширина:  |  Высота:  |  Размер: 582 B

Просмотреть файл

@ -461,11 +461,11 @@
box-sizing: border-box;
min-height: 29px;
--progress-recording-background: #ffebeb;
--progress-playing-background: #ebf6ff;
--progress-recording-background: hsl(0, 100%, 97%);
--progress-playing-background: hsl(207, 100%, 97%);
--recording-marker-background: hsl(14.9, 100%, 47.3%);
--recording-marker-background-hover: hsl(14.9, 100%, 37.3%);
--recording-marker-background: hsl(14.9, 100%, 67%);
--recording-marker-background-hover: hsl(14.9, 100%, 47%);
--command-button-size: 14px;
--command-button-primary-size: 20px;
}
@ -490,6 +490,17 @@
background: var(--progress-playing-background);
}
.webreplay-player #overlay:not(.recording) .progress::after {
background: var(--purple-50);
width: 1px;
height: 100%;
right: 0;
opacity: 0.4;
display: block;
content: "";
position: absolute;
}
.webreplay-player .recording .progress {
background: var(--progress-recording-background);
}
@ -501,7 +512,7 @@
height: 7px;
border-radius: 4.5px;
top: calc(50% - 3.5px);
background: var(--blue-50);
background: var(--blue-40);
}
.webreplay-player .message.overlayed {
@ -513,6 +524,15 @@
border-color: #fff;
}
.webreplay-player .message.highlighted {
background-color: var(--blue-60);
transform: scale(1.25);
}
.webreplay-player .recording .message.highlighted {
background-color: var(--recording-marker-background-hover);
}
.webreplay-player .recording .message.overlayed {
border-color: var(--progress-recording-background);
}
@ -588,7 +608,7 @@
.webreplay-player .progress-line {
width: 0%;
height: 1px;
background: #0074e8;
background: var(--blue-40);
position: absolute;
left: 0;
right: 10px;

Просмотреть файл

@ -111,18 +111,18 @@ a {
}
.message.paused::before {
background: #d8461f;
background: var(--purple-50);
opacity: 0.6;
width: 100vw;
height: 1px;
top: 0px;
bottom: 0px;
left: -3px;
display: block;
content: "";
position: absolute;
}
.message.paused ~ .message {
.message.paused ~ .message:not(.command):not(.result) .message-body-wrapper {
opacity: 0.5;
}
@ -215,9 +215,18 @@ a {
}
.message:hover > .icon.rewindable {
background-image: url(chrome://devtools/skin/images/webconsole/jump.svg);
background-size: 14px 14px;
background-image: url(resource://devtools/client/debugger/new/images/next-circle.svg);
cursor: pointer;
transform: rotate(180deg);
}
/*
* we flip the next.svg icon by default because when we're
* not paused, we would jump back. We remove the transform here
* because we want to jump forward.
*/
.message.paused ~ .message:hover .icon.rewindable {
transform: none;
}
.message > .message-body-wrapper {

Просмотреть файл

@ -44,7 +44,9 @@ class Message extends Component {
attachment: PropTypes.any,
stacktrace: PropTypes.any,
messageId: PropTypes.string,
executionPoint: PropTypes.string,
executionPoint: PropTypes.shape({
progress: PropTypes.number,
}),
scrollToMessage: PropTypes.bool,
exceptionDocURL: PropTypes.string,
request: PropTypes.object,
@ -79,6 +81,7 @@ class Message extends Component {
this.onLearnMoreClick = this.onLearnMoreClick.bind(this);
this.toggleMessage = this.toggleMessage.bind(this);
this.onContextMenu = this.onContextMenu.bind(this);
this.onMouseEvent = this.onMouseEvent.bind(this);
this.renderIcon = this.renderIcon.bind(this);
}
@ -122,6 +125,13 @@ class Message extends Component {
e.preventDefault();
}
onMouseEvent(ev) {
const {messageId, serviceContainer, executionPoint} = this.props;
if (serviceContainer.canRewind() && executionPoint) {
serviceContainer.onMessageHover(ev.type, messageId);
}
}
renderIcon() {
const { level, messageId, executionPoint, serviceContainer } = this.props;
@ -151,14 +161,19 @@ class Message extends Component {
exceptionDocURL,
timeStamp = Date.now(),
timestampsVisible,
executionPoint,
notes,
} = this.props;
topLevelClasses.push("message", source, type, level, isPaused ? "paused" : "");
topLevelClasses.push("message", source, type, level);
if (open) {
topLevelClasses.push("open");
}
if (isPaused) {
topLevelClasses.push("paused");
}
let timestampEl;
if (timestampsVisible === true) {
timestampEl = dom.span({
@ -262,9 +277,14 @@ class Message extends Component {
const bodyElements = Array.isArray(messageBody) ? messageBody : [messageBody];
const mouseEvents = serviceContainer.canRewind() && executionPoint
? { onMouseEnter: this.onMouseEvent, onMouseLeave: this.onMouseEvent }
: {};
return dom.div({
className: topLevelClasses.join(" "),
onContextMenu: this.onContextMenu,
...mouseEvents,
ref: node => {
this.messageNode = node;
},

Просмотреть файл

@ -238,6 +238,11 @@ WebConsoleOutputWrapper.prototype = {
},
jumpToExecutionPoint: executionPoint =>
this.toolbox.threadClient.timeWarp(executionPoint),
onMessageHover: (type, messageId) => {
const message = getMessage(store.getState(), messageId);
this.hud.emit("message-hover", type, message);
},
});
}
@ -507,6 +512,10 @@ WebConsoleOutputWrapper.prototype = {
return store;
},
subscribeToStore: function(callback) {
store.subscribe(() => callback(store.getState()));
},
// Called by pushing close button.
closeSplitConsole() {
this.toolbox.closeSplitConsole();

Просмотреть файл

@ -9,7 +9,9 @@ const dom = require("devtools/client/shared/vendor/react-dom-factories");
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
const { LocalizationHelper } = require("devtools/shared/l10n");
const L10N = new LocalizationHelper("devtools/client/locales/toolbox.properties");
const L10N = new LocalizationHelper(
"devtools/client/locales/toolbox.properties"
);
const getFormatStr = (key, a) => L10N.getFormatStr(`toolbox.replay.${key}`, a);
const { div } = dom;
@ -83,6 +85,7 @@ class WebReplayPlayer extends Component {
recording: true,
paused: false,
messages: [],
highlightedMessage: null,
};
this.overlayWidth = 0;
}
@ -92,22 +95,30 @@ class WebReplayPlayer extends Component {
this.threadClient.addListener("paused", this.onPaused.bind(this));
this.threadClient.addListener("resumed", this.onResumed.bind(this));
this.threadClient.addListener("progress", this.onProgress.bind(this));
this.activeConsole._client.addListener(
"consoleAPICall",
this.onMessage.bind(this)
);
this.toolbox.getPanelWhenReady("webconsole").then(panel => {
const consoleFrame = panel.hud.ui;
consoleFrame.on("message-hover", this.onConsoleMessageHover.bind(this));
consoleFrame.consoleOutput.subscribeToStore(
this.onConsoleUpdate.bind(this)
);
});
}
componentDidUpdate() {
this.overlayWidth = this.updateOverlayWidth();
}
get toolbox() {
return this.props.toolbox;
}
get threadClient() {
return this.props.toolbox.threadClient;
return this.toolbox.threadClient;
}
get activeConsole() {
return this.props.toolbox.target.activeConsole;
return this.toolbox.target.activeConsole;
}
isRecording() {
@ -164,8 +175,27 @@ class WebReplayPlayer extends Component {
this.setState(newState);
}
onMessage(_, packet) {
this.setState({ messages: this.state.messages.concat(packet.message) });
onConsoleUpdate(consoleState) {
const {
messages: { visibleMessages, messagesById },
} = consoleState;
const messages = visibleMessages.map(id => messagesById.get(id));
if (visibleMessages != this.state.visibleMessages) {
this.setState({ messages, visibleMessages });
}
}
onConsoleMessageHover(type, message) {
if (type == "mouseleave") {
return this.setState({ highlightedMessage: null });
}
if (type == "mouseenter") {
return this.setState({ highlightedMessage: message.id });
}
return null;
}
seek(executionPoint) {
@ -300,7 +330,7 @@ class WebReplayPlayer extends Component {
}
renderMessage(message, index) {
const { messages, executionPoint } = this.state;
const { messages, executionPoint, highlightedMessage } = this.state;
const offset = this.getOffset(message.executionPoint);
const previousMessage = messages[index - 1];
@ -318,10 +348,13 @@ class WebReplayPlayer extends Component {
this.getDistanceFrom(message.executionPoint, executionPoint) >
markerWidth / 2;
const isHighlighted = highlightedMessage == message.id;
return dom.a({
className: classname("message", {
overlayed: isOverlayed,
future: isFuture,
highlighted: isHighlighted,
}),
style: {
left: `${offset - markerWidth / 2}px`,