diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml index d08574fb1..39ad6689d 100644 --- a/.github/workflows/nodejs.yml +++ b/.github/workflows/nodejs.yml @@ -140,7 +140,7 @@ jobs: # These extra installed packages are required by the browser helpers. # Other ecosystem installation/SDK gates remain tracked by CI-01. run: | - yarn test:package --include=artplayer-plugin-ambilight,artplayer-proxy-canvas 2>&1 | tee refactor/.cache/ci/browser-packages.log + yarn test:package --include=artplayer-plugin-ambilight,artplayer-proxy-canvas,artplayer-plugin-document-pip 2>&1 | tee refactor/.cache/ci/browser-packages.log node --input-type=module -e 'import fs from "node:fs"; const { output } = JSON.parse(fs.readFileSync("refactor/.cache/packages/latest.json")); fs.appendFileSync(process.env.GITHUB_ENV, `ARTPLAYER_BROWSER_ARTIFACTS=${output}/browser-artifacts.json\n`);' - name: Run all source browser checks if: ${{ !cancelled() }} diff --git a/packages/artplayer-plugin-document-pip/ARCHITECTURE.md b/packages/artplayer-plugin-document-pip/ARCHITECTURE.md index 4e72bb5ad..fa1fedbed 100644 --- a/packages/artplayer-plugin-document-pip/ARCHITECTURE.md +++ b/packages/artplayer-plugin-document-pip/ARCHITECTURE.md @@ -89,3 +89,9 @@ plugin 1.1.0; candidate core's event-document focus checks fix this independentl Candidate web fullscreen stays in the current ownerDocument body and restores its captured placement. This does not prove native fullscreen availability inside PiP, background throttling or physical-device support; PKG-DPIP-05 stays open. + +`canvas-dpip.spec.js` additionally checks native Canvas video/track adoption, +subtitle seeking and all three close/destroy endings. Its installed mode requires +the verified five-package map, including Document PiP; the helper rejects missing +maps, stale inputs and conflicting source/artifact overrides. This is integration +evidence, not full package release acceptance or physical-device evidence. diff --git a/packages/artplayer-proxy-canvas/ARCHITECTURE.md b/packages/artplayer-proxy-canvas/ARCHITECTURE.md index 4848d61a6..64efcc6e4 100644 --- a/packages/artplayer-proxy-canvas/ARCHITECTURE.md +++ b/packages/artplayer-proxy-canvas/ARCHITECTURE.md @@ -106,3 +106,10 @@ single-track ownership, ordinary Canvas child insertion/removal and escaped inse after destroy on old/new cores. It also belongs to the installed browser scope; keep its bytes and native engine provenance with the lifecycle report. See `refactor/canvas-validation.md` and the task evidence for actual outcomes and limitations. + +`canvas-dpip.spec.js` follows the same Canvas, backing video and native subtitle +track into actual Document PiP windows. It covers playback/draw advancement, +paused subtitle seeking and close/native-close/destroy resource ownership. The +installed scope requires Document PiP's own verified package bytes as well; a +source helper may not fill a missing installed package. Unsupported native APIs +are recorded separately and never treated as native playback evidence. diff --git a/refactor/baselines/canvas-dpip-validation.json b/refactor/baselines/canvas-dpip-validation.json new file mode 100644 index 000000000..3660725d7 --- /dev/null +++ b/refactor/baselines/canvas-dpip-validation.json @@ -0,0 +1,2280 @@ +{ + "task": "PKG-CANVAS-05", + "status": "doing", + "date": "2026-09-14", + "node": "24.21.0", + "yarn": "1.22.22", + "reports": [ + { + "directory": "refactor/.cache/canvas-dpip-source-report", + "sha256": "171fda8ba6082d8eae0acdcf8920d607b62349e6a5b6bb3471dc179b81e18cf8", + "stats": { + "startTime": "2026-09-14T10:54:43.011Z", + "duration": 24945.471, + "expected": 18, + "skipped": 0, + "unexpected": 0, + "flaky": 0 + }, + "result": { + "scope": "source", + "exitCode": 0, + "signal": null, + "args": [ + "test", + "--config=playwright.source.config.js", + "test/browser/canvas-dpip.spec.js", + "--workers=1" + ] + }, + "inputs": [], + "browsers": { + "chromium": "153.0.8010.12", + "firefox": "155.0", + "webkit": "26.6" + }, + "nativeCases": 12, + "unavailableCases": 6, + "records": [ + { + "core": "published", + "ending": "close", + "browserName": "chromium", + "supported": true, + "canvas": { + "name": "candidate-source", + "sha256": "3d4887505ccc68d683f4805ebae25a8f383e13da82b3f213dc134900752c3bb6" + }, + "dpip": { + "name": "candidate-source", + "sha256": "2daf44990a250f1122ce38481f3ed8c41d2f8c65a0d5ed99ab2633e46b703db1" + }, + "outcome": "native-window-playback-subtitles-and-cleanup", + "entered": { + "active": true, + "sameCanvas": true, + "playerAdopted": true, + "videoAdopted": true, + "trackAdopted": true, + "time": 0.337776, + "draws": 21 + }, + "final": { + "active": false, + "connected": false, + "paused": true, + "src": null, + "pending": 0, + "draws": 39, + "placeholders": 0, + "events": [ + true, + false + ], + "errors": [] + }, + "observed": { + "active": false, + "paused": true, + "time": 0, + "draws": 39, + "subtitle": "\n PiP caption\n ", + "errors": [], + "events": [ + true, + false + ] + } + }, + { + "core": "published", + "ending": "native-close", + "browserName": "chromium", + "supported": true, + "canvas": { + "name": "candidate-source", + "sha256": "3d4887505ccc68d683f4805ebae25a8f383e13da82b3f213dc134900752c3bb6" + }, + "dpip": { + "name": "candidate-source", + "sha256": "2daf44990a250f1122ce38481f3ed8c41d2f8c65a0d5ed99ab2633e46b703db1" + }, + "outcome": "native-window-playback-subtitles-and-cleanup", + "entered": { + "active": true, + "sameCanvas": true, + "playerAdopted": true, + "videoAdopted": true, + "trackAdopted": true, + "time": 0.299013, + "draws": 19 + }, + "final": { + "active": false, + "connected": false, + "paused": true, + "src": null, + "pending": 0, + "draws": 35, + "placeholders": 0, + "events": [ + true, + false + ], + "errors": [] + }, + "observed": { + "active": false, + "paused": true, + "time": 0, + "draws": 35, + "subtitle": "\n PiP caption\n ", + "errors": [], + "events": [ + true, + false + ] + } + }, + { + "core": "published", + "ending": "destroy", + "browserName": "chromium", + "supported": true, + "canvas": { + "name": "candidate-source", + "sha256": "3d4887505ccc68d683f4805ebae25a8f383e13da82b3f213dc134900752c3bb6" + }, + "dpip": { + "name": "candidate-source", + "sha256": "2daf44990a250f1122ce38481f3ed8c41d2f8c65a0d5ed99ab2633e46b703db1" + }, + "outcome": "native-window-playback-subtitles-and-cleanup", + "entered": { + "active": true, + "sameCanvas": true, + "playerAdopted": true, + "videoAdopted": true, + "trackAdopted": true, + "time": 0.28948, + "draws": 18 + }, + "final": { + "active": false, + "connected": false, + "paused": true, + "src": null, + "pending": 0, + "draws": 27, + "placeholders": 0, + "events": [ + true + ], + "errors": [] + }, + "observed": { + "active": false, + "paused": true, + "time": 0, + "draws": 27, + "subtitle": "\n PiP caption\n ", + "errors": [], + "events": [ + true + ] + } + }, + { + "core": "candidate", + "ending": "close", + "browserName": "chromium", + "supported": true, + "canvas": { + "name": "candidate-source", + "sha256": "3d4887505ccc68d683f4805ebae25a8f383e13da82b3f213dc134900752c3bb6" + }, + "dpip": { + "name": "candidate-source", + "sha256": "2daf44990a250f1122ce38481f3ed8c41d2f8c65a0d5ed99ab2633e46b703db1" + }, + "outcome": "native-window-playback-subtitles-and-cleanup", + "entered": { + "active": true, + "sameCanvas": true, + "playerAdopted": true, + "videoAdopted": true, + "trackAdopted": true, + "time": 0.306242, + "draws": 19 + }, + "final": { + "active": false, + "connected": false, + "paused": true, + "src": null, + "pending": 0, + "draws": 36, + "placeholders": 0, + "events": [ + true, + false + ], + "errors": [] + }, + "observed": { + "active": false, + "paused": true, + "time": 0, + "draws": 36, + "subtitle": "\n PiP caption\n ", + "errors": [], + "events": [ + true, + false + ] + } + }, + { + "core": "candidate", + "ending": "native-close", + "browserName": "chromium", + "supported": true, + "canvas": { + "name": "candidate-source", + "sha256": "3d4887505ccc68d683f4805ebae25a8f383e13da82b3f213dc134900752c3bb6" + }, + "dpip": { + "name": "candidate-source", + "sha256": "2daf44990a250f1122ce38481f3ed8c41d2f8c65a0d5ed99ab2633e46b703db1" + }, + "outcome": "native-window-playback-subtitles-and-cleanup", + "entered": { + "active": true, + "sameCanvas": true, + "playerAdopted": true, + "videoAdopted": true, + "trackAdopted": true, + "time": 0.304979, + "draws": 19 + }, + "final": { + "active": false, + "connected": false, + "paused": true, + "src": null, + "pending": 0, + "draws": 36, + "placeholders": 0, + "events": [ + true, + false + ], + "errors": [] + }, + "observed": { + "active": false, + "paused": true, + "time": 0, + "draws": 36, + "subtitle": "\n PiP caption\n ", + "errors": [], + "events": [ + true, + false + ] + } + }, + { + "core": "candidate", + "ending": "destroy", + "browserName": "chromium", + "supported": true, + "canvas": { + "name": "candidate-source", + "sha256": "3d4887505ccc68d683f4805ebae25a8f383e13da82b3f213dc134900752c3bb6" + }, + "dpip": { + "name": "candidate-source", + "sha256": "2daf44990a250f1122ce38481f3ed8c41d2f8c65a0d5ed99ab2633e46b703db1" + }, + "outcome": "native-window-playback-subtitles-and-cleanup", + "entered": { + "active": true, + "sameCanvas": true, + "playerAdopted": true, + "videoAdopted": true, + "trackAdopted": true, + "time": 0.291426, + "draws": 18 + }, + "final": { + "active": false, + "connected": false, + "paused": true, + "src": null, + "pending": 0, + "draws": 27, + "placeholders": 0, + "events": [ + true + ], + "errors": [] + }, + "observed": { + "active": false, + "paused": true, + "time": 0, + "draws": 27, + "subtitle": "\n PiP caption\n ", + "errors": [], + "events": [ + true + ] + } + }, + { + "core": "published", + "ending": "close", + "browserName": "firefox", + "supported": true, + "canvas": { + "name": "candidate-source", + "sha256": "3d4887505ccc68d683f4805ebae25a8f383e13da82b3f213dc134900752c3bb6" + }, + "dpip": { + "name": "candidate-source", + "sha256": "2daf44990a250f1122ce38481f3ed8c41d2f8c65a0d5ed99ab2633e46b703db1" + }, + "outcome": "native-window-playback-subtitles-and-cleanup", + "entered": { + "active": true, + "sameCanvas": true, + "playerAdopted": true, + "videoAdopted": true, + "trackAdopted": true, + "time": 0.416666, + "draws": 19 + }, + "final": { + "active": false, + "connected": false, + "paused": true, + "src": null, + "pending": 0, + "draws": 54, + "placeholders": 0, + "events": [ + true, + false + ], + "errors": [] + }, + "observed": { + "active": false, + "paused": true, + "time": 0, + "draws": 54, + "subtitle": "\n PiP caption\n ", + "errors": [], + "events": [ + true, + false + ] + } + }, + { + "core": "published", + "ending": "native-close", + "browserName": "firefox", + "supported": true, + "canvas": { + "name": "candidate-source", + "sha256": "3d4887505ccc68d683f4805ebae25a8f383e13da82b3f213dc134900752c3bb6" + }, + "dpip": { + "name": "candidate-source", + "sha256": "2daf44990a250f1122ce38481f3ed8c41d2f8c65a0d5ed99ab2633e46b703db1" + }, + "outcome": "native-window-playback-subtitles-and-cleanup", + "entered": { + "active": true, + "sameCanvas": true, + "playerAdopted": true, + "videoAdopted": true, + "trackAdopted": true, + "time": 0.333332, + "draws": 16 + }, + "final": { + "active": false, + "connected": false, + "paused": true, + "src": null, + "pending": 0, + "draws": 51, + "placeholders": 0, + "events": [ + true, + false + ], + "errors": [] + }, + "observed": { + "active": false, + "paused": true, + "time": 0, + "draws": 51, + "subtitle": "\n PiP caption\n ", + "errors": [], + "events": [ + true, + false + ] + } + }, + { + "core": "published", + "ending": "destroy", + "browserName": "firefox", + "supported": true, + "canvas": { + "name": "candidate-source", + "sha256": "3d4887505ccc68d683f4805ebae25a8f383e13da82b3f213dc134900752c3bb6" + }, + "dpip": { + "name": "candidate-source", + "sha256": "2daf44990a250f1122ce38481f3ed8c41d2f8c65a0d5ed99ab2633e46b703db1" + }, + "outcome": "native-window-playback-subtitles-and-cleanup", + "entered": { + "active": true, + "sameCanvas": true, + "playerAdopted": true, + "videoAdopted": true, + "trackAdopted": true, + "time": 0.333332, + "draws": 16 + }, + "final": { + "active": false, + "connected": false, + "paused": true, + "src": null, + "pending": 0, + "draws": 25, + "placeholders": 0, + "events": [ + true + ], + "errors": [] + }, + "observed": { + "active": false, + "paused": true, + "time": 0, + "draws": 25, + "subtitle": "\n PiP caption\n ", + "errors": [], + "events": [ + true + ] + } + }, + { + "core": "candidate", + "ending": "close", + "browserName": "firefox", + "supported": true, + "canvas": { + "name": "candidate-source", + "sha256": "3d4887505ccc68d683f4805ebae25a8f383e13da82b3f213dc134900752c3bb6" + }, + "dpip": { + "name": "candidate-source", + "sha256": "2daf44990a250f1122ce38481f3ed8c41d2f8c65a0d5ed99ab2633e46b703db1" + }, + "outcome": "native-window-playback-subtitles-and-cleanup", + "entered": { + "active": true, + "sameCanvas": true, + "playerAdopted": true, + "videoAdopted": true, + "trackAdopted": true, + "time": 0.374999, + "draws": 18 + }, + "final": { + "active": false, + "connected": false, + "paused": true, + "src": null, + "pending": 0, + "draws": 57, + "placeholders": 0, + "events": [ + true, + false + ], + "errors": [] + }, + "observed": { + "active": false, + "paused": true, + "time": 0, + "draws": 57, + "subtitle": "\n PiP caption\n ", + "errors": [], + "events": [ + true, + false + ] + } + }, + { + "core": "candidate", + "ending": "native-close", + "browserName": "firefox", + "supported": true, + "canvas": { + "name": "candidate-source", + "sha256": "3d4887505ccc68d683f4805ebae25a8f383e13da82b3f213dc134900752c3bb6" + }, + "dpip": { + "name": "candidate-source", + "sha256": "2daf44990a250f1122ce38481f3ed8c41d2f8c65a0d5ed99ab2633e46b703db1" + }, + "outcome": "native-window-playback-subtitles-and-cleanup", + "entered": { + "active": true, + "sameCanvas": true, + "playerAdopted": true, + "videoAdopted": true, + "trackAdopted": true, + "time": 0.499999, + "draws": 25 + }, + "final": { + "active": false, + "connected": false, + "paused": true, + "src": null, + "pending": 0, + "draws": 46, + "placeholders": 0, + "events": [ + true, + false + ], + "errors": [] + }, + "observed": { + "active": false, + "paused": true, + "time": 0, + "draws": 46, + "subtitle": "\n PiP caption\n ", + "errors": [], + "events": [ + true, + false + ] + } + }, + { + "core": "candidate", + "ending": "destroy", + "browserName": "firefox", + "supported": true, + "canvas": { + "name": "candidate-source", + "sha256": "3d4887505ccc68d683f4805ebae25a8f383e13da82b3f213dc134900752c3bb6" + }, + "dpip": { + "name": "candidate-source", + "sha256": "2daf44990a250f1122ce38481f3ed8c41d2f8c65a0d5ed99ab2633e46b703db1" + }, + "outcome": "native-window-playback-subtitles-and-cleanup", + "entered": { + "active": true, + "sameCanvas": true, + "playerAdopted": true, + "videoAdopted": true, + "trackAdopted": true, + "time": 0.333332, + "draws": 16 + }, + "final": { + "active": false, + "connected": false, + "paused": true, + "src": null, + "pending": 0, + "draws": 27, + "placeholders": 0, + "events": [ + true + ], + "errors": [] + }, + "observed": { + "active": false, + "paused": true, + "time": 0, + "draws": 27, + "subtitle": "\n PiP caption\n ", + "errors": [], + "events": [ + true + ] + } + }, + { + "core": "published", + "ending": "close", + "browserName": "webkit", + "supported": false, + "canvas": { + "name": "candidate-source", + "sha256": "3d4887505ccc68d683f4805ebae25a8f383e13da82b3f213dc134900752c3bb6" + }, + "dpip": { + "name": "candidate-source", + "sha256": "2daf44990a250f1122ce38481f3ed8c41d2f8c65a0d5ed99ab2633e46b703db1" + }, + "outcome": "native-document-pip-unavailable" + }, + { + "core": "published", + "ending": "native-close", + "browserName": "webkit", + "supported": false, + "canvas": { + "name": "candidate-source", + "sha256": "3d4887505ccc68d683f4805ebae25a8f383e13da82b3f213dc134900752c3bb6" + }, + "dpip": { + "name": "candidate-source", + "sha256": "2daf44990a250f1122ce38481f3ed8c41d2f8c65a0d5ed99ab2633e46b703db1" + }, + "outcome": "native-document-pip-unavailable" + }, + { + "core": "published", + "ending": "destroy", + "browserName": "webkit", + "supported": false, + "canvas": { + "name": "candidate-source", + "sha256": "3d4887505ccc68d683f4805ebae25a8f383e13da82b3f213dc134900752c3bb6" + }, + "dpip": { + "name": "candidate-source", + "sha256": "2daf44990a250f1122ce38481f3ed8c41d2f8c65a0d5ed99ab2633e46b703db1" + }, + "outcome": "native-document-pip-unavailable" + }, + { + "core": "candidate", + "ending": "close", + "browserName": "webkit", + "supported": false, + "canvas": { + "name": "candidate-source", + "sha256": "3d4887505ccc68d683f4805ebae25a8f383e13da82b3f213dc134900752c3bb6" + }, + "dpip": { + "name": "candidate-source", + "sha256": "2daf44990a250f1122ce38481f3ed8c41d2f8c65a0d5ed99ab2633e46b703db1" + }, + "outcome": "native-document-pip-unavailable" + }, + { + "core": "candidate", + "ending": "native-close", + "browserName": "webkit", + "supported": false, + "canvas": { + "name": "candidate-source", + "sha256": "3d4887505ccc68d683f4805ebae25a8f383e13da82b3f213dc134900752c3bb6" + }, + "dpip": { + "name": "candidate-source", + "sha256": "2daf44990a250f1122ce38481f3ed8c41d2f8c65a0d5ed99ab2633e46b703db1" + }, + "outcome": "native-document-pip-unavailable" + }, + { + "core": "candidate", + "ending": "destroy", + "browserName": "webkit", + "supported": false, + "canvas": { + "name": "candidate-source", + "sha256": "3d4887505ccc68d683f4805ebae25a8f383e13da82b3f213dc134900752c3bb6" + }, + "dpip": { + "name": "candidate-source", + "sha256": "2daf44990a250f1122ce38481f3ed8c41d2f8c65a0d5ed99ab2633e46b703db1" + }, + "outcome": "native-document-pip-unavailable" + } + ] + }, + { + "directory": "refactor/.cache/canvas-dpip-installed-main-report", + "sha256": "549db0da6c59e26f5b391b2c70036f8d2fffa1c2744c5aefed1b475a9fa8634e", + "stats": { + "startTime": "2026-09-14T10:59:43.092Z", + "duration": 73515.901, + "expected": 51, + "skipped": 0, + "unexpected": 0, + "flaky": 0 + }, + "result": { + "scope": "installed", + "exitCode": 0, + "signal": null, + "args": [ + "test", + "--config=playwright.installed.config.js", + "test/browser/canvas-dpip.spec.js", + "test/browser/canvas-subtitles.spec.js", + "test/browser/canvas-lifecycle.spec.js", + "test/browser/ambilight-proxy.spec.js", + "--workers=1" + ] + }, + "inputs": [ + { + "name": "artplayer", + "member": "package/dist/artplayer.js", + "sha256": "9148a369636f814b03e6a1ed2a7db222a31b159f93e8a94d52c5a9dde2dfe6a6", + "archiveSha256": "60edd42a29e939632568851018cf0e230eb60fb54a2597c0877d496795bbcc6e" + }, + { + "name": "artplayer-plugin-chapter", + "member": "package/dist/artplayer-plugin-chapter.js", + "sha256": "179c3ea90ddc85da27b46f000110e32d464dfa5c8c394e98316dc1b75d6f2a72", + "archiveSha256": "784c581410185908ea66aadaf06336970117e06f16dd4c66a618b196bfaba698" + }, + { + "name": "artplayer-plugin-ambilight", + "member": "package/dist/artplayer-plugin-ambilight.js", + "sha256": "21ee5227f26cfff6e82ee6932f6eafc9741fa3be0ec2f5252a47b53ceea14599", + "archiveSha256": "7e88a0c9a643612f54e1674c1406c2bb7d18b1dd1f6fc05e7f49749301b310f3" + }, + { + "name": "artplayer-proxy-canvas", + "member": "package/dist/artplayer-proxy-canvas.js", + "sha256": "f572ea536107b7823452c5a99447b69e9226a3582c79dda95a4248cca819ec18", + "archiveSha256": "074e51779de545d46f234e2052e05de63c0f1189c031b08c0b23702b6ceb5325" + }, + { + "name": "artplayer-plugin-document-pip", + "member": "package/dist/artplayer-plugin-document-pip.js", + "sha256": "56440aad3ed06be55ec3a57d781f7de239e9aecf40a067e88f2cffcd01a86d6a", + "archiveSha256": "5caf75a1de5af1b19a133be6d35e3573b25783ae376d15af3846cef392f17b7c" + } + ], + "browsers": { + "chromium": "153.0.8010.12", + "firefox": "155.0", + "webkit": "26.6" + }, + "nativeCases": 12, + "unavailableCases": 6, + "records": [ + { + "core": "published", + "ending": "close", + "browserName": "chromium", + "supported": true, + "canvas": { + "name": "candidate-installed", + "sha256": "f572ea536107b7823452c5a99447b69e9226a3582c79dda95a4248cca819ec18" + }, + "dpip": { + "name": "candidate-installed", + "sha256": "56440aad3ed06be55ec3a57d781f7de239e9aecf40a067e88f2cffcd01a86d6a" + }, + "outcome": "native-window-playback-subtitles-and-cleanup", + "entered": { + "active": true, + "sameCanvas": true, + "playerAdopted": true, + "videoAdopted": true, + "trackAdopted": true, + "time": 0.308638, + "draws": 19 + }, + "final": { + "active": false, + "connected": false, + "paused": true, + "src": null, + "pending": 0, + "draws": 36, + "placeholders": 0, + "events": [ + true, + false + ], + "errors": [] + }, + "observed": { + "active": false, + "paused": true, + "time": 0, + "draws": 36, + "subtitle": "\n PiP caption\n ", + "errors": [], + "events": [ + true, + false + ] + } + }, + { + "core": "published", + "ending": "native-close", + "browserName": "chromium", + "supported": true, + "canvas": { + "name": "candidate-installed", + "sha256": "f572ea536107b7823452c5a99447b69e9226a3582c79dda95a4248cca819ec18" + }, + "dpip": { + "name": "candidate-installed", + "sha256": "56440aad3ed06be55ec3a57d781f7de239e9aecf40a067e88f2cffcd01a86d6a" + }, + "outcome": "native-window-playback-subtitles-and-cleanup", + "entered": { + "active": true, + "sameCanvas": true, + "playerAdopted": true, + "videoAdopted": true, + "trackAdopted": true, + "time": 0.306925, + "draws": 19 + }, + "final": { + "active": false, + "connected": false, + "paused": true, + "src": null, + "pending": 0, + "draws": 35, + "placeholders": 0, + "events": [ + true, + false + ], + "errors": [] + }, + "observed": { + "active": false, + "paused": true, + "time": 0, + "draws": 35, + "subtitle": "\n PiP caption\n ", + "errors": [], + "events": [ + true, + false + ] + } + }, + { + "core": "published", + "ending": "destroy", + "browserName": "chromium", + "supported": true, + "canvas": { + "name": "candidate-installed", + "sha256": "f572ea536107b7823452c5a99447b69e9226a3582c79dda95a4248cca819ec18" + }, + "dpip": { + "name": "candidate-installed", + "sha256": "56440aad3ed06be55ec3a57d781f7de239e9aecf40a067e88f2cffcd01a86d6a" + }, + "outcome": "native-window-playback-subtitles-and-cleanup", + "entered": { + "active": true, + "sameCanvas": true, + "playerAdopted": true, + "videoAdopted": true, + "trackAdopted": true, + "time": 0.30837, + "draws": 19 + }, + "final": { + "active": false, + "connected": false, + "paused": true, + "src": null, + "pending": 0, + "draws": 28, + "placeholders": 0, + "events": [ + true + ], + "errors": [] + }, + "observed": { + "active": false, + "paused": true, + "time": 0, + "draws": 28, + "subtitle": "\n PiP caption\n ", + "errors": [], + "events": [ + true + ] + } + }, + { + "core": "candidate", + "ending": "close", + "browserName": "chromium", + "supported": true, + "canvas": { + "name": "candidate-installed", + "sha256": "f572ea536107b7823452c5a99447b69e9226a3582c79dda95a4248cca819ec18" + }, + "dpip": { + "name": "candidate-installed", + "sha256": "56440aad3ed06be55ec3a57d781f7de239e9aecf40a067e88f2cffcd01a86d6a" + }, + "outcome": "native-window-playback-subtitles-and-cleanup", + "entered": { + "active": true, + "sameCanvas": true, + "playerAdopted": true, + "videoAdopted": true, + "trackAdopted": true, + "time": 0.320015, + "draws": 20 + }, + "final": { + "active": false, + "connected": false, + "paused": true, + "src": null, + "pending": 0, + "draws": 37, + "placeholders": 0, + "events": [ + true, + false + ], + "errors": [] + }, + "observed": { + "active": false, + "paused": true, + "time": 0, + "draws": 37, + "subtitle": "\n PiP caption\n ", + "errors": [], + "events": [ + true, + false + ] + } + }, + { + "core": "candidate", + "ending": "native-close", + "browserName": "chromium", + "supported": true, + "canvas": { + "name": "candidate-installed", + "sha256": "f572ea536107b7823452c5a99447b69e9226a3582c79dda95a4248cca819ec18" + }, + "dpip": { + "name": "candidate-installed", + "sha256": "56440aad3ed06be55ec3a57d781f7de239e9aecf40a067e88f2cffcd01a86d6a" + }, + "outcome": "native-window-playback-subtitles-and-cleanup", + "entered": { + "active": true, + "sameCanvas": true, + "playerAdopted": true, + "videoAdopted": true, + "trackAdopted": true, + "time": 0.309014, + "draws": 19 + }, + "final": { + "active": false, + "connected": false, + "paused": true, + "src": null, + "pending": 0, + "draws": 36, + "placeholders": 0, + "events": [ + true, + false + ], + "errors": [] + }, + "observed": { + "active": false, + "paused": true, + "time": 0, + "draws": 36, + "subtitle": "\n PiP caption\n ", + "errors": [], + "events": [ + true, + false + ] + } + }, + { + "core": "candidate", + "ending": "destroy", + "browserName": "chromium", + "supported": true, + "canvas": { + "name": "candidate-installed", + "sha256": "f572ea536107b7823452c5a99447b69e9226a3582c79dda95a4248cca819ec18" + }, + "dpip": { + "name": "candidate-installed", + "sha256": "56440aad3ed06be55ec3a57d781f7de239e9aecf40a067e88f2cffcd01a86d6a" + }, + "outcome": "native-window-playback-subtitles-and-cleanup", + "entered": { + "active": true, + "sameCanvas": true, + "playerAdopted": true, + "videoAdopted": true, + "trackAdopted": true, + "time": 0.291057, + "draws": 18 + }, + "final": { + "active": false, + "connected": false, + "paused": true, + "src": null, + "pending": 0, + "draws": 27, + "placeholders": 0, + "events": [ + true + ], + "errors": [] + }, + "observed": { + "active": false, + "paused": true, + "time": 0, + "draws": 27, + "subtitle": "\n PiP caption\n ", + "errors": [], + "events": [ + true + ] + } + }, + { + "core": "published", + "ending": "close", + "browserName": "firefox", + "supported": true, + "canvas": { + "name": "candidate-installed", + "sha256": "f572ea536107b7823452c5a99447b69e9226a3582c79dda95a4248cca819ec18" + }, + "dpip": { + "name": "candidate-installed", + "sha256": "56440aad3ed06be55ec3a57d781f7de239e9aecf40a067e88f2cffcd01a86d6a" + }, + "outcome": "native-window-playback-subtitles-and-cleanup", + "entered": { + "active": true, + "sameCanvas": true, + "playerAdopted": true, + "videoAdopted": true, + "trackAdopted": true, + "time": 0.333332, + "draws": 17 + }, + "final": { + "active": false, + "connected": false, + "paused": true, + "src": null, + "pending": 0, + "draws": 52, + "placeholders": 0, + "events": [ + true, + false + ], + "errors": [] + }, + "observed": { + "active": false, + "paused": true, + "time": 0, + "draws": 52, + "subtitle": "\n PiP caption\n ", + "errors": [], + "events": [ + true, + false + ] + } + }, + { + "core": "published", + "ending": "native-close", + "browserName": "firefox", + "supported": true, + "canvas": { + "name": "candidate-installed", + "sha256": "f572ea536107b7823452c5a99447b69e9226a3582c79dda95a4248cca819ec18" + }, + "dpip": { + "name": "candidate-installed", + "sha256": "56440aad3ed06be55ec3a57d781f7de239e9aecf40a067e88f2cffcd01a86d6a" + }, + "outcome": "native-window-playback-subtitles-and-cleanup", + "entered": { + "active": true, + "sameCanvas": true, + "playerAdopted": true, + "videoAdopted": true, + "trackAdopted": true, + "time": 0.333332, + "draws": 15 + }, + "final": { + "active": false, + "connected": false, + "paused": true, + "src": null, + "pending": 0, + "draws": 36, + "placeholders": 0, + "events": [ + true, + false + ], + "errors": [] + }, + "observed": { + "active": false, + "paused": true, + "time": 0, + "draws": 36, + "subtitle": "\n PiP caption\n ", + "errors": [], + "events": [ + true, + false + ] + } + }, + { + "core": "published", + "ending": "destroy", + "browserName": "firefox", + "supported": true, + "canvas": { + "name": "candidate-installed", + "sha256": "f572ea536107b7823452c5a99447b69e9226a3582c79dda95a4248cca819ec18" + }, + "dpip": { + "name": "candidate-installed", + "sha256": "56440aad3ed06be55ec3a57d781f7de239e9aecf40a067e88f2cffcd01a86d6a" + }, + "outcome": "native-window-playback-subtitles-and-cleanup", + "entered": { + "active": true, + "sameCanvas": true, + "playerAdopted": true, + "videoAdopted": true, + "trackAdopted": true, + "time": 0.416666, + "draws": 20 + }, + "final": { + "active": false, + "connected": false, + "paused": true, + "src": null, + "pending": 0, + "draws": 33, + "placeholders": 0, + "events": [ + true + ], + "errors": [] + }, + "observed": { + "active": false, + "paused": true, + "time": 0, + "draws": 33, + "subtitle": "\n PiP caption\n ", + "errors": [], + "events": [ + true + ] + } + }, + { + "core": "candidate", + "ending": "close", + "browserName": "firefox", + "supported": true, + "canvas": { + "name": "candidate-installed", + "sha256": "f572ea536107b7823452c5a99447b69e9226a3582c79dda95a4248cca819ec18" + }, + "dpip": { + "name": "candidate-installed", + "sha256": "56440aad3ed06be55ec3a57d781f7de239e9aecf40a067e88f2cffcd01a86d6a" + }, + "outcome": "native-window-playback-subtitles-and-cleanup", + "entered": { + "active": true, + "sameCanvas": true, + "playerAdopted": true, + "videoAdopted": true, + "trackAdopted": true, + "time": 0.374999, + "draws": 17 + }, + "final": { + "active": false, + "connected": false, + "paused": true, + "src": null, + "pending": 0, + "draws": 52, + "placeholders": 0, + "events": [ + true, + false + ], + "errors": [] + }, + "observed": { + "active": false, + "paused": true, + "time": 0, + "draws": 52, + "subtitle": "\n PiP caption\n ", + "errors": [], + "events": [ + true, + false + ] + } + }, + { + "core": "candidate", + "ending": "native-close", + "browserName": "firefox", + "supported": true, + "canvas": { + "name": "candidate-installed", + "sha256": "f572ea536107b7823452c5a99447b69e9226a3582c79dda95a4248cca819ec18" + }, + "dpip": { + "name": "candidate-installed", + "sha256": "56440aad3ed06be55ec3a57d781f7de239e9aecf40a067e88f2cffcd01a86d6a" + }, + "outcome": "native-window-playback-subtitles-and-cleanup", + "entered": { + "active": true, + "sameCanvas": true, + "playerAdopted": true, + "videoAdopted": true, + "trackAdopted": true, + "time": 0.333332, + "draws": 17 + }, + "final": { + "active": false, + "connected": false, + "paused": true, + "src": null, + "pending": 0, + "draws": 52, + "placeholders": 0, + "events": [ + true, + false + ], + "errors": [] + }, + "observed": { + "active": false, + "paused": true, + "time": 0, + "draws": 52, + "subtitle": "\n PiP caption\n ", + "errors": [], + "events": [ + true, + false + ] + } + }, + { + "core": "candidate", + "ending": "destroy", + "browserName": "firefox", + "supported": true, + "canvas": { + "name": "candidate-installed", + "sha256": "f572ea536107b7823452c5a99447b69e9226a3582c79dda95a4248cca819ec18" + }, + "dpip": { + "name": "candidate-installed", + "sha256": "56440aad3ed06be55ec3a57d781f7de239e9aecf40a067e88f2cffcd01a86d6a" + }, + "outcome": "native-window-playback-subtitles-and-cleanup", + "entered": { + "active": true, + "sameCanvas": true, + "playerAdopted": true, + "videoAdopted": true, + "trackAdopted": true, + "time": 0.374999, + "draws": 17 + }, + "final": { + "active": false, + "connected": false, + "paused": true, + "src": null, + "pending": 0, + "draws": 27, + "placeholders": 0, + "events": [ + true + ], + "errors": [] + }, + "observed": { + "active": false, + "paused": true, + "time": 0, + "draws": 27, + "subtitle": "\n PiP caption\n ", + "errors": [], + "events": [ + true + ] + } + }, + { + "core": "published", + "ending": "close", + "browserName": "webkit", + "supported": false, + "canvas": { + "name": "candidate-installed", + "sha256": "f572ea536107b7823452c5a99447b69e9226a3582c79dda95a4248cca819ec18" + }, + "dpip": { + "name": "candidate-installed", + "sha256": "56440aad3ed06be55ec3a57d781f7de239e9aecf40a067e88f2cffcd01a86d6a" + }, + "outcome": "native-document-pip-unavailable" + }, + { + "core": "published", + "ending": "native-close", + "browserName": "webkit", + "supported": false, + "canvas": { + "name": "candidate-installed", + "sha256": "f572ea536107b7823452c5a99447b69e9226a3582c79dda95a4248cca819ec18" + }, + "dpip": { + "name": "candidate-installed", + "sha256": "56440aad3ed06be55ec3a57d781f7de239e9aecf40a067e88f2cffcd01a86d6a" + }, + "outcome": "native-document-pip-unavailable" + }, + { + "core": "published", + "ending": "destroy", + "browserName": "webkit", + "supported": false, + "canvas": { + "name": "candidate-installed", + "sha256": "f572ea536107b7823452c5a99447b69e9226a3582c79dda95a4248cca819ec18" + }, + "dpip": { + "name": "candidate-installed", + "sha256": "56440aad3ed06be55ec3a57d781f7de239e9aecf40a067e88f2cffcd01a86d6a" + }, + "outcome": "native-document-pip-unavailable" + }, + { + "core": "candidate", + "ending": "close", + "browserName": "webkit", + "supported": false, + "canvas": { + "name": "candidate-installed", + "sha256": "f572ea536107b7823452c5a99447b69e9226a3582c79dda95a4248cca819ec18" + }, + "dpip": { + "name": "candidate-installed", + "sha256": "56440aad3ed06be55ec3a57d781f7de239e9aecf40a067e88f2cffcd01a86d6a" + }, + "outcome": "native-document-pip-unavailable" + }, + { + "core": "candidate", + "ending": "native-close", + "browserName": "webkit", + "supported": false, + "canvas": { + "name": "candidate-installed", + "sha256": "f572ea536107b7823452c5a99447b69e9226a3582c79dda95a4248cca819ec18" + }, + "dpip": { + "name": "candidate-installed", + "sha256": "56440aad3ed06be55ec3a57d781f7de239e9aecf40a067e88f2cffcd01a86d6a" + }, + "outcome": "native-document-pip-unavailable" + }, + { + "core": "candidate", + "ending": "destroy", + "browserName": "webkit", + "supported": false, + "canvas": { + "name": "candidate-installed", + "sha256": "f572ea536107b7823452c5a99447b69e9226a3582c79dda95a4248cca819ec18" + }, + "dpip": { + "name": "candidate-installed", + "sha256": "56440aad3ed06be55ec3a57d781f7de239e9aecf40a067e88f2cffcd01a86d6a" + }, + "outcome": "native-document-pip-unavailable" + } + ] + }, + { + "directory": "refactor/.cache/canvas-dpip-installed-legacy-report", + "sha256": "8013645129c01e409c179a45406bab9bb2267a915ecde7dd02f23869dcbdd5f6", + "stats": { + "startTime": "2026-09-14T11:02:08.402Z", + "duration": 70739.78600000001, + "expected": 51, + "skipped": 0, + "unexpected": 0, + "flaky": 0 + }, + "result": { + "scope": "installed", + "exitCode": 0, + "signal": null, + "args": [ + "test", + "--config=playwright.installed.config.js", + "test/browser/canvas-dpip.spec.js", + "test/browser/canvas-subtitles.spec.js", + "test/browser/canvas-lifecycle.spec.js", + "test/browser/ambilight-proxy.spec.js", + "--workers=1" + ] + }, + "inputs": [ + { + "name": "artplayer", + "member": "package/dist/artplayer.legacy.js", + "sha256": "9bde35a6d64234cae15300e37b03204c075ef26126058f48bb3bf97d6e002449", + "archiveSha256": "60edd42a29e939632568851018cf0e230eb60fb54a2597c0877d496795bbcc6e" + }, + { + "name": "artplayer-plugin-chapter", + "member": "package/dist/artplayer-plugin-chapter.legacy.js", + "sha256": "dade919ed044cbf46ac16e505f538f86a86edccf177da2e14d79d111559d4bef", + "archiveSha256": "784c581410185908ea66aadaf06336970117e06f16dd4c66a618b196bfaba698" + }, + { + "name": "artplayer-plugin-ambilight", + "member": "package/dist/artplayer-plugin-ambilight.legacy.js", + "sha256": "c3ed1a54e44a0202811d70378cb19edf87b1ebd010d05fe96ebe64d0b4301d5d", + "archiveSha256": "7e88a0c9a643612f54e1674c1406c2bb7d18b1dd1f6fc05e7f49749301b310f3" + }, + { + "name": "artplayer-proxy-canvas", + "member": "package/dist/artplayer-proxy-canvas.legacy.js", + "sha256": "069092ba27b48b7a54affc9c3095b6032c063b1f61c15fcb3249130ea18b9326", + "archiveSha256": "074e51779de545d46f234e2052e05de63c0f1189c031b08c0b23702b6ceb5325" + }, + { + "name": "artplayer-plugin-document-pip", + "member": "package/dist/artplayer-plugin-document-pip.legacy.js", + "sha256": "67dd14de3cc16a04ae6c1938dcba61b1355f4a15e5fa06f62a41b18d14d4d356", + "archiveSha256": "5caf75a1de5af1b19a133be6d35e3573b25783ae376d15af3846cef392f17b7c" + } + ], + "browsers": { + "chromium": "153.0.8010.12", + "firefox": "155.0", + "webkit": "26.6" + }, + "nativeCases": 12, + "unavailableCases": 6, + "records": [ + { + "core": "published", + "ending": "close", + "browserName": "chromium", + "supported": true, + "canvas": { + "name": "candidate-installed", + "sha256": "069092ba27b48b7a54affc9c3095b6032c063b1f61c15fcb3249130ea18b9326" + }, + "dpip": { + "name": "candidate-installed", + "sha256": "67dd14de3cc16a04ae6c1938dcba61b1355f4a15e5fa06f62a41b18d14d4d356" + }, + "outcome": "native-window-playback-subtitles-and-cleanup", + "entered": { + "active": true, + "sameCanvas": true, + "playerAdopted": true, + "videoAdopted": true, + "trackAdopted": true, + "time": 0.306795, + "draws": 19 + }, + "final": { + "active": false, + "connected": false, + "paused": true, + "src": null, + "pending": 0, + "draws": 35, + "placeholders": 0, + "events": [ + true, + false + ], + "errors": [] + }, + "observed": { + "active": false, + "paused": true, + "time": 0, + "draws": 35, + "subtitle": "\n PiP caption\n ", + "errors": [], + "events": [ + true, + false + ] + } + }, + { + "core": "published", + "ending": "native-close", + "browserName": "chromium", + "supported": true, + "canvas": { + "name": "candidate-installed", + "sha256": "069092ba27b48b7a54affc9c3095b6032c063b1f61c15fcb3249130ea18b9326" + }, + "dpip": { + "name": "candidate-installed", + "sha256": "67dd14de3cc16a04ae6c1938dcba61b1355f4a15e5fa06f62a41b18d14d4d356" + }, + "outcome": "native-window-playback-subtitles-and-cleanup", + "entered": { + "active": true, + "sameCanvas": true, + "playerAdopted": true, + "videoAdopted": true, + "trackAdopted": true, + "time": 0.292589, + "draws": 18 + }, + "final": { + "active": false, + "connected": false, + "paused": true, + "src": null, + "pending": 0, + "draws": 35, + "placeholders": 0, + "events": [ + true, + false + ], + "errors": [] + }, + "observed": { + "active": false, + "paused": true, + "time": 0, + "draws": 35, + "subtitle": "\n PiP caption\n ", + "errors": [], + "events": [ + true, + false + ] + } + }, + { + "core": "published", + "ending": "destroy", + "browserName": "chromium", + "supported": true, + "canvas": { + "name": "candidate-installed", + "sha256": "069092ba27b48b7a54affc9c3095b6032c063b1f61c15fcb3249130ea18b9326" + }, + "dpip": { + "name": "candidate-installed", + "sha256": "67dd14de3cc16a04ae6c1938dcba61b1355f4a15e5fa06f62a41b18d14d4d356" + }, + "outcome": "native-window-playback-subtitles-and-cleanup", + "entered": { + "active": true, + "sameCanvas": true, + "playerAdopted": true, + "videoAdopted": true, + "trackAdopted": true, + "time": 0.292246, + "draws": 18 + }, + "final": { + "active": false, + "connected": false, + "paused": true, + "src": null, + "pending": 0, + "draws": 27, + "placeholders": 0, + "events": [ + true + ], + "errors": [] + }, + "observed": { + "active": false, + "paused": true, + "time": 0, + "draws": 27, + "subtitle": "\n PiP caption\n ", + "errors": [], + "events": [ + true + ] + } + }, + { + "core": "candidate", + "ending": "close", + "browserName": "chromium", + "supported": true, + "canvas": { + "name": "candidate-installed", + "sha256": "069092ba27b48b7a54affc9c3095b6032c063b1f61c15fcb3249130ea18b9326" + }, + "dpip": { + "name": "candidate-installed", + "sha256": "67dd14de3cc16a04ae6c1938dcba61b1355f4a15e5fa06f62a41b18d14d4d356" + }, + "outcome": "native-window-playback-subtitles-and-cleanup", + "entered": { + "active": true, + "sameCanvas": true, + "playerAdopted": true, + "videoAdopted": true, + "trackAdopted": true, + "time": 0.306536, + "draws": 19 + }, + "final": { + "active": false, + "connected": false, + "paused": true, + "src": null, + "pending": 0, + "draws": 36, + "placeholders": 0, + "events": [ + true, + false + ], + "errors": [] + }, + "observed": { + "active": false, + "paused": true, + "time": 0, + "draws": 36, + "subtitle": "\n PiP caption\n ", + "errors": [], + "events": [ + true, + false + ] + } + }, + { + "core": "candidate", + "ending": "native-close", + "browserName": "chromium", + "supported": true, + "canvas": { + "name": "candidate-installed", + "sha256": "069092ba27b48b7a54affc9c3095b6032c063b1f61c15fcb3249130ea18b9326" + }, + "dpip": { + "name": "candidate-installed", + "sha256": "67dd14de3cc16a04ae6c1938dcba61b1355f4a15e5fa06f62a41b18d14d4d356" + }, + "outcome": "native-window-playback-subtitles-and-cleanup", + "entered": { + "active": true, + "sameCanvas": true, + "playerAdopted": true, + "videoAdopted": true, + "trackAdopted": true, + "time": 0.290253, + "draws": 18 + }, + "final": { + "active": false, + "connected": false, + "paused": true, + "src": null, + "pending": 0, + "draws": 35, + "placeholders": 0, + "events": [ + true, + false + ], + "errors": [] + }, + "observed": { + "active": false, + "paused": true, + "time": 0, + "draws": 35, + "subtitle": "\n PiP caption\n ", + "errors": [], + "events": [ + true, + false + ] + } + }, + { + "core": "candidate", + "ending": "destroy", + "browserName": "chromium", + "supported": true, + "canvas": { + "name": "candidate-installed", + "sha256": "069092ba27b48b7a54affc9c3095b6032c063b1f61c15fcb3249130ea18b9326" + }, + "dpip": { + "name": "candidate-installed", + "sha256": "67dd14de3cc16a04ae6c1938dcba61b1355f4a15e5fa06f62a41b18d14d4d356" + }, + "outcome": "native-window-playback-subtitles-and-cleanup", + "entered": { + "active": true, + "sameCanvas": true, + "playerAdopted": true, + "videoAdopted": true, + "trackAdopted": true, + "time": 0.302723, + "draws": 19 + }, + "final": { + "active": false, + "connected": false, + "paused": true, + "src": null, + "pending": 0, + "draws": 28, + "placeholders": 0, + "events": [ + true + ], + "errors": [] + }, + "observed": { + "active": false, + "paused": true, + "time": 0, + "draws": 28, + "subtitle": "\n PiP caption\n ", + "errors": [], + "events": [ + true + ] + } + }, + { + "core": "published", + "ending": "close", + "browserName": "firefox", + "supported": true, + "canvas": { + "name": "candidate-installed", + "sha256": "069092ba27b48b7a54affc9c3095b6032c063b1f61c15fcb3249130ea18b9326" + }, + "dpip": { + "name": "candidate-installed", + "sha256": "67dd14de3cc16a04ae6c1938dcba61b1355f4a15e5fa06f62a41b18d14d4d356" + }, + "outcome": "native-window-playback-subtitles-and-cleanup", + "entered": { + "active": true, + "sameCanvas": true, + "playerAdopted": true, + "videoAdopted": true, + "trackAdopted": true, + "time": 0.458332, + "draws": 22 + }, + "final": { + "active": false, + "connected": false, + "paused": true, + "src": null, + "pending": 0, + "draws": 42, + "placeholders": 0, + "events": [ + true, + false + ], + "errors": [] + }, + "observed": { + "active": false, + "paused": true, + "time": 0, + "draws": 42, + "subtitle": "\n PiP caption\n ", + "errors": [], + "events": [ + true, + false + ] + } + }, + { + "core": "published", + "ending": "native-close", + "browserName": "firefox", + "supported": true, + "canvas": { + "name": "candidate-installed", + "sha256": "069092ba27b48b7a54affc9c3095b6032c063b1f61c15fcb3249130ea18b9326" + }, + "dpip": { + "name": "candidate-installed", + "sha256": "67dd14de3cc16a04ae6c1938dcba61b1355f4a15e5fa06f62a41b18d14d4d356" + }, + "outcome": "native-window-playback-subtitles-and-cleanup", + "entered": { + "active": true, + "sameCanvas": true, + "playerAdopted": true, + "videoAdopted": true, + "trackAdopted": true, + "time": 0.458332, + "draws": 23 + }, + "final": { + "active": false, + "connected": false, + "paused": true, + "src": null, + "pending": 0, + "draws": 43, + "placeholders": 0, + "events": [ + true, + false + ], + "errors": [] + }, + "observed": { + "active": false, + "paused": true, + "time": 0, + "draws": 43, + "subtitle": "\n PiP caption\n ", + "errors": [], + "events": [ + true, + false + ] + } + }, + { + "core": "published", + "ending": "destroy", + "browserName": "firefox", + "supported": true, + "canvas": { + "name": "candidate-installed", + "sha256": "069092ba27b48b7a54affc9c3095b6032c063b1f61c15fcb3249130ea18b9326" + }, + "dpip": { + "name": "candidate-installed", + "sha256": "67dd14de3cc16a04ae6c1938dcba61b1355f4a15e5fa06f62a41b18d14d4d356" + }, + "outcome": "native-window-playback-subtitles-and-cleanup", + "entered": { + "active": true, + "sameCanvas": true, + "playerAdopted": true, + "videoAdopted": true, + "trackAdopted": true, + "time": 0.499999, + "draws": 25 + }, + "final": { + "active": false, + "connected": false, + "paused": true, + "src": null, + "pending": 0, + "draws": 36, + "placeholders": 0, + "events": [ + true + ], + "errors": [] + }, + "observed": { + "active": false, + "paused": true, + "time": 0, + "draws": 36, + "subtitle": "\n PiP caption\n ", + "errors": [], + "events": [ + true + ] + } + }, + { + "core": "candidate", + "ending": "close", + "browserName": "firefox", + "supported": true, + "canvas": { + "name": "candidate-installed", + "sha256": "069092ba27b48b7a54affc9c3095b6032c063b1f61c15fcb3249130ea18b9326" + }, + "dpip": { + "name": "candidate-installed", + "sha256": "67dd14de3cc16a04ae6c1938dcba61b1355f4a15e5fa06f62a41b18d14d4d356" + }, + "outcome": "native-window-playback-subtitles-and-cleanup", + "entered": { + "active": true, + "sameCanvas": true, + "playerAdopted": true, + "videoAdopted": true, + "trackAdopted": true, + "time": 0.333332, + "draws": 16 + }, + "final": { + "active": false, + "connected": false, + "paused": true, + "src": null, + "pending": 0, + "draws": 35, + "placeholders": 0, + "events": [ + true, + false + ], + "errors": [] + }, + "observed": { + "active": false, + "paused": true, + "time": 0, + "draws": 35, + "subtitle": "\n PiP caption\n ", + "errors": [], + "events": [ + true, + false + ] + } + }, + { + "core": "candidate", + "ending": "native-close", + "browserName": "firefox", + "supported": true, + "canvas": { + "name": "candidate-installed", + "sha256": "069092ba27b48b7a54affc9c3095b6032c063b1f61c15fcb3249130ea18b9326" + }, + "dpip": { + "name": "candidate-installed", + "sha256": "67dd14de3cc16a04ae6c1938dcba61b1355f4a15e5fa06f62a41b18d14d4d356" + }, + "outcome": "native-window-playback-subtitles-and-cleanup", + "entered": { + "active": true, + "sameCanvas": true, + "playerAdopted": true, + "videoAdopted": true, + "trackAdopted": true, + "time": 0.458332, + "draws": 24 + }, + "final": { + "active": false, + "connected": false, + "paused": true, + "src": null, + "pending": 0, + "draws": 59, + "placeholders": 0, + "events": [ + true, + false + ], + "errors": [] + }, + "observed": { + "active": false, + "paused": true, + "time": 0, + "draws": 59, + "subtitle": "\n PiP caption\n ", + "errors": [], + "events": [ + true, + false + ] + } + }, + { + "core": "candidate", + "ending": "destroy", + "browserName": "firefox", + "supported": true, + "canvas": { + "name": "candidate-installed", + "sha256": "069092ba27b48b7a54affc9c3095b6032c063b1f61c15fcb3249130ea18b9326" + }, + "dpip": { + "name": "candidate-installed", + "sha256": "67dd14de3cc16a04ae6c1938dcba61b1355f4a15e5fa06f62a41b18d14d4d356" + }, + "outcome": "native-window-playback-subtitles-and-cleanup", + "entered": { + "active": true, + "sameCanvas": true, + "playerAdopted": true, + "videoAdopted": true, + "trackAdopted": true, + "time": 0.374999, + "draws": 16 + }, + "final": { + "active": false, + "connected": false, + "paused": true, + "src": null, + "pending": 0, + "draws": 27, + "placeholders": 0, + "events": [ + true + ], + "errors": [] + }, + "observed": { + "active": false, + "paused": true, + "time": 0, + "draws": 27, + "subtitle": "\n PiP caption\n ", + "errors": [], + "events": [ + true + ] + } + }, + { + "core": "published", + "ending": "close", + "browserName": "webkit", + "supported": false, + "canvas": { + "name": "candidate-installed", + "sha256": "069092ba27b48b7a54affc9c3095b6032c063b1f61c15fcb3249130ea18b9326" + }, + "dpip": { + "name": "candidate-installed", + "sha256": "67dd14de3cc16a04ae6c1938dcba61b1355f4a15e5fa06f62a41b18d14d4d356" + }, + "outcome": "native-document-pip-unavailable" + }, + { + "core": "published", + "ending": "native-close", + "browserName": "webkit", + "supported": false, + "canvas": { + "name": "candidate-installed", + "sha256": "069092ba27b48b7a54affc9c3095b6032c063b1f61c15fcb3249130ea18b9326" + }, + "dpip": { + "name": "candidate-installed", + "sha256": "67dd14de3cc16a04ae6c1938dcba61b1355f4a15e5fa06f62a41b18d14d4d356" + }, + "outcome": "native-document-pip-unavailable" + }, + { + "core": "published", + "ending": "destroy", + "browserName": "webkit", + "supported": false, + "canvas": { + "name": "candidate-installed", + "sha256": "069092ba27b48b7a54affc9c3095b6032c063b1f61c15fcb3249130ea18b9326" + }, + "dpip": { + "name": "candidate-installed", + "sha256": "67dd14de3cc16a04ae6c1938dcba61b1355f4a15e5fa06f62a41b18d14d4d356" + }, + "outcome": "native-document-pip-unavailable" + }, + { + "core": "candidate", + "ending": "close", + "browserName": "webkit", + "supported": false, + "canvas": { + "name": "candidate-installed", + "sha256": "069092ba27b48b7a54affc9c3095b6032c063b1f61c15fcb3249130ea18b9326" + }, + "dpip": { + "name": "candidate-installed", + "sha256": "67dd14de3cc16a04ae6c1938dcba61b1355f4a15e5fa06f62a41b18d14d4d356" + }, + "outcome": "native-document-pip-unavailable" + }, + { + "core": "candidate", + "ending": "native-close", + "browserName": "webkit", + "supported": false, + "canvas": { + "name": "candidate-installed", + "sha256": "069092ba27b48b7a54affc9c3095b6032c063b1f61c15fcb3249130ea18b9326" + }, + "dpip": { + "name": "candidate-installed", + "sha256": "67dd14de3cc16a04ae6c1938dcba61b1355f4a15e5fa06f62a41b18d14d4d356" + }, + "outcome": "native-document-pip-unavailable" + }, + { + "core": "candidate", + "ending": "destroy", + "browserName": "webkit", + "supported": false, + "canvas": { + "name": "candidate-installed", + "sha256": "069092ba27b48b7a54affc9c3095b6032c063b1f61c15fcb3249130ea18b9326" + }, + "dpip": { + "name": "candidate-installed", + "sha256": "67dd14de3cc16a04ae6c1938dcba61b1355f4a15e5fa06f62a41b18d14d4d356" + }, + "outcome": "native-document-pip-unavailable" + } + ] + } + ], + "package": { + "directory": "refactor/.cache/packages/run-z8nBqx", + "sha256": "a762ae5ad3211b6b371935891651ac055e5afedfaab086f95a7a1782d6681f22", + "packages": [ + { + "name": "artplayer", + "sha256": "60edd42a29e939632568851018cf0e230eb60fb54a2597c0877d496795bbcc6e" + }, + { + "name": "artplayer-plugin-chapter", + "sha256": "784c581410185908ea66aadaf06336970117e06f16dd4c66a618b196bfaba698" + }, + { + "name": "artplayer-plugin-ambilight", + "sha256": "7e88a0c9a643612f54e1674c1406c2bb7d18b1dd1f6fc05e7f49749301b310f3" + }, + { + "name": "artplayer-proxy-canvas", + "sha256": "074e51779de545d46f234e2052e05de63c0f1189c031b08c0b23702b6ceb5325" + }, + { + "name": "artplayer-plugin-document-pip", + "sha256": "5caf75a1de5af1b19a133be6d35e3573b25783ae376d15af3846cef392f17b7c" + } + ], + "consumerScope": "36 runtime and 5 legacy/8 precise type groups cover core/chapter; extra packages have build/pack/install/hash checks, not full consumer acceptance." + }, + "loader": { + "original": { + "log": "refactor/.cache/canvas-dpip-loader-red.log", + "sha256": "281b6ec03bcc81253ca3464c0f8762292f58e1ede3d0496a4370c56f80ddc306", + "tests": 4, + "pass": 3, + "fail": 1, + "reason": "dpipCandidate silently built source despite an explicit absent installed map." + }, + "fixed": { + "log": "refactor/.cache/canvas-dpip-engineering.log", + "sha256": "ccdd04e50a5d3b55cf7800825084f82ac63fe8fb200daabfcb158fbd4ef52731", + "tests": 46, + "pass": 46, + "scope": "Package loader/file contracts, runner and CI workflow; includes missing DPIP installation mutation." + } + }, + "checks": { + "strictToolsTypes": true, + "targetedLint": true, + "rootLint": "0 errors, one existing generated declaration warning", + "actionlint": true, + "strictToolchain": true + }, + "limitations": [ + "Windows desktop engines only, no physical Safari or mobile devices.", + "WebKit lacks native Document PiP here; its six returns are capability records, not native playback passes.", + "No remote CI execution or cancellation verification.", + "No full ecosystem installed suite, type acceptance for Document PiP or full publication audit in this checkpoint.", + "No production runtime change, dependency addition, push, deployment or npm publication." + ] +} diff --git a/refactor/changes/2026-09-14-PKG-CANVAS-05-native-pip.md b/refactor/changes/2026-09-14-PKG-CANVAS-05-native-pip.md new file mode 100644 index 000000000..456aa7002 --- /dev/null +++ b/refactor/changes/2026-09-14-PKG-CANVAS-05-native-pip.md @@ -0,0 +1,42 @@ +# PKG-CANVAS-05 Canvas + 原生 Document PiP 检查点 + +进行中。新增 canvas-dpip.spec.js 验证实际 requestWindow、Canvas/底层 video/字幕 +轨道跨文档归属、持续绘制与时间推进、暂停寻址字幕、插件关闭/原生关闭/销毁。 +使用真实本地媒体和 VTT,RAF 观察器转发原生调度,不替换 PiP API 或媒体状态。 +不支持的引擎只记录能力缺口,不计作原生窗口播放通过。 + +首轮为源码组合,18 项成功退出包含 Chromium/Firefox 12 项真实窗口播放与 +WebKit 6 项 API 不可用记录。初版明确拒绝安装 map,避免把源码 Document PiP +混称为已安装包;报告保留在 canvas-dpip-source-report。 + +## 安装与 CI 范围扩展 + +package-check 通过已有 dpip-contract 核对发布文件,将 Document PiP 加入显式 +可选包注册表。现在五包经过源码快照、三格式构建、pack、仓库外离线安装和冻结 +重装,再校验全部文件摘要。core/chapter 的36运行时、5旧类型+8精确类型模式 +仍是原来的两包范围;不把它们当作 Document PiP 的完整消费者验收。 + +dpipCandidate 现在优先核验显式安装 map,缺文件/缺包/旧输入报错,不再回退到 +源码;冻结 workspace 与独立 ARTPLAYER_DPIP_ARTIFACT 覆盖也不能混用。这个 +错误已有旧红新绿:此前不存在的安装map被静默忽略,Node测试3通过1失败; +修正后包/runner/CI检查共46通过,包含缺 Document PiP 安装步骤的工作流反例。 + +installed scope 和 CI 准备步骤增加第五包及 canvas-dpip.spec.js,仍保留完整 +source suite、不吞失败、两份独立报告及原消费者执行顺序。main/legacy 的实际 +浏览器结果与输入摘要见[证据](../baselines/canvas-dpip-validation.json):每种格式 +均为51项成功退出,其中12项原生PiP、6项API不可用、33项Canvas字幕/生命周期与 +Ambilight组合。原生窗口覆盖六种 core/ending 组合各在 Chromium/Firefox 执行, +不把 WebKit 的能力分支计为原生播放。这里不代表远端 CI 已运行。 +严格工具 TS、专项/根 lint(0错误/1既有warning)、actionlint 和固定工具链通过。 +未新增依赖,也没有更改浏览器重试、超时或媒体断言;新测试初版样式诊断已修复。 + +## 维护与限制 + +Canvas、Document PiP 的架构说明和测试/CI入口已同步。本次没有生产运行时修改, +不修改已有全局源码/安装性能门槛。重跑时先提供含五包的安装map,再运行 README +所列命令;source 对照仍独立存在。报告中的 API 不可用分支不是设备支持通过。 + +PKG-CANVAS-05 仍 doing:实际Safari/移动设备、后台节流和完整生态组合没有闭环。 +DPIP-05/06、Canvas-06 及全量远端 CI/发布任务仍开放。作为本任务检查点单独本地 +提交,提交审计通过后接续;没有推送、部署或npm发布。回退需一并还原五包CI配置、 +加载器、测试与文档,不能只删除安装准备步骤而保留依赖它的新scope。 diff --git a/refactor/ci-setup.md b/refactor/ci-setup.md index 96dbc987b..8b5858462 100644 --- a/refactor/ci-setup.md +++ b/refactor/ci-setup.md @@ -2,7 +2,7 @@ 浏览器CI现在分别执行 `yarn test:browser:source` 和 `yarn test:browser:installed`。前者清除继承的安装map并保留全部spec;后者要求已 -核验的core/chapter/Ambilight/Canvas安装,执行明确列出的installed子集。两步不 +核验的core/chapter/Ambilight/Canvas/Document PiP安装,执行明确列出的installed子集。两步不 吞失败,普通失败后仍保存另一范围的证据,分别上传browser-source/browser-installed。 入口与报告规则见[维护说明](../scripts/browser-validation/README.md)。source全量 耗时/60分钟预算、其余插件安装矩阵与远端运行仍待CI-01/CI-04,不计为已通过。 diff --git a/refactor/plan.md b/refactor/plan.md index 9d3834a4e..da4328357 100644 --- a/refactor/plan.md +++ b/refactor/plan.md @@ -4,7 +4,7 @@ 基线:`40fcda6a37d0049d42e49c1e64e70d4fd9ba5f7f`。总任务 259 项,范围 22 个包及工作区/示例。 -状态:todo 50 / doing 19 / blocked 0 / done 190 / deferred 0。风险 L/M/H 表示兼容风险,不表示工期。 +状态:todo 49 / doing 20 / blocked 0 / done 190 / deferred 0。风险 L/M/H 表示兼容风险,不表示工期。 前置依赖是启动条件;验收是完成条件。任务可以继续拆分,但不能复用或悄悄删除旧 ID。 @@ -347,7 +347,7 @@ | PKG-CANVAS-02 | artplayer-proxy-canvas
建立特有行为与错误测试 | PKG-CANVAS-01, ENG-03, ENG-05 | ready/loadeddata/canplay、play/seek、回调、resize 与销毁 | 旧版本行为可重跑,成功/失败/切源/销毁有必要断言 | H | done | | PKG-CANVAS-03 | artplayer-proxy-canvas
整理内部职责与资源 | PKG-CANVAS-02, CORE-06, CORE-11, CORE-16 | video adapter/原 canvas 方法/RAF 绘制/事件订阅分离 | 结构变化和缺陷修复分开记录;原 API/事件/资源生命周期通过 | H | done | | PKG-CANVAS-04 | artplayer-proxy-canvas
迁移自有源码和公开类型 | PKG-CANVAS-03, ENG-04, ENG-06, CORE-07 | canvas 与媒体能力的精确组合类型,保持 Result | 严格类型检查、旧消费样例通过;声明路径/导出和同步异步兼容 | H | done | -| PKG-CANVAS-05 | artplayer-proxy-canvas
验证新旧核心和组合 | PKG-CANVAS-04, CORE-22, PKG-DPIP-04, PKG-FACTORY-01, PKG-CANVAS-SUBTITLE-01 | 真实 video/canvas 绘制、字幕和 document PiP 恢复 | 最终核心与原支持范围核心分别通过;设备/SDK 缺证据不能标完成 | H | todo | +| PKG-CANVAS-05 | artplayer-proxy-canvas
验证新旧核心和组合 | PKG-CANVAS-04, CORE-22, PKG-DPIP-04, PKG-FACTORY-01, PKG-CANVAS-SUBTITLE-01 | 真实 video/canvas 绘制、字幕和 document PiP 恢复 | 最终核心与原支持范围核心分别通过;设备/SDK 缺证据不能标完成 | H | doing | | PKG-CANVAS-06 | artplayer-proxy-canvas
验证分发并同步文档 | PKG-CANVAS-05, ENG-07 | canvas.js、三种产物、调用兼容与资源证据 | tarball 入口/资源、类型、8082 demo 和 README 一致,有回退记录 | H | todo | | PKG-CANVAS-SUBTITLE-01 | artplayer-proxy-canvas
修复 Canvas 代理原生字幕轨道挂载 | PKG-CANVAS-04 | 初始字幕能力、原生轨道挂载与资源生命周期适配及新旧核心回归 | 复现旧字幕 cues 为空;新旧核心加载、寻址、切换、销毁通过;普通 Canvas 节点行为和分发入口保持兼容 | M | done | @@ -611,6 +611,7 @@ - PKG-CANVAS-02: [记录](changes/2026-09-12-PKG-CANVAS-02-tests.md) [记录](baselines/canvas-behavior-validation.json) [记录](canvas-validation.md) - PKG-CANVAS-03: [记录](changes/2026-09-12-PKG-CANVAS-03-lifecycle.md) [记录](baselines/canvas-lifecycle-validation.json) [记录](canvas-validation.md) - PKG-CANVAS-04: [记录](changes/2026-09-12-PKG-CANVAS-04-types.md) [记录](baselines/canvas-types-validation.json) [记录](canvas-validation.md) +- PKG-CANVAS-05: [记录](changes/2026-09-14-PKG-CANVAS-05-native-pip.md) [记录](baselines/canvas-dpip-validation.json) [记录](baselines/canvas-subtitles-validation.json) [记录](baselines/ambilight-installed-validation.json) - PKG-MB-01: [记录](baselines/mb-release.json) [记录](baselines/mb-surface.json) [记录](baselines/mb-contract.md) [记录](baselines/mb-contract-validation.json) [记录](mb-validation.md) [记录](changes/2026-09-12-PKG-MB-01-contract.md) - PKG-MB-02: [记录](changes/2026-09-12-PKG-MB-02-checkpoint.md) [记录](baselines/mb-media-checkpoint.json) [记录](mb-validation.md) [记录](changes/2026-09-12-PKG-MB-02-baseline.md) [记录](baselines/mb-behavior-validation.json) - PKG-MB-03: [记录](changes/2026-09-12-PKG-MB-03-input.md) [记录](baselines/mb-input-validation.json) diff --git a/refactor/progress.md b/refactor/progress.md index 61167c732..e04064c7d 100644 --- a/refactor/progress.md +++ b/refactor/progress.md @@ -1,5 +1,20 @@ # 进度与证据 +## PKG-CANVAS-05 原生 PiP 与五包安装检查点(仍 doing) + +源码首轮18项:Chromium/Firefox实际原生窗口12通过,WebKit仅6项API不可用记录。 +Canvas、底层video与track一起移入窗口,播放/绘制继续,暂停seek字幕正确;插件 +关闭、原生关闭和销毁均恢复/释放节点、窗口、占位与RAF。不是iframe模拟。 +进一步把Document PiP加入已核验安装包注册与CI:五包离线安装/冻结重装和指纹检查 +通过,main/legacy各51项成功退出(各12原生PiP+6不可用+33其他组合)。 +修复dpip加载器忽略安装map回退源码的问题:旧Node测试3通过1失败,修正后工程/ +工作流46通过;DPIP-INSTALLED-PROVENANCE-01按该证据关闭。缺map/缺包/陈旧输入及 +冲突覆盖均不允许源码替代。严格工具TS、lint(1既有warning)、actionlint/工具链通过。 +见[检查点](changes/2026-09-14-PKG-CANVAS-05-native-pip.md)和 +[证据](baselines/canvas-dpip-validation.json)。259项:190 done、20 doing、49 todo。 +Canvas真机/后台节流、DPIP完整消费者/设备、完整远端CI和发布仍开放;没有生产 +运行时修改、依赖新增、push、部署或发布。该检查点独立提交后审计再继续下一步。 + ## PKG-CANVAS-SUBTITLE-01 原生字幕轨道修复完成 新旧核心三引擎复现 Canvas 字幕为空。先前说明需修正:初始失败中的 $track diff --git a/refactor/risk-table.md b/refactor/risk-table.md index de4824069..479d4512f 100644 --- a/refactor/risk-table.md +++ b/refactor/risk-table.md @@ -273,3 +273,4 @@ | VCONSOLE-LIFECYCLE-01 | resolved / 已复现 | vConsole delayed log, panel and scroller work outlives its owner | SITE-07, SITE-05, SITE-VCONSOLE-01 | | CHAPTER-FULLSCREEN-GEOMETRY-01 | resolved / 已复现 | Chromium 原生全屏后章节 hover 标题曾出现后又隐藏 | PKG-CHAPTER-05, PKG-CHAPTER-HOVER-01 | | CANVAS-SUBTITLE-01 | resolved / 已复现 | Canvas底层video缺初始字幕轨道且普通Canvas挂载不能加载原生字幕 | PKG-CANVAS-SUBTITLE-01, PKG-CANVAS-05 | +| DPIP-INSTALLED-PROVENANCE-01 | resolved / 已复现 | Document PiP测试加载器忽略显式安装map并回退到源码 | PKG-CANVAS-05, CI-01 | diff --git a/refactor/risks.json b/refactor/risks.json index 2a7e61f70..2c0e4ed4c 100644 --- a/refactor/risks.json +++ b/refactor/risks.json @@ -6241,6 +6241,30 @@ "test/browser/canvas-subtitles.spec.js" ], "resolutionRationale": "Initial candidate and routing-only controls each fail all six subtitle cases. Bootstrap + insertion adaptation passes; npm 1.1.0 independently fails all six. Expanded native source tests pass 24; installed main and legacy pass 33 each; final six pass after adding baseline selection. Captions also visibly render in the local demo. This fixes subtitle capability/insertion only; broader Canvas integration remains open." + }, + { + "id": "DPIP-INSTALLED-PROVENANCE-01", + "title": "Document PiP测试加载器忽略显式安装map并回退到源码", + "confirmation": "reproduced", + "status": "resolved", + "owners": [ + "PKG-CANVAS-05", + "CI-01" + ], + "evidence": [ + "test/package-check.test.js", + "refactor/baselines/canvas-dpip-validation.json" + ], + "compatibleResolution": "使用已安装文件及source/build指纹,缺输入拒绝执行;保留独立source模式。", + "closureCriteria": "缺安装map旧红新绿,实际五包main/legacy原生组合与工作流配置验证。", + "workspaceState": "Explicit map takes precedence; DPIP frozen/artifact overrides conflict instead of overriding installed provenance. Current CI prepares five packages before browser scopes.", + "resolutionEvidence": [ + "refactor/changes/2026-09-14-PKG-CANVAS-05-native-pip.md", + "refactor/baselines/canvas-dpip-validation.json", + "test/helpers/dpip.js", + "refactor/scripts/ci-workflow.test.mjs" + ], + "resolutionRationale": "Old loader test reports missing expected rejection; updated loader rejects missing/stale maps and conflicting modes. Five-package isolated build/pack/install supplies main and legacy native PiP tests with package hashes. Local workflow mutation protects the required DPIP preparation; remote runs remain unverified." } ] } diff --git a/refactor/scripts/ci-workflow.mjs b/refactor/scripts/ci-workflow.mjs index abfd57739..2713103a0 100644 --- a/refactor/scripts/ci-workflow.mjs +++ b/refactor/scripts/ci-workflow.mjs @@ -90,10 +90,10 @@ export function validateCIWorkflow(source) { const vueIndex = browser.steps.findIndex(step => step.run === 'yarn test:vue-consumer 2>&1 | tee refactor/.cache/ci/vue-consumer.log') assert(vueIndex > consumerIndex && !Object.hasOwn(browser.steps[vueIndex], 'if'), 'Run Vue installed consumers after restoring canonical Node') assert(browser.steps.some(step => step.uses?.startsWith('actions/upload-artifact@') && step.if === 'always()' && step.with.path.split('\n').includes('refactor/.cache/vue-consumer-*/')), 'Retain Vue consumer failure evidence') - const extraIndex = browser.steps.findIndex(step => step.run?.startsWith('yarn test:package --include=artplayer-plugin-ambilight,artplayer-proxy-canvas ')) + const extraIndex = browser.steps.findIndex(step => step.run?.startsWith('yarn test:package --include=artplayer-plugin-ambilight,artplayer-proxy-canvas,artplayer-plugin-document-pip ')) const sourceEngineIndex = browser.steps.findIndex(step => step.run === 'yarn test:browser:source 2>&1 | tee refactor/.cache/ci/browser-source.log') const engineIndex = browser.steps.findIndex(step => step.run === 'yarn test:browser:installed 2>&1 | tee refactor/.cache/ci/browser-installed.log') - assert(extraIndex > Math.max(consumerIndex, reactIndex, vueIndex) && extraIndex < engineIndex, 'Prepare installed Ambilight/Canvas after consumer probes and before browser checks') + assert(extraIndex > Math.max(consumerIndex, reactIndex, vueIndex) && extraIndex < engineIndex, 'Prepare installed Ambilight/Canvas/Document PiP after consumer probes and before browser checks') assert(!Object.hasOwn(browser.steps[extraIndex], 'if') && browser.steps[extraIndex].run.includes('GITHUB_ENV') && browser.steps[extraIndex].run.includes('ARTPLAYER_BROWSER_ARTIFACTS='), 'Always select the additional installed browser artifact map') assert(sourceEngineIndex > extraIndex && sourceEngineIndex < engineIndex, 'Retain complete source checks before the additional installed suite') for (const index of [sourceEngineIndex, engineIndex]) { diff --git a/refactor/scripts/ci-workflow.test.mjs b/refactor/scripts/ci-workflow.test.mjs index 0a7682dbb..c7be6233f 100644 --- a/refactor/scripts/ci-workflow.test.mjs +++ b/refactor/scripts/ci-workflow.test.mjs @@ -34,6 +34,10 @@ for (const [name, mutate] of [ ['wrong consumer Node version', w => w.jobs['browser-smoke'].steps.find(s => s.id === 'consumer-node-20').with['node-version'] = '24'], ['skipped installed consumer', w => w.jobs['browser-smoke'].steps.find(s => s.run?.includes('--expected-node 22.12.0')).if = 'false'], ['missing browser plugin installation', w => w.jobs['browser-smoke'].steps.find(s => s.run?.startsWith('yarn test:package --include=')).run = 'echo omitted'], + ['missing Document PiP browser installation', (w) => { + const step = w.jobs['browser-smoke'].steps.find(s => s.run?.startsWith('yarn test:package --include=')) + step.run = step.run.replace(',artplayer-plugin-document-pip', '') + }], ['skipped browser plugin installation', w => w.jobs['browser-smoke'].steps.find(s => s.run?.startsWith('yarn test:package --include=')).if = 'false'], ['missing source browser checks', w => w.jobs['browser-smoke'].steps.find(s => s.run?.startsWith('yarn test:browser:source ')).run = 'echo omitted'], ['filtered source browser checks', w => w.jobs['browser-smoke'].steps.find(s => s.run?.startsWith('yarn test:browser:source ')).run += ' --grep subset'], diff --git a/refactor/tasks.json b/refactor/tasks.json index 0f66e9744..03adaae29 100644 --- a/refactor/tasks.json +++ b/refactor/tasks.json @@ -3638,11 +3638,16 @@ "PKG-FACTORY-01", "PKG-CANVAS-SUBTITLE-01" ], - "status": "todo", + "status": "doing", "risk": "H", "deliverable": "真实 video/canvas 绘制、字幕和 document PiP 恢复", "acceptance": "最终核心与原支持范围核心分别通过;设备/SDK 缺证据不能标完成", - "evidence": [] + "evidence": [ + "changes/2026-09-14-PKG-CANVAS-05-native-pip.md", + "baselines/canvas-dpip-validation.json", + "baselines/canvas-subtitles-validation.json", + "baselines/ambilight-installed-validation.json" + ] }, { "id": "PKG-CANVAS-06", diff --git a/scripts/browser-validation/README.md b/scripts/browser-validation/README.md index dc4e293dd..4161e8b69 100644 --- a/scripts/browser-validation/README.md +++ b/scripts/browser-validation/README.md @@ -7,8 +7,8 @@ mislabel source/explicit-vendor SDK tests. The caller's environment is untouched Other deliberate fixture switches retain their existing meaning. `yarn test:browser:installed` requires ARTPLAYER_BROWSER_ARTIFACTS from -`yarn test:package --include=artplayer-plugin-ambilight,artplayer-proxy-canvas`. -Both launcher and config validate the selected four packages' installed hashes +`yarn test:package --include=artplayer-plugin-ambilight,artplayer-proxy-canvas,artplayer-plugin-document-pip`. +Both launcher and config validate the selected five packages' installed hashes and source/build freshness. `scope.ts` lists the currently supported installed test files. This is a growing installed subset, not full ecosystem acceptance. Source checks remain complete when the installed list grows. diff --git a/scripts/browser-validation/scope.ts b/scripts/browser-validation/scope.ts index ca66ce1b9..459b2d601 100644 --- a/scripts/browser-validation/scope.ts +++ b/scripts/browser-validation/scope.ts @@ -2,7 +2,7 @@ import type { PlaywrightTestConfig } from '@playwright/test' import assert from 'node:assert/strict' export type BrowserScope = 'source' | 'installed' -export const installedPackages = ['artplayer', 'artplayer-plugin-chapter', 'artplayer-plugin-ambilight', 'artplayer-proxy-canvas'] +export const installedPackages = ['artplayer', 'artplayer-plugin-chapter', 'artplayer-plugin-ambilight', 'artplayer-proxy-canvas', 'artplayer-plugin-document-pip'] export const installedTests = [ 'playback.spec.js', 'lifecycle.spec.js', @@ -14,6 +14,7 @@ export const installedTests = [ 'ambilight-proxy.spec.js', 'canvas-lifecycle.spec.js', 'canvas-subtitles.spec.js', + 'canvas-dpip.spec.js', ] export function browserInvocation(scope: string, args: string[], environment: NodeJS.ProcessEnv) { diff --git a/scripts/package-check.mjs b/scripts/package-check.mjs index 55ca9851b..bb1da14c3 100644 --- a/scripts/package-check.mjs +++ b/scripts/package-check.mjs @@ -75,6 +75,7 @@ export async function checkPackages({ release = false, include = [] } = {}) { const supported = { 'artplayer-plugin-ambilight': async () => (await import('../refactor/scripts/ambilight-contract.mjs')).verifyAmbilightContract(), 'artplayer-proxy-canvas': async () => (await import('../refactor/scripts/canvas-contract.mjs')).verifyCanvasContract(), + 'artplayer-plugin-document-pip': async () => (await import('../refactor/scripts/dpip-contract.mjs')).verifyDpipContract(), } assert(new Set(include).size === include.length, 'Duplicate additional package') const historicalFiles = new Map() diff --git a/test/browser/README.md b/test/browser/README.md index 1280dd40f..37c0bc793 100644 --- a/test/browser/README.md +++ b/test/browser/README.md @@ -1,5 +1,11 @@ # Browser regression entry +`canvas-dpip.spec.js` tests actual native windows with the Canvas video and subtitle +track, continued playback, paused cue seeking and close/native-close/destroy. +Chromium/Firefox native outcomes and WebKit API-unavailable outcomes must be +counted separately. The installed scope requires five verified packages, including +Document PiP; `dpipCandidate` cannot substitute a source build for a missing map. + `canvas-subtitles.spec.js` covers native track bootstrap/load/seek/replacement and cleanup on old/new cores, including ordinary Canvas child operations. It is part of both browser scopes. `ARTPLAYER_CANVAS_SUBTITLE_BASELINE=1.1.0` runs the exact diff --git a/test/browser/canvas-dpip.spec.js b/test/browser/canvas-dpip.spec.js new file mode 100644 index 000000000..d25b207c6 --- /dev/null +++ b/test/browser/canvas-dpip.spec.js @@ -0,0 +1,126 @@ +import assert from 'node:assert/strict' +import process from 'node:process' +import { hash } from '../../refactor/scripts/releases.mjs' +import { canvasCandidate } from '../helpers/canvas.js' +import { dpipCandidate } from '../helpers/dpip.js' +import { expect, test } from './fixtures.js' + +const canvas = await canvasCandidate() +const dpip = await dpipCandidate() +if (process.env.ARTPLAYER_BROWSER_ARTIFACTS) + assert(canvas.name === 'candidate-installed' && dpip.name === 'candidate-installed', 'Installed Canvas PiP requires both verified packages') + +for (const core of ['published', 'candidate']) { + for (const ending of ['close', 'native-close', 'destroy']) { + test(`${core}: canvas and subtitles survive native Document PiP ${ending}`, async ({ page, context, browserName }, testInfo) => { + await page.route('**/canvas-pip.vtt', route => route.fulfill({ contentType: 'text/vtt', body: 'WEBVTT\n\n00:00:00.000 --> 00:00:03.000\nOpening caption\n\n00:00:03.000 --> 00:00:08.000\nPiP caption\n' })) + await page.goto(`/test/player.html?core=${core}`) + const supported = await page.evaluate(() => typeof window.documentPictureInPicture?.requestWindow === 'function') + const evidence = { core, ending, browserName, supported, canvas: { name: canvas.name, sha256: hash(canvas.source) }, dpip: { name: dpip.name, sha256: hash(dpip.code) }, outcome: 'incomplete' } + if (!supported) { + evidence.outcome = 'native-document-pip-unavailable' + await testInfo.attach('canvas-dpip', { contentType: 'application/json', body: JSON.stringify(evidence) }) + return + } + await page.addScriptTag({ content: `(() => { + const pending = new Set(); + const requestAnimationFrame = callback => { const id = window.requestAnimationFrame(time => { pending.delete(id); callback(time); }); pending.add(id); return id; }; + const cancelAnimationFrame = id => { pending.delete(id); window.cancelAnimationFrame(id); }; + const module = { exports: {} }; const exports = module.exports; + ${canvas.source}; window.canvasFactory = module.exports.default || module.exports; + window.canvasPending = pending; + })();` }) + await page.addScriptTag({ content: `(() => { const module = { exports: {} }; const exports = module.exports; ${dpip.code}; window.dpipFactory = module.exports.default || module.exports; })();` }) + await page.evaluate(() => { + window.canvasDraws = 0 + window.canvasErrors = [] + window.canvasPipEvents = [] + window.art = new window.Artplayer({ + container: '.player', + url: '/test/pattern.mp4', + muted: true, + loop: true, + subtitle: { url: '/canvas-pip.vtt' }, + proxy: window.canvasFactory((context, video) => { + window.canvasDraws++ + window.canvasContext = context + window.canvasVideo = video + }), + plugins: [window.dpipFactory({ fallbackToVideoPiP: false })], + }) + window.originalCanvas = window.art.video + window.originalPlayer = window.art.template.$player + window.originalParent = window.originalPlayer.parentNode + window.pip = window.art.plugins.artplayerPluginDocumentPip + window.art.on('document-pip', active => window.canvasPipEvents.push(active)) + window.art.on('artplayerProxyCanvas:error', error => window.canvasErrors.push(String(error))) + document.querySelector('#play').onclick = () => window.art.play() + const open = document.createElement('button') + open.id = 'canvas-open-pip' + open.textContent = 'Open Canvas PiP' + open.onclick = () => { + window.openingCanvasPip = window.pip.open() + } + document.body.append(open) + }) + try { + await expect.poll(() => page.evaluate(() => window.art.isReady && window.art.subtitle.cues.length === 2)).toBe(true) + await page.click('#play') + await expect.poll(() => page.evaluate(() => window.canvasDraws)).toBeGreaterThan(1) + const popupPromise = context.waitForEvent('page', { timeout: 8000 }) + await page.click('#canvas-open-pip') + const popup = await popupPromise + await page.evaluate(() => window.openingCanvasPip) + await expect(popup.locator('canvas.art-video')).toBeVisible() + evidence.entered = await page.evaluate(() => ({ + active: window.pip.isActive, + sameCanvas: window.art.video === window.originalCanvas, + playerAdopted: window.originalPlayer.ownerDocument === window.documentPictureInPicture.window.document, + videoAdopted: window.canvasVideo.ownerDocument === window.originalPlayer.ownerDocument, + trackAdopted: window.art.template.$track.ownerDocument === window.originalPlayer.ownerDocument, + time: window.art.currentTime, + draws: window.canvasDraws, + })) + expect(evidence.entered.active && evidence.entered.sameCanvas && evidence.entered.playerAdopted && evidence.entered.videoAdopted && evidence.entered.trackAdopted).toBe(true) + await expect.poll(() => page.evaluate(() => window.canvasDraws)).toBeGreaterThan(evidence.entered.draws + 2) + await expect.poll(() => page.evaluate(() => window.art.currentTime)).toBeGreaterThan(evidence.entered.time + 0.1) + await page.evaluate(() => { + window.art.pause() + window.art.currentTime = 4 + }) + await expect(popup.locator('.art-subtitle')).toHaveText('PiP caption') + await expect.poll(() => page.evaluate(() => window.canvasPending.size)).toBe(0) + expect(await page.evaluate(() => window.canvasContext.getImageData(0, 0, 1, 1).data[3])).toBe(255) + if (ending === 'destroy') { + await page.evaluate(() => window.art.destroy(false)) + } + else { + if (ending === 'native-close') + await page.evaluate(() => window.documentPictureInPicture.window.close()) + else await page.evaluate(() => window.pip.close()) + await expect.poll(() => page.evaluate(() => window.pip.isActive)).toBe(false) + expect(await page.evaluate(() => window.originalPlayer.parentNode === window.originalParent && window.canvasVideo.ownerDocument === document && window.originalCanvas === window.art.video)).toBe(true) + await expect(page.locator('.art-subtitle')).toHaveText('PiP caption') + await page.click('#play') + await expect.poll(() => page.evaluate(() => window.art.currentTime)).toBeGreaterThan(4.1) + await page.evaluate(() => window.art.destroy(false)) + } + await expect.poll(() => page.evaluate(() => !window.documentPictureInPicture.window || window.documentPictureInPicture.window.closed)).toBe(true) + const draws = await page.evaluate(() => window.canvasDraws) + await page.evaluate(() => new Promise(resolve => requestAnimationFrame(() => requestAnimationFrame(resolve)))) + evidence.final = await page.evaluate(() => ({ active: window.pip.isActive, connected: window.canvasVideo.isConnected, paused: window.canvasVideo.paused, src: window.canvasVideo.getAttribute('src'), pending: window.canvasPending.size, draws: window.canvasDraws, placeholders: document.querySelectorAll('.artplayer-document-pip-placeholder').length, events: window.canvasPipEvents, errors: window.canvasErrors })) + expect(evidence.final).toEqual({ active: false, connected: false, paused: true, src: null, pending: 0, draws, placeholders: 0, events: ending === 'destroy' ? [true] : [true, false], errors: [] }) + evidence.outcome = 'native-window-playback-subtitles-and-cleanup' + } + finally { + evidence.observed = await page.evaluate(() => ({ active: window.pip?.isActive, paused: window.canvasVideo?.paused, time: window.canvasVideo?.currentTime, draws: window.canvasDraws, subtitle: window.art?.template.$subtitle?.textContent, errors: window.canvasErrors, events: window.canvasPipEvents })) + await page.evaluate(() => { + if (!window.art.isDestroy) + window.art.destroy(false) + window.documentPictureInPicture.window?.close() + }) + await testInfo.attach('canvas-dpip', { contentType: 'application/json', body: JSON.stringify(evidence) }) + } + }) + } +} diff --git a/test/helpers/dpip.js b/test/helpers/dpip.js index 6f63ac026..32d14028b 100644 --- a/test/helpers/dpip.js +++ b/test/helpers/dpip.js @@ -8,14 +8,22 @@ import { build, transform } from 'esbuild' import less from 'less' import { verifyDpipContract } from '../../refactor/scripts/dpip-contract.mjs' import { readMember } from '../../refactor/scripts/releases.mjs' +import { verifyInstalledArtifacts } from '../../scripts/installed-artifacts.mjs' import { getEntryFile } from '../../scripts/projects.js' export async function dpipCandidate() { + const root = fileURLToPath(new URL('../../', import.meta.url)) + if (process.env.ARTPLAYER_BROWSER_ARTIFACTS) { + assert.notEqual(process.env.ARTPLAYER_DPIP_BASELINE, '1', 'Installed candidate cannot use the frozen workspace') + assert(!process.env.ARTPLAYER_DPIP_ARTIFACT, 'Installed candidate cannot override Document PiP artifact') + const { inputs } = verifyInstalledArtifacts(root, process.env.ARTPLAYER_BROWSER_ARTIFACTS, ['artplayer', 'artplayer-plugin-chapter', 'artplayer-plugin-document-pip']) + const input = inputs.find(item => item.name === 'artplayer-plugin-document-pip') + return { name: 'candidate-installed', code: await fs.readFile(input.file, 'utf8'), provenance: input } + } if (process.env.ARTPLAYER_DPIP_BASELINE === '1') return (await dpipHistorical()).find(item => item.name === 'frozen-workspace') if (process.env.ARTPLAYER_DPIP_ARTIFACT) return { name: 'candidate-artifact', code: await fs.readFile(process.env.ARTPLAYER_DPIP_ARTIFACT, 'utf8') } - const root = fileURLToPath(new URL('../../', import.meta.url)) const output = await build({ entryPoints: [getEntryFile(path.join(root, 'packages/artplayer-plugin-document-pip'))], bundle: true, write: false, format: 'cjs', target: 'es2020', plugins: [{ name: 'dpip-less-inline', setup(build) { diff --git a/test/package-check.test.js b/test/package-check.test.js index 83ed960c3..82101aa2f 100644 --- a/test/package-check.test.js +++ b/test/package-check.test.js @@ -9,25 +9,34 @@ import { removeConsumer, runtimeConsumer } from '../scripts/package-consumer.mjs import { emitterContracts } from './contracts/emitter.js' import { ambilightCandidate } from './helpers/ambilight.js' import { canvasCandidate } from './helpers/canvas.js' +import { dpipCandidate } from './helpers/dpip.js' test('Additional browser packages cannot be mistaken for full release consumer acceptance', async () => { await assert.rejects(checkPackages({ release: true, include: ['artplayer-plugin-ambilight'] }), /not full release acceptance/) }) test('Explicit installed plugin maps never fall back to source or frozen workspace', async () => { - const keys = ['ARTPLAYER_BROWSER_ARTIFACTS', 'ARTPLAYER_AMBILIGHT_BASELINE', 'ARTPLAYER_CANVAS_BASELINE'] + const keys = ['ARTPLAYER_BROWSER_ARTIFACTS', 'ARTPLAYER_AMBILIGHT_BASELINE', 'ARTPLAYER_CANVAS_BASELINE', 'ARTPLAYER_DPIP_BASELINE', 'ARTPLAYER_DPIP_ARTIFACT'] const previous = Object.fromEntries(keys.map(key => [key, process.env[key]])) try { process.env.ARTPLAYER_BROWSER_ARTIFACTS = path.resolve('refactor/.cache/absent-installed-plugin-map.json') assert(!fs.existsSync(process.env.ARTPLAYER_BROWSER_ARTIFACTS)) delete process.env.ARTPLAYER_AMBILIGHT_BASELINE delete process.env.ARTPLAYER_CANVAS_BASELINE + delete process.env.ARTPLAYER_DPIP_BASELINE + delete process.env.ARTPLAYER_DPIP_ARTIFACT await assert.rejects(ambilightCandidate(), { code: 'ENOENT' }) await assert.rejects(canvasCandidate(), { code: 'ENOENT' }) + await assert.rejects(dpipCandidate(), { code: 'ENOENT' }) process.env.ARTPLAYER_AMBILIGHT_BASELINE = '1' process.env.ARTPLAYER_CANVAS_BASELINE = '1' + process.env.ARTPLAYER_DPIP_BASELINE = '1' await assert.rejects(ambilightCandidate(), /cannot use the frozen workspace/) await assert.rejects(canvasCandidate(), /cannot use the frozen workspace/) + await assert.rejects(dpipCandidate(), /cannot use the frozen workspace/) + delete process.env.ARTPLAYER_DPIP_BASELINE + process.env.ARTPLAYER_DPIP_ARTIFACT = 'override.js' + await assert.rejects(dpipCandidate(), /cannot override Document PiP artifact/) } finally { for (const key of keys) { diff --git a/test/package/README.md b/test/package/README.md index 374348c17..6ade06249 100644 --- a/test/package/README.md +++ b/test/package/README.md @@ -1,9 +1,9 @@ # Installed package checks Run `yarn test:package` with the pinned Node and Yarn after a frozen install. The -default consumer scope is core and chapter. For Ambilight/Canvas browser work, -run `yarn test:package --include=artplayer-plugin-ambilight,artplayer-proxy-canvas`. -These two additional packages have reviewed published contracts and use the same +default consumer scope is core and chapter. For Ambilight/Canvas/Document PiP browser work, +run `yarn test:package --include=artplayer-plugin-ambilight,artplayer-proxy-canvas,artplayer-plugin-document-pip`. +These three additional packages have reviewed published contracts and use the same source snapshot/build/pack/offline-install/frozen-reinstall pipeline. Installed files are copied only after archive hash checks. The report explicitly separates the core/chapter runtime/type scope from the additional browser package scope;