浏览代码

feat: implement changes for TrustedTypes API

Sv443 8 月之前
父节点
当前提交
a6b82c4f18

+ 2 - 1
src/components/MarkdownDialog.ts

@@ -1,3 +1,4 @@
+import { setInnerHtmlTrusted } from "src/utils/dom.js";
 import { BytmDialog, type BytmDialogOptions } from "./BytmDialog.js";
 import { marked } from "marked";
 
@@ -39,7 +40,7 @@ export class MarkdownDialog extends BytmDialog {
 
     const markdownEl = document.createElement("div");
     markdownEl.classList.add("bytm-markdown-dialog-content");
-    markdownEl.innerHTML = await MarkdownDialog.parseMd(mdCont);
+    setInnerHtmlTrusted(markdownEl, await MarkdownDialog.parseMd(mdCont));
 
     bodyEl.appendChild(markdownEl);
 

+ 6 - 6
src/components/hotkeyInput.ts

@@ -1,5 +1,5 @@
 import { emitSiteEvent, siteEvents } from "../siteEvents.js";
-import { onInteraction, t } from "../utils/index.js";
+import { onInteraction, setInnerHtmlTrusted, t } from "../utils/index.js";
 import type { HotkeyObj } from "../types.js";
 import "./hotkeyInput.css";
 
@@ -51,7 +51,7 @@ export function createHotkeyInput({ initialValue, onChange, createTitle }: Hotke
     inputElem.innerText = curHk?.code ?? t("hotkey_input_click_to_change");
     inputElem.dataset.state = "inactive";
     inputElem.ariaLabel = inputElem.title = createTitle(hotkeyToString(curHk));
-    infoElem.innerHTML = curHk ? getHotkeyInfoHtml(curHk) : "";
+    setInnerHtmlTrusted(infoElem, curHk ? getHotkeyInfoHtml(curHk) : "");
   };
 
   const activate = () => {
@@ -72,14 +72,14 @@ export function createHotkeyInput({ initialValue, onChange, createTitle }: Hotke
     currentHotkey = initialValue!;
     deactivate();
     inputElem.innerText = initialValue!.code;
-    infoElem.innerHTML = getHotkeyInfoHtml(initialValue!);
+    setInnerHtmlTrusted(infoElem, getHotkeyInfoHtml(initialValue!));
     resetElem.classList.add("bytm-hidden");
   };
 
   onInteraction(resetElem, resetClicked);
 
   if(initialValue)
-    infoElem.innerHTML = getHotkeyInfoHtml(initialValue);
+    setInnerHtmlTrusted(infoElem, getHotkeyInfoHtml(initialValue));
 
   let lastKeyDown: HotkeyObj | undefined;
 
@@ -100,7 +100,7 @@ export function createHotkeyInput({ initialValue, onChange, createTitle }: Hotke
 
     inputElem.innerText = hotkey.code;
     inputElem.dataset.state = "inactive";
-    infoElem.innerHTML = getHotkeyInfoHtml(hotkey);
+    setInnerHtmlTrusted(infoElem, getHotkeyInfoHtml(hotkey));
     inputElem.ariaLabel = inputElem.title = t("hotkey_input_click_to_cancel_tooltip");
 
     onChange(hotkey);
@@ -144,7 +144,7 @@ export function createHotkeyInput({ initialValue, onChange, createTitle }: Hotke
 
     inputElem.innerText = hotkey.code;
     inputElem.dataset.state = "inactive";
-    infoElem.innerHTML = getHotkeyInfoHtml(hotkey);
+    setInnerHtmlTrusted(infoElem, getHotkeyInfoHtml(hotkey));
   });
 
   siteEvents.on("cfgMenuClosed", deactivate);

+ 2 - 2
src/components/longButton.ts

@@ -1,4 +1,4 @@
-import { onInteraction, resourceAsString } from "../utils/index.js";
+import { onInteraction, resourceAsString, setInnerHtmlTrusted } from "../utils/index.js";
 import { createRipple } from "./ripple.js";
 import type { ResourceKey } from "../types.js";
 
@@ -92,7 +92,7 @@ export async function createLongBtn({
   if("src" in rest)
     (imgElem as HTMLImageElement).src = rest.src;
   else
-    imgElem.innerHTML = await resourceAsString(rest.resourceName as "_") ?? "";
+    setInnerHtmlTrusted(imgElem, await resourceAsString(rest.resourceName as "_") ?? "");
 
   const txtElem = document.createElement("span");
   txtElem.classList.add("bytm-generic-long-btn-txt", "bytm-no-select");

+ 2 - 2
src/components/toast.ts

@@ -1,5 +1,5 @@
 import { clamp, pauseFor } from "@sv443-network/userutils";
-import { info, resourceAsString } from "../utils/index.js";
+import { info, resourceAsString, setInnerHtmlTrusted } from "../utils/index.js";
 import { getFeature } from "../config.js";
 import type { ResourceKey } from "../types.js";
 import "./toast.css";
@@ -74,7 +74,7 @@ export async function showIconToast({
     toastIcon.classList.add("bytm-toast-icon");
     const iconHtml = await resourceAsString(rest.icon);
     if(iconHtml)
-      toastIcon.innerHTML = iconHtml;
+      setInnerHtmlTrusted(toastIcon, iconHtml);
 
     if("iconFill" in rest && rest.iconFill)
       toastIcon.style.setProperty("--toast-icon-fill", rest.iconFill);

+ 2 - 2
src/components/toggleInput.ts

@@ -1,5 +1,5 @@
 import { randomId } from "@sv443-network/userutils";
-import { t } from "../utils/index.js";
+import { setInnerHtmlTrusted, t } from "../utils/index.js";
 import "./toggleInput.css";
 
 export interface ToggleInputProps {
@@ -47,7 +47,7 @@ export async function createToggleInput({
 
   const toggleKnobEl = document.createElement("div");
   toggleKnobEl.classList.add("bytm-toggle-input-knob");
-  toggleKnobEl.innerHTML = " ";
+  setInnerHtmlTrusted(toggleKnobEl, " ");
 
   const toggleElClicked = (e: Event) => {
     e.preventDefault();

+ 2 - 2
src/dialogs/changelog.ts

@@ -1,4 +1,4 @@
-import { getChangelogHtmlWithDetails, t } from "../utils/index.js";
+import { getChangelogHtmlWithDetails, setInnerHtmlTrusted, t } from "../utils/index.js";
 import { BytmDialog } from "../components/index.js";
 import { scriptInfo } from "../constants.js";
 
@@ -56,7 +56,7 @@ async function renderBody() {
   const mdContElem = document.createElement("div");
   mdContElem.id = "bytm-changelog-dialog-text";
   mdContElem.classList.add("bytm-markdown-container");
-  mdContElem.innerHTML = await getChangelogHtmlWithDetails();
+  setInnerHtmlTrusted(mdContElem, await getChangelogHtmlWithDetails());
 
   contElem.appendChild(mdContElem);
 

+ 2 - 2
src/dialogs/featHelp.ts

@@ -1,4 +1,4 @@
-import { resourceAsString, t } from "../utils/index.js";
+import { resourceAsString, setInnerHtmlTrusted, t } from "../utils/index.js";
 import { BytmDialog } from "../components/index.js";
 import { featInfo } from "../features/index.js";
 import type { FeatureKey } from "../types.js";
@@ -39,7 +39,7 @@ async function renderHeader() {
   const headerEl = document.createElement("div");
   const helpIconSvg = await resourceAsString("icon-help");
   if(helpIconSvg)
-    headerEl.innerHTML = helpIconSvg;
+    setInnerHtmlTrusted(headerEl, helpIconSvg);
 
   return headerEl;
 }

+ 2 - 2
src/dialogs/versionNotif.ts

@@ -1,5 +1,5 @@
 import { host, mode, scriptInfo } from "../constants.js";
-import { getChangelogMd, getResourceUrl, onInteraction, parseMarkdown, t } from "../utils/index.js";
+import { getChangelogMd, getResourceUrl, onInteraction, parseMarkdown, setInnerHtmlTrusted, t } from "../utils/index.js";
 import { BytmDialog, createToggleInput } from "../components/index.js";
 import { getFeature, getFeatures, setFeatures } from "../config.js";
 import pkg from "../../package.json" with { type: "json" };
@@ -89,7 +89,7 @@ async function renderBody({
   const changelogEl = document.createElement("p");
   changelogEl.id = "bytm-version-notif-changelog-cont";
   changelogEl.classList.add("bytm-markdown-container");
-  changelogEl.innerHTML = changelogHtml;
+  setInnerHtmlTrusted(changelogEl, changelogHtml);
 
   changelogEl.querySelectorAll("a").forEach((a) => {
     a.target = "_blank";

+ 6 - 6
src/dialogs/welcome.ts

@@ -1,4 +1,4 @@
-import { getResourceUrl, initTranslations, setLocale, t, warn, type TrLocale } from "../utils/index.js";
+import { getResourceUrl, initTranslations, setInnerHtmlTrusted, setLocale, t, warn, type TrLocale } from "../utils/index.js";
 import { BytmDialog } from "../components/index.js";
 import { openCfgMenu } from "../menu/menu_old.js";
 import { mode, scriptInfo } from "../constants.js";
@@ -171,11 +171,11 @@ function retranslateWelcomeMenu() {
       e.textContent = e.ariaLabel = t("close");
       e.ariaLabel = e.title = t("close_menu_tooltip");
     },
-    "#bytm-welcome-text-line1": (e: HTMLElement) => e.innerHTML = e.ariaLabel = t("welcome_text_line_1"),
-    "#bytm-welcome-text-line2": (e: HTMLElement) => e.innerHTML = e.ariaLabel = t("welcome_text_line_2", scriptInfo.name),
-    "#bytm-welcome-text-line3": (e: HTMLElement) => e.innerHTML = e.ariaLabel = t("welcome_text_line_3", scriptInfo.name, ...getLink(`${pkg.hosts.greasyfork}/feedback`), ...getLink(pkg.hosts.openuserjs)),
-    "#bytm-welcome-text-line4": (e: HTMLElement) => e.innerHTML = e.ariaLabel = t("welcome_text_line_4", ...getLink(pkg.funding.url)),
-    "#bytm-welcome-text-line5": (e: HTMLElement) => e.innerHTML = e.ariaLabel = t("welcome_text_line_5", ...getLink(pkg.bugs.url)),
+    "#bytm-welcome-text-line1": (e: HTMLElement) => setInnerHtmlTrusted(e, e.ariaLabel = t("welcome_text_line_1")),
+    "#bytm-welcome-text-line2": (e: HTMLElement) => setInnerHtmlTrusted(e, e.ariaLabel = t("welcome_text_line_2", scriptInfo.name)),
+    "#bytm-welcome-text-line3": (e: HTMLElement) => setInnerHtmlTrusted(e, e.ariaLabel = t("welcome_text_line_3", scriptInfo.name, ...getLink(`${pkg.hosts.greasyfork}/feedback`), ...getLink(pkg.hosts.openuserjs))),
+    "#bytm-welcome-text-line4": (e: HTMLElement) => setInnerHtmlTrusted(e, e.ariaLabel = t("welcome_text_line_4", ...getLink(pkg.funding.url))),
+    "#bytm-welcome-text-line5": (e: HTMLElement) => setInnerHtmlTrusted(e, e.ariaLabel = t("welcome_text_line_5", ...getLink(pkg.bugs.url))),
   };
 
   for(const [selector, fn] of Object.entries(changes)) {

+ 3 - 3
src/features/input.ts

@@ -1,5 +1,5 @@
 import { DataStore, clamp, compress, decompress } from "@sv443-network/userutils";
-import { error, getVideoTime, info, log, warn, getDomain, compressionSupported, t, clearNode, resourceAsString, getCurrentChannelId, currentMediaType, sanitizeChannelId, addStyleFromResource, isValidChannelId, getVideoElement } from "../utils/index.js";
+import { error, getVideoTime, info, log, warn, getDomain, compressionSupported, t, clearNode, resourceAsString, getCurrentChannelId, currentMediaType, sanitizeChannelId, addStyleFromResource, isValidChannelId, getVideoElement, setInnerHtmlTrusted } from "../utils/index.js";
 import type { AutoLikeData, Domain } from "../types.js";
 import { disableBeforeUnload } from "./behavior.js";
 import { emitSiteEvent, siteEvents } from "../siteEvents.js";
@@ -369,7 +369,7 @@ async function addAutoLikeToggleBtn(siblingEl: HTMLElement, channelId: string, c
         const imgEl = buttonEl.querySelector<HTMLElement>(".bytm-generic-btn-img");
         const imgHtml = await resourceAsString(`icon-auto_like${toggled ? "_enabled" : ""}`);
         if(imgEl && imgHtml)
-          imgEl.innerHTML = imgHtml;
+          setInnerHtmlTrusted(imgEl, imgHtml);
 
         if(autoLikeStore.getData().channels.find((ch) => ch.id === chanId) === undefined) {
           await autoLikeStore.setData({
@@ -417,6 +417,6 @@ async function addAutoLikeToggleBtn(siblingEl: HTMLElement, channelId: string, c
     const imgEl = buttonEl.querySelector<HTMLElement>(".bytm-generic-btn-img");
     const imgHtml = await resourceAsString(`icon-auto_like${enabled ? "_enabled" : ""}`);
     if(imgEl && imgHtml)
-      imgEl.innerHTML = imgHtml;
+      setInnerHtmlTrusted(imgEl, imgHtml);
   });
 }

+ 2 - 2
src/features/layout.ts

@@ -2,7 +2,7 @@ import { addParent, autoPlural, debounce, fetchAdvanced, pauseFor } from "@sv443
 import { getFeature, getFeatures } from "../config.js";
 import { siteEvents } from "../siteEvents.js";
 import { addSelectorListener } from "../observers.js";
-import { error, getResourceUrl, log, warn, t, onInteraction, openInTab, getBestThumbnailUrl, getDomain, currentMediaType, domLoaded, waitVideoElementReady, addStyleFromResource, fetchVideoVotes, getWatchId, getLocale, tp, getVideoTime } from "../utils/index.js";
+import { error, getResourceUrl, log, warn, t, onInteraction, openInTab, getBestThumbnailUrl, getDomain, currentMediaType, domLoaded, waitVideoElementReady, addStyleFromResource, fetchVideoVotes, getWatchId, getLocale, tp, getVideoTime, setInnerHtmlTrusted } from "../utils/index.js";
 import { mode, scriptInfo } from "../constants.js";
 import { openCfgMenu } from "../menu/menu_old.js";
 import { createCircularBtn, createRipple } from "../components/index.js";
@@ -56,7 +56,7 @@ export async function improveLogo() {
     addSelectorListener("navBar", "ytmusic-logo a", {
       listener: (logoElem) => {
         logoElem.classList.add("bytm-mod-logo", "bytm-no-select");
-        logoElem.innerHTML = svg;
+        setInnerHtmlTrusted(logoElem, svg);
 
         logoElem.querySelectorAll("ellipse").forEach((e) => {
           e.classList.add("bytm-mod-logo-ellipse");

+ 2 - 2
src/features/volume.ts

@@ -1,6 +1,6 @@
 import { addParent, type Stringifiable } from "@sv443-network/userutils";
 import { getFeature } from "../config.js";
-import { addStyleFromResource, error, log, resourceAsString, setGlobalCssVar, t, waitVideoElementReady } from "../utils/index.js";
+import { addStyleFromResource, error, log, resourceAsString, setGlobalCssVar, setInnerHtmlTrusted, t, waitVideoElementReady } from "../utils/index.js";
 import { siteEvents } from "../siteEvents.js";
 import { featInfo } from "./index.js";
 import "./volume.css";
@@ -78,7 +78,7 @@ async function addVolumeSliderLabel(sliderElem: HTMLInputElement, sliderContaine
     if(linkIconHtml) {
       const linkIconElem = document.createElement("div");
       linkIconElem.id = "bytm-vol-slider-shared";
-      linkIconElem.innerHTML = linkIconHtml;
+      setInnerHtmlTrusted(linkIconElem, linkIconHtml);
       linkIconElem.role = "alert";
       linkIconElem.ariaLive = "polite";
       linkIconElem.title = linkIconElem.ariaLabel = t("volume_shared_tooltip");

+ 6 - 6
src/menu/menu_old.ts

@@ -2,7 +2,7 @@ import { compress, debounce, isScrollable, type Stringifiable } from "@sv443-net
 import { type defaultData, formatVersion, getFeature, getFeatures, migrations, setFeatures } from "../config.js";
 import { buildNumber, compressionFormat, host, mode, scriptInfo } from "../constants.js";
 import { featInfo, disableBeforeUnload } from "../features/index.js";
-import { error, getResourceUrl, info, log, resourceAsString, getLocale, hasKey, initTranslations, setLocale, t, arrayWithSeparators, tp, type TrKey, onInteraction, getDomain, copyToClipboard, warn, compressionSupported, tryToDecompressAndParse } from "../utils/index.js";
+import { error, getResourceUrl, info, log, resourceAsString, getLocale, hasKey, initTranslations, setLocale, t, arrayWithSeparators, tp, type TrKey, onInteraction, getDomain, copyToClipboard, warn, compressionSupported, tryToDecompressAndParse, setInnerHtmlTrusted } from "../utils/index.js";
 import { emitSiteEvent, siteEvents } from "../siteEvents.js";
 import { getChangelogDialog, getFeatHelpDialog } from "../dialogs/index.js";
 import type { FeatureCategory, FeatureKey, FeatureConfig, HotkeyObj, FeatureInfo } from "../types.js";
@@ -414,13 +414,13 @@ async function mountCfgMenu() {
 
         let adornmentElem: undefined | HTMLElement;
 
-        const adornContent = ftInfo.textAdornment?.();
-        const adornContentAw = adornContent instanceof Promise ? await adornContent : adornContent;
-        if((typeof adornContent === "string" || adornContent instanceof Promise) && typeof adornContentAw !== "undefined") {
+        const adornContentAsync = ftInfo.textAdornment?.();
+        const adornContent = adornContentAsync instanceof Promise ? await adornContentAsync : adornContentAsync;
+        if((typeof adornContentAsync === "string" || adornContentAsync instanceof Promise) && typeof adornContent !== "undefined") {
           adornmentElem = document.createElement("span");
           adornmentElem.id = `bytm-ftitem-${featKey}-adornment`;
           adornmentElem.classList.add("bytm-ftitem-adornment");
-          adornmentElem.innerHTML = adornContentAw;
+          setInnerHtmlTrusted(adornmentElem, adornContent);
         }
 
         let helpElem: undefined | HTMLDivElement;
@@ -438,7 +438,7 @@ async function mountCfgMenu() {
             helpElem.ariaLabel = helpElem.title = t("feature_help_button_tooltip", t(`feature_desc_${featKey}`));
             helpElem.role = "button";
             helpElem.tabIndex = 0;
-            helpElem.innerHTML = helpElemImgHtml;
+            setInnerHtmlTrusted(helpElem, helpElemImgHtml);
             onInteraction(helpElem, async (e: MouseEvent | KeyboardEvent) => {
               e.preventDefault();
               e.stopPropagation();