Skip to content

Commit

Permalink
fix(replay): use shadow dom for replay elements
Browse files Browse the repository at this point in the history
  • Loading branch information
blakebyrnes committed Oct 5, 2020
1 parent daf9431 commit b19b382
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 57 deletions.
10 changes: 6 additions & 4 deletions replay-app/frontend/src/pages/command-overlay/index.vue
Expand Up @@ -49,22 +49,24 @@ export default class CommandOverlay extends Vue {
.CommandOverlay {
@include overlayStyle();
min-height: 80px;
padding: 1px;
box-sizing: border-box;
padding: 10px 25px 10px 15px;
overflow: hidden;
.title {
margin: 20px;
margin-top: 5px;
box-sizing: content-box;
wrap-word: break-word;
word-break: break-word;
text-align: center;
}
.resultBox {
margin: 20px;
font-size: 0.9em;
.label {
color: #3c3c3c;
font-style: italic;
margin-right: 5px;
}
.value {
word-break: break-word;
&.error {
font-style: italic;
color: #717171;
Expand Down
5 changes: 3 additions & 2 deletions replay-app/frontend/src/pages/header/index.vue
Expand Up @@ -36,7 +36,7 @@
button(v-if="saSession.tabs.length > 1" @click="showTabs" ref="tabRef")
.text {{activeTabIdx + 1}}/{{saSession.tabs.length}}
.address-bar
Icon(:src="ICON_LOCK" :size=16 iconStyle="transform: 'scale(-1,1)'")
Icon(:src="ICON_LOCK" :size=16 iconStyle="transform: 'scale(-1,1)'" disabled="true")
.text {{currentUrl}}

</template>
Expand Down Expand Up @@ -379,7 +379,8 @@ export default class HeaderPage extends Vue {
}
.Icon {
margin-top: 1px;
width: 30px;
width: 16px;
height: 13px;
}
}
}
Expand Down
120 changes: 69 additions & 51 deletions replay-app/injected-scripts/domReplayer.ts
Expand Up @@ -6,8 +6,14 @@ import { IMouseEvent, IScrollRecord } from '~shared/interfaces/ISaSession';
const idMap = new Map<number, Node>();
const preserveElements = ['HTML', 'HEAD', 'BODY'];

let maxHighlightTop = -1;
let minHighlightTop = 10e3;
let lastHighlightNodes: number[] = [];
const domChangeList = [];

const replayNode = document.createElement('sa-replay');
const replayShadow = replayNode.attachShadow({ mode: 'closed' });

console.log('DomReplayer loaded.');
// @ts-ignore
window.replayEvents = async function replayEvents(
Expand All @@ -17,8 +23,15 @@ window.replayEvents = async function replayEvents(
scrollEvent,
) {
await domContentLoaded;
console.log('Events: changes=%s, highlighted=%s, hasMouse=%s, hasScroll=%s', changeEvents?.length ?? 0, resultNodeIds?.length ?? 0, !!mouseEvent, !!scrollEvent);
console.log(
'Events: changes=%s, highlighted=%s, hasMouse=%s, hasScroll=%s',
changeEvents?.length ?? 0,
resultNodeIds?.length ?? 0,
!!mouseEvent,
!!scrollEvent,
);
if (changeEvents) applyDomChanges(changeEvents);
document.body.appendChild(replayNode);
if (resultNodeIds !== undefined) highlightNodes(resultNodeIds);
if (mouseEvent) updateMouse(mouseEvent);
if (scrollEvent) updateScroll(scrollEvent);
Expand Down Expand Up @@ -86,6 +99,12 @@ function applyDomChanges(changeEvents: IDomChangeEvent[]) {
continue;
}

if (tagName && tagName.toLowerCase() === 'noscript') {
if (!changeEvent.attributes) changeEvent.attributes = {};
if (changeEvent.attributes.style) changeEvent.attributes.style += ';display:none';
else changeEvent.attributes.style = 'display:none';
}

let node: Node;
let parentNode: Node;
try {
Expand Down Expand Up @@ -165,7 +184,6 @@ function resetLocation(href: string, commandId: number) {
window.history.replaceState({}, 'Replay', href);
window.scrollTo({ top: 0 });
document.documentElement.innerHTML = '';
document.head.appendChild(styleElement);
idMap.clear();
while (document.documentElement.previousSibling) {
const prev = document.documentElement.previousSibling;
Expand All @@ -189,10 +207,18 @@ function setNodeAttributes(node: Element, data: IDomChangeEvent) {
if (!data.attributes) return;
for (const [name, value] of Object.entries(data.attributes)) {
const ns = data.attributeNamespaces ? data.attributeNamespaces[name] : null;
if (name === 'xmlns' || name.startsWith('xmlns') || node.tagName === 'HTML') {
node.setAttribute(name, value);
} else {
node.setAttributeNS(ns || null, name, value);
try {
if (name === 'xmlns' || name.startsWith('xmlns') || node.tagName === 'HTML') {
node.setAttribute(name, value);
} else {
node.setAttributeNS(ns || null, name, value);
}
} catch (err) {
if (
!err.toString().match(/not a valid attribute name/) &&
!err.toString().match(/qualified name/)
)
throw err;
}
}
}
Expand Down Expand Up @@ -247,10 +273,34 @@ function deserializeNode(data: IDomChangeEvent, parent?: Element): Node {
/////// / DOM HIGHLIGHTER ///////////////////////////////////////////////////////////////////////////

const styleElement = document.createElement('style');
styleElement.innerHTML = `
noscript {
display:none
styleElement.textContent = `
sa-overflow-bar {
width: 500px;
background-color:#3498db;
margin:0 auto;
height: 100%;
box-shadow: 3px 0 0 0 #3498db;
display:block;
}
sa-overflow {
display:block;
width:100%;
height:8px;
position:fixed;
pointer-events: none;
}
sa-highlight {
z-index:10000;
position:absolute;
box-shadow: 1px 1px 3px 0 #3498db;
border-radius:3px;
border:1px solid #3498db;
padding:5px;
pointer-events: none;
}
sa-mouse-pointer {
pointer-events: none;
position: absolute;
Expand Down Expand Up @@ -286,34 +336,8 @@ styleElement.innerHTML = `
transition: none;
border-color: rgba(0,255,0,0.9);
}
sa-overflow-bar {
width: 500px;
background-color:#3498db;
margin:0 auto;
height: 100%;
box-shadow: 3px 0 0 0 #3498db;
display:block;
}
sa-overflow {
display:block;
width:100%;
height:8px;
position:fixed;
pointer-events: none;
}
sa-highlight {
z-index:10000;
position:absolute;
box-shadow: 1px 1px 3px 0 #3498db;
border-radius:3px;
border:1px solid #3498db;
padding:5px;
pointer-events: none;
}
`;
replayShadow.appendChild(styleElement);

const highlightElements: any[] = [];

Expand All @@ -327,15 +351,11 @@ const showMoreDown = document.createElement('sa-overflow');
showMoreDown.setAttribute('style', 'bottom:0;');
showMoreDown.innerHTML = overflowBar;

let maxHighlightTop = -1;
let minHighlightTop = 10e3;
let lastHighlightNodes: number[] = [];

function buildHover() {
const hoverNode = document.createElement('sa-highlight');

highlightElements.push(hoverNode);
document.head.appendChild(styleElement);
document.body.appendChild(hoverNode);
replayShadow.appendChild(hoverNode);
return hoverNode;
}

Expand All @@ -345,7 +365,6 @@ function highlightNodes(nodeIds: number[]) {
try {
minHighlightTop = 10e3;
maxHighlightTop = -1;
document.head.appendChild(styleElement);
for (let i = 0; i < length; i += 1) {
const node = idMap.get(nodeIds[i]);
const hoverNode = i >= highlightElements.length ? buildHover() : highlightElements[i];
Expand All @@ -364,7 +383,6 @@ function highlightNodes(nodeIds: number[]) {

if (bounds.y > maxHighlightTop) maxHighlightTop = bounds.y;
if (bounds.y + bounds.height < minHighlightTop) minHighlightTop = bounds.y + bounds.height;

document.body.appendChild(hoverNode);
}

Expand All @@ -379,13 +397,13 @@ function highlightNodes(nodeIds: number[]) {

function checkOverflows() {
if (maxHighlightTop > window.innerHeight + window.scrollY) {
document.body.appendChild(showMoreDown);
replayShadow.appendChild(showMoreDown);
} else {
showMoreDown.remove();
}

if (minHighlightTop < window.scrollY) {
document.body.appendChild(showMoreUp);
replayShadow.appendChild(showMoreUp);
} else {
showMoreUp.remove();
}
Expand All @@ -395,18 +413,18 @@ document.addEventListener('scroll', () => checkOverflows());

/////// mouse ///////
let lastMouseEvent: IMouseEvent;
const box = document.createElement('sa-mouse-pointer');
const mouse = document.createElement('sa-mouse-pointer');
replayShadow.appendChild(mouse);

function updateMouse(mouseEvent: IMouseEvent) {
lastMouseEvent = mouseEvent;
document.body.appendChild(box);
if (mouseEvent.pageX !== undefined) {
box.style.left = `${mouseEvent.pageX}px`;
box.style.top = `${mouseEvent.pageY}px`;
mouse.style.left = `${mouseEvent.pageX}px`;
mouse.style.top = `${mouseEvent.pageY}px`;
}
if (mouseEvent.buttons !== undefined) {
for (let i = 0; i < 5; i += 1) {
box.classList.toggle(`button-${i}`, (mouseEvent.buttons & (1 << i)) !== 0);
mouse.classList.toggle(`button-${i}`, (mouseEvent.buttons & (1 << i)) !== 0);
}
}
}
Expand Down

0 comments on commit b19b382

Please sign in to comment.