error fix + embedding extensability (#110)

* embed mode: make embedding options more extensible
ui: unify service worker not available errors, show same error in standard/embed mode, including when loaded over http
- more customizations for extended embed, allow hiding 'browse contents' button
- simplify title updates
- make error page / icon more customizable
- autoupdating in editable mode: don't update if previous one didn't finish, make customizable, don't autoupdate at all over >=100 pages

* dependencies: update ruffle to 2022-07-02

* bump to 1.6.1, update CHANGELIST
This commit is contained in:
Ilya Kreymer
2022-07-05 10:48:32 -07:00
committed by GitHub
parent 61d25c2c3d
commit 54fd92c273
17 changed files with 312 additions and 198 deletions
+5
View File
@@ -1,5 +1,10 @@
## CHANGES ## CHANGES
v1.6.1
- Embed/Library: Make embedding more extensable for use as a library (for archiveweb.page extensions)
- UI: Unified service worker error messaging for regular and embed mode, show correct error when loaded over http.
- Dependencies: update ruffle to 2022-07-02
v1.6.0 v1.6.0
- Embeds: Support embedding without using webworker with 'noWebWorker' attrib added to `<replay-web-page>` - Embeds: Support embedding without using webworker with 'noWebWorker' attrib added to `<replay-web-page>`
- Fidelity: improved replay of pages that use eval() in global scope (via wabac.js) - Fidelity: improved replay of pages that use eval() in global scope (via wabac.js)
+1 -1
View File
@@ -1,7 +1,7 @@
{ {
"name": "replaywebpage", "name": "replaywebpage",
"productName": "ReplayWeb.page", "productName": "ReplayWeb.page",
"version": "1.6.0", "version": "1.6.1",
"description": "Serverless Web Archive Replay", "description": "Serverless Web Archive Replay",
"repository": "https://github.com/webrecorder/replayweb.page", "repository": "https://github.com/webrecorder/replayweb.page",
"homepage": "https://replayweb.page/", "homepage": "https://replayweb.page/",
+1 -1
View File
@@ -1,7 +1,7 @@
{ {
"name": "replaywebpage", "name": "replaywebpage",
"productName": "ReplayWeb.page", "productName": "ReplayWeb.page",
"version": "1.6.0", "version": "1.6.1",
"description": "Serverless Web Archive Replay", "description": "Serverless Web Archive Replay",
"repository": "https://github.com/webrecorder/replayweb.page", "repository": "https://github.com/webrecorder/replayweb.page",
"homepage": "https://replayweb.page/", "homepage": "https://replayweb.page/",
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+34 -10
View File
@@ -65,6 +65,7 @@ class Coll extends LitElement
this.hasStory = false; this.hasStory = false;
this.editable = false; this.editable = false;
this.browsable = true;
this.showSidebar = localStorage.getItem("pages:showSidebar") === "1"; this.showSidebar = localStorage.getItem("pages:showSidebar") === "1";
this.splitter = null; this.splitter = null;
@@ -73,7 +74,11 @@ class Coll extends LitElement
this.favIconUrl = ""; this.favIconUrl = "";
this.autoUpdateInterval = 10;
this._autoUpdater = null;
this.appName = "ReplayWeb.page"; this.appName = "ReplayWeb.page";
this.appVersion = VERSION;
this.appLogo = rwpLogo; this.appLogo = rwpLogo;
} }
@@ -102,13 +107,17 @@ class Coll extends LitElement
embed: { type: String }, embed: { type: String },
editable: { type: Boolean }, editable: { type: Boolean },
browsable: { type: Boolean },
isVisible: { type: Boolean }, isVisible: { type: Boolean },
favIconUrl: {type: String }, favIconUrl: {type: String },
appName: { type: String }, appName: { type: String },
appVersion: { type: String },
appLogo: { type: String }, appLogo: { type: String },
autoUpdateInterval: { type: Number }
}; };
} }
@@ -129,6 +138,18 @@ class Coll extends LitElement
} }
} }
async runUpdateLoop() {
try {
// only autoupdate if interval is set, and number of pages < 100 to avoid messing up scrolling
while (this.editable && this.autoUpdateInterval && (!this.collInfo || this.collInfo.pages.length < 100)) {
await new Promise(resolve => setTimeout(resolve, this.autoUpdateInterval * 1000));
await this.doUpdateInfo(true);
}
} finally {
this._autoUpdater = null;
}
}
updated(changedProperties) { updated(changedProperties) {
// if (changedProperties.has("url") || changedProperties.has("ts")) { // if (changedProperties.has("url") || changedProperties.has("ts")) {
// if (this.url.startsWith("rwp?")) { // if (this.url.startsWith("rwp?")) {
@@ -142,10 +163,8 @@ class Coll extends LitElement
this.doUpdateInfo(); this.doUpdateInfo();
} }
if (changedProperties.has("editable")) { if (changedProperties.has("editable")) {
if (this.editable) { if (this.editable && this.autoUpdateInterval && !this._autoUpdater) {
this._pollColl = setInterval(() => this.doUpdateInfo(true), 10000); this._autoUpdater = this.runUpdateLoop();
} else if (this._pollColl) {
clearInterval(this._pollColl);
} }
} }
if (changedProperties.has("tabData")) { if (changedProperties.has("tabData")) {
@@ -369,7 +388,11 @@ class Coll extends LitElement
} }
static get styles() { static get styles() {
return wrapCss(css` return wrapCss(Coll.compStyles);
}
static get compStyles() {
return css`
:host { :host {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@@ -450,8 +473,7 @@ class Coll extends LitElement
flex: auto; flex: auto;
} }
${Coll.replayBarStyles} ${Coll.replayBarStyles}`;
`);
} }
static get replayBarStyles() { static get replayBarStyles() {
@@ -760,12 +782,13 @@ class Coll extends LitElement
` : ""} ` : ""}
</span> </span>
</a> </a>
${this.browsable ? html`
<a href="#" role="button" class="button narrow is-borderless is-hidden-mobile ${!isReplay ? "grey-disabled" : ""}" @click="${this.onShowPages}" @keyup="${clickOnSpacebarPress}" <a href="#" role="button" class="button narrow is-borderless is-hidden-mobile ${!isReplay ? "grey-disabled" : ""}" @click="${this.onShowPages}" @keyup="${clickOnSpacebarPress}"
?disabled="${!isReplay}" title="Browse Contents" aria-label="Browse Contents" aria-controls="contents"> ?disabled="${!isReplay}" title="Browse Contents" aria-label="Browse Contents" aria-controls="contents">
<span class="icon is-small"> <span class="icon is-small">
<fa-icon size="1.0em" class="has-text-grey" aria-hidden="true" .svg="${farListAlt}"></fa-icon> <fa-icon size="1.0em" class="has-text-grey" aria-hidden="true" .svg="${farListAlt}"></fa-icon>
</span> </span>
</a> </a>` : ""}
${this.renderExtraToolbar(false)} ${this.renderExtraToolbar(false)}
<form @submit="${this.onSubmit}"> <form @submit="${this.onSubmit}">
<div class="control is-expanded ${showFavIcon ? "has-icons-left" : ""}"> <div class="control is-expanded ${showFavIcon ? "has-icons-left" : ""}">
@@ -813,12 +836,13 @@ class Coll extends LitElement
</span> </span>
<span>Reload</span> <span>Reload</span>
</a> </a>
${this.browsable ? html`
<a href="#" role="button" class="dropdown-item is-hidden-tablet ${!isReplay ? "grey-disabled" : ""}" @click="${this.onShowPages}" @keyup="${clickOnSpacebarPress}"> <a href="#" role="button" class="dropdown-item is-hidden-tablet ${!isReplay ? "grey-disabled" : ""}" @click="${this.onShowPages}" @keyup="${clickOnSpacebarPress}">
<span class="icon is-small"> <span class="icon is-small">
<fa-icon size="1.0em" class="has-text-grey" aria-hidden="true" .svg="${farListAlt}"></fa-icon> <fa-icon size="1.0em" class="has-text-grey" aria-hidden="true" .svg="${farListAlt}"></fa-icon>
</span> </span>
<span>Browse Contents</span> <span>Browse Contents</span>
</a> </a>` : ""}
${this.renderExtraToolbar(true)} ${this.renderExtraToolbar(true)}
${!this.editable ? html` ${!this.editable ? html`
<hr class="dropdown-divider is-hidden-desktop"> <hr class="dropdown-divider is-hidden-desktop">
@@ -845,7 +869,7 @@ class Coll extends LitElement
<a href="#" role="button" class="dropdown-item" @click="${this.onAbout}"> <a href="#" role="button" class="dropdown-item" @click="${this.onAbout}">
<fa-icon class="menu-logo" size="1.0rem" aria-hidden="true" .svg=${this.appLogo}></fa-icon> <fa-icon class="menu-logo" size="1.0rem" aria-hidden="true" .svg=${this.appLogo}></fa-icon>
<span>&nbsp;About ${this.appName}</span> <span>&nbsp;About ${this.appName}</span>
<span class="menu-version">(${VERSION})</span> <span class="menu-version">(${this.appVersion})</span>
</a> </a>
</div> </div>
</div> </div>
+48 -10
View File
@@ -8,6 +8,8 @@ import { wrapCss, rwpLogo } from "./misc";
var scriptSrc = document.currentScript && document.currentScript.src; var scriptSrc = document.currentScript && document.currentScript.src;
var defaultReplayFile = "";
// =========================================================================== // ===========================================================================
class Embed extends LitElement class Embed extends LitElement
@@ -15,13 +17,16 @@ class Embed extends LitElement
constructor() { constructor() {
super(); super();
this.replaybase = "./replay/"; this.replaybase = "./replay/";
this.replayfile = defaultReplayFile;
// eslint-disable-next-line no-undef // eslint-disable-next-line no-undef
this.swName = __SW_NAME__; this.swName = __SW_NAME__;
this.mainElementName = "replay-app-main";
this.view = "replay"; this.view = "replay";
this.ts = ""; this.ts = "";
this.url = ""; this.url = "";
this.query = ""; this.query = "";
this.config = ""; this.config = "";
this.customConfig = null;
this.coll = ""; this.coll = "";
this.paramString = null; this.paramString = null;
this.deepLink = false; this.deepLink = false;
@@ -30,6 +35,11 @@ class Embed extends LitElement
this.reloadCount = 0; this.reloadCount = 0;
this.noSandbox = false; this.noSandbox = false;
this.noWebWorker = false; this.noWebWorker = false;
this.logo = rwpLogo;
}
static setDefaultReplayFile(replayfile) {
defaultReplayFile = replayfile;
} }
static get properties() { static get properties() {
@@ -39,6 +49,8 @@ class Embed extends LitElement
query: { type: String }, query: { type: String },
source: { type: String }, source: { type: String },
src: { type: String },
view: { type: String }, view: { type: String },
embed: { type: String }, embed: { type: String },
@@ -72,13 +84,14 @@ class Embed extends LitElement
} }
} }
firstUpdated() { handleMessage(event) {
this.doRegister();
window.addEventListener("message", (event) => {
const iframe = this.renderRoot.querySelector("iframe"); const iframe = this.renderRoot.querySelector("iframe");
if (iframe && event.source === iframe.contentWindow) { if (iframe && event.source === iframe.contentWindow) {
if (!event.data.view) {
return;
}
if (event.data.title) { if (event.data.title) {
this.title = event.data.title; this.title = event.data.title;
} }
@@ -92,7 +105,12 @@ class Embed extends LitElement
url.hash = "#" + currHash.toString(); url.hash = "#" + currHash.toString();
window.history.replaceState({}, "", url); window.history.replaceState({}, "", url);
} }
}); }
firstUpdated() {
this.doRegister();
window.addEventListener("message", (event) => this.handleMessage(event));
if (this.deepLink) { if (this.deepLink) {
this.updateFromHash(); this.updateFromHash();
@@ -117,21 +135,41 @@ class Embed extends LitElement
} }
} }
mergeConfigs() {
if (!this.customConfig) {
return this.config;
}
if (this.config) {
const config = {...this.customConfig, ...JSON.parse(this.config)};
return JSON.stringify(config);
} else {
return JSON.stringify(this.customConfig);
}
}
updated(changedProperties) { updated(changedProperties) {
if (changedProperties.has("url") || if (changedProperties.has("url") ||
changedProperties.has("ts") || changedProperties.has("ts") ||
changedProperties.has("query") || changedProperties.has("query") ||
changedProperties.has("view") || changedProperties.has("view") ||
changedProperties.has("source")) { changedProperties.has("source") ||
changedProperties.has("src")) {
this.embed = this.embed || "default"; this.embed = this.embed || "default";
if (this.src) {
this.source = this.src;
}
const source = new URL(this.source, document.baseURI); const source = new URL(this.source, document.baseURI);
const config = this.mergeConfigs();
const params = { const params = {
source, source,
customColl: this.coll, customColl: this.coll,
config: this.config, config,
basePageUrl: window.location.href.split("#")[0], basePageUrl: window.location.href.split("#")[0],
embed: this.embed, embed: this.embed,
}; };
@@ -186,7 +224,7 @@ class Embed extends LitElement
allow-popups allow-popups-to-escape-sandbox allow-presentation allow-scripts\ allow-popups allow-popups-to-escape-sandbox allow-presentation allow-scripts\
allow-same-origin allow-forms" : undefined)}" allow-same-origin allow-forms" : undefined)}"
@load="${this.onLoad}" src="${this.replaybase}?${this.paramString}#${this.hashString}" allow="autoplay *; fullscreen" @load="${this.onLoad}" src="${this.replaybase}${this.replayfile}?${this.paramString}#${this.hashString}" allow="autoplay *; fullscreen"
title="Replay of ${this.title ? `${this.title}:` :""} ${this.url}"></iframe> title="Replay of ${this.title ? `${this.title}:` :""} ${this.url}"></iframe>
` : html``} ` : html``}
@@ -194,7 +232,7 @@ class Embed extends LitElement
${this.errorMessage ? html` ${this.errorMessage ? html`
<section class="full-width"> <section class="full-width">
<div class="has-text-centered"> <div class="has-text-centered">
<fa-icon class="logo" id="wrlogo" size="2.5rem" .svg=${rwpLogo} aria-hidden="true"></fa-icon> <fa-icon class="logo" id="wrlogo" size="2.5rem" .svg=${this.logo} aria-hidden="true"></fa-icon>
</div> </div>
<div class="error">${this.errorMessage}</div> <div class="error">${this.errorMessage}</div>
</section> </section>
@@ -213,7 +251,7 @@ class Embed extends LitElement
this.reloadCount = 0; this.reloadCount = 0;
if (win.customElements.get("replay-app-main")) { if (win.customElements.get(this.mainElementName)) {
return; return;
} }
+22 -11
View File
@@ -3,7 +3,7 @@ import { wrapCss, IS_APP } from "./misc";
import prettyBytes from "pretty-bytes"; import prettyBytes from "pretty-bytes";
import { parseURLSchemeHostPath } from "./pageutils"; import { getSWErrorMsg, parseURLSchemeHostPath } from "./pageutils";
// =========================================================================== // ===========================================================================
@@ -125,15 +125,11 @@ class Loader extends LitElement
this.percent = this.currentSize = this.totalSize = 0; this.percent = this.currentSize = this.totalSize = 0;
if (!navigator.serviceWorker) { const noSWError = getSWErrorMsg();
if (noSWError) {
this.state = "errored"; this.state = "errored";
if (window.location.protocol === "http:") { this.error = noSWError;
this.error = `\
Sorry, the ReplayWeb.page system must be loaded from an HTTPS URL, but was loaded from: ${window.location.host}.
Please try loading this page from an HTTPS URL`;
} else {
this.error = "Sorry, this browser is not supported. Please try a different browser\n(If you're using Firefox, try without Private Mode)";
}
this.errorAllowRetry = false; this.errorAllowRetry = false;
return; return;
} }
@@ -176,6 +172,10 @@ You can select a file to upload from the main page by clicking the 'Choose File.
source = {sourceUrl, loadUrl: url.href}; source = {sourceUrl, loadUrl: url.href};
} }
break; break;
case "proxy":
sourceUrl = "proxy:" + sourceUrl.slice("proxy://".length);
break;
} }
} catch (e) { } catch (e) {
console.log(e); console.log(e);
@@ -189,11 +189,22 @@ You can select a file to upload from the main page by clicking the 'Choose File.
this.state = "started"; this.state = "started";
const msg = {"msg_type": "addColl", "name": this.coll, skipExisting: true, file: source}; const msg = {
msg_type: "addColl",
name: this.coll,
skipExisting: true,
file: source
};
if (this.loadInfo && this.loadInfo.extraConfig) { if (this.loadInfo) {
if (this.loadInfo.extraConfig) {
msg.extraConfig = this.loadInfo.extraConfig; msg.extraConfig = this.loadInfo.extraConfig;
} }
// todo: too special case?
if (sourceUrl.startsWith("proxy:") && msg.extraConfig && msg.extraConfig.recording) {
msg.type = "recordingproxy";
}
}
if (!navigator.serviceWorker.controller) { if (!navigator.serviceWorker.controller) {
await new Promise((resolve) => { await new Promise((resolve) => {
+4 -2
View File
@@ -138,12 +138,14 @@ class WrModal extends LitElement
super(); super();
this.title = ""; this.title = "";
this.bgClass = ""; this.bgClass = "";
this.noBgClose = false;
} }
static get properties() { static get properties() {
return { return {
title: { type: String }, title: { type: String },
bgClass: { type: String } bgClass: { type: String },
noBgClose: { type: Boolean }
}; };
} }
@@ -167,7 +169,7 @@ class WrModal extends LitElement
render() { render() {
return html` return html`
<div class="modal is-active"> <div class="modal is-active">
<div class="modal-background" @click="${this.onClose}"></div> <div class="modal-background" @click="${() => !this.noBgClose && this.onClose()}"></div>
<div class="modal-card"> <div class="modal-card">
<header class="modal-card-head ${this.bgClass}"> <header class="modal-card-head ${this.bgClass}">
<p class="modal-card-title is-3">${this.title}</p> <p class="modal-card-title is-3">${this.title}</p>
+25 -6
View File
@@ -1,5 +1,11 @@
import { register } from "register-service-worker"; import { register } from "register-service-worker";
let appName = "ReplayWeb.page";
// ===========================================================================
function setAppName(newAppName) {
appName = newAppName;
}
// =========================================================================== // ===========================================================================
function registerSW(name = "sw.js", scope = "./") { function registerSW(name = "sw.js", scope = "./") {
@@ -10,10 +16,9 @@ function registerSW(name = "sw.js", scope = "./") {
reject = rej; reject = rej;
}); });
if (!navigator.serviceWorker) { const errMsg = getSWErrorMsg();
const errMsg = `Sorry, ReplayWeb.page won't work in this browser as Service Workers are not supported.
Please try a different browser. if (errMsg) {
(Service Workers are disabled in Firefox in Private Mode. If Using Private Mode in Firefox, try regular mode)`;
console.error(errMsg); console.error(errMsg);
reject(errMsg); reject(errMsg);
} }
@@ -27,13 +32,27 @@ Please try a different browser.
error (error) { error (error) {
console.error("Error during service worker registration:", error); console.error("Error during service worker registration:", error);
reject(`ReplayWeb.page could not be loaded due to the following error:\n${error.toString()}`); reject(`${appName} could not be loaded due to the following error:\n${error.toString()}`);
} }
}); });
return p; return p;
} }
function getSWErrorMsg() {
if (navigator.serviceWorker) {
return null;
}
if (window.location.protocol === "http:") {
return `\
Sorry, the ${appName} system must be loaded from an HTTPS URL, but was loaded from: ${window.location.host}.
Please try loading this page from an HTTPS URL`;
} else {
return `Sorry, ${appName} won't work in this browser as Service Workers are not supported.
Please try a different browser.
(Service Workers are disabled in Firefox in Private Mode. If Using Private Mode in Firefox, try regular mode)`;
}
}
// =========================================================================== // ===========================================================================
async function digestMessage(message, hashtype) { async function digestMessage(message, hashtype) {
@@ -137,4 +156,4 @@ function parseURLSchemeHostPath(url) {
export { digestMessage, tsToDate, getTS, getPageDateTS, getReplayLink, sourceToId, parseURLSchemeHostPath, export { digestMessage, tsToDate, getTS, getPageDateTS, getReplayLink, sourceToId, parseURLSchemeHostPath,
registerSW }; registerSW, getSWErrorMsg, setAppName };
+16 -6
View File
@@ -10,6 +10,7 @@ class Replay extends LitElement
super(); super();
this.replayUrl = ""; this.replayUrl = "";
this.replayTS = ""; this.replayTS = "";
this.actualTS = "";
this.url = ""; this.url = "";
this.ts = ""; this.ts = "";
this.title = ""; this.title = "";
@@ -32,6 +33,7 @@ class Replay extends LitElement
// actual replay url // actual replay url
replayUrl: { type: String }, replayUrl: { type: String },
replayTS: { type: String }, replayTS: { type: String },
actualTS: { type: String },
title: { type: String }, title: { type: String },
iframeUrl: { type: String }, iframeUrl: { type: String },
@@ -107,6 +109,18 @@ class Replay extends LitElement
this.dispatchEvent(new CustomEvent("coll-tab-nav", {detail: {replaceLoc: true, data}})); this.dispatchEvent(new CustomEvent("coll-tab-nav", {detail: {replaceLoc: true, data}}));
} }
if (this.title && (changedProperties.has("title") || changedProperties.has("actualTS"))) {
const detail = {
title: this.title,
url: this.replayUrl,
// send actual ts even if live
ts: this.actualTS,
replayTitle: true
};
this.dispatchEvent(new CustomEvent("update-title", {bubbles: true, composed: true, detail}));
}
} }
setDisablePointer(disable) { setDisablePointer(disable) {
@@ -122,7 +136,8 @@ class Replay extends LitElement
if (iframe && event.source === iframe.contentWindow) { if (iframe && event.source === iframe.contentWindow) {
if (event.data.wb_type === "load" || event.data.wb_type === "replace-url") { if (event.data.wb_type === "load" || event.data.wb_type === "replace-url") {
this.replayTS = event.data.ts; this.replayTS = event.data.is_live ? "" : event.data.ts;
this.actualTS = event.data.ts;
this.replayUrl = event.data.url; this.replayUrl = event.data.url;
this.title = event.data.title || this.title; this.title = event.data.title || this.title;
this.clearLoading(iframe.contentWindow); this.clearLoading(iframe.contentWindow);
@@ -135,11 +150,6 @@ class Replay extends LitElement
this.title = event.data.title; this.title = event.data.title;
} }
} }
if (this.title) {
const detail = {title: this.title, replayTitle: true};
this.dispatchEvent(new CustomEvent("update-title", {bubbles: true, composed: true, detail}));
}
} }
onReAuthed(event) { onReAuthed(event) {
+1 -1
View File
@@ -2,7 +2,7 @@ import { LitElement, html, css, unsafeCSS } from "lit";
import { wrapCss } from "./misc"; import { wrapCss } from "./misc";
import { unsafeHTML } from "lit/directives/unsafe-html.js"; import { unsafeHTML } from "lit/directives/unsafe-html.js";
import marked from "marked"; import { marked } from "marked";
import { getTS, getReplayLink } from "./pageutils"; import { getTS, getReplayLink } from "./pageutils";
+136 -135
View File
File diff suppressed because one or more lines are too long