dsh-mpkg-wallpaper 3.9.0 → 3.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +3 -3
- package/README.md +3 -3
- package/lib/client.js +113 -23
- package/package.json +1 -1
package/README.en.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
Adds background wallpapers to the [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) Web UI (`dsh web`): **Wallpaper Engine `.mpkg` parsing, Steam Workshop folders, video/web/image wallpapers, time-of-day switching for Time Variation wallpapers, a full-screen blur system, theme colours and glass surfaces, a local wallpaper library, scheduled rotation, a Now playing widget and one-click update**. Almost every appearance detail is adjustable.
|
|
8
8
|
|
|
9
|
-
> Version scope: this document describes the implementation shipped as **`3.9.
|
|
9
|
+
> Version scope: this document describes the implementation shipped as **`3.9.1`** in `package.json`. The publish surface is **15 files** (`lib/` runtime files + `package.json`, `icon.svg`, `cordis.patch.yml`, `README.md`, `README.en.md`, `THIRD-PARTY.md`, `LICENSE`; measured with `npm pack --dry-run`: 15 files / 2,088,240 B unpacked); `lib/liquid-glass/**`, `lib/liquid-glass-bundle.js`, `dist/`, `tools/` and `docs/` never enter the npm package (`package.json:8-21`). Defaults that changed: **`npNowPlaying` off → on** (since 3.8.0) and **`powPauseHidden` off → on** (since 3.9.0, migrating only profiles that never set it; see [What changed in this version](#what-changed-in-this-version)).
|
|
10
10
|
|
|
11
11
|
---
|
|
12
12
|
|
|
@@ -560,7 +560,7 @@ This section is a file-by-file map of the repository: what enters the npm packag
|
|
|
560
560
|
|
|
561
561
|
```text
|
|
562
562
|
dsh-mpkg-wallpaper/
|
|
563
|
-
├── package.json # version 3.9.
|
|
563
|
+
├── package.json # version 3.9.1; dsh.bundle + dsh.client declarations; files allow-list = publish surface (15 files)
|
|
564
564
|
├── cordis.patch.yml # install declaration used by dsh plugin add
|
|
565
565
|
├── LICENSE # MIT
|
|
566
566
|
├── THIRD-PARTY.md # third-party provenance / clean-room records / licence attribution (shipped)
|
|
@@ -610,7 +610,7 @@ The 12 steps of `tools/check.sh`: ① syntax ② panel smoke + P-66 + folder pic
|
|
|
610
610
|
|
|
611
611
|
## What changed in this version
|
|
612
612
|
|
|
613
|
-
Current version = **3.9.
|
|
613
|
+
Current version = **3.9.1** in `package.json` (this release line: 3.8.0 → 3.8.1 → 3.8.2 → 3.9.0 → **3.9.1**). Release prerequisites, commands and rollback live in [`docs/RELEASE.md`](docs/RELEASE.md); this round's change list is in [`docs/RELEASE-READY-3.8.0.md`](docs/RELEASE-READY-3.8.0.md); the root causes and on-device readings for Now playing / wallpaper sound are in [`docs/NOW-PLAYING-DSH.md`](docs/NOW-PLAYING-DSH.md) §7.7.
|
|
614
614
|
|
|
615
615
|
- **Now playing is on by default and yields its slot**: the `npNowPlaying` setting is **on by default** (`DEFAULT_NP_NOW_PLAYING` in `lib/client.js`), still in the *Wallpaper* tab directly under the existing `mute` toggle (`toggleRow(t("npNowPlaying"), …)` in `lib/client.js`). Turning it off returns to zero injection (mechanism and criteria in [Now playing and wallpaper sound](#now-playing-and-wallpaper-sound)). Being on by default requires **yielding**: when another plugin has already injected an element into the same host slot we do not mount (or we retract) and leave a queryable state `data-mpw-np-yield` (`occupantOf()` in `lib/now-playing.js`; judged both before and after mounting, and it comes back once the occupant leaves). The component itself is a **line-by-line port of Bencho's "Now playing" (MIT)** with the upstream comments kept verbatim (attribution in `THIRD-PARTY.md` §6); pure math and component are split into `lib/now-playing-math.js` / `lib/now-playing.js` and **inlined byte-for-byte** into the `MPW-NP-GEN-START/END` region of `lib/client.js` by `tools/build-now-playing.mjs`, guarded by two independent drift gates. Mounting and collapse criteria: see [Now playing and wallpaper sound](#now-playing-and-wallpaper-sound).
|
|
616
616
|
- **Wallpaper sound is actually wired up**: the data source only accepts the media element that is **really playing right now** (the hidden shell `#mpw-bgVideo` exists in the DOM under **every** wallpaper type, and the old implementation treated the first selector match as "the current media") ⇒ a video wallpaper's play/pause/mute land on the real element (the **`mute` switch now really reaches the element — unmuting really produces sound**; the old code hard-coded `video.muted = true` and never assigned it from the setting again); audio files that **really exist** in the wallpaper folder play through our own `<audio>` (the scope accepts `mpkgKey="custom|<folder>"`; the old code only accepted `folderName`, so a web wallpaper in a custom folder always built a library route and got a 404) ⇒ **previous/next follow the track-list order and wrap around**, instead of "restart"; for a web wallpaper's in-frame sound only **mute** is a real channel (`canPlay=false` is reported honestly — we do not pretend we can pause in-frame WebAudio); while our player is playing, the frame is force-muted so the same track cannot play twice.
|
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
给 [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) Web 界面(`dsh web`)添加背景壁纸的插件:**Wallpaper Engine `.mpkg` 解析、Steam 创意工坊目录、视频/网页/图片壁纸、时间变化壁纸的多时段切换、整屏虚化体系、主题色与玻璃外观、本地壁纸库、定时轮换、Now playing 控件、一键更新**。外观细节几乎全部可调。
|
|
8
8
|
|
|
9
|
-
> 版本口径:本文件描述的是 `package.json` 里 **`3.9.
|
|
9
|
+
> 版本口径:本文件描述的是 `package.json` 里 **`3.9.1`** 这一版实现。发布面共 **15 个文件**(`lib/` 8 个运行时文件 + `package.json`、`icon.svg`、`cordis.patch.yml`、`README.md`、`README.en.md`、`THIRD-PARTY.md`、`LICENSE`;`npm pack --dry-run` 实测 15 文件 / unpacked 2 088 240 B);`lib/liquid-glass/**`、`lib/liquid-glass-bundle.js`、`dist/`、`tools/`、`docs/` 都不进 npm 包(`package.json:8-21`)。本轮的默认档变化:**`npNowPlaying` 关 → 开**(3.8.0 起)与 **`powPauseHidden` 关 → 开**(3.9.0 起,只迁移"从没设过"的存量档;详见[这一版新增/变更](#这一版新增变更))。
|
|
10
10
|
|
|
11
11
|
---
|
|
12
12
|
|
|
@@ -560,7 +560,7 @@ node tools/build-bundle.mjs
|
|
|
560
560
|
|
|
561
561
|
```text
|
|
562
562
|
dsh-mpkg-wallpaper/
|
|
563
|
-
├── package.json # 版本 3.9.
|
|
563
|
+
├── package.json # 版本 3.9.1;dsh.bundle + dsh.client 声明;files 白名单 = 发布面(15 文件)
|
|
564
564
|
├── cordis.patch.yml # 插件安装声明(dsh plugin add 使用)
|
|
565
565
|
├── LICENSE # MIT
|
|
566
566
|
├── THIRD-PARTY.md # 第三方来历/洁净室记录与许可归属(随包发布)
|
|
@@ -610,7 +610,7 @@ node tools/style-scope-guard.mjs # 判据是末行「… OK / … ALLOWLISTED
|
|
|
610
610
|
|
|
611
611
|
## 这一版新增/变更
|
|
612
612
|
|
|
613
|
-
当前版本 = `package.json` 的 **3.9.
|
|
613
|
+
当前版本 = `package.json` 的 **3.9.1**(本轮发布线 3.8.0 → 3.8.1 → 3.8.2 → 3.9.0 → **3.9.1**)。发布前置/命令/回滚见 [`docs/RELEASE.md`](docs/RELEASE.md),本轮改动清单见 [`docs/RELEASE-READY-3.8.0.md`](docs/RELEASE-READY-3.8.0.md),Now playing / 壁纸声音的根因与真机读数见 [`docs/NOW-PLAYING-DSH.md`](docs/NOW-PLAYING-DSH.md) §7.7。
|
|
614
614
|
|
|
615
615
|
- **Now playing 默认挂载 + 会让位**:设置项 `npNowPlaying` **默认开**(`lib/client.js` 的 `DEFAULT_NP_NOW_PLAYING`),仍在「壁纸设置」tab、紧挨既有 `mute` 下方(`lib/client.js` 的 `toggleRow(t("npNowPlaying"), …)`)。关掉即回到零注入(机制与判据见 [Now playing 与壁纸声音](#now-playing-与壁纸声音))。默认开的前提是**礼让**:宿主同一位置已经有别的插件注入的元素时不挂/撤下,并留一个可查询状态 `data-mpw-np-yield`(`lib/now-playing.js` 的 `occupantOf()`;挂载前与挂载后都判,占用者走了再回来)。组件本体是 **Bencho 的 "Now playing"(MIT)逐行移植**(注释原文保留,归属见 `THIRD-PARTY.md` §6);纯数学与组件拆成 `lib/now-playing-math.js` / `lib/now-playing.js`,由 `tools/build-now-playing.mjs` **逐字节内联**进 `lib/client.js` 的 `MPW-NP-GEN-START/END` 生成区,配两道漂移门禁(生成器自比 + 产物反抠比对)。挂载与收起判据见 [Now playing 与壁纸声音](#now-playing-与壁纸声音)。
|
|
616
616
|
- **壁纸声音真的接线**:数据源只认**当前真的在放**的那个媒体元素(页面里那个隐藏空壳 `#mpw-bgVideo` 在**任何壁纸类型下都在 DOM 里**,旧实现按选择器命中它就当"当前媒体")⇒ 视频壁纸的播放/暂停/静音落在真实元素上(**`mute` 开关真的落到元素上:关掉静音就真的出声**,旧写法把 `video.muted` 写死 `true`,之后再没有代码按设置赋值);壁纸目录里**真实存在**的音频文件由我们自己的 `<audio>` 播(作用域认 `mpkgKey="custom|<folder>"`,旧写法只认 `folderName` ⇒ 自定义目录里的 web 壁纸永远拼成 library 路由 404)⇒ **上一首/下一首按清单顺序环形切换**,不再是"回到开头";web 壁纸帧内那份声音只有**静音**这一条真通道(`canPlay=false`,如实显示,不假装能暂停帧内 WebAudio);我们放音时帧内被强制静音(防同一首放两遍)。
|
package/lib/client.js
CHANGED
|
@@ -8712,6 +8712,18 @@ function sectionSigNow() {
|
|
|
8712
8712
|
return st.join(" <- ");
|
|
8713
8713
|
} catch (e) { return "" }
|
|
8714
8714
|
}
|
|
8715
|
+
/** ①(2026-09-20 彻查「静音下仍出声」) 这一条记录**来自哪个窗口**:
|
|
8716
|
+
* 主页面 / 同源 iframe(壁纸帧、别的插件帧…)。跨源帧我们的钩子装不进去 ⇒ 如实标 `foreign:true`,
|
|
8717
|
+
* 绝不把"看不到"说成"没有"(用户能据此知道"声音来自某个跨源帧,本插件管不到它的 WebAudio")。 */
|
|
8718
|
+
function mpwAuditWin(win) {
|
|
8719
|
+
try {
|
|
8720
|
+
const w = win || window;
|
|
8721
|
+
const isTop = (() => { try { return w === w.top } catch (e) { return false } })();
|
|
8722
|
+
let url = "";
|
|
8723
|
+
try { url = String((w.location && w.location.href) || "") } catch (e) { url = "cross-origin" }
|
|
8724
|
+
return { top: isTop, url: url.replace(/token=[^&]*/, "token=…").slice(0, 120), foreign: url === "cross-origin" };
|
|
8725
|
+
} catch (e) { return { top: true, url: "", foreign: false } }
|
|
8726
|
+
}
|
|
8715
8727
|
function mpwAuditEl(el) {
|
|
8716
8728
|
try {
|
|
8717
8729
|
if (!el) return null;
|
|
@@ -8721,6 +8733,17 @@ function sectionSigNow() {
|
|
|
8721
8733
|
connected: el.isConnected !== false,
|
|
8722
8734
|
src: src.replace(/token=[^&]*/, "token=…").slice(0, 90),
|
|
8723
8735
|
hasSrcAttr: !!(el.getAttribute && el.getAttribute("src")),
|
|
8736
|
+
/* 声音素材的**归属指纹**(用户问"这声音是谁放的"时,一眼能分类):
|
|
8737
|
+
ours = 我们自己的元素/接口;whale = 那个第三方鲸鱼插件;frame = 壁纸帧内;other = 未知 */
|
|
8738
|
+
owner: (() => {
|
|
8739
|
+
try {
|
|
8740
|
+
const own = (String(el.id || "") === "mpw-bgVideo") || /(^|\s)mpw[-_]/.test(String(el.className || "")) || /\/api\/mpkg-wallpaper\//.test(src);
|
|
8741
|
+
if (own) return "ours";
|
|
8742
|
+
if (/\/dsh-whale\//.test(src)) return "whale-widget";
|
|
8743
|
+
if (el.ownerDocument && el.ownerDocument !== document) return "frame";
|
|
8744
|
+
return "other";
|
|
8745
|
+
} catch (e) { return "unknown" }
|
|
8746
|
+
})(),
|
|
8724
8747
|
};
|
|
8725
8748
|
} catch (e) { return null }
|
|
8726
8749
|
}
|
|
@@ -8730,11 +8753,11 @@ function sectionSigNow() {
|
|
|
8730
8753
|
return { on: s.npNowPlaying !== void 0 ? !!s.npNowPlaying : DEFAULT_NP_NOW_PLAYING, link: npLinkOn(s), mute: s.mute !== void 0 ? !!s.mute : true };
|
|
8731
8754
|
} catch (e) { return null }
|
|
8732
8755
|
}
|
|
8733
|
-
function mpwAuditPush(kind, el, extra) {
|
|
8756
|
+
function mpwAuditPush(kind, el, extra, win) {
|
|
8734
8757
|
try {
|
|
8735
8758
|
if (!mpwAuditOn()) return null;
|
|
8736
8759
|
const rec = Object.assign({
|
|
8737
|
-
t: Date.now(), kind: String(kind || ""), who: mpwAuditWho(), el: mpwAuditEl(el),
|
|
8760
|
+
t: Date.now(), kind: String(kind || ""), who: mpwAuditWho(), el: mpwAuditEl(el), win: mpwAuditWin(win),
|
|
8738
8761
|
muted: el ? !!el.muted : null, volume: el && typeof el.volume === "number" ? Number(el.volume.toFixed(2)) : null,
|
|
8739
8762
|
paused: el ? !!el.paused : null, currentTime: el && typeof el.currentTime === "number" ? Number(el.currentTime.toFixed(2)) : null,
|
|
8740
8763
|
hidden: (() => { try { return document.hidden === true } catch (e) { return null } })(),
|
|
@@ -8751,7 +8774,14 @@ function sectionSigNow() {
|
|
|
8751
8774
|
③ hidden 期间的 play / 取消静音。
|
|
8752
8775
|
命中 ⇒ 立刻把**审计窗口**(最近 12 条)POST 到 /diag(脱敏、截断、5s 节流)。 */
|
|
8753
8776
|
const audible = !!(rec.el && rec.paused === false && rec.muted === false && Number(rec.volume) > 0);
|
|
8777
|
+
/* ①(2026-09-20 用户点名「静音状态下还是突然冒出来的声音」) **这一条**才是那次投诉的直接判据:
|
|
8778
|
+
设置项 `mute === true`(面板写着静音),元素却处在**可听**状态(`!paused && !muted && volume>0`)
|
|
8779
|
+
—— 不管是谁把它改成这样的(我们的哪条路径 / 壁纸帧内 / 别的插件),都必须立刻上报并带上**是谁改的**。
|
|
8780
|
+
旧判据只有"可听播放"这一条泛判据(设置是开是关都报),既漏了"静音下可听"这个组合的强调,
|
|
8781
|
+
也没把 `np.mute` 与可听状态**放在一起**判断。 */
|
|
8782
|
+
const muteOnAudible = !!(rec.np && rec.np.mute === true && audible);
|
|
8754
8783
|
const suspicious = audible
|
|
8784
|
+
|| muteOnAudible
|
|
8755
8785
|
|| !!(rec.el && rec.el.connected === false && rec.paused === false)
|
|
8756
8786
|
|| (kind === "play" && rec.hidden === true)
|
|
8757
8787
|
|| ((kind === "unmute" || kind === "volume") && rec.hidden === true && rec.muted === false);
|
|
@@ -8761,7 +8791,8 @@ function sectionSigNow() {
|
|
|
8761
8791
|
mpwAuditLastBeacon = Date.now();
|
|
8762
8792
|
try {
|
|
8763
8793
|
mpwWebDiag("audio-audit", {
|
|
8764
|
-
trigger:
|
|
8794
|
+
trigger: muteOnAudible ? "mute-on-but-audible"
|
|
8795
|
+
: (audible ? "audible-playback" : (rec.el && rec.el.connected === false ? "detached-playing" : "hidden-transition")),
|
|
8765
8796
|
record: rec, window: list.slice(-12), count: list.length,
|
|
8766
8797
|
visibility: (() => { try { return document.visibilityState } catch (e) { return null } })(),
|
|
8767
8798
|
});
|
|
@@ -8772,19 +8803,24 @@ function sectionSigNow() {
|
|
|
8772
8803
|
return rec;
|
|
8773
8804
|
} catch (e) { return null }
|
|
8774
8805
|
}
|
|
8775
|
-
|
|
8776
|
-
|
|
8777
|
-
|
|
8806
|
+
/** ①(2026-09-20 彻查「静音下仍出声」) 对**任意窗口**装审计钩子。
|
|
8807
|
+
* 为什么必须能装进子窗口:真机上"突然响一声"的声源有两类是我们看不到的 ——
|
|
8808
|
+
* ① **壁纸帧内**的 `<audio>/<video>`(同源模式帧有**自己独立的 `HTMLMediaElement.prototype`**,
|
|
8809
|
+
* 在主窗口上打补丁对它完全无效 ⇒ 旧审计对帧内声音是"瞎的");
|
|
8810
|
+
* ② 别的插件帧里的声音(同一原因)。
|
|
8811
|
+
* 跨源帧装不进去(浏览器安全模型)⇒ 由 `mpwAuditWin()` 如实标 `foreign:true`,不谎报"没有声音"。
|
|
8812
|
+
* `who` 的栈里会带上帧 URL,用户/我们都能据此指认"这条声音是谁放的"。 */
|
|
8813
|
+
function mpwAuditPatchWin(win) {
|
|
8814
|
+
if (!win) return false;
|
|
8815
|
+
try { if (win.__mpwAuditedWin) return false } catch (e) { return false }
|
|
8816
|
+
try { win.__mpwAuditedWin = true } catch (e) { /* 只读窗口也继续装 */ }
|
|
8817
|
+
const push = (kind, el, extra) => { try { return mpwAuditPush(kind, el, extra, win) } catch (e) { return null } };
|
|
8818
|
+
/* ① `<audio>/<video>`:play / muted / volume */
|
|
8778
8819
|
try {
|
|
8779
|
-
|
|
8780
|
-
window.__mpwAudioAudit.push = mpwAuditPush;
|
|
8781
|
-
window.__mpwAudioAudit.clear = () => { try { window.__mpwAudioAudit.list.length = 0 } catch (e) {} };
|
|
8782
|
-
} catch (e) {}
|
|
8783
|
-
try {
|
|
8784
|
-
const P = globalThis.HTMLMediaElement && HTMLMediaElement.prototype;
|
|
8820
|
+
const P = win.HTMLMediaElement && win.HTMLMediaElement.prototype;
|
|
8785
8821
|
if (P && typeof P.play === "function" && !P.__mpwAudited) {
|
|
8786
8822
|
const origPlay = P.play;
|
|
8787
|
-
P.play = function () {
|
|
8823
|
+
P.play = function () { push("play", this); return origPlay.apply(this, arguments) };
|
|
8788
8824
|
for (const prop of ["muted", "volume"]) {
|
|
8789
8825
|
try {
|
|
8790
8826
|
const d = Object.getOwnPropertyDescriptor(P, prop);
|
|
@@ -8794,7 +8830,7 @@ function sectionSigNow() {
|
|
|
8794
8830
|
const bMuted = !!this.muted, bVol = this.volume;
|
|
8795
8831
|
set.call(this, v);
|
|
8796
8832
|
const kind = prop === "muted" ? (this.muted ? "mute" : "unmute") : "volume";
|
|
8797
|
-
if (prop === "muted" ? bMuted !== !!this.muted : bVol !== this.volume)
|
|
8833
|
+
if (prop === "muted" ? bMuted !== !!this.muted : bVol !== this.volume) push(kind, this, { before: { muted: bMuted, volume: bVol } });
|
|
8798
8834
|
return v;
|
|
8799
8835
|
};
|
|
8800
8836
|
wrapped.__mpwAudited = true;
|
|
@@ -8804,34 +8840,88 @@ function sectionSigNow() {
|
|
|
8804
8840
|
P.__mpwAudited = true;
|
|
8805
8841
|
}
|
|
8806
8842
|
} catch (e) {}
|
|
8843
|
+
/* ② `new Audio(...)`(绕过 DOM 标签的声源,最常见的"响一声") */
|
|
8807
8844
|
try {
|
|
8808
|
-
if (typeof
|
|
8809
|
-
const OrigAudio =
|
|
8810
|
-
const Wrapped = function () { const a = new OrigAudio(...arguments); try {
|
|
8845
|
+
if (typeof win.Audio === "function" && !win.Audio.__mpwAudited) {
|
|
8846
|
+
const OrigAudio = win.Audio;
|
|
8847
|
+
const Wrapped = function () { const a = new OrigAudio(...arguments); try { push("new-Audio", a, { args: Array.prototype.slice.call(arguments).join(",").slice(0, 80) }) } catch (e) {} return a };
|
|
8811
8848
|
Wrapped.__mpwAudited = true;
|
|
8812
8849
|
try { Wrapped.prototype = OrigAudio.prototype } catch (e) {}
|
|
8813
|
-
|
|
8850
|
+
win.Audio = Wrapped;
|
|
8814
8851
|
}
|
|
8815
8852
|
} catch (e) {}
|
|
8853
|
+
/* ③ WebAudio:构造 / decodeAudioData / **真的出声那一刻**(AudioBufferSourceNode.start)
|
|
8854
|
+
④(2026-09-20 补) 只记"建了 AudioContext"是没用的:Live2D 角色语音那类声源
|
|
8855
|
+
走 `AudioBufferSourceNode.start()` 出声,**不碰任何 `<audio>` 标签** ——
|
|
8856
|
+
这正是"设置静音却仍然响一声"最可能的声源,必须记到"谁在什么时候 start 了它"。 */
|
|
8816
8857
|
for (const key of ["AudioContext", "webkitAudioContext"]) {
|
|
8817
8858
|
try {
|
|
8818
|
-
const Orig =
|
|
8859
|
+
const Orig = win[key];
|
|
8819
8860
|
if (typeof Orig !== "function" || Orig.__mpwAudited) continue;
|
|
8820
|
-
const Wrapped = function () { const c = new Orig(...arguments); try {
|
|
8861
|
+
const Wrapped = function () { const c = new Orig(...arguments); try { push("AudioContext", null, { ctor: key, state: c && c.state }) } catch (e) {} return c };
|
|
8821
8862
|
Wrapped.__mpwAudited = true;
|
|
8822
8863
|
try { Wrapped.prototype = Orig.prototype } catch (e) {}
|
|
8823
|
-
|
|
8864
|
+
win[key] = Wrapped;
|
|
8824
8865
|
} catch (e) {}
|
|
8825
8866
|
}
|
|
8826
8867
|
try {
|
|
8827
|
-
const AC =
|
|
8868
|
+
const AC = win.AudioContext || win.webkitAudioContext;
|
|
8828
8869
|
const proto = AC && AC.prototype;
|
|
8829
8870
|
if (proto && typeof proto.decodeAudioData === "function" && !proto.__mpwAudited) {
|
|
8830
8871
|
const orig = proto.decodeAudioData;
|
|
8831
|
-
proto.decodeAudioData = function () {
|
|
8872
|
+
proto.decodeAudioData = function () { push("decodeAudioData", null, { ctxState: this && this.state }); return orig.apply(this, arguments) };
|
|
8832
8873
|
proto.__mpwAudited = true;
|
|
8833
8874
|
}
|
|
8875
|
+
if (proto && typeof proto.resume === "function" && !proto.__mpwResumed) {
|
|
8876
|
+
const origResume = proto.resume;
|
|
8877
|
+
proto.resume = function () { push("audioctx-resume", null, { ctxState: this && this.state }); return origResume.apply(this, arguments) };
|
|
8878
|
+
proto.__mpwResumed = true;
|
|
8879
|
+
}
|
|
8880
|
+
} catch (e) {}
|
|
8881
|
+
for (const [nodeKey, nodeName] of [["AudioBufferSourceNode", "buffer-source"], ["OscillatorNode", "oscillator"], ["ConstantSourceNode", "constant"]]) {
|
|
8882
|
+
try {
|
|
8883
|
+
const Ctor = win[nodeKey];
|
|
8884
|
+
const np = Ctor && Ctor.prototype;
|
|
8885
|
+
if (np && typeof np.start === "function" && !np.__mpwAudited) {
|
|
8886
|
+
const origStart = np.start;
|
|
8887
|
+
np.start = function () {
|
|
8888
|
+
let ctxState = null, dur = null;
|
|
8889
|
+
try { ctxState = this.context && this.context.state } catch (e) {}
|
|
8890
|
+
try { dur = this.buffer && this.buffer.duration } catch (e) {}
|
|
8891
|
+
push("webaudio-start", null, { node: nodeName, ctxState: ctxState, bufferSec: dur == null ? null : Number(Number(dur).toFixed(3)), ourCtx: !!this.context });
|
|
8892
|
+
return origStart.apply(this, arguments)
|
|
8893
|
+
};
|
|
8894
|
+
np.__mpwAudited = true;
|
|
8895
|
+
}
|
|
8896
|
+
} catch (e) {}
|
|
8897
|
+
}
|
|
8898
|
+
/* ④ 递归同源子帧(帧内元素有自己的原型对象;跨源会抛 ⇒ 记一条"装不上"的痕) */
|
|
8899
|
+
try {
|
|
8900
|
+
for (let i = 0; i < win.frames.length; i++) {
|
|
8901
|
+
try { mpwAuditPatchWin(win.frames[i]) } catch (e) { try { mpwAuditPush("frame-cross-origin", null, { idx: i, reason: String(e && e.name || e).slice(0, 40) }, win) } catch (e2) {} }
|
|
8902
|
+
}
|
|
8903
|
+
} catch (e) {}
|
|
8904
|
+
return true;
|
|
8905
|
+
}
|
|
8906
|
+
/** 新出现的 iframe(壁纸帧常是晚一步插进来的)⇒ 重新扫一遍;每秒一次、上限 60 次,零长尾开销。 */
|
|
8907
|
+
let mpwAuditFrameScans = 0;
|
|
8908
|
+
function mpwAuditScanFrames() {
|
|
8909
|
+
try {
|
|
8910
|
+
if (mpwAuditFrameScans++ > 60) return;
|
|
8911
|
+
mpwAuditPatchWin(window);
|
|
8912
|
+
} catch (e) {}
|
|
8913
|
+
}
|
|
8914
|
+
function mpwInstallAudioAudit() {
|
|
8915
|
+
if (mpwAuditInstalled || !mpwAuditOn()) return false;
|
|
8916
|
+
mpwAuditInstalled = true;
|
|
8917
|
+
try {
|
|
8918
|
+
window.__mpwAudioAudit = window.__mpwAudioAudit || { list: [], suspicious: 0 };
|
|
8919
|
+
window.__mpwAudioAudit.push = mpwAuditPush;
|
|
8920
|
+
window.__mpwAudioAudit.clear = () => { try { window.__mpwAudioAudit.list.length = 0 } catch (e) {} };
|
|
8921
|
+
window.__mpwAudioAudit.scanFrames = () => { try { mpwAuditFrameScans = 0; mpwAuditScanFrames(); return true } catch (e) { return false } };
|
|
8834
8922
|
} catch (e) {}
|
|
8923
|
+
try { mpwAuditPatchWin(window); } catch (e) {}
|
|
8924
|
+
try { setInterval(mpwAuditScanFrames, 1000); } catch (e) {}
|
|
8835
8925
|
try { mpwTrace("audio-audit:installed", {}); } catch (e) {}
|
|
8836
8926
|
return true;
|
|
8837
8927
|
}
|
package/package.json
CHANGED