fix(iframe): [PKG-IFRAME-05] checkpoint player and editor integration

This commit is contained in:
Harvey Zhao committed 2026-09-13 02:17:44 +08:00
1 parent 047e2df945
commit b35183c7af
15 files changed
+2547 -14

No files matched your search

+8
View File
@@ -15,6 +15,11 @@ const iframe = new ArtplayerToolIframe({
url: '/iframe.html',
})
window.addEventListener('artplayer:example:cleanup', () => {
iframe.destroy()
$iframe.remove()
}, { once: true })
iframe.message(({ type, data }) => {
switch (type) {
case 'fullscreenWeb':
@@ -44,4 +49,7 @@ iframe.commit(() => {
data: state,
})
})
}).catch((error) => {
if (!iframe.destroyed)
console.error(error)
})
+2 -1
View File
@@ -205,8 +205,9 @@
}
function runCode() {
window.dispatchEvent(new Event('artplayer:example:cleanup'))
// Destroy all existing instances
Artplayer.instances.forEach((art) => art.destroy(true))
Artplayer.instances.slice().forEach((art) => art.destroy(true))
const value = editor.getValue()
eval(value)
+7 -2
View File
@@ -229,12 +229,17 @@
<div class="popinner"></div>
</div>
<script src="./assets/js/console.js"></script>
<script src="./assets/js/vs/loader.js"></script>
<script>
var artplayer = document.createElement('script');
var monacoLoader = document.createElement('script');
var common = document.createElement('script');
artplayer.onload = function () {
monacoLoader.src = "./assets/js/vs/loader.js";
document.body.appendChild(monacoLoader);
}
monacoLoader.onload = function () {
common.src = "./assets/js/common.js";
document.body.appendChild(common);
}
@@ -274,4 +279,4 @@
</script>
</body>
</html>
</html>
+20 -2
View File
@@ -164,5 +164,23 @@ No version bump or publication is implied by declaration validation.
The existing local demo remains at
`http://localhost:8082/?libs=./uncompiled/artplayer-tool-iframe/index.js&example=iframe`.
Full demo/player integration, native devices and final release checks are later
gates; the controlled iframe fixture is not evidence that those have passed.
`iframe-player.spec.js` runs the real parent example, child HTML and stylesheet
with core 4.5.9, frozen 5.4.0 and the candidate, all four new/old bridge pairings,
and same/cross-origin documents. Historical global-name aliases are test-only.
It exercises actual media playback, seek/rate, source switch, fullscreenWeb
controls and independent core/tool destruction. Hover the player before clicking
controls, as a user does; old cores hide their controls during playback.
`iframe-editor.spec.js` opens the actual docs index and local Monaco, clicks Run
repeatedly, and switches from an iframe example to two parent players and then
an empty example. The editor loads the core before Monaco's AMD loader so the
UMD core exposes its script global. Before each Run it dispatches the docs-only
`artplayer:example:cleanup` event, then destroys a snapshot of parent instances.
The iframe example subscribes once to destroy its tool and remove its frame;
its initial commit handles cancellation during cleanup. This is an editor
convention, not automatic DOM-removal behavior or a new player API. Standalone
integrations remain responsible for calling destroy themselves.
Physical devices, actual BFCache restoration, interrupted navigation and final
distribution remain separate gates. Passing desktop player/editor tests does not
complete those gates or establish the old helper/package-name migration.
+404
View File
@@ -0,0 +1,404 @@
{
"schemaVersion": 1,
"task": "PKG-IFRAME-05",
"sourceCommit": "047e2df945fffe5699def60f2cf0497c00264117",
"capturedAt": "2026-09-12T17:50:39.882Z",
"association": {
"commit": "824baca8b408d9f3e0eb2f3d7e0eb2bdee2ff3a8",
"file": "packages/artplayer/package.json",
"version": "4.5.9",
"sha256LF": "7fe8f4a728c4895d2323a4214f024239f03b985f530529a5d257fa010252c195"
},
"release": {
"name": "artplayer",
"version": "4.5.9",
"metadataUrl": "https://registry.npmjs.org/artplayer/4.5.9",
"tarball": "https://registry.npmjs.org/artplayer/-/artplayer-4.5.9.tgz",
"integrity": "sha512-x5JzGdXkw4GFdztvPkVnm8TOWrGDcvGKKMPb9wuP6UfyftlLM3UQctmmalfAMnQSxugJwq2Kb9ix9OG2SgbJJA==",
"sha256": "79138da2b88c7bdc17d3cc8396244be733b188d27c87abc495cab778a0382cae",
"files": {
"package/README.md": "2070b94087839a80dc01616a3114cbe2d3a4950018188f433752880daf7843f8",
"package/dist/artplayer.js": "09a9f09e3a64a02ca7c27af710d7254f886f0bfb09fd54bf3a728ce863e5a497",
"package/dist/artplayer.legacy.js": "d5f02fdc5eee4f87aa835ce96749780d8e30434691549d25871a85ac6f5f74ba",
"package/package.json": "7fe8f4a728c4895d2323a4214f024239f03b985f530529a5d257fa010252c195",
"package/src/config/index.js": "6419ff29300032a0c3535a396d7c977cf4ac09d9edc6b3a04eeec7256ea5f6d4",
"package/src/contextmenu/aspectRatio.js": "428a46e0c696ded7a9224b08adf90745ab3bbb94b45f412b3135d1024c59c535",
"package/src/contextmenu/close.js": "4df0e913b1755409f034cbb54027b4518f05490ef66ad84b6f0e995899f41bc4",
"package/src/contextmenu/flip.js": "11bf11dff061ea30e495b7636d67e999efa3704d45e91f0d9b4a64c33dd8928d",
"package/src/contextmenu/index.js": "26dbf22b76faeb3dcbe5a1facf4519d4f736f1d4f800c8480e2ef608e367cf3e",
"package/src/contextmenu/info.js": "b7286de4d650d115493ef98891b77ff0deddb7e6c5725d93c8874890304885c1",
"package/src/contextmenu/playbackRate.js": "8b143613a7cbf36eeb364881537df08008be9f5e242623d433d10c1515fca732",
"package/src/contextmenu/version.js": "d15f750e2af7c9de92ce85fd765541cd0deb00dbff6fa7544b90ba2e00354f40",
"package/src/control/airplay.js": "3f5a5c82edfd8e0104311d1bc6224a216e8823d977f4d3d2efd2230232f1096e",
"package/src/control/fullscreen.js": "8bc660ac9e231c94c07a44ef5168bc1e8d1bca47c504f3e2303a20af35e76fdf",
"package/src/control/fullscreenWeb.js": "c4e8f2ed78556ed5a1fef61477d4fd4acd2bbf2226b0969604cabea3b8cf8152",
"package/src/control/index.js": "ce0e745aba98953b55acf15141d50ca0a3fcc1a9b166b882b6091d3ce643a97d",
"package/src/control/loop.js": "343626b1b528629fa132441d9ce2c2c56931039a26bf88ed9ee8f480e9bf4408",
"package/src/control/pip.js": "abf03561ccf8209e58b961c32d013a24e190b275f5c420f237eb5c9eea836de7",
"package/src/control/playAndPause.js": "e29d017f0c629a583b970d08ffe91169d43c573e2fa50e5467182a3e2ca06d34",
"package/src/control/progress.js": "d324da4a3fe64229d787c877bee246b5e942400e011f36479f2af50d16682a5a",
"package/src/control/quality.js": "50df35cfa1291124bc16d5209c9adf5445de4612961017fa2dbcf9f8b4bf33db",
"package/src/control/screenshot.js": "66d1baddda0e7aeecfa13b345b95c388e9b030e56ed41b6ab5bd7fd65305fb8c",
"package/src/control/setting.js": "e0b1a7bed99f6cb073f5e238c94575fdebd58ce5a7ce28f7a94ce6d3cc6eb8ae",
"package/src/control/thumbnails.js": "8d57e2943e3c504e62d8fa0787ee80b328ec75f3109105468dd52777cdfeefc2",
"package/src/control/time.js": "7c8c001a32bc4eccb73d16018f7cfa2ee7340bf33a1cd9626774bfd17d5267c0",
"package/src/control/volume.js": "54f789c71eb4b259977b608b3549ad43cb0f1a76f9803404b583b6010bd0d6e0",
"package/src/events/clickInit.js": "5b9cec4fd9c7051fd3e7049e613e0731861edb1fe2288298bf659bbf723c3ef1",
"package/src/events/gestureInit.js": "83776cef23b5c891daee7ef1e501bb4557ca24a3543d72543a65b6a0e6ccf514",
"package/src/events/hoverInit.js": "35758e0c93b10cfd0f0767a96eeeff3da9aca9ac0ac12f21081274ab6128a0b6",
"package/src/events/index.js": "d0a68560c887f2beb0ac5b3ff4ff8ef68e5564e3b106c33c4819b1b272cd1493",
"package/src/events/mousemoveInit.js": "99383ba26bfb723f90fd0b068de4042c874bc630f20881b2ef0986bb0d353dd2",
"package/src/events/resizeInit.js": "edd47861d969e26edeb04e5cb70e310326840130db8091db3e5df44a80644edb",
"package/src/events/viewInit.js": "96b555f228454a81e86db0955480e8d524977dab85c5e94e4afea1b6360d9511",
"package/src/hotkey.js": "31c6b15d462ec6e1128171e8ee02d1ee4414b40b088175dbad756e95bd6e71e3",
"package/src/i18n/cs.json": "75da976dcb4e341f5d37015ab60eff911d95e1d5072ab8d22c77569b36615b25",
"package/src/i18n/es.json": "f0199ffc619ef83837d80f0c0668a78a0ace1e2bdcad6086197a6fbd5bcd3975",
"package/src/i18n/fa.json": "e8ec174ec2447414c951b2d68fee8f0815cc413829307a84a878b3f04ad3fd87",
"package/src/i18n/index.js": "4499278688f66e377c4a605cf6a2be9781f3d2746878e38d5099f1c4d15805b8",
"package/src/i18n/pl.json": "6641c32942442feb7dcb9b7eef84b0f38704777da1b3980df424a4843f22709f",
"package/src/i18n/zh-cn.json": "a783ad7d1fead8ab1eb5e1ce93d386e86b9b8f88cc57073ab45f7b12228cdb70",
"package/src/i18n/zh-tw.json": "7117b438b706f6e2cb0c07eb6095271c12a75a745e392a78ab87b1b253be392b",
"package/src/icons/airplay.svg": "095f71bbac1aabde07963c645cb2071f790873fb2730363224e004321a01242a",
"package/src/icons/arrow-left.svg": "ac8cb60bf1f9436b9fc27eef2c65787dd879f01fb7ed2070b74be8a4d3167e0e",
"package/src/icons/arrow-right.svg": "9e2aa8a84ee9879bb70fb4fbae8eeb2a020e9a02c7bde14dede819d33acdff3c",
"package/src/icons/aspect-ratio.svg": "a71df3064cba45f402698b4c11fe21798b9af9aa6c5d0d689262889ffa386ab1",
"package/src/icons/check.svg": "672230ecd40c42b5354aad702ba6b018a9b765655bc96a53ba003fee41d8413a",
"package/src/icons/close.svg": "bb9e23e5305d11ec3d8f75a50ecdf3150deab8614c7df2af588873f2d78f06c2",
"package/src/icons/config.svg": "adbe547dfde4b44399da319ccdfc29fca57bcdbec8c6af5e36a2eeb97ecc2041",
"package/src/icons/error.svg": "d85a72e27ceaa38221637405cb0a5be270a297da58f1b0428498f568fab7570a",
"package/src/icons/fast-forward.svg": "709887f67e76e76d17fec37025c7405d85e9be28466d49602c1aa18732c89cef",
"package/src/icons/flip.svg": "ea6d9328823bb8454af65250030f02584239fcf8fbc2fb937dd1a1bac7602c28",
"package/src/icons/fullscreen-off.svg": "612577a443da55433bef698f1f4a426d0223c70cf1bcec3b27221bff753ae621",
"package/src/icons/fullscreen-on.svg": "bbec64253a42ef5ea26f6289d58254a7e86af3c0d627b2eb2ae357ef47cd04db",
"package/src/icons/fullscreen-web-off.svg": "57fa0e8780baa7f27befdeeb9ec2e772a1185a469a5f7ce05ed760d93ac8c434",
"package/src/icons/fullscreen-web-on.svg": "c6283f3ef124ea47e27b725ef5757a0b586dd5371cbefcdf2180dfabfe6375a9",
"package/src/icons/index.js": "fc6ecf6fa72e22e23e24265989c51809fc01f9df32d64e3aac3a0482cd8f30f4",
"package/src/icons/loading.svg": "ffcf5d814e6f3931a370bb4f0d2675381c2adb8f6d29dd1ee8f5df33d8175e75",
"package/src/icons/lock.svg": "1acb0566c08175200cdd47f0ce7f72ca1ae2761962cc3b134be4d44398cfcee2",
"package/src/icons/pause.svg": "9efe05ebf5610befddebcac51b3c9a491001189ff86204cb47928eb1617f6c7a",
"package/src/icons/pip.svg": "52d1e150d1364aed20917d83a2f7ab7589fb9df9655f3d3afaea3c4387800812",
"package/src/icons/play.svg": "c35562d20028d52ce329f35f90496a06d4dc5b6dc39df7ebcfb027f6f2393958",
"package/src/icons/playback-rate.svg": "cbc025c7780a5e5c31160182d70131202fa41a661d20ec2933e0bb1f488f1cd8",
"package/src/icons/screenshot.svg": "49f7d90198fb0bcbc29848f40b0a8c042b78c07ecef1cfc5184e2e84a61b4bd4",
"package/src/icons/setting.svg": "7f6454d1c7f037d5a7144a898abedc89ca339534166d620dfa45efcd4c74ee3c",
"package/src/icons/state.svg": "271a80268957209b2455ccccf4a42e89fafe990d04b2bcbe22624fcd63e43411",
"package/src/icons/switch-off.svg": "0bf5803437823035b816fb8bec148c06b4e01f85ea987cedda87269da6aab412",
"package/src/icons/switch-on.svg": "1d87b42af5e7141f9defdc790567534288d84858d5068dd15093d5ef12f37f9c",
"package/src/icons/unlock.svg": "f341e80530c2b6b218495c7ed3b9e4f268d70d2c931c0bf76efed1add8bab745",
"package/src/icons/volume-close.svg": "b9dbeaf9043c8014cd29d0e4a21a82ce567bc6b85497c310cca5f68bceeb31b2",
"package/src/icons/volume.svg": "baebc4ede3013202a4b13202a8d961cb0e5e065ee03733816022b2541d13884f",
"package/src/index.js": "b2ba71983647ec524e28fb386ea3bb4e56fbdcf96d7d35fb3f7f53d7e718ad91",
"package/src/info.js": "c46324b58695160372192e23c8fd002e1ce723a90ae340673f2e05331b5681a1",
"package/src/layer.js": "ed576b5a805684852de55b56e65bf295ef8422e51e58a7f0fb7ad9987639f262",
"package/src/libs/screenfull.js": "3ff8b0abec9b100b4c25476627f7bd505466ae77a0700c95347e9e6e05f45bc6",
"package/src/loading.js": "f47a310ae4acfbd45bf34786901377844a08b4acc40d169f99b1af7ab02e4988",
"package/src/mask.js": "c28ab44ab96255f9357d39e184a048aa12f0822073d38a82c63d59b6cd2a0936",
"package/src/mobile.js": "b0a54604e064c69eefa5c575c585bb15c1bc2410464c1e06492e098810f47163",
"package/src/notice.js": "ef17bef61d6ff1e67f5e722a100ab5f5f9e4779f1297b06e16b5a9a32168cd67",
"package/src/player/airplayMix.js": "83346ea049d0a17b6943926f67e6c3a1e7242bcd978933dd77cd16ae3dca39ef",
"package/src/player/aspectRatioMix.js": "17f3bd0fb2b022d2a5c04229083f6f45ec113a94a8c447121f6e78408ba8284d",
"package/src/player/attrMix.js": "ae4103f3ede68f9ae87a16fa2c1e2b3bd3b03ec6190eb0ea8ace0c4b61216a29",
"package/src/player/autoHeightMix.js": "5595fc9544697dbc6c9791eff1bc44bb0d3d313f5b3edc94fa433b6e6413e7b4",
"package/src/player/autoSizeMix.js": "d50402921a1034f341f48141481a6cdec9ff5c465d52fa1a5a94372388315927",
"package/src/player/currentTimeMix.js": "41e5a6ac03d0dcd6883d5b08e2c74e0421b3d68e819f2b9c1e7f27aaf26bd7e8",
"package/src/player/durationMix.js": "87922cfb4a55db593e1c0187bf1912cf5b7c9b3b0136341723692a60f95efe01",
"package/src/player/eventInit.js": "838b0331d0f25fb041b4471196d4b3d1e33568386d965f2ec8cdfccb484f9b47",
"package/src/player/flipMix.js": "416485570461e7ef3c0346b528bedd8846bd24473d51ce6e33ac7abf61a24dfd",
"package/src/player/fullscreenMix.js": "60321f7b46d3255b4f51d2a39ef99ee0ed81c298d0625d7dcca31ff8266c292f",
"package/src/player/fullscreenWebMix.js": "661559d1840250c6c021c908bf1e0b1e9cee6e241263ad1d0265d23ab64865d1",
"package/src/player/index.js": "d63b1a31fed6d519919efced58a3f3109bbc3df7e0213d858fe98171ccc4a95f",
"package/src/player/loadedMix.js": "cd984138d0bee78d8e822ae424a4f61adc5530327283e2db72b1942f6b464f22",
"package/src/player/loopMix.js": "d6f7126565cd52ed09d1ce1cee969519e26f1e442209bc3b8d8aadaef4d030ac",
"package/src/player/miniMix.js": "5df31888db9551b460b0f19201081718fcfabfa0e30e52c837233914dcfc3316",
"package/src/player/normalSizeMix.js": "aa48cf09644115b4f99ddf56f8bc452b43e6c0c62aaa5de71d3f4d0431c2ff9f",
"package/src/player/optionInit.js": "536103f236d781765db594064ba493e812df743af72ba129746b705ea0e413ef",
"package/src/player/pauseMix.js": "f96b7d2f17a01144bb5299612a794b4e971d7e389444f3769a272fa164f82886",
"package/src/player/pipMix.js": "3249cec87eeead678ab9234d23278b33adade6f93e5266c988a45368a4f26eaf",
"package/src/player/playMix.js": "1e37d4ff7d5a5508c9f656d391993ec28a5a4627aca7917d4601164f6fdc5d63",
"package/src/player/playbackRateMix.js": "38ff6968dbbc8c5dc889902281eb1eeb478a81ff00eb828107f1e7637cda2742",
"package/src/player/playedMix.js": "0d345bac5914a3403cc2b7f5c8d68ab61a4c53e9085602d8e28950dab7175936",
"package/src/player/playingMix.js": "2ba6c2c9e495bcabb91da43d0c8162742c80f9fe39ee60e15eb01e7c5761348f",
"package/src/player/posterMix.js": "0202481b7f90490bf07fa78e19ef78423af3ad2366a6b41377df1b7a9966e71f",
"package/src/player/rectMix.js": "9eb0a052fce5cb4e6fa1999221155407842275dc022f18a713e3e71ac7c22187",
"package/src/player/screenshotMix.js": "1600f46823c297e3af15345e2d5d5a6eb8366949985bf7e63ee33054337abc5c",
"package/src/player/seekMix.js": "2bb1f1923aad7a989b4cadb3fe79e02176adb123425a356a93541eb8cd0885d4",
"package/src/player/subtitleOffsetMix.js": "5dfc80bce7921ce305259fe0a6f92a79f6bb09c1c7ca00132e3595a25fcdfca7",
"package/src/player/switchMix.js": "74520ed23d1a6f11f0bf281e2e5cf0f35431766cfd5ce88f41cd11e8e45240da",
"package/src/player/themeMix.js": "35c923209ded64afa75c1c7bbf44b96de6bf58bd9170751724b640237b701dd1",
"package/src/player/titleMix.js": "2d3a5a0484952cd995f0e2f1de17ba9063a5940ddf8590c04c1296ff59e8ec30",
"package/src/player/toggleMix.js": "9ec0e69ade2445e8e583eb94d226825efe2c109274eeed244c1e035e39b024a9",
"package/src/player/typeMix.js": "2a2284a7c1368c33054c8f8df473fa2cac71bb97dced04b3c61097854e05351e",
"package/src/player/urlMix.js": "3957a430d37cd1ba590a51b5a3cdc24f04ce5ef45e1404498e9f608b6085f884",
"package/src/player/volumeMix.js": "f698ca1e3981eb7f2d67cdff024c0c91909e98f93652586a5d307f286be56603",
"package/src/plugins/autoOrientation.js": "c23b982db741a7087af848aea8309bbafae8d2431fd4e5b2998942fb23dd8814",
"package/src/plugins/autoPlayback.js": "35887d2a11ae76f67ddee9aee9665381a1a262a0c6142c247144550b8da6473a",
"package/src/plugins/fastForward.js": "77f2bf8f4d3b81328da0ca95bc74eb5f8b36ab70b0f0fa406d90ee5448ada697",
"package/src/plugins/index.js": "2f70c5025752f00f95d9e57e59f27f38d0bb8ede4d2e67138e94eff6d6f7c96e",
"package/src/plugins/lock.js": "ebb0af389ac698af3122da8bb6ac6a8d0e883e28c9f0962b183cda76db82fde7",
"package/src/plugins/miniProgressBar.js": "3e807b2c6d3824bc6be4b2def34301c06f382c0dcb7b3d349f3f3f327898f4b3",
"package/src/scheme/index.js": "c18829bd98c8b65cda856ce2963fb7e89388af3c067b6604cc9e7b9be349f884",
"package/src/setting/aspectRatio.js": "ed5998c89033ecb665c31841f26dd0571e114b7a6a33de46578fc621226f1e5c",
"package/src/setting/flip.js": "ada27f9e1ed9233b8f8de378529bcf82b90c0c96d4dacbf4e9526bceaf63795e",
"package/src/setting/index.js": "0ccbea9989a3b378301922eaefaf84930a9d2007881d39c5ee02fb3c456dcf54",
"package/src/setting/playbackRate.js": "206e52797ab05af902a2bf9e788d65f6ca282d8758d75886e1028f9afdc338fa",
"package/src/setting/subtitleOffset.js": "b31618fcf928ad4711df0f114ef6b146fcb57b5815cde37db38aa332b3491ee6",
"package/src/storage.js": "a5e5385a38039fd454810eb756d4cdb5e2e67b9001c6259d283079e6914e5d03",
"package/src/style/artplayer.less": "a53ac3e2da8975cc6b79f1308ab3db9bfcabeea87762160aef9e5f3eb8ca00e7",
"package/src/style/aspectRatio.less": "c2e5a5c0d4f4a740f8bbe30cbbdc71650ec0790e05bdba3929ef5d08a86ea9c6",
"package/src/style/autoSize.less": "bbd93211ff1d4831c3bf9f09abe8d9397c72627f824a53904924d9a19bde7063",
"package/src/style/bottom.less": "87ca1416ec14e7a947dfc48e5b498c6ff04baf1a58c25cab8993cdbcaf5a6e3a",
"package/src/style/contextmenu.less": "68c66222ab7e7382897b9798f1efcf3eb83011989054ac46117b999a6a20de0d",
"package/src/style/danmuku.less": "f2f488c5140ee5cc09226784e1222fcd0b9b917f7e86179ff9922abf4a90edd8",
"package/src/style/flip.less": "7b7be6602d1672c7d17a726051273c320eec83a925be509e2545df7a387123b7",
"package/src/style/fullscreenWeb.less": "5a3135a40f6ddfa81ab0a7212f10814286d72ae694d2c8867d340a9ab37475f8",
"package/src/style/hideCursor.less": "e05a85e4c0afd202a0becc2f0f5fb4bb1b49f56802c495f2a816099416d9c22b",
"package/src/style/hint.less": "8a6e35615ce131fceff06e8075ed6a64f4bc7bad9c05b9c7171a2acfb291678f",
"package/src/style/index.less": "941783cb4bf34cdeff4ae8731d6779a0e413c0584ec9aa2b0e7008ba05270f9b",
"package/src/style/info.less": "7fd144fb1829f4f819519189f1bb263a8564c476375e887baa9fb8d161b2f3be",
"package/src/style/layers.less": "9b14a15f10a6a01d93f91f59082fa82b624d91aacdd3d0249d28f1b42dd290ce",
"package/src/style/loading.less": "fd1411beb6390c71ba4f7a812e32c8e28c2fb7b3dac54a02d20f3278cd2892f8",
"package/src/style/mask.less": "73ddb09137a246784280a833007e4e6d4319282740e96e7373555d8c2fc38460",
"package/src/style/mini.less": "e0195898ab8058817855edf0f849415c29581abf9b7df4f10d81b70bfe6480b6",
"package/src/style/notice.less": "0c6c3c3cfe586b135bb46fc32c6d7bba549f9b12c9bb289058556e2493e1bddd",
"package/src/style/plugins.less": "97256ee575ab284548f1ec1d310da2d0308314e8e8f00c111dbe9f37efa5b4b3",
"package/src/style/poster.less": "3039b30d18bcae180f51a166f01f8a331d417bea4df900d44fec3006167ddc65",
"package/src/style/selector.less": "a3bb23990abbc8de2eb22b751571c15ced1edb2680cca138b101b7977c08ba66",
"package/src/style/setting.less": "c7e75ad83634f6a38ec6fc3e2a63933a9a25a14e7f7f96468101b12b5d0caa46",
"package/src/style/subtitle.less": "efaa207e18235dba383e556ebe01eb9a7f77fa5259f8736b65b03ab07d2ffcca",
"package/src/style/video.less": "c8a4fcdf7070a70b724e6d39d78b9c243ff18d8f8b6fbb8700d647799ff9756b",
"package/src/subtitle.js": "c1b06d4c1f78717fcb910e9e8749ff7b792c850aa782dd1c212744d3eb8f497e",
"package/src/template.js": "865feabf867c2e6040213df21f2bf1ad3e485abe08929a309c741f7cbe016ec2",
"package/src/utils/compatibility.js": "c8ce4609aeddeb2870430ecf2afc24aa101aca78b44c299eb7e23a2d7a7a0073",
"package/src/utils/component.js": "915e2b23e401327dd83f3ebaebc0f086d600e0505b9c7a79092c5dff8926aeeb",
"package/src/utils/dom.js": "d82466f14e9f70b6e1e8d153505087e97be51c64c9e6d55d6bd65ded19a6ead4",
"package/src/utils/emitter.js": "637c2b65c89a34ca3208335a892258540eefc49cb8f87f981c69496fe49efa89",
"package/src/utils/error.js": "3b395e07922fb3fcbb7de34e662ada1914b3499dac6f109e522c432bd705f3d1",
"package/src/utils/file.js": "c02139c54e569bf635f90d32b7ccb9d6cb8ba5ff126071e6c79b34a134b11cb2",
"package/src/utils/format.js": "d7c127506e456a8597f620acc7e7740416aa5fb6ba599fee0bf922b5e45cfd41",
"package/src/utils/index.js": "585394c7bfda8e8548cfb92c73ae1e45cccc55c48d683ca9a48099debdf4b98e",
"package/src/utils/property.js": "f5fe167348f7b716f2ae4ecfb301340272d1b25a4a20aa765702b32aa844fa35",
"package/src/utils/subtitle.js": "4356afbc9c81e4364cf03729825591f17c36ffc1efce0ad861170366edc5d561",
"package/src/utils/time.js": "99e8277f29db0a680252506a950dcba339f43ef53a24a12209ad30b129bfc2f0",
"package/src/whitelist.js": "340078e2fe5c4ee4239888fb3ac9a0ccb8ff80f479e22ac4b4cad1a3f5cf66c9",
"package/tsconfig.json": "bf1616499e177df22713d30f25dd255295c43d34310185403b2dfb1c4970c793",
"package/types/artplayer.d.ts": "c27309012de1ac041240fd3e5b54f4ebf90e7057deb843726de5509db64a8973",
"package/types/component.d.ts": "46cf95e905d0365a47377bbb19d7bdc99ee0503e2961c66a3f6a550c5fb7f14d",
"package/types/config.d.ts": "3e2e3294802653fbab62f05d5d656411c657641d8f7a3ce64295eef44b37ef62",
"package/types/events.d.ts": "8df2a08e2db70e094b7265cf611770bbef528a485b40e367ea1bd365f262db57",
"package/types/i18n.d.ts": "9613286a44154854e80d73ec267436f35baeca9387da554c6a1abd7975e4ad12",
"package/types/icons.d.ts": "a4d2fa1a85755cc40d0ac9d0dadb7bedae1872b3306ba434f0fd817c0b2ac3b6",
"package/types/option.d.ts": "a4eb4789dbc84be555c171c919533b7bec5b79033ed5a92ef257319349201b29",
"package/types/player.d.ts": "a83ad6421df84cd5dfb6ff945328f7094d46bc0a764227ab8ba5f16a04f04836",
"package/types/setting.d.ts": "fbf66bb3fd75e4d57052aafc41f752f8fca1c801ad502ff06ef11a6ab6b86200",
"package/types/subtitle.d.ts": "6b5c5a1541aaab169506fa51a4d8b10d953f0e3ec81ebbec4729707523751ea4",
"package/types/template.d.ts": "7dde655c5a6e482afb62215d7e1dd640ee6956a18d4a2d490ade4d9fdf144c10",
"package/types/utils.d.ts": "1bf046a761aa915b8eb78447ffb287b9d9c80cf2124b90312c5437651caf2a8b"
},
"manifest": {
"license": "MIT",
"name": "artplayer",
"version": "4.5.9",
"main": "dist/artplayer.js",
"legacy": "dist/artplayer.legacy.js",
"types": "types/artplayer.d.ts",
"homepage": "https://artplayer.org",
"author": "Harvey Zack <laozhaochaguan@gmail.com>",
"browserslist": "> 0.5%, last 2 versions, not dead",
"description": "ArtPlayer.js is a modern and full featured HTML5 video player",
"repository": {
"type": "git",
"url": "git+https://github.com/zhw2590582/ArtPlayer.git"
},
"bugs": {
"url": "https://github.com/zhw2590582/ArtPlayer/issues"
},
"keywords": [
"html5",
"video",
"player"
],
"dependencies": {
"option-validator": "^2.0.6",
"screenfull": "^6.0.2"
}
}
},
"archiveMembers": [
"package/README.md",
"package/dist/artplayer.js",
"package/dist/artplayer.legacy.js",
"package/package.json",
"package/src/config/index.js",
"package/src/contextmenu/aspectRatio.js",
"package/src/contextmenu/close.js",
"package/src/contextmenu/flip.js",
"package/src/contextmenu/index.js",
"package/src/contextmenu/info.js",
"package/src/contextmenu/playbackRate.js",
"package/src/contextmenu/version.js",
"package/src/control/airplay.js",
"package/src/control/fullscreen.js",
"package/src/control/fullscreenWeb.js",
"package/src/control/index.js",
"package/src/control/loop.js",
"package/src/control/pip.js",
"package/src/control/playAndPause.js",
"package/src/control/progress.js",
"package/src/control/quality.js",
"package/src/control/screenshot.js",
"package/src/control/setting.js",
"package/src/control/thumbnails.js",
"package/src/control/time.js",
"package/src/control/volume.js",
"package/src/events/clickInit.js",
"package/src/events/gestureInit.js",
"package/src/events/hoverInit.js",
"package/src/events/index.js",
"package/src/events/mousemoveInit.js",
"package/src/events/resizeInit.js",
"package/src/events/viewInit.js",
"package/src/hotkey.js",
"package/src/i18n/cs.json",
"package/src/i18n/es.json",
"package/src/i18n/fa.json",
"package/src/i18n/index.js",
"package/src/i18n/pl.json",
"package/src/i18n/zh-cn.json",
"package/src/i18n/zh-tw.json",
"package/src/icons/airplay.svg",
"package/src/icons/arrow-left.svg",
"package/src/icons/arrow-right.svg",
"package/src/icons/aspect-ratio.svg",
"package/src/icons/check.svg",
"package/src/icons/close.svg",
"package/src/icons/config.svg",
"package/src/icons/error.svg",
"package/src/icons/fast-forward.svg",
"package/src/icons/flip.svg",
"package/src/icons/fullscreen-off.svg",
"package/src/icons/fullscreen-on.svg",
"package/src/icons/fullscreen-web-off.svg",
"package/src/icons/fullscreen-web-on.svg",
"package/src/icons/index.js",
"package/src/icons/loading.svg",
"package/src/icons/lock.svg",
"package/src/icons/pause.svg",
"package/src/icons/pip.svg",
"package/src/icons/play.svg",
"package/src/icons/playback-rate.svg",
"package/src/icons/screenshot.svg",
"package/src/icons/setting.svg",
"package/src/icons/state.svg",
"package/src/icons/switch-off.svg",
"package/src/icons/switch-on.svg",
"package/src/icons/unlock.svg",
"package/src/icons/volume-close.svg",
"package/src/icons/volume.svg",
"package/src/index.js",
"package/src/info.js",
"package/src/layer.js",
"package/src/libs/screenfull.js",
"package/src/loading.js",
"package/src/mask.js",
"package/src/mobile.js",
"package/src/notice.js",
"package/src/player/airplayMix.js",
"package/src/player/aspectRatioMix.js",
"package/src/player/attrMix.js",
"package/src/player/autoHeightMix.js",
"package/src/player/autoSizeMix.js",
"package/src/player/currentTimeMix.js",
"package/src/player/durationMix.js",
"package/src/player/eventInit.js",
"package/src/player/flipMix.js",
"package/src/player/fullscreenMix.js",
"package/src/player/fullscreenWebMix.js",
"package/src/player/index.js",
"package/src/player/loadedMix.js",
"package/src/player/loopMix.js",
"package/src/player/miniMix.js",
"package/src/player/normalSizeMix.js",
"package/src/player/optionInit.js",
"package/src/player/pauseMix.js",
"package/src/player/pipMix.js",
"package/src/player/playMix.js",
"package/src/player/playbackRateMix.js",
"package/src/player/playedMix.js",
"package/src/player/playingMix.js",
"package/src/player/posterMix.js",
"package/src/player/rectMix.js",
"package/src/player/screenshotMix.js",
"package/src/player/seekMix.js",
"package/src/player/subtitleOffsetMix.js",
"package/src/player/switchMix.js",
"package/src/player/themeMix.js",
"package/src/player/titleMix.js",
"package/src/player/toggleMix.js",
"package/src/player/typeMix.js",
"package/src/player/urlMix.js",
"package/src/player/volumeMix.js",
"package/src/plugins/autoOrientation.js",
"package/src/plugins/autoPlayback.js",
"package/src/plugins/fastForward.js",
"package/src/plugins/index.js",
"package/src/plugins/lock.js",
"package/src/plugins/miniProgressBar.js",
"package/src/scheme/index.js",
"package/src/setting/aspectRatio.js",
"package/src/setting/flip.js",
"package/src/setting/index.js",
"package/src/setting/playbackRate.js",
"package/src/setting/subtitleOffset.js",
"package/src/storage.js",
"package/src/style/artplayer.less",
"package/src/style/aspectRatio.less",
"package/src/style/autoSize.less",
"package/src/style/bottom.less",
"package/src/style/contextmenu.less",
"package/src/style/danmuku.less",
"package/src/style/flip.less",
"package/src/style/fullscreenWeb.less",
"package/src/style/hideCursor.less",
"package/src/style/hint.less",
"package/src/style/index.less",
"package/src/style/info.less",
"package/src/style/layers.less",
"package/src/style/loading.less",
"package/src/style/mask.less",
"package/src/style/mini.less",
"package/src/style/notice.less",
"package/src/style/plugins.less",
"package/src/style/poster.less",
"package/src/style/selector.less",
"package/src/style/setting.less",
"package/src/style/subtitle.less",
"package/src/style/video.less",
"package/src/subtitle.js",
"package/src/template.js",
"package/src/utils/compatibility.js",
"package/src/utils/component.js",
"package/src/utils/dom.js",
"package/src/utils/emitter.js",
"package/src/utils/error.js",
"package/src/utils/file.js",
"package/src/utils/format.js",
"package/src/utils/index.js",
"package/src/utils/property.js",
"package/src/utils/subtitle.js",
"package/src/utils/time.js",
"package/src/whitelist.js",
"package/tsconfig.json",
"package/types/artplayer.d.ts",
"package/types/component.d.ts",
"package/types/config.d.ts",
"package/types/events.d.ts",
"package/types/i18n.d.ts",
"package/types/icons.d.ts",
"package/types/option.d.ts",
"package/types/player.d.ts",
"package/types/setting.d.ts",
"package/types/subtitle.d.ts",
"package/types/template.d.ts",
"package/types/utils.d.ts"
]
}
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,112 @@
# PKG-IFRAME-05 real player and docs integration
Started at 047e2df945fffe5699def60f2cf0497c00264117 with a clean worktree.
Task 04 completed type and installed-consumer validation. This task remains
responsible for actual player/media behavior, including old supported cores,
same/cross-origin documents, navigation and demo integration.
The real docs/assets/example/iframe.js and docs/iframe.html are integration inputs.
The original plugin release is associated with core 4.5.9; freeze its actual npm
archive rather than substituting the unrelated 4.5.5 or 5.1.7 fixtures. Current
published core is the already frozen 5.4.0; candidate is the actual current build.
Browser scope and any fixture adaptation must be recorded. A standalone player
harness using the docs inputs is not the complete Monaco editor or physical
device/BFCache acceptance. A normal history reload is not cached restoration.
The editor currently destroys parent Artplayer.instances before eval, but the
iframe tool lives outside that registry: repeated-run cleanup needs evidence.
No task completion, risk closure, version or release claim is made at this point.
## Player and editor checkpoint
The frozen 4.5.9 npm archive has 176 members. `iframe-core.test.mjs` checks the
historical release association, complete member inventory, manifest and every
member hash; the browser server independently verifies its selected entry.
The player matrix has 72 rows: three cores (4.5.9, frozen 5.4.0, candidate), two
origins, four old/new parent-child bridge pairs and three browser engines. It
loads the actual docs iframe HTML, example and stylesheet, adapting only the
child URL and test-only historical global-name aliases. Each row decodes and
plays actual video, seeks, changes rate, clicks fullscreenWeb controls, checks
parent geometry/messages, switches media, destroys the player and proves that
the tool can still execute a separate request. Helper/package aliases are not
implemented by this test adaptation.
The additional three editor rows use the actual docs index, local Monaco and Run
button. Only the iframe constructor is observed through a test subclass; the
external advertising script is stubbed in this test, with no production change.
The test runs the iframe example three times, rejects an outstanding request on
cleanup, switches to two parent players, then runs an empty example and checks
that both parent players were destroyed.
Real defects found and fixed:
- Loading Monaco's AMD loader before the UMD core produced `Artplayer is not
defined` before editor initialization. The docs index now loads the core,
Monaco loader and editor bootstrap in dependency order.
- Repeated Run left the previous iframe tool alive. The editor now emits the
docs-only `artplayer:example:cleanup` event before evaluating another example;
the iframe example listens once, destroys its tool and removes its frame.
Initial setup handles expected cancellation when the example is cleaned up.
- The parent instance registry is mutable during destroy. Run now iterates a
snapshot, and the two-player editor assertion checks complete cleanup.
All changes are in the docs integration and test infrastructure; the core/tool
runtime source and distribution bytes are unchanged. No public player API is
added or removed. Standalone consumers still own tool destruction.
## Failed probes and limits
Failed reports and complete trace/result directories are retained in `.cache`:
`iframe05-player-probe*`, `iframe05-player-source-before*`,
`iframe05-editor-before*`, `iframe05-editor-cleanup-before*`, and
`iframe05-editor-cleanup-probe*`.
The first player probe had an incorrect test URL; response status is now checked
before injecting bytes. The next probe exposed the existing serialized-body
regex's requirement for a recognizable `resolve(...)` call: compilation of an
inline object argument added newlines. The test now assigns the result to a local
variable and calls `resolve(result)`, preserving the historical protocol.
The initial full matrix passed 68/72. All four failures were old core 4.5.9 on
cross-origin WebKit, where traces showed successful playback but an invisible
control during click. Moving the pointer over the player, as an actual user
does, reveals the controls; no forced click, timeout increase or product API
change was used. The original failed traces remain available.
The first editor probe reproduced the AMD startup defect. After that fix, a
probe attempted Run while the full-screen iframe covered it; the test now exits
fullscreen through its real control first. The corrected probe then reproduced
the live old-tool cleanup defect. Subsequent editor checks cover real Run clicks.
This is a desktop integration checkpoint, not task completion. Actual BFCache
restoration, physical devices, externally interrupted navigation and final
8082/distribution acceptance remain open. Normal history reload does not count
as BFCache, and Windows WebKit does not count as physical Safari. Old unmarked
peers retain the limitations recorded in ADR-026. Final verification counts and
input/report hashes are in `baselines/iframe-integration-validation.json`.
## Verification
- Source, main and legacy each passed all 75 desktop browser rows, with no
retries, skipped rows or flaky results: 225 passing cases in total.
- Historical archive verification passed for all 176 members.
- Browser test/server lint and docs JavaScript syntax checks passed.
- ESM, i18n and SSR imports: 3 passing tests.
- Full CI passed 1544 tests (1365 unit, 14 engineering, 165 historical/baseline)
and strict checking of 330 production TypeScript files. The browser counts
above are independent native-browser runs, not unit-test counts.
- Configured coverage passed with 0 gate violations across 224 runtime files
and 26 documented/type-only exclusions. Lines: 61.04%, branches: 86.7%,
functions: 66.75%. This does not claim full coverage of every package.
- The generated core editor declaration retains the pre-existing unused
`ts/no-namespace` disable warning, also present in task 04's final CI log.
There are no new lint errors; no generated declaration was hand-edited.
No dependencies, package versions, runtime build bytes, push or publication were
changed. Roll back the docs index, common Run handler and iframe example together
to restore the preceding integration; this also restores the documented defects.
Task status remains doing and all three iframe risks remain open.
Checkpoint subject: `fix(iframe): [PKG-IFRAME-05] checkpoint player and editor integration`.
+3 -2
View File
@@ -4,7 +4,7 @@
基线:`40fcda6a37d0049d42e49c1e64e70d4fd9ba5f7f`。总任务 222 项,范围 22 个包及工作区/示例。
状态:todo 104 / doing 9 / blocked 0 / done 109 / deferred 0。风险 L/M/H 表示兼容风险,不表示工期。
状态:todo 103 / doing 10 / blocked 0 / done 109 / deferred 0。风险 L/M/H 表示兼容风险,不表示工期。
前置依赖是启动条件;验收是完成条件。任务可以继续拆分,但不能复用或悄悄删除旧 ID。
@@ -348,7 +348,7 @@
| PKG-IFRAME-02 | artplayer-tool-iframe<br>建立特有行为与错误测试 | PKG-IFRAME-01, ENG-03, ENG-05 | 跨窗口消息、ID 匹配、请求失败、重复 inject、销毁中请求 | 旧版本行为可重跑,成功/失败/切源/销毁有必要断言 | H | done |
| PKG-IFRAME-03 | artplayer-tool-iframe<br>整理内部职责与资源 | PKG-IFRAME-02, CORE-02, BASE-07 | 请求注册/响应匹配/监听清理分离;origin/source 安全边界独立决策 | 结构变化和缺陷修复分开记录;原 API/事件/资源生命周期通过 | H | done |
| PKG-IFRAME-04 | artplayer-tool-iframe<br>迁移自有源码和公开类型 | PKG-IFRAME-03, ENG-04, ENG-06, CORE-07 | 消息联合类型、回调/Promise 推导、旧公开字段兼容;旧npm export=与实际namespace、额外helper协议分别核验 | 严格类型检查、旧消费样例通过;声明路径/导出和同步异步兼容 | H | done |
| PKG-IFRAME-05 | artplayer-tool-iframe<br>验证新旧核心和组合 | PKG-IFRAME-04, CORE-22 | 真实同源/跨源 iframe、既有 commit 协议;安全变化有独立结论;新旧核心/demo、实际 BFCache/设备和外部中断导航验收,旧端无文档标记的限制明确 | 最终核心与原支持范围核心分别通过;设备/SDK 缺证据不能标完成 | H | todo |
| PKG-IFRAME-05 | artplayer-tool-iframe<br>验证新旧核心和组合 | PKG-IFRAME-04, CORE-22 | 真实同源/跨源 iframe、既有 commit 协议;安全变化有独立结论;新旧核心/demo、实际 BFCache/设备和外部中断导航验收,旧端无文档标记的限制明确 | 最终核心与原支持范围核心分别通过;设备/SDK 缺证据不能标完成 | H | doing |
| PKG-IFRAME-06 | artplayer-tool-iframe<br>验证分发并同步文档 | PKG-IFRAME-05, ENG-07 | iframe.js、示例集成和原 script/class 导出验证;旧包名/额外helper深入口与新工具名的迁移结论; 保留旧npm Function回调及namespace/helper与工具类的区别,核验实际编译后消费而非仅声明通过 | tarball 入口/资源、类型、8082 demo 和 README 一致,有回退记录 | H | todo |
## 5 包迁移:artplayer-tool-thumbnail
@@ -534,4 +534,5 @@
- PKG-IFRAME-02: [记录](changes/2026-09-12-PKG-IFRAME-02-behavior.md) [记录](baselines/iframe-behavior-validation.json)
- PKG-IFRAME-03: [记录](changes/2026-09-12-PKG-IFRAME-03-requests-checkpoint.md) [记录](baselines/iframe-requests-checkpoint.json) [记录](changes/2026-09-12-PKG-IFRAME-03-boundaries-checkpoint.md) [记录](baselines/iframe-boundaries-checkpoint.json) [记录](iframe-message-boundary.md) [记录](changes/2026-09-13-PKG-IFRAME-03-navigation.md) [记录](baselines/iframe-navigation-validation.json) [记录](iframe-document-protocol.md)
- PKG-IFRAME-04: [记录](changes/2026-09-13-PKG-IFRAME-04-types.md) [记录](baselines/iframe-types-validation.json)
- PKG-IFRAME-05: [记录](changes/2026-09-13-PKG-IFRAME-05-integration.md) [记录](baselines/iframe-integration-validation.json)
- PKG-FACTORY-01: [记录](baselines/factory-assignment-gaps.json) [记录](baselines/factory-compatibility-proposals.json) [记录](factory-compatibility-decision.md) [记录](changes/2026-09-12-PKG-FACTORY-01-decision.md)
+12
View File
@@ -1,5 +1,17 @@
# 进度与证据
## PKG-IFRAME-05 播放器与真实编辑器检查点(doing)
冻结原支持核心4.5.9的176个npm文件,与已冻结5.4.0、候选核心组成真实媒体矩阵。
源码/main/legacy各75项通过:每份72组同源/跨域、新旧父子端组合,加3个实际
Monaco页面Run流程。覆盖播放、seek/rate、网页全屏、切源、播放器/工具独立销毁。
实测修复docs的AMD加载顺序、重复Run遗留iframe工具和请求、多个父播放器清理。
源码和运行产物字节未变,旧公开API保持;失败探针、trace及后续通过报告均留存。
全仓CI1544、330生产TS检查、3导入测试通过,配置覆盖率门槛0违例(224运行文件)。
见[变更](changes/2026-09-13-PKG-IFRAME-05-integration.md)和
[验证](baselines/iframe-integration-validation.json)。实际BFCache、物理设备、外部
中断导航及最终分发仍待验收,不因此关闭任务或风险。222项:109 done、10 doing、103 todo。
## PKG-IFRAME-04 完成:公开类型、模块入口和编辑器
保留默认类的required data、void静态接收、readonly、resove及旧commit推导,增加
+8 -4
View File
@@ -3776,7 +3776,7 @@
],
"status": "open",
"confirmation": "reproduced",
"compatibleResolution": "Owned requests and cleanup cover terminal failures. ADR-026 adds document-scoped cancellation, provisional source changes, same-document fragment preservation and queued final navigation. Keep open for PKG-IFRAME-05 full player/demo, actual BFCache/device and interrupted-navigation acceptance; unchanged legacy peers have documented document-identity limits.",
"compatibleResolution": "Owned requests and ADR-026 document cancellation are implemented. IFRAME-05 now verifies actual desktop players and Monaco Run on source/main/legacy, including pending-request and multiple-player cleanup. Keep open for actual BFCache/device and interrupted-navigation acceptance; unchanged legacy peers retain documented document-identity limits.",
"closureCriteria": "Old behavior and candidate fixes have deterministic race/resource tests plus actual iframe integration.",
"evidence": [
"refactor/baselines/iframe-contract.md",
@@ -3790,7 +3790,9 @@
"refactor/baselines/iframe-boundaries-checkpoint.json",
"refactor/changes/2026-09-13-PKG-IFRAME-03-navigation.md",
"refactor/baselines/iframe-navigation-validation.json",
"refactor/iframe-document-protocol.md"
"refactor/iframe-document-protocol.md",
"refactor/changes/2026-09-13-PKG-IFRAME-05-integration.md",
"refactor/baselines/iframe-integration-validation.json"
]
},
{
@@ -3802,7 +3804,7 @@
],
"status": "open",
"confirmation": "reproduced",
"compatibleResolution": "ADR-025 binds actual native peers; ADR-026 correlates negotiated document messages without breaking normal legacy wire. This is not an origin allowlist or commit sandbox. Keep open for PKG-IFRAME-05 complete integration/release review and documented legacy/custom unmarked-message limits.",
"compatibleResolution": "ADR-025 binds actual native peers; ADR-026 correlates negotiated documents. IFRAME-05 adds actual same/cross-origin media and editor evidence on three core versions, four bridge pairings and three desktop engines. This is not an origin allowlist or commit sandbox. Keep final acceptance and legacy/custom unmarked-message limits open.",
"closureCriteria": "Controlled and real same/cross-origin evidence plus an explicit documented compatibility decision and supported trust model.",
"evidence": [
"refactor/baselines/iframe-contract.md",
@@ -3815,7 +3817,9 @@
"refactor/iframe-message-boundary.md",
"refactor/changes/2026-09-13-PKG-IFRAME-03-navigation.md",
"refactor/baselines/iframe-navigation-validation.json",
"refactor/iframe-document-protocol.md"
"refactor/iframe-document-protocol.md",
"refactor/changes/2026-09-13-PKG-IFRAME-05-integration.md",
"refactor/baselines/iframe-integration-validation.json"
]
},
{
+19
View File
@@ -0,0 +1,19 @@
import assert from 'node:assert/strict'
import fs from 'node:fs'
import test from 'node:test'
import { archiveFiles, ensureArchive, hash, readMember } from './releases.mjs'
test('Iframe historical core matches its release association and every frozen archive member', async () => {
const baseline = JSON.parse(fs.readFileSync(new URL('../baselines/iframe-core.json', import.meta.url)))
const iframe = JSON.parse(fs.readFileSync(new URL('../baselines/iframe-release.json', import.meta.url)))
assert.deepEqual(baseline.association, iframe.release.historicalCore)
assert.equal(baseline.release.name, 'artplayer')
assert.equal(baseline.release.version, baseline.association.version)
const archive = await ensureArchive(baseline.release)
assert.deepEqual(archiveFiles(archive), baseline.archiveMembers)
assert.deepEqual(Object.keys(baseline.release.files).sort(), baseline.archiveMembers)
for (const [member, expected] of Object.entries(baseline.release.files))
assert.equal(hash(readMember(archive, member)), expected, member)
assert.deepEqual(JSON.parse(readMember(archive, 'package/package.json')), baseline.release.manifest)
assert(Object.hasOwn(baseline.release.files, `package/${baseline.release.manifest.main}`))
})
+5 -2
View File
@@ -3704,11 +3704,14 @@
"PKG-IFRAME-04",
"CORE-22"
],
"status": "todo",
"status": "doing",
"risk": "H",
"deliverable": "真实同源/跨源 iframe、既有 commit 协议;安全变化有独立结论;新旧核心/demo、实际 BFCache/设备和外部中断导航验收,旧端无文档标记的限制明确",
"acceptance": "最终核心与原支持范围核心分别通过;设备/SDK 缺证据不能标完成",
"evidence": []
"evidence": [
"changes/2026-09-13-PKG-IFRAME-05-integration.md",
"baselines/iframe-integration-validation.json"
]
},
{
"id": "PKG-IFRAME-06",
+72
View File
@@ -0,0 +1,72 @@
import fs from 'node:fs'
import { hash } from '../../refactor/scripts/releases.mjs'
import { iframeCandidate } from '../helpers/iframe.js'
import { expect, test } from './fixtures.js'
const candidate = await iframeCandidate()
test('Iframe docs editor Run releases the previous tool and its pending requests', async ({ page }, testInfo) => {
await page.route('https://pagead2.googlesyndication.com/**', route => route.fulfill({ contentType: 'text/javascript', body: '' }))
await page.route('**/uncompiled/artplayer-tool-iframe/index.js', route => route.fulfill({
contentType: 'text/javascript',
body: `${candidate.code}\nif (window === window.top) {
window.demoTools = [];
window.ArtplayerToolIframe = class extends window.ArtplayerToolIframe {
constructor(option) { super(option); window.demoTools.push(this); }
};
}`,
}))
await page.goto('/?libs=./uncompiled/artplayer-tool-iframe/index.js&example=iframe')
await expect.poll(() => page.evaluate(() => window.demoTools?.length)).toBe(1)
const firstFrame = page.frames().find(frame => frame.url().endsWith('/iframe.html'))
await expect.poll(() => firstFrame.evaluate(() => window.Artplayer?.instances[0]?.isReady)).toBe(true)
await page.evaluate(() => {
window.demoTools[0].commit(() => {
const art = window.Artplayer.instances[0]
art.fullscreenWeb = true
})
})
await expect(page.locator('iframe')).toHaveClass('fullscreenWeb')
await firstFrame.locator('.art-video-player').hover()
await firstFrame.locator('.art-control-fullscreenWeb').click()
await expect(page.locator('iframe')).not.toHaveClass('fullscreenWeb')
await page.evaluate(() => {
window.pendingSettlement = 'pending'
window.demoTools[0].commit((resolve) => {
window.finishPendingDemo = () => resolve(1)
}).then(() => window.pendingSettlement = 'resolved', () => window.pendingSettlement = 'rejected')
})
await page.locator('.run').click()
await expect.poll(() => page.evaluate(() => window.demoTools.length)).toBe(2)
await expect.poll(() => page.evaluate(() => window.demoTools[0].destroyed)).toBe(true)
await expect.poll(() => page.evaluate(() => window.pendingSettlement)).toBe('rejected')
await expect(page.locator('iframe')).not.toHaveClass('fullscreenWeb')
const secondFrame = page.frames().find(frame => frame.url().endsWith('/iframe.html'))
await expect.poll(() => secondFrame.evaluate(() => window.Artplayer?.instances[0]?.isReady)).toBe(true)
await page.locator('.run').click()
await expect.poll(() => page.evaluate(() => window.demoTools.length)).toBe(3)
expect(await page.evaluate(() => window.demoTools.map(tool => tool.destroyed))).toEqual([true, true, false])
await page.evaluate(() => {
const model = window.monaco.editor.getModels().find(model => model.getLanguageId() === 'javascript')
model.setValue(`
document.querySelector('.artplayer-app').innerHTML = '<div class="first"></div><div class="second"></div>';
window.parentPlayers = ['.first', '.second'].map(container => new Artplayer({ container, url: '/test/pattern.mp4' }));
`)
const url = new URL(location.href)
url.searchParams.delete('example')
history.replaceState(null, '', url)
})
await page.locator('.run').click()
await expect.poll(() => page.evaluate(() => window.Artplayer.instances.length)).toBe(2)
expect(await page.evaluate(() => window.demoTools.every(tool => tool.destroyed))).toBe(true)
await page.evaluate(() => {
window.previousPlayers = window.parentPlayers
window.monaco.editor.getModels().find(model => model.getLanguageId() === 'javascript').setValue('window.exampleFinished = true')
})
await page.locator('.run').click()
await expect.poll(() => page.evaluate(() => window.exampleFinished)).toBe(true)
expect(await page.evaluate(() => window.Artplayer.instances.length)).toBe(0)
expect(await page.evaluate(() => window.previousPlayers.every(art => art.isDestroy))).toBe(true)
await testInfo.attach('iframe-editor', { contentType: 'application/json', body: JSON.stringify({ candidateSha256: hash(candidate.code), files: Object.fromEntries(['docs/index.html', 'docs/iframe.html', 'docs/assets/example/iframe.js', 'docs/assets/js/common.js'].map(file => [file, hash(fs.readFileSync(file))])), scope: 'Actual docs index, local Monaco and Run button. Only the iframe constructor is observed through a test subclass; external advertising script is stubbed in this test only. No physical-device/BFCache claim.' }) })
await page.evaluate(() => window.demoTools.forEach(tool => tool.destroy()))
})
+135
View File
@@ -0,0 +1,135 @@
import fs from 'node:fs'
import { hash } from '../../refactor/scripts/releases.mjs'
import { iframeCandidate, iframeHistorical } from '../helpers/iframe.js'
import { expect, test } from './fixtures.js'
const candidate = await iframeCandidate()
const published = (await iframeHistorical()).find(item => item.name === 'published-artplayer-plugin-iframe.js')
const demo = fs.readFileSync('docs/assets/example/iframe.js', 'utf8')
const childHtml = fs.readFileSync('docs/iframe.html', 'utf8')
const bridges = [[candidate, candidate], [published, candidate], [candidate, published], [published, published]]
for (const core of ['published-4.5.9', 'published', 'candidate']) {
for (const relation of ['same-origin', 'cross-origin']) {
for (const [parentTool, childTool] of bridges) {
test(`Iframe player ${core}/${relation}/${parentTool.name}/${childTool.name}`, async ({ page, request }, testInfo) => {
const coreResponse = await request.get(`/${core}/artplayer.js`)
expect(coreResponse.ok()).toBe(true)
const coreBytes = await coreResponse.body()
await page.addInitScript(() => {
window.mediaEvents = []
window.documentWitness = Math.random().toString(36)
for (const name of ['loadedmetadata', 'loadeddata', 'canplay', 'playing', 'pause', 'seeking', 'seeked', 'emptied', 'error']) {
addEventListener(name, (event) => {
if (event.target instanceof HTMLVideoElement)
window.mediaEvents.push({ type: name, source: event.target.currentSrc, time: event.target.currentTime, error: event.target.error?.code })
}, true)
}
})
const bridge = implementation => `${implementation.code}\n${implementation === published ? 'window.ArtplayerToolIframe = window.ArtplayerPluginIframe;' : ''}`
await page.route('**/uncompiled/artplayer-tool-iframe/index.js', route => route.fulfill({ contentType: 'text/javascript', body: bridge(childTool) }))
await page.route('**/uncompiled/artplayer/index.js', route => route.fulfill({ contentType: 'text/javascript', body: coreBytes }))
await page.goto('/test/player.html?core=published')
await page.addStyleTag({ url: '/assets/css/style.css' })
await page.evaluate(() => {
document.body.innerHTML = '<div class="artplayer-app" style="width:640px;height:360px"></div>'
window.messages = []
})
await page.addScriptTag({ content: bridge(parentTool) })
const parentOrigin = new URL(page.url()).origin
const childOrigin = relation === 'cross-origin' ? parentOrigin.replace('127.0.0.1', 'localhost') : parentOrigin
const url = `${childOrigin}/iframe.html`
const adaptedDemo = demo.replace('url: \'/iframe.html\'', `url: '${url}'`)
expect(adaptedDemo).not.toBe(demo)
await page.addScriptTag({ content: `${adaptedDemo}\nwindow.demoTool = iframe; window.demoFrame = $iframe; const originalCallback = iframe.messageCallback; iframe.message(function (packet) { originalCallback.call(this, packet); window.messages.push(packet); });` })
let child
try {
await expect.poll(() => page.frames().some(frame => frame.url() === url)).toBe(true)
child = page.frames().find(frame => frame.url() === url)
await expect.poll(() => child.evaluate(() => {
const art = window.Artplayer?.instances[0]
return Boolean(art && art.video.readyState >= 2 && art.video.videoWidth > 0)
})).toBe(true)
const initial = await page.evaluate(() => window.demoTool.commit(() => {
const art = window.Artplayer.instances[0]
art.muted = true
return { version: window.Artplayer.version, width: art.video.videoWidth, height: art.video.videoHeight, duration: art.duration, instances: window.Artplayer.instances.length, source: art.video.currentSrc }
}))
expect(initial.instances).toBe(1)
expect(initial.source).toContain('/assets/sample/video.mp4')
expect(initial.width).toBeGreaterThan(0)
const play = await page.evaluate(() => window.demoTool.commit((resolve) => {
window.Artplayer.instances[0].play().then(() => {
const result = { playing: !window.Artplayer.instances[0].video.paused }
resolve(result)
}).catch((error) => {
const result = { error: error.message }
resolve(result)
})
}))
expect(play).toEqual({ playing: true })
await expect.poll(() => child.evaluate(() => window.Artplayer.instances[0].currentTime)).toBeGreaterThan(0.15)
await child.locator('.art-video-player').hover()
await child.locator('.art-control-fullscreenWeb').click()
await expect(page.locator('iframe')).toHaveClass('fullscreenWeb')
const fullscreen = await page.locator('iframe').boundingBox()
expect(fullscreen.width).toBe(page.viewportSize().width)
expect(fullscreen.height).toBe(page.viewportSize().height)
await child.locator('.art-video-player').hover()
await child.locator('.art-control-fullscreenWeb').click()
await expect(page.locator('iframe')).not.toHaveClass('fullscreenWeb')
expect(await page.evaluate(() => window.messages.filter(packet => packet.type === 'fullscreenWeb').map(packet => packet.data))).toEqual([true, false])
await page.evaluate(() => window.demoTool.commit(() => {
const art = window.Artplayer.instances[0]
art.pause()
art.currentTime = 1
art.playbackRate = 1.25
}))
await expect.poll(() => child.evaluate(() => window.Artplayer.instances[0].video.seeking)).toBe(false)
const seek = await child.evaluate(() => ({ time: window.Artplayer.instances[0].currentTime, rate: window.Artplayer.instances[0].video.playbackRate, paused: window.Artplayer.instances[0].video.paused }))
expect(seek.time).toBeCloseTo(1, 1)
expect(seek.rate).toBe(1.25)
expect(seek.paused).toBe(true)
const switched = await page.evaluate(() => window.demoTool.commit((resolve) => {
const art = window.Artplayer.instances[0]
art.switchUrl('/test/pattern.mp4').then(() => {
const result = { source: art.video.currentSrc, width: art.video.videoWidth }
resolve(result)
}).catch((error) => {
const result = { error: error.message }
resolve(result)
})
}))
expect(switched.error).toBeUndefined()
expect(switched.source).toContain('/test/pattern.mp4')
await expect.poll(() => child.evaluate(() => window.Artplayer.instances[0].video.readyState >= 2)).toBe(true)
const events = await child.evaluate(() => window.mediaEvents)
expect(events.some(event => event.type === 'playing')).toBe(true)
expect(events.some(event => event.type === 'seeked')).toBe(true)
expect(events.filter(event => event.type === 'error')).toEqual([])
const destroyed = await page.evaluate(() => window.demoTool.commit(() => {
window.Artplayer.instances[0].destroy()
return { instances: window.Artplayer.instances.length, players: document.querySelectorAll('.art-video-player').length }
}))
expect(destroyed).toEqual({ instances: 0, players: 0 })
const afterDestroy = await page.evaluate(() => window.demoTool.commit(() => {
return 17
}))
expect(afterDestroy).toBe(17)
await testInfo.attach('iframe-player', { contentType: 'application/json', body: JSON.stringify({ core, coreSha256: hash(coreBytes), parent: { name: parentTool.name, sha256: hash(parentTool.code) }, child: { name: childTool.name, sha256: hash(childTool.code) }, relation, initial, seek, switched, destroyed, events, demoSha256: hash(demo), adaptedDemoSha256: hash(adaptedDemo), childHtmlSha256: hash(childHtml), scope: 'Actual docs iframe.html, parent example and CSS in a minimal parent harness; only source URL and explicit test-only old global-name aliases are adapted. Actual media plays/seeks/switches, fullscreenWeb control sends messages and both core/tool lifetimes are tested independently. No complete Monaco/device/BFCache claim.' }) })
}
finally {
if (child && !child.isDetached()) {
await child.evaluate(() => {
for (const art of [...(window.Artplayer?.instances || [])]) art.destroy()
})
}
await page.evaluate(() => {
window.demoTool?.destroy()
window.demoFrame?.remove()
})
}
})
}
}
}
+6 -1
View File
@@ -69,6 +69,11 @@ const vastCoreMember = `package/${vastCore.manifest.main}`
const vastCoreBytes = readMember(await ensureArchive(vastCore), vastCoreMember)
assert.equal(hash(vastCoreBytes), vastCore.files[vastCoreMember], 'Historical VAST core differs from frozen release')
add('/published-5.1.7/artplayer.js', vastCoreBytes, { kind: 'npm-release', version: vastCore.version, integrity: vastCore.integrity, member: vastCoreMember })
const iframeCore = JSON.parse(fs.readFileSync(path.join(workspace, 'refactor/baselines/iframe-core.json'), 'utf8')).release
const iframeCoreMember = `package/${iframeCore.manifest.main}`
const iframeCoreBytes = readMember(await ensureArchive(iframeCore), iframeCoreMember)
assert.equal(hash(iframeCoreBytes), iframeCore.files[iframeCoreMember], 'Historical Iframe core differs from frozen release')
add('/published-4.5.9/artplayer.js', iframeCoreBytes, { kind: 'npm-release', version: iframeCore.version, integrity: iframeCore.integrity, member: iframeCoreMember })
add('/test/audio-tone.m4a', fs.readFileSync(path.join(workspace, 'test/browser/media/audio-tone.m4a')), { kind: 'generated-media', file: 'test/browser/media/audio-tone.m4a' })
const mime = { '.html': 'text/html', '.js': 'text/javascript', '.mjs': 'text/javascript', '.json': 'application/json', '.css': 'text/css', '.mp4': 'video/mp4', '.m4a': 'audio/mp4', '.webm': 'video/webm', '.vtt': 'text/vtt', '.svg': 'image/svg+xml', '.png': 'image/png', '.jpg': 'image/jpeg' }
@@ -138,7 +143,7 @@ const server = http.createServer((req, res) => {
if (url.pathname === '/test/player.html') {
const core = url.searchParams.get('core') || 'candidate'
const chapter = url.searchParams.get('chapter') || 'candidate'
assert(['candidate', 'published', 'published-4.5.5', 'published-5.1.7'].includes(core) && ['candidate', 'published'].includes(chapter), 'Invalid test combination')
assert(['candidate', 'published', 'published-4.5.5', 'published-4.5.9', 'published-5.1.7'].includes(core) && ['candidate', 'published'].includes(chapter), 'Invalid test combination')
const html = fs.readFileSync(path.join(workspace, 'test/browser/player.html'), 'utf8')
.replace('__CORE__', core)
.replace('__CHAPTER__', chapter)