From 17befee4b1df7cf4ec29bbda4fd3271567f44223 Mon Sep 17 00:00:00 2001 From: Harvey Zhao Date: Sun, 13 Sep 2026 05:29:59 +0800 Subject: [PATCH] fix(thumbnail): [PKG-TOOL-THUMB-04] protect event names and nested once dispatch --- docs/compiled/artplayer-tool-thumbnail.js | 2 +- .../artplayer-tool-thumbnail.legacy.js | 2 +- docs/compiled/artplayer-tool-thumbnail.mjs | 16 +- package.json | 4 +- .../artplayer-tool-thumbnail/ARCHITECTURE.md | 11 +- .../dist/artplayer-tool-thumbnail.js | 2 +- .../dist/artplayer-tool-thumbnail.legacy.js | 2 +- .../dist/artplayer-tool-thumbnail.mjs | 16 +- .../artplayer-tool-thumbnail/src/emitter.ts | 18 +- .../thumbnail-emitter-validation.json | 8727 +++++++++++++++++ .../2026-09-13-PKG-TOOL-THUMB-04-emitter.md | 39 + refactor/plan.md | 2 +- refactor/progress.md | 12 + refactor/risk-table.md | 1 + refactor/risks.json | 34 +- refactor/tasks.json | 4 +- refactor/third-party.json | 8 +- test/browser/thumbnail-emitter.spec.js | 47 + test/thumbnail-emitter.test.js | 137 + 19 files changed, 9058 insertions(+), 26 deletions(-) create mode 100644 refactor/baselines/thumbnail-emitter-validation.json create mode 100644 refactor/changes/2026-09-13-PKG-TOOL-THUMB-04-emitter.md create mode 100644 test/browser/thumbnail-emitter.spec.js create mode 100644 test/thumbnail-emitter.test.js diff --git a/docs/compiled/artplayer-tool-thumbnail.js b/docs/compiled/artplayer-tool-thumbnail.js index 4bee91cde..e2b100bfb 100644 --- a/docs/compiled/artplayer-tool-thumbnail.js +++ b/docs/compiled/artplayer-tool-thumbnail.js @@ -31,4 +31,4 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -!function(e,t){"object"===typeof exports&&"undefined"!==typeof module?module.exports=t():"function"==typeof define&&define.amd?(e.ArtplayerToolThumbnail=t(),define(function(){return e.ArtplayerToolThumbnail})):(e="undefined"!==typeof globalThis?globalThis:e||self).ArtplayerToolThumbnail=t()}(this,function(){"use strict";class e{on(e,t,n){const r=this.e||(this.e={});return(r[e]||(r[e]=[])).push({fn:t,ctx:n}),this}once(e,t,n){const r=this,o=t;function i(...t){r.off(e,i),o.apply(n,t)}return i._=t,this.on(e,i,n)}emit(e,...t){const n=((this.e||(this.e={}))[e]||[]).slice();for(let r=0;rn.pause?.(),()=>n.removeAttribute?.("src"),()=>n.load?.(),()=>n.parentNode?.removeChild(n));i([...t.sourceListeners.splice(0),...c,...[...r].map(e=>()=>o(t.sourceUrls,e)),...[...s].map(e=>()=>o(t.thumbnailUrls,e))])}function a(e,t,n){return Math.max(Math.min(e,Math.max(t,n)),Math.min(t,n))}function l(e,t){let s=t.epoch;const c=e.video;let l,u,d,h,p=!1,f=!1,m=()=>{};const b=[],y=new Promise((e,t)=>{l=e,u=t});y.catch(()=>{});const g=()=>!p&&!t.closed&&t.epoch===s&&t.job===h;function v(){return!p&&(p=!0,t.job===h&&(t.job=null,e.processing=!1),i([()=>clearTimeout(d),m,...b.splice(0)]),!0)}function w(t,n=!0){if(!p){try{v()}catch(r){t||(t=r)}if(n)try{e.emit("error",t?.message)}catch(o){t=o}u(t)}}function U(t){if(!g())return;f=!0,e.processing=!0;let r=0;const i=()=>{if(!g())return;if(r===t.points.length)return void function(){if(g())try{v(),e.emit("done"),l()}catch(t){let r=t;try{e.emit("error",t?.message)}catch(n){r=n}u(r)}}();const s=t.points[r];let a=!1,d=!1;const h=c.oncanplay,p=()=>{if(!(!g()||a||c.seeking||void 0!==c.readyState&&c.readyState<2)){a=!0;try{if(m(),t.context.drawImage(c,s.x,s.y,t.width,t.height),!g())return;t.canvas.toBlob(s=>{if(g()&&!d){d=!0;try{if(!s)throw new Error("Unable to create thumbnail image");const c=function(e,t,r){const i=n(e),s=URL.createObjectURL(t);if(i.thumbnailUrls.add(s),!r())return o(i.thumbnailUrls,s),null;const c=new Set([...i.thumbnailUrls,e.thumbnailUrl]);i.thumbnailUrl=s,e.thumbnailUrl=s;for(const n of c)n&&n!==s&&o(i.thumbnailUrls,n);return r()?s:null}(e,s,g);if(!c)return;e.emit("update",c,(r+1)/t.number),r++,Promise.resolve().then(i)}catch(c){w(c)}}})}catch(l){w(l)}}};m=()=>{c.oncanplay===p&&(c.oncanplay=h),c.removeEventListener("seeked",p)};try{c.oncanplay=p,c.addEventListener("seeked",p),c.currentTime=s.time,c.readyState>=2&&!c.seeking&&Promise.resolve().then(p)}catch(f){w(f)}};Promise.resolve().then(i)}function E(n=!1){if(g()&&!f)if(clearTimeout(d),!t.loading&&c.duration){f=!0;try{const t=function(e,t){const{video:n}=t,{width:r,number:o,begin:i,end:s}=e.option,c=n.videoHeight/n.videoWidth*r,l=(n,r)=>{try{e.errorHandle(n,r)}catch(o){throw t.reported=o,t.wasReported=!0,o}};e.option.height=c,e.option.begin=a(i,0,n.duration),e.option.end=a(s||n.duration,i,n.duration),l(e.option.end>e.option.begin,"End time must be greater than the start time"),l(Number.isFinite(n.duration),"Video duration must be finite"),e.duration=e.option.end-e.option.begin,e.density=o/e.duration,l(e.file&&n,"Please select the video file first"),l(!e.processing,"There is currently a task in progress, please wait a moment..."),l(e.density<=1,`The preview density cannot be greater than 1, but got ${e.density}`);const u=e.creatScreenshotDate(),d=e.creatCanvas(),h=d.getContext("2d");return e.emit("canvas",d),{width:r,height:c,number:o,points:u,canvas:d,context:h}}(e,h);U(t)}catch(r){if(w(r,!n&&!(h.wasReported&&r===h.reported)),n)throw r}}else d=setTimeout(()=>E(),1e3),g()||clearTimeout(d)}h={video:c,promise:y,ready:E,fail:w,cancel(e){w(r(e),!1)},adopt:t=>!p&&!f&&c===e.video&&(s=t,!0)},t.job=h;try{for(const[e,t]of[["error",()=>{g()&&w(new Error(`Unable to load video: media error ${c.error?.code||0}`))}],["loadedmetadata",()=>E()],["durationchange",()=>E()]])if(b.push(()=>c.removeEventListener(e,t)),c.addEventListener(e,t),!g())return i([()=>c.removeEventListener(e,t),...b.splice(0)]),y;c.error?w(new Error(`Unable to load video: media error ${c.error.code}`)):E(!0)}catch(j){throw w(j,!1),j}return y}const u=new WeakMap;function d(e){let t;for(const[r,o]of e.listeners.splice(0))try{e.input.removeEventListener(r,o)}catch(n){t||(t=n)}if(e.wrapper){try{e.input.parentNode&&e.input.parentNode.removeChild(e.input)}catch(n){t||(t=n)}"relative"===e.wrapper.style.position&&(e.wrapper.style.position=e.position)}if(t)throw t}function h(e,t){try{for(const[n,r]of t)e.listeners.push([n,r]),e.input.addEventListener(n,r)}catch(n){try{d(e)}catch{}throw n}}function p(e){const t=u.get(e);t&&(u.delete(e),d(t))}class f extends e{constructor(e={}){super(),this.processing=!1,this.option={};try{this.setup(Object.assign({},f.DEFAULTS,e)),this.video=f.creatVideo(),n(this).video=this.video,this.duration=0,this.inputChange=this.inputChange.bind(this),this.ondrop=this.ondrop.bind(this),function(e,t){const n=u.get(e);n.callbacks=[["change",e.inputChange],["dragover",t],["drop",e.ondrop]],h(n,n.callbacks)}(this,f.ondragover)}catch(t){s(this,"construction failed");try{p(this)}catch{}try{c(this)}catch{}throw t}}static get DEFAULTS(){return{number:60,width:160,height:90,column:10,begin:0,end:Number.NaN}}static ondragover(e){e.preventDefault()}ondrop(e){if(n(this).closed)return;e.preventDefault();const t=e.dataTransfer.files[0];this.loadVideo(t)}setup(e={}){return n(this).closed||function(e,t){const o=n(e),i=++o.inputEpoch,s=()=>!o.closed&&o.inputEpoch===i,c=Object.assign({},e.option,t),l=c.fileInput;e.errorHandle(l instanceof Element,"The 'fileInput' is not a Element");for(const n of["number","width","column","begin","end"])e.errorHandle("number"===typeof c[n],`The '${n}' is not a number`);if(c.number=a(c.number,10,1e3),c.width=a(c.width,10,1e3),c.column=a(c.column,1,1e3),!s())throw r("input setup superseded");const p=u.get(e);if(p&&(l===p.input||l===p.wrapper))return c.fileInput=p.input,e.option=c,c;const f={input:l,wrapper:null,position:"",listeners:[],callbacks:p?.callbacks||null};try{if("INPUT"===l.tagName&&"file"===l.type||(f.wrapper=l,f.position=l.style.position,f.input=document.createElement("input"),f.input.type="file",Object.assign(f.input.style,{position:"absolute",width:"100%",height:"100%",left:"0",top:"0",right:"0",bottom:"0",opacity:"0"}),l.style.position="relative",l.appendChild(f.input)),!s())throw r("input setup superseded");if(f.callbacks&&h(f,f.callbacks),!s())throw r("input setup superseded")}catch(m){try{d(f)}catch{}throw m}c.fileInput=f.input,u.set(e,f),e.option=c,p&&d(p)}(this,e),this}static creatVideo(){const e=document.createElement("video");e.style.position="absolute",e.style.top="-9999px",e.style.left="-9999px",e.muted=!0,e.controls=!0;try{document.body.appendChild(e)}catch(t){throw e.parentNode&&e.parentNode.removeChild(e),t}return e}inputChange(e){if(n(this).closed)return;const t=this.option.fileInput.files[0];this.loadVideo(t),e.target.value=""}loadVideo(e){!function(e,t){const r=n(e);if(!t||r.closed)return;const s=r.epoch,c=e.video,a=c.canPlayType(t.type);if(e.errorHandle("maybe"===a||"probably"===a,`Playback of this file format is not supported: ${t.type}`),r.closed||r.epoch!==s)return;const l=URL.createObjectURL(t);if(r.sourceUrls.add(l),r.closed||r.epoch!==s)return void o(r.sourceUrls,l);const u=++r.epoch;!r.sourceUrl&&!e.file&&r.job?.adopt(u)||r.job?.cancel("source changed"),r.loading=!0;const d=()=>!r.closed&&r.epoch===u,h=r.sourceListeners,p=[];r.sourceListeners=p;let f=()=>{},m=!1;const b=()=>{if(!d()||m||c.currentSrc&&c.currentSrc!==l)return;m=!0,r.loading=!1;const t=new Error(`Unable to load video: media error ${c.error?.code||0}`);r.job?r.job.fail(t):e.emit("error",t.message)},y=()=>{!d()||c.currentSrc&&c.currentSrc!==l||(r.loading=!1,r.job?.ready())};try{if(i(h.splice(0)),!d())return void o(r.sourceUrls,l);for(const[e,t]of[["error",b],["loadedmetadata",y]])if(f=()=>c.removeEventListener(e,t),p.push(f),c.addEventListener(e,t),!d())return i([f,...p.splice(0)]),void o(r.sourceUrls,l);if(e.videoUrl=l,e.file=t,e.emit("file",t),!d())return void o(r.sourceUrls,l);if(c.src=l,!d())return void o(r.sourceUrls,l);r.sourceUrl=l;for(const e of[...r.sourceUrls])e!==l&&o(r.sourceUrls,e);d()&&e.emit("video",c)}catch(g){d()&&(r.loading=!1,r.job?.cancel("source setup failed"));try{i([f,...p.splice(0)])}catch{}throw o(r.sourceUrls,l),g}}(this,e)}start(){return function(e){const t=n(e);if(t.closed){const e=Promise.reject(r("destroyed"));return e.catch(()=>{}),e}return e.errorHandle(!t.job,"There is currently a task in progress, please wait a moment..."),l(e,t)}(this)}creatScreenshotDate(){return function(e,t){const{number:n,width:r,height:o,column:i,begin:s}=e,c=t/n,a=[s+c];for(;a.length({time:e-c/2,x:t%i*r,y:Math.floor(t/i)*o}))}(this.option,this.duration)}creatCanvas(){return function(e){const{number:t,width:n,height:r,column:o}=e,i=document.createElement("canvas"),s=i.getContext("2d");return i.width=n*o,i.height=Math.ceil(t/o)*r+30,s.fillStyle="black",s.fillRect(0,0,i.width,i.height),s.font="14px Georgia",s.fillStyle="#fff",s.fillText(`From: https://artplayer.org/, Number: ${t}, Width: ${n}, Height: ${r}, Column: ${o}`,10,i.height-11),i}(this.option)}download(){if(n(this).closed)return this;this.errorHandle(this.file&&this.thumbnailUrl,"Download does not seem to be ready, please create preview first"),this.errorHandle(!this.processing,"There is currently a task in progress, please wait a moment...");const e=function(e,t){const n=document.createElement("a"),r=`${function(e){const t=e.split(".");return t.pop(),t.join(".")}(e.name)}.png`;n.download=r,n.href=t;try{document.body.appendChild(n),n.click()}finally{n.parentNode&&n.parentNode.removeChild(n)}return r}(this.file,this.thumbnailUrl);return this.emit("download",e),this}errorHandle(e,t){if(!e)throw this.emit("error",t),new Error(t)}destroy(){s(this)&&i([()=>p(this),()=>c(this),()=>this.emit("destroy")])}}return f}); +!function(e,t){"object"===typeof exports&&"undefined"!==typeof module?module.exports=t():"function"==typeof define&&define.amd?(e.ArtplayerToolThumbnail=t(),define(function(){return e.ArtplayerToolThumbnail})):(e="undefined"!==typeof globalThis?globalThis:e||self).ArtplayerToolThumbnail=t()}(this,function(){"use strict";const e=(e,t)=>Object.prototype.hasOwnProperty.call(e,t);class t{on(t,n,r){const o=this.e||(this.e={});let i=e(o,t)?o[t]:void 0;return i||(i=[],Object.defineProperty(o,t,{value:i,enumerable:!0,configurable:!0,writable:!0})),i.push({fn:n,ctx:r}),this}once(e,t,n){const r=this,o=t;let i=!1;function s(...t){i||(i=!0,r.off(e,s),o.apply(n,t))}return s._=t,this.on(e,s,n)}emit(t,...n){const r=this.e||(this.e={}),o=(e(r,t)&&r[t]||[]).slice();for(let e=0;er.pause?.(),()=>r.removeAttribute?.("src"),()=>r.load?.(),()=>r.parentNode?.removeChild(r));s([...t.sourceListeners.splice(0),...c,...[...n].map(e=>()=>i(t.sourceUrls,e)),...[...o].map(e=>()=>i(t.thumbnailUrls,e))])}function l(e,t,n){return Math.max(Math.min(e,Math.max(t,n)),Math.min(t,n))}function u(e,t){let n=t.epoch;const c=e.video;let a,u,d,h,p=!1,f=!1,m=()=>{};const b=[],y=new Promise((e,t)=>{a=e,u=t});y.catch(()=>{});const g=()=>!p&&!t.closed&&t.epoch===n&&t.job===h;function v(){return!p&&(p=!0,t.job===h&&(t.job=null,e.processing=!1),s([()=>clearTimeout(d),m,...b.splice(0)]),!0)}function w(t,n=!0){if(!p){try{v()}catch(r){t||(t=r)}if(n)try{e.emit("error",t?.message)}catch(o){t=o}u(t)}}function U(t){if(!g())return;f=!0,e.processing=!0;let n=0;const o=()=>{if(!g())return;if(n===t.points.length)return void function(){if(g())try{v(),e.emit("done"),a()}catch(t){let r=t;try{e.emit("error",t?.message)}catch(n){r=n}u(r)}}();const s=t.points[n];let l=!1,d=!1;const h=c.oncanplay,p=()=>{if(!(!g()||l||c.seeking||void 0!==c.readyState&&c.readyState<2)){l=!0;try{if(m(),t.context.drawImage(c,s.x,s.y,t.width,t.height),!g())return;t.canvas.toBlob(s=>{if(g()&&!d){d=!0;try{if(!s)throw new Error("Unable to create thumbnail image");const c=function(e,t,n){const o=r(e),s=URL.createObjectURL(t);if(o.thumbnailUrls.add(s),!n())return i(o.thumbnailUrls,s),null;const c=new Set([...o.thumbnailUrls,e.thumbnailUrl]);o.thumbnailUrl=s,e.thumbnailUrl=s;for(const r of c)r&&r!==s&&i(o.thumbnailUrls,r);return n()?s:null}(e,s,g);if(!c)return;e.emit("update",c,(n+1)/t.number),n++,Promise.resolve().then(o)}catch(c){w(c)}}})}catch(a){w(a)}}};m=()=>{c.oncanplay===p&&(c.oncanplay=h),c.removeEventListener("seeked",p)};try{c.oncanplay=p,c.addEventListener("seeked",p),c.currentTime=s.time,c.readyState>=2&&!c.seeking&&Promise.resolve().then(p)}catch(f){w(f)}};Promise.resolve().then(o)}function E(n=!1){if(g()&&!f)if(clearTimeout(d),!t.loading&&c.duration){f=!0;try{const t=function(e,t){const{video:n}=t,{width:r,number:o,begin:i,end:s}=e.option,c=n.videoHeight/n.videoWidth*r,a=(n,r)=>{try{e.errorHandle(n,r)}catch(o){throw t.reported=o,t.wasReported=!0,o}};e.option.height=c,e.option.begin=l(i,0,n.duration),e.option.end=l(s||n.duration,i,n.duration),a(e.option.end>e.option.begin,"End time must be greater than the start time"),a(Number.isFinite(n.duration),"Video duration must be finite"),e.duration=e.option.end-e.option.begin,e.density=o/e.duration,a(e.file&&n,"Please select the video file first"),a(!e.processing,"There is currently a task in progress, please wait a moment..."),a(e.density<=1,`The preview density cannot be greater than 1, but got ${e.density}`);const u=e.creatScreenshotDate(),d=e.creatCanvas(),h=d.getContext("2d");return e.emit("canvas",d),{width:r,height:c,number:o,points:u,canvas:d,context:h}}(e,h);U(t)}catch(r){if(w(r,!n&&!(h.wasReported&&r===h.reported)),n)throw r}}else d=setTimeout(()=>E(),1e3),g()||clearTimeout(d)}h={video:c,promise:y,ready:E,fail:w,cancel(e){w(o(e),!1)},adopt:t=>!p&&!f&&c===e.video&&(n=t,!0)},t.job=h;try{for(const[e,t]of[["error",()=>{g()&&w(new Error(`Unable to load video: media error ${c.error?.code||0}`))}],["loadedmetadata",()=>E()],["durationchange",()=>E()]])if(b.push(()=>c.removeEventListener(e,t)),c.addEventListener(e,t),!g())return s([()=>c.removeEventListener(e,t),...b.splice(0)]),y;c.error?w(new Error(`Unable to load video: media error ${c.error.code}`)):E(!0)}catch(j){throw w(j,!1),j}return y}const d=new WeakMap;function h(e){let t;for(const[r,o]of e.listeners.splice(0))try{e.input.removeEventListener(r,o)}catch(n){t||(t=n)}if(e.wrapper){try{e.input.parentNode&&e.input.parentNode.removeChild(e.input)}catch(n){t||(t=n)}"relative"===e.wrapper.style.position&&(e.wrapper.style.position=e.position)}if(t)throw t}function p(e,t){try{for(const[n,r]of t)e.listeners.push([n,r]),e.input.addEventListener(n,r)}catch(n){try{h(e)}catch{}throw n}}function f(e){const t=d.get(e);t&&(d.delete(e),h(t))}class m extends t{constructor(e={}){super(),this.processing=!1,this.option={};try{this.setup(Object.assign({},m.DEFAULTS,e)),this.video=m.creatVideo(),r(this).video=this.video,this.duration=0,this.inputChange=this.inputChange.bind(this),this.ondrop=this.ondrop.bind(this),function(e,t){const n=d.get(e);n.callbacks=[["change",e.inputChange],["dragover",t],["drop",e.ondrop]],p(n,n.callbacks)}(this,m.ondragover)}catch(t){c(this,"construction failed");try{f(this)}catch{}try{a(this)}catch{}throw t}}static get DEFAULTS(){return{number:60,width:160,height:90,column:10,begin:0,end:Number.NaN}}static ondragover(e){e.preventDefault()}ondrop(e){if(r(this).closed)return;e.preventDefault();const t=e.dataTransfer.files[0];this.loadVideo(t)}setup(e={}){return r(this).closed||function(e,t){const n=r(e),i=++n.inputEpoch,s=()=>!n.closed&&n.inputEpoch===i,c=Object.assign({},e.option,t),a=c.fileInput;e.errorHandle(a instanceof Element,"The 'fileInput' is not a Element");for(const r of["number","width","column","begin","end"])e.errorHandle("number"===typeof c[r],`The '${r}' is not a number`);if(c.number=l(c.number,10,1e3),c.width=l(c.width,10,1e3),c.column=l(c.column,1,1e3),!s())throw o("input setup superseded");const u=d.get(e);if(u&&(a===u.input||a===u.wrapper))return c.fileInput=u.input,e.option=c,c;const f={input:a,wrapper:null,position:"",listeners:[],callbacks:u?.callbacks||null};try{if("INPUT"===a.tagName&&"file"===a.type||(f.wrapper=a,f.position=a.style.position,f.input=document.createElement("input"),f.input.type="file",Object.assign(f.input.style,{position:"absolute",width:"100%",height:"100%",left:"0",top:"0",right:"0",bottom:"0",opacity:"0"}),a.style.position="relative",a.appendChild(f.input)),!s())throw o("input setup superseded");if(f.callbacks&&p(f,f.callbacks),!s())throw o("input setup superseded")}catch(m){try{h(f)}catch{}throw m}c.fileInput=f.input,d.set(e,f),e.option=c,u&&h(u)}(this,e),this}static creatVideo(){const e=document.createElement("video");e.style.position="absolute",e.style.top="-9999px",e.style.left="-9999px",e.muted=!0,e.controls=!0;try{document.body.appendChild(e)}catch(t){throw e.parentNode&&e.parentNode.removeChild(e),t}return e}inputChange(e){if(r(this).closed)return;const t=this.option.fileInput.files[0];this.loadVideo(t),e.target.value=""}loadVideo(e){!function(e,t){const n=r(e);if(!t||n.closed)return;const o=n.epoch,c=e.video,a=c.canPlayType(t.type);if(e.errorHandle("maybe"===a||"probably"===a,`Playback of this file format is not supported: ${t.type}`),n.closed||n.epoch!==o)return;const l=URL.createObjectURL(t);if(n.sourceUrls.add(l),n.closed||n.epoch!==o)return void i(n.sourceUrls,l);const u=++n.epoch;!n.sourceUrl&&!e.file&&n.job?.adopt(u)||n.job?.cancel("source changed"),n.loading=!0;const d=()=>!n.closed&&n.epoch===u,h=n.sourceListeners,p=[];n.sourceListeners=p;let f=()=>{},m=!1;const b=()=>{if(!d()||m||c.currentSrc&&c.currentSrc!==l)return;m=!0,n.loading=!1;const t=new Error(`Unable to load video: media error ${c.error?.code||0}`);n.job?n.job.fail(t):e.emit("error",t.message)},y=()=>{!d()||c.currentSrc&&c.currentSrc!==l||(n.loading=!1,n.job?.ready())};try{if(s(h.splice(0)),!d())return void i(n.sourceUrls,l);for(const[e,t]of[["error",b],["loadedmetadata",y]])if(f=()=>c.removeEventListener(e,t),p.push(f),c.addEventListener(e,t),!d())return s([f,...p.splice(0)]),void i(n.sourceUrls,l);if(e.videoUrl=l,e.file=t,e.emit("file",t),!d())return void i(n.sourceUrls,l);if(c.src=l,!d())return void i(n.sourceUrls,l);n.sourceUrl=l;for(const e of[...n.sourceUrls])e!==l&&i(n.sourceUrls,e);d()&&e.emit("video",c)}catch(g){d()&&(n.loading=!1,n.job?.cancel("source setup failed"));try{s([f,...p.splice(0)])}catch{}throw i(n.sourceUrls,l),g}}(this,e)}start(){return function(e){const t=r(e);if(t.closed){const e=Promise.reject(o("destroyed"));return e.catch(()=>{}),e}return e.errorHandle(!t.job,"There is currently a task in progress, please wait a moment..."),u(e,t)}(this)}creatScreenshotDate(){return function(e,t){const{number:n,width:r,height:o,column:i,begin:s}=e,c=t/n,a=[s+c];for(;a.length({time:e-c/2,x:t%i*r,y:Math.floor(t/i)*o}))}(this.option,this.duration)}creatCanvas(){return function(e){const{number:t,width:n,height:r,column:o}=e,i=document.createElement("canvas"),s=i.getContext("2d");return i.width=n*o,i.height=Math.ceil(t/o)*r+30,s.fillStyle="black",s.fillRect(0,0,i.width,i.height),s.font="14px Georgia",s.fillStyle="#fff",s.fillText(`From: https://artplayer.org/, Number: ${t}, Width: ${n}, Height: ${r}, Column: ${o}`,10,i.height-11),i}(this.option)}download(){if(r(this).closed)return this;this.errorHandle(this.file&&this.thumbnailUrl,"Download does not seem to be ready, please create preview first"),this.errorHandle(!this.processing,"There is currently a task in progress, please wait a moment...");const e=function(e,t){const n=document.createElement("a"),r=`${function(e){const t=e.split(".");return t.pop(),t.join(".")}(e.name)}.png`;n.download=r,n.href=t;try{document.body.appendChild(n),n.click()}finally{n.parentNode&&n.parentNode.removeChild(n)}return r}(this.file,this.thumbnailUrl);return this.emit("download",e),this}errorHandle(e,t){if(!e)throw this.emit("error",t),new Error(t)}destroy(){c(this)&&s([()=>f(this),()=>a(this),()=>this.emit("destroy")])}}return m}); diff --git a/docs/compiled/artplayer-tool-thumbnail.legacy.js b/docs/compiled/artplayer-tool-thumbnail.legacy.js index 86ca050d8..8a27da060 100644 --- a/docs/compiled/artplayer-tool-thumbnail.legacy.js +++ b/docs/compiled/artplayer-tool-thumbnail.legacy.js @@ -31,4 +31,4 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -!function(e,t){"object"===typeof exports&&"undefined"!==typeof module?module.exports=t():"function"==typeof define&&define.amd?(e.ArtplayerToolThumbnail=t(),define(function(){return e.ArtplayerToolThumbnail})):(e="undefined"!==typeof globalThis?globalThis:e||self).ArtplayerToolThumbnail=t()}(this,function(){"use strict";class e{on(e,t,n){const r=this.e||(this.e={});return(r[e]||(r[e]=[])).push({fn:t,ctx:n}),this}once(e,t,n){const r=this,o=t;function i(...t){r.off(e,i),o.apply(n,t)}return i._=t,this.on(e,i,n)}emit(e,...t){const n=((this.e||(this.e={}))[e]||[]).slice();for(let r=0;r{var e;return null==(e=n.pause)?void 0:e.call(n)},()=>{var e;return null==(e=n.removeAttribute)?void 0:e.call(n,"src")},()=>{var e;return null==(e=n.load)?void 0:e.call(n)},()=>{var e;return null==(e=n.parentNode)?void 0:e.removeChild(n)});i([...t.sourceListeners.splice(0),...c,...[...r].map(e=>()=>o(t.sourceUrls,e)),...[...s].map(e=>()=>o(t.thumbnailUrls,e))])}function l(e,t,n){return Math.max(Math.min(e,Math.max(t,n)),Math.min(t,n))}function a(e,t){let s=t.epoch;const c=e.video;let a,u,d,h,p=!1,f=!1,m=()=>{};const b=[],v=new Promise((e,t)=>{a=e,u=t});v.catch(()=>{});const y=()=>!p&&!t.closed&&t.epoch===s&&t.job===h;function g(){return!p&&(p=!0,t.job===h&&(t.job=null,e.processing=!1),i([()=>clearTimeout(d),m,...b.splice(0)]),!0)}function w(t,n=!0){if(!p){try{g()}catch(r){t||(t=r)}if(n)try{e.emit("error",null==t?void 0:t.message)}catch(o){t=o}u(t)}}function U(t){if(!y())return;f=!0,e.processing=!0;let r=0;const i=()=>{if(!y())return;if(r===t.points.length)return void function(){if(y())try{g(),e.emit("done"),a()}catch(t){let r=t;try{e.emit("error",null==t?void 0:t.message)}catch(n){r=n}u(r)}}();const s=t.points[r];let l=!1,d=!1;const h=c.oncanplay,p=()=>{if(!(!y()||l||c.seeking||void 0!==c.readyState&&c.readyState<2)){l=!0;try{if(m(),t.context.drawImage(c,s.x,s.y,t.width,t.height),!y())return;t.canvas.toBlob(s=>{if(y()&&!d){d=!0;try{if(!s)throw new Error("Unable to create thumbnail image");const c=function(e,t,r){const i=n(e),s=URL.createObjectURL(t);if(i.thumbnailUrls.add(s),!r())return o(i.thumbnailUrls,s),null;const c=new Set([...i.thumbnailUrls,e.thumbnailUrl]);i.thumbnailUrl=s,e.thumbnailUrl=s;for(const n of c)n&&n!==s&&o(i.thumbnailUrls,n);return r()?s:null}(e,s,y);if(!c)return;e.emit("update",c,(r+1)/t.number),r++,Promise.resolve().then(i)}catch(c){w(c)}}})}catch(a){w(a)}}};m=()=>{c.oncanplay===p&&(c.oncanplay=h),c.removeEventListener("seeked",p)};try{c.oncanplay=p,c.addEventListener("seeked",p),c.currentTime=s.time,c.readyState>=2&&!c.seeking&&Promise.resolve().then(p)}catch(f){w(f)}};Promise.resolve().then(i)}function E(n=!1){if(y()&&!f)if(clearTimeout(d),!t.loading&&c.duration){f=!0;try{const t=function(e,t){const{video:n}=t,{width:r,number:o,begin:i,end:s}=e.option,c=n.videoHeight/n.videoWidth*r,a=(n,r)=>{try{e.errorHandle(n,r)}catch(o){throw t.reported=o,t.wasReported=!0,o}};e.option.height=c,e.option.begin=l(i,0,n.duration),e.option.end=l(s||n.duration,i,n.duration),a(e.option.end>e.option.begin,"End time must be greater than the start time"),a(Number.isFinite(n.duration),"Video duration must be finite"),e.duration=e.option.end-e.option.begin,e.density=o/e.duration,a(e.file&&n,"Please select the video file first"),a(!e.processing,"There is currently a task in progress, please wait a moment..."),a(e.density<=1,`The preview density cannot be greater than 1, but got ${e.density}`);const u=e.creatScreenshotDate(),d=e.creatCanvas(),h=d.getContext("2d");return e.emit("canvas",d),{width:r,height:c,number:o,points:u,canvas:d,context:h}}(e,h);U(t)}catch(r){if(w(r,!n&&!(h.wasReported&&r===h.reported)),n)throw r}}else d=setTimeout(()=>E(),1e3),y()||clearTimeout(d)}h={video:c,promise:v,ready:E,fail:w,cancel(e){w(r(e),!1)},adopt:t=>!p&&!f&&c===e.video&&(s=t,!0)},t.job=h;try{for(const[e,t]of[["error",()=>{var e;y()&&w(new Error(`Unable to load video: media error ${(null==(e=c.error)?void 0:e.code)||0}`))}],["loadedmetadata",()=>E()],["durationchange",()=>E()]])if(b.push(()=>c.removeEventListener(e,t)),c.addEventListener(e,t),!y())return i([()=>c.removeEventListener(e,t),...b.splice(0)]),v;c.error?w(new Error(`Unable to load video: media error ${c.error.code}`)):E(!0)}catch(j){throw w(j,!1),j}return v}const u=new WeakMap;function d(e){let t;for(const[r,o]of e.listeners.splice(0))try{e.input.removeEventListener(r,o)}catch(n){t||(t=n)}if(e.wrapper){try{e.input.parentNode&&e.input.parentNode.removeChild(e.input)}catch(n){t||(t=n)}"relative"===e.wrapper.style.position&&(e.wrapper.style.position=e.position)}if(t)throw t}function h(e,t){try{for(const[n,r]of t)e.listeners.push([n,r]),e.input.addEventListener(n,r)}catch(n){try{d(e)}catch(r){}throw n}}function p(e){const t=u.get(e);t&&(u.delete(e),d(t))}class f extends e{constructor(e={}){super(),this.processing=!1,this.option={};try{this.setup(Object.assign({},f.DEFAULTS,e)),this.video=f.creatVideo(),n(this).video=this.video,this.duration=0,this.inputChange=this.inputChange.bind(this),this.ondrop=this.ondrop.bind(this),function(e,t){const n=u.get(e);n.callbacks=[["change",e.inputChange],["dragover",t],["drop",e.ondrop]],h(n,n.callbacks)}(this,f.ondragover)}catch(t){s(this,"construction failed");try{p(this)}catch(r){}try{c(this)}catch(r){}throw t}}static get DEFAULTS(){return{number:60,width:160,height:90,column:10,begin:0,end:Number.NaN}}static ondragover(e){e.preventDefault()}ondrop(e){if(n(this).closed)return;e.preventDefault();const t=e.dataTransfer.files[0];this.loadVideo(t)}setup(e={}){return n(this).closed||function(e,t){const o=n(e),i=++o.inputEpoch,s=()=>!o.closed&&o.inputEpoch===i,c=Object.assign({},e.option,t),a=c.fileInput;e.errorHandle(a instanceof Element,"The 'fileInput' is not a Element");for(const n of["number","width","column","begin","end"])e.errorHandle("number"===typeof c[n],`The '${n}' is not a number`);if(c.number=l(c.number,10,1e3),c.width=l(c.width,10,1e3),c.column=l(c.column,1,1e3),!s())throw r("input setup superseded");const p=u.get(e);if(p&&(a===p.input||a===p.wrapper))return c.fileInput=p.input,e.option=c,c;const f={input:a,wrapper:null,position:"",listeners:[],callbacks:(null==p?void 0:p.callbacks)||null};try{if("INPUT"===a.tagName&&"file"===a.type||(f.wrapper=a,f.position=a.style.position,f.input=document.createElement("input"),f.input.type="file",Object.assign(f.input.style,{position:"absolute",width:"100%",height:"100%",left:"0",top:"0",right:"0",bottom:"0",opacity:"0"}),a.style.position="relative",a.appendChild(f.input)),!s())throw r("input setup superseded");if(f.callbacks&&h(f,f.callbacks),!s())throw r("input setup superseded")}catch(m){try{d(f)}catch(b){}throw m}c.fileInput=f.input,u.set(e,f),e.option=c,p&&d(p)}(this,e),this}static creatVideo(){const e=document.createElement("video");e.style.position="absolute",e.style.top="-9999px",e.style.left="-9999px",e.muted=!0,e.controls=!0;try{document.body.appendChild(e)}catch(t){throw e.parentNode&&e.parentNode.removeChild(e),t}return e}inputChange(e){if(n(this).closed)return;const t=this.option.fileInput.files[0];this.loadVideo(t),e.target.value=""}loadVideo(e){!function(e,t){var r,s,c;const l=n(e);if(!t||l.closed)return;const a=l.epoch,u=e.video,d=u.canPlayType(t.type);if(e.errorHandle("maybe"===d||"probably"===d,`Playback of this file format is not supported: ${t.type}`),l.closed||l.epoch!==a)return;const h=URL.createObjectURL(t);if(l.sourceUrls.add(h),l.closed||l.epoch!==a)return void o(l.sourceUrls,h);const p=++l.epoch;!l.sourceUrl&&!e.file&&(null==(r=l.job)?void 0:r.adopt(p))||null==(s=l.job)||s.cancel("source changed"),l.loading=!0;const f=()=>!l.closed&&l.epoch===p,m=l.sourceListeners,b=[];l.sourceListeners=b;let v=()=>{},y=!1;const g=()=>{var t;if(!f()||y||u.currentSrc&&u.currentSrc!==h)return;y=!0,l.loading=!1;const n=new Error(`Unable to load video: media error ${(null==(t=u.error)?void 0:t.code)||0}`);l.job?l.job.fail(n):e.emit("error",n.message)},w=()=>{var e;!f()||u.currentSrc&&u.currentSrc!==h||(l.loading=!1,null==(e=l.job)||e.ready())};try{if(i(m.splice(0)),!f())return void o(l.sourceUrls,h);for(const[e,t]of[["error",g],["loadedmetadata",w]])if(v=()=>u.removeEventListener(e,t),b.push(v),u.addEventListener(e,t),!f())return i([v,...b.splice(0)]),void o(l.sourceUrls,h);if(e.videoUrl=h,e.file=t,e.emit("file",t),!f())return void o(l.sourceUrls,h);if(u.src=h,!f())return void o(l.sourceUrls,h);l.sourceUrl=h;for(const e of[...l.sourceUrls])e!==h&&o(l.sourceUrls,e);f()&&e.emit("video",u)}catch(U){f()&&(l.loading=!1,null==(c=l.job)||c.cancel("source setup failed"));try{i([v,...b.splice(0)])}catch(E){}throw o(l.sourceUrls,h),U}}(this,e)}start(){return function(e){const t=n(e);if(t.closed){const e=Promise.reject(r("destroyed"));return e.catch(()=>{}),e}return e.errorHandle(!t.job,"There is currently a task in progress, please wait a moment..."),a(e,t)}(this)}creatScreenshotDate(){return function(e,t){const{number:n,width:r,height:o,column:i,begin:s}=e,c=t/n,l=[s+c];for(;l.length({time:e-c/2,x:t%i*r,y:Math.floor(t/i)*o}))}(this.option,this.duration)}creatCanvas(){return function(e){const{number:t,width:n,height:r,column:o}=e,i=document.createElement("canvas"),s=i.getContext("2d");return i.width=n*o,i.height=Math.ceil(t/o)*r+30,s.fillStyle="black",s.fillRect(0,0,i.width,i.height),s.font="14px Georgia",s.fillStyle="#fff",s.fillText(`From: https://artplayer.org/, Number: ${t}, Width: ${n}, Height: ${r}, Column: ${o}`,10,i.height-11),i}(this.option)}download(){if(n(this).closed)return this;this.errorHandle(this.file&&this.thumbnailUrl,"Download does not seem to be ready, please create preview first"),this.errorHandle(!this.processing,"There is currently a task in progress, please wait a moment...");const e=function(e,t){const n=document.createElement("a"),r=`${function(e){const t=e.split(".");return t.pop(),t.join(".")}(e.name)}.png`;n.download=r,n.href=t;try{document.body.appendChild(n),n.click()}finally{n.parentNode&&n.parentNode.removeChild(n)}return r}(this.file,this.thumbnailUrl);return this.emit("download",e),this}errorHandle(e,t){if(!e)throw this.emit("error",t),new Error(t)}destroy(){s(this)&&i([()=>p(this),()=>c(this),()=>this.emit("destroy")])}}return f}); +!function(e,t){"object"===typeof exports&&"undefined"!==typeof module?module.exports=t():"function"==typeof define&&define.amd?(e.ArtplayerToolThumbnail=t(),define(function(){return e.ArtplayerToolThumbnail})):(e="undefined"!==typeof globalThis?globalThis:e||self).ArtplayerToolThumbnail=t()}(this,function(){"use strict";const e=(e,t)=>Object.prototype.hasOwnProperty.call(e,t);class t{on(t,n,r){const o=this.e||(this.e={});let i=e(o,t)?o[t]:void 0;return i||(i=[],Object.defineProperty(o,t,{value:i,enumerable:!0,configurable:!0,writable:!0})),i.push({fn:n,ctx:r}),this}once(e,t,n){const r=this,o=t;let i=!1;function s(...t){i||(i=!0,r.off(e,s),o.apply(n,t))}return s._=t,this.on(e,s,n)}emit(t,...n){const r=this.e||(this.e={}),o=(e(r,t)&&r[t]||[]).slice();for(let e=0;e{var e;return null==(e=r.pause)?void 0:e.call(r)},()=>{var e;return null==(e=r.removeAttribute)?void 0:e.call(r,"src")},()=>{var e;return null==(e=r.load)?void 0:e.call(r)},()=>{var e;return null==(e=r.parentNode)?void 0:e.removeChild(r)});s([...t.sourceListeners.splice(0),...c,...[...n].map(e=>()=>i(t.sourceUrls,e)),...[...o].map(e=>()=>i(t.thumbnailUrls,e))])}function a(e,t,n){return Math.max(Math.min(e,Math.max(t,n)),Math.min(t,n))}function u(e,t){let n=t.epoch;const c=e.video;let l,u,d,h,p=!1,f=!1,m=()=>{};const b=[],v=new Promise((e,t)=>{l=e,u=t});v.catch(()=>{});const y=()=>!p&&!t.closed&&t.epoch===n&&t.job===h;function g(){return!p&&(p=!0,t.job===h&&(t.job=null,e.processing=!1),s([()=>clearTimeout(d),m,...b.splice(0)]),!0)}function w(t,n=!0){if(!p){try{g()}catch(r){t||(t=r)}if(n)try{e.emit("error",null==t?void 0:t.message)}catch(o){t=o}u(t)}}function U(t){if(!y())return;f=!0,e.processing=!0;let n=0;const o=()=>{if(!y())return;if(n===t.points.length)return void function(){if(y())try{g(),e.emit("done"),l()}catch(t){let r=t;try{e.emit("error",null==t?void 0:t.message)}catch(n){r=n}u(r)}}();const s=t.points[n];let a=!1,d=!1;const h=c.oncanplay,p=()=>{if(!(!y()||a||c.seeking||void 0!==c.readyState&&c.readyState<2)){a=!0;try{if(m(),t.context.drawImage(c,s.x,s.y,t.width,t.height),!y())return;t.canvas.toBlob(s=>{if(y()&&!d){d=!0;try{if(!s)throw new Error("Unable to create thumbnail image");const c=function(e,t,n){const o=r(e),s=URL.createObjectURL(t);if(o.thumbnailUrls.add(s),!n())return i(o.thumbnailUrls,s),null;const c=new Set([...o.thumbnailUrls,e.thumbnailUrl]);o.thumbnailUrl=s,e.thumbnailUrl=s;for(const r of c)r&&r!==s&&i(o.thumbnailUrls,r);return n()?s:null}(e,s,y);if(!c)return;e.emit("update",c,(n+1)/t.number),n++,Promise.resolve().then(o)}catch(c){w(c)}}})}catch(l){w(l)}}};m=()=>{c.oncanplay===p&&(c.oncanplay=h),c.removeEventListener("seeked",p)};try{c.oncanplay=p,c.addEventListener("seeked",p),c.currentTime=s.time,c.readyState>=2&&!c.seeking&&Promise.resolve().then(p)}catch(f){w(f)}};Promise.resolve().then(o)}function E(n=!1){if(y()&&!f)if(clearTimeout(d),!t.loading&&c.duration){f=!0;try{const t=function(e,t){const{video:n}=t,{width:r,number:o,begin:i,end:s}=e.option,c=n.videoHeight/n.videoWidth*r,l=(n,r)=>{try{e.errorHandle(n,r)}catch(o){throw t.reported=o,t.wasReported=!0,o}};e.option.height=c,e.option.begin=a(i,0,n.duration),e.option.end=a(s||n.duration,i,n.duration),l(e.option.end>e.option.begin,"End time must be greater than the start time"),l(Number.isFinite(n.duration),"Video duration must be finite"),e.duration=e.option.end-e.option.begin,e.density=o/e.duration,l(e.file&&n,"Please select the video file first"),l(!e.processing,"There is currently a task in progress, please wait a moment..."),l(e.density<=1,`The preview density cannot be greater than 1, but got ${e.density}`);const u=e.creatScreenshotDate(),d=e.creatCanvas(),h=d.getContext("2d");return e.emit("canvas",d),{width:r,height:c,number:o,points:u,canvas:d,context:h}}(e,h);U(t)}catch(r){if(w(r,!n&&!(h.wasReported&&r===h.reported)),n)throw r}}else d=setTimeout(()=>E(),1e3),y()||clearTimeout(d)}h={video:c,promise:v,ready:E,fail:w,cancel(e){w(o(e),!1)},adopt:t=>!p&&!f&&c===e.video&&(n=t,!0)},t.job=h;try{for(const[e,t]of[["error",()=>{var e;y()&&w(new Error(`Unable to load video: media error ${(null==(e=c.error)?void 0:e.code)||0}`))}],["loadedmetadata",()=>E()],["durationchange",()=>E()]])if(b.push(()=>c.removeEventListener(e,t)),c.addEventListener(e,t),!y())return s([()=>c.removeEventListener(e,t),...b.splice(0)]),v;c.error?w(new Error(`Unable to load video: media error ${c.error.code}`)):E(!0)}catch(j){throw w(j,!1),j}return v}const d=new WeakMap;function h(e){let t;for(const[r,o]of e.listeners.splice(0))try{e.input.removeEventListener(r,o)}catch(n){t||(t=n)}if(e.wrapper){try{e.input.parentNode&&e.input.parentNode.removeChild(e.input)}catch(n){t||(t=n)}"relative"===e.wrapper.style.position&&(e.wrapper.style.position=e.position)}if(t)throw t}function p(e,t){try{for(const[n,r]of t)e.listeners.push([n,r]),e.input.addEventListener(n,r)}catch(n){try{h(e)}catch(r){}throw n}}function f(e){const t=d.get(e);t&&(d.delete(e),h(t))}class m extends t{constructor(e={}){super(),this.processing=!1,this.option={};try{this.setup(Object.assign({},m.DEFAULTS,e)),this.video=m.creatVideo(),r(this).video=this.video,this.duration=0,this.inputChange=this.inputChange.bind(this),this.ondrop=this.ondrop.bind(this),function(e,t){const n=d.get(e);n.callbacks=[["change",e.inputChange],["dragover",t],["drop",e.ondrop]],p(n,n.callbacks)}(this,m.ondragover)}catch(t){c(this,"construction failed");try{f(this)}catch(n){}try{l(this)}catch(n){}throw t}}static get DEFAULTS(){return{number:60,width:160,height:90,column:10,begin:0,end:Number.NaN}}static ondragover(e){e.preventDefault()}ondrop(e){if(r(this).closed)return;e.preventDefault();const t=e.dataTransfer.files[0];this.loadVideo(t)}setup(e={}){return r(this).closed||function(e,t){const n=r(e),i=++n.inputEpoch,s=()=>!n.closed&&n.inputEpoch===i,c=Object.assign({},e.option,t),l=c.fileInput;e.errorHandle(l instanceof Element,"The 'fileInput' is not a Element");for(const r of["number","width","column","begin","end"])e.errorHandle("number"===typeof c[r],`The '${r}' is not a number`);if(c.number=a(c.number,10,1e3),c.width=a(c.width,10,1e3),c.column=a(c.column,1,1e3),!s())throw o("input setup superseded");const u=d.get(e);if(u&&(l===u.input||l===u.wrapper))return c.fileInput=u.input,e.option=c,c;const f={input:l,wrapper:null,position:"",listeners:[],callbacks:(null==u?void 0:u.callbacks)||null};try{if("INPUT"===l.tagName&&"file"===l.type||(f.wrapper=l,f.position=l.style.position,f.input=document.createElement("input"),f.input.type="file",Object.assign(f.input.style,{position:"absolute",width:"100%",height:"100%",left:"0",top:"0",right:"0",bottom:"0",opacity:"0"}),l.style.position="relative",l.appendChild(f.input)),!s())throw o("input setup superseded");if(f.callbacks&&p(f,f.callbacks),!s())throw o("input setup superseded")}catch(m){try{h(f)}catch(b){}throw m}c.fileInput=f.input,d.set(e,f),e.option=c,u&&h(u)}(this,e),this}static creatVideo(){const e=document.createElement("video");e.style.position="absolute",e.style.top="-9999px",e.style.left="-9999px",e.muted=!0,e.controls=!0;try{document.body.appendChild(e)}catch(t){throw e.parentNode&&e.parentNode.removeChild(e),t}return e}inputChange(e){if(r(this).closed)return;const t=this.option.fileInput.files[0];this.loadVideo(t),e.target.value=""}loadVideo(e){!function(e,t){var n,o,c;const l=r(e);if(!t||l.closed)return;const a=l.epoch,u=e.video,d=u.canPlayType(t.type);if(e.errorHandle("maybe"===d||"probably"===d,`Playback of this file format is not supported: ${t.type}`),l.closed||l.epoch!==a)return;const h=URL.createObjectURL(t);if(l.sourceUrls.add(h),l.closed||l.epoch!==a)return void i(l.sourceUrls,h);const p=++l.epoch;!l.sourceUrl&&!e.file&&(null==(n=l.job)?void 0:n.adopt(p))||null==(o=l.job)||o.cancel("source changed"),l.loading=!0;const f=()=>!l.closed&&l.epoch===p,m=l.sourceListeners,b=[];l.sourceListeners=b;let v=()=>{},y=!1;const g=()=>{var t;if(!f()||y||u.currentSrc&&u.currentSrc!==h)return;y=!0,l.loading=!1;const n=new Error(`Unable to load video: media error ${(null==(t=u.error)?void 0:t.code)||0}`);l.job?l.job.fail(n):e.emit("error",n.message)},w=()=>{var e;!f()||u.currentSrc&&u.currentSrc!==h||(l.loading=!1,null==(e=l.job)||e.ready())};try{if(s(m.splice(0)),!f())return void i(l.sourceUrls,h);for(const[e,t]of[["error",g],["loadedmetadata",w]])if(v=()=>u.removeEventListener(e,t),b.push(v),u.addEventListener(e,t),!f())return s([v,...b.splice(0)]),void i(l.sourceUrls,h);if(e.videoUrl=h,e.file=t,e.emit("file",t),!f())return void i(l.sourceUrls,h);if(u.src=h,!f())return void i(l.sourceUrls,h);l.sourceUrl=h;for(const e of[...l.sourceUrls])e!==h&&i(l.sourceUrls,e);f()&&e.emit("video",u)}catch(U){f()&&(l.loading=!1,null==(c=l.job)||c.cancel("source setup failed"));try{s([v,...b.splice(0)])}catch(E){}throw i(l.sourceUrls,h),U}}(this,e)}start(){return function(e){const t=r(e);if(t.closed){const e=Promise.reject(o("destroyed"));return e.catch(()=>{}),e}return e.errorHandle(!t.job,"There is currently a task in progress, please wait a moment..."),u(e,t)}(this)}creatScreenshotDate(){return function(e,t){const{number:n,width:r,height:o,column:i,begin:s}=e,c=t/n,l=[s+c];for(;l.length({time:e-c/2,x:t%i*r,y:Math.floor(t/i)*o}))}(this.option,this.duration)}creatCanvas(){return function(e){const{number:t,width:n,height:r,column:o}=e,i=document.createElement("canvas"),s=i.getContext("2d");return i.width=n*o,i.height=Math.ceil(t/o)*r+30,s.fillStyle="black",s.fillRect(0,0,i.width,i.height),s.font="14px Georgia",s.fillStyle="#fff",s.fillText(`From: https://artplayer.org/, Number: ${t}, Width: ${n}, Height: ${r}, Column: ${o}`,10,i.height-11),i}(this.option)}download(){if(r(this).closed)return this;this.errorHandle(this.file&&this.thumbnailUrl,"Download does not seem to be ready, please create preview first"),this.errorHandle(!this.processing,"There is currently a task in progress, please wait a moment...");const e=function(e,t){const n=document.createElement("a"),r=`${function(e){const t=e.split(".");return t.pop(),t.join(".")}(e.name)}.png`;n.download=r,n.href=t;try{document.body.appendChild(n),n.click()}finally{n.parentNode&&n.parentNode.removeChild(n)}return r}(this.file,this.thumbnailUrl);return this.emit("download",e),this}errorHandle(e,t){if(!e)throw this.emit("error",t),new Error(t)}destroy(){c(this)&&s([()=>f(this),()=>l(this),()=>this.emit("destroy")])}}return m}); diff --git a/docs/compiled/artplayer-tool-thumbnail.mjs b/docs/compiled/artplayer-tool-thumbnail.mjs index 7fbe1017c..5adc54695 100644 --- a/docs/compiled/artplayer-tool-thumbnail.mjs +++ b/docs/compiled/artplayer-tool-thumbnail.mjs @@ -31,16 +31,26 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ +const owns = (object, name) => Object.prototype.hasOwnProperty.call(object, name); class Emitter { on(name, fn, ctx) { const e = this.e || (this.e = {}); - (e[name] || (e[name] = [])).push({ fn, ctx }); + let listeners = owns(e, name) ? e[name] : void 0; + if (!listeners) { + listeners = []; + Object.defineProperty(e, name, { value: listeners, enumerable: true, configurable: true, writable: true }); + } + listeners.push({ fn, ctx }); return this; } once(name, fn, ctx) { const self = this; const callback = fn; + let fired = false; function listener(...args) { + if (fired) + return; + fired = true; self.off(name, listener); callback.apply(ctx, args); } @@ -49,7 +59,7 @@ class Emitter { } emit(name, ...data) { const e = this.e || (this.e = {}); - const evtArr = (e[name] || []).slice(); + const evtArr = (owns(e, name) ? e[name] || [] : []).slice(); for (let i = 0; i < evtArr.length; i += 1) { evtArr[i].fn.apply(evtArr[i].ctx, data); } @@ -57,7 +67,7 @@ class Emitter { } off(name, callback) { const e = this.e || (this.e = {}); - const evts = e[name]; + const evts = owns(e, name) ? e[name] : void 0; const liveEvents = []; if (evts && callback) { for (let i = 0, len = evts.length; i < len; i += 1) { diff --git a/package.json b/package.json index 1513bb672..4b6128ab2 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,7 @@ "ci:build": "yarn build:types && yarn build all && yarn build:i18n && yarn build:ts && yarn build:docs && yarn test:imports", "test:imports": "node --test test/esm.test.js test/i18n.test.js test/ssr.test.js", "typecheck": "node scripts/typecheck.mjs", - "test:unit": "node --test test/thumbnail-runtime.test.js test/thumbnail-vendor.test.js test/thumbnail-lifecycle.test.js test/thumbnail-input.test.js test/thumbnail.test.js test/iframe-navigation.test.js test/iframe-boundaries.test.js test/iframe-lifecycle.test.js test/iframe.test.js test/mediabunny.test.js test/mediabunny-shim.test.js test/mediabunny-coordination.test.js test/mediabunny-video.test.js test/mediabunny-audio.test.js test/mediabunny-hls.test.js test/mediabunny-entry.test.js test/mediabunny-capability.test.js test/mediabunny-load.test.js test/mediabunny-input.test.js test/dpip.test.js test/dpip-lifecycle.test.js test/canvas.test.js test/canvas-lifecycle.test.js test/ambilight.test.js test/ambilight-lifecycle.test.js test/ambilight-proxy.test.js test/vast.test.js test/vast-lifecycle.test.js test/ads.test.js test/ads-lifecycle.test.js test/playback.test.js test/dash-control.test.js test/dash-contract.test.js test/dash-lifecycle.test.js test/dash-events.test.js test/hls-control.test.js test/audio-track.test.js test/public-behavior.test.js test/helpers.test.js test/chapter.test.js test/utils.test.js test/resource-scope.test.js test/instance-lifecycle.test.js test/options.test.js test/media-hosts.test.js test/plugins.test.js test/source.test.js test/playback-properties.test.js test/media-events.test.js test/template-resources.test.js test/core-vendor.test.js test/component-resources.test.js test/setting-model.test.js test/setting-layout.test.js test/setting-resources.test.js test/subtitle.test.js test/display-native.test.js test/display-video-fullscreen.test.js test/display-pip.test.js test/display-mini.test.js test/display-sizing.test.js test/display-orientation.test.js test/hotkey.test.js test/listener-registry.test.js test/global-events.test.js test/pointer-events.test.js test/gesture.test.js test/event-scheduling.test.js test/notice.test.js test/fast-forward.test.js test/auto-playback.test.js test/builtin-layers.test.js test/prompt-components.test.js test/screenshot.test.js test/thumbnails.test.js test/progress.test.js test/environment.test.js test/storage.test.js test/facade-properties.test.js test/dom-boundaries.test.js test/initialization.test.js test/entry.test.js test/accessibility-button.test.js test/accessibility-focus.test.js test/accessibility-slider.test.js", + "test:unit": "node --test test/thumbnail-emitter.test.js test/thumbnail-runtime.test.js test/thumbnail-vendor.test.js test/thumbnail-lifecycle.test.js test/thumbnail-input.test.js test/thumbnail.test.js test/iframe-navigation.test.js test/iframe-boundaries.test.js test/iframe-lifecycle.test.js test/iframe.test.js test/mediabunny.test.js test/mediabunny-shim.test.js test/mediabunny-coordination.test.js test/mediabunny-video.test.js test/mediabunny-audio.test.js test/mediabunny-hls.test.js test/mediabunny-entry.test.js test/mediabunny-capability.test.js test/mediabunny-load.test.js test/mediabunny-input.test.js test/dpip.test.js test/dpip-lifecycle.test.js test/canvas.test.js test/canvas-lifecycle.test.js test/ambilight.test.js test/ambilight-lifecycle.test.js test/ambilight-proxy.test.js test/vast.test.js test/vast-lifecycle.test.js test/ads.test.js test/ads-lifecycle.test.js test/playback.test.js test/dash-control.test.js test/dash-contract.test.js test/dash-lifecycle.test.js test/dash-events.test.js test/hls-control.test.js test/audio-track.test.js test/public-behavior.test.js test/helpers.test.js test/chapter.test.js test/utils.test.js test/resource-scope.test.js test/instance-lifecycle.test.js test/options.test.js test/media-hosts.test.js test/plugins.test.js test/source.test.js test/playback-properties.test.js test/media-events.test.js test/template-resources.test.js test/core-vendor.test.js test/component-resources.test.js test/setting-model.test.js test/setting-layout.test.js test/setting-resources.test.js test/subtitle.test.js test/display-native.test.js test/display-video-fullscreen.test.js test/display-pip.test.js test/display-mini.test.js test/display-sizing.test.js test/display-orientation.test.js test/hotkey.test.js test/listener-registry.test.js test/global-events.test.js test/pointer-events.test.js test/gesture.test.js test/event-scheduling.test.js test/notice.test.js test/fast-forward.test.js test/auto-playback.test.js test/builtin-layers.test.js test/prompt-components.test.js test/screenshot.test.js test/thumbnails.test.js test/progress.test.js test/environment.test.js test/storage.test.js test/facade-properties.test.js test/dom-boundaries.test.js test/initialization.test.js test/entry.test.js test/accessibility-button.test.js test/accessibility-focus.test.js test/accessibility-slider.test.js", "test:coverage": "node --test test/coverage.test.js && node scripts/coverage.mjs", "test": "yarn test:node && yarn test:baseline", "test:browser": "playwright test", @@ -83,7 +83,7 @@ "generate:media:hls": "node scripts/generate-hls-fixture.mjs", "test:iframe": "node --test test/iframe-navigation.test.js test/iframe-boundaries.test.js test/iframe.test.js test/iframe-lifecycle.test.js refactor/scripts/iframe-contract.test.mjs refactor/scripts/iframe-types.test.mjs", "test:iframe-types-package": "node refactor/scripts/iframe-package-types.mjs", - "test:thumbnail": "node --test test/thumbnail-runtime.test.js test/thumbnail-vendor.test.js test/thumbnail-lifecycle.test.js test/thumbnail-input.test.js test/thumbnail.test.js refactor/scripts/thumbnail-contract.test.mjs refactor/scripts/thumbnail-types.test.mjs", + "test:thumbnail": "node --test test/thumbnail-emitter.test.js test/thumbnail-runtime.test.js test/thumbnail-vendor.test.js test/thumbnail-lifecycle.test.js test/thumbnail-input.test.js test/thumbnail.test.js refactor/scripts/thumbnail-contract.test.mjs refactor/scripts/thumbnail-types.test.mjs", "test:thumbnail-types-package": "node refactor/scripts/thumbnail-package-types.mjs" }, "browserslist": "last 1 Chrome version", diff --git a/packages/artplayer-tool-thumbnail/ARCHITECTURE.md b/packages/artplayer-tool-thumbnail/ARCHITECTURE.md index 4255b7f01..9d5aed572 100644 --- a/packages/artplayer-tool-thumbnail/ARCHITECTURE.md +++ b/packages/artplayer-tool-thumbnail/ARCHITECTURE.md @@ -138,10 +138,19 @@ build includes the complete notice in main, legacy and ESM bundle headers. Current packed contents include the complete notice and no implementation source; new release candidates must repeat the installed check. Do not replace this local emitter with the core emitter as part of a type-only change: dispatch semantics -and historical prototype-key handling need their own behavior review. +need their own behavior review. The subsequent emitter fix checks only own +registry keys, defines new slots as ordinary enumerable/writable/configurable +data properties and leaves the registry prototype unchanged. Inherited getters +and setters cannot intercept event registration or dispatch. Each once wrapper +is consumed before removal/invocation so nested snapshots cannot invoke it twice, +including after a callback throws. New registrations of the same callback remain +independent. Normal snapshot order and off(originalCallback) are preserved. `test/thumbnail-runtime.test.js` compares upstream, recovered/historical bundles and the selected candidate for dispatch behavior, and checks candidate public descriptors and validation. `test/thumbnail-vendor.test.js` verifies fixed source bytes, complete notices and dist/docs equality. `test/types/thumbnail-runtime.ts` checks source consumers with positive cases and six rejected invalid uses. +`test/thumbnail-emitter.test.js` reproduces the old key-collision/nested-once +failures and checks the candidate; `test/browser/thumbnail-emitter.spec.js` repeats +the behavior with the real browser class and native DOM cleanup. diff --git a/packages/artplayer-tool-thumbnail/dist/artplayer-tool-thumbnail.js b/packages/artplayer-tool-thumbnail/dist/artplayer-tool-thumbnail.js index 4bee91cde..e2b100bfb 100644 --- a/packages/artplayer-tool-thumbnail/dist/artplayer-tool-thumbnail.js +++ b/packages/artplayer-tool-thumbnail/dist/artplayer-tool-thumbnail.js @@ -31,4 +31,4 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -!function(e,t){"object"===typeof exports&&"undefined"!==typeof module?module.exports=t():"function"==typeof define&&define.amd?(e.ArtplayerToolThumbnail=t(),define(function(){return e.ArtplayerToolThumbnail})):(e="undefined"!==typeof globalThis?globalThis:e||self).ArtplayerToolThumbnail=t()}(this,function(){"use strict";class e{on(e,t,n){const r=this.e||(this.e={});return(r[e]||(r[e]=[])).push({fn:t,ctx:n}),this}once(e,t,n){const r=this,o=t;function i(...t){r.off(e,i),o.apply(n,t)}return i._=t,this.on(e,i,n)}emit(e,...t){const n=((this.e||(this.e={}))[e]||[]).slice();for(let r=0;rn.pause?.(),()=>n.removeAttribute?.("src"),()=>n.load?.(),()=>n.parentNode?.removeChild(n));i([...t.sourceListeners.splice(0),...c,...[...r].map(e=>()=>o(t.sourceUrls,e)),...[...s].map(e=>()=>o(t.thumbnailUrls,e))])}function a(e,t,n){return Math.max(Math.min(e,Math.max(t,n)),Math.min(t,n))}function l(e,t){let s=t.epoch;const c=e.video;let l,u,d,h,p=!1,f=!1,m=()=>{};const b=[],y=new Promise((e,t)=>{l=e,u=t});y.catch(()=>{});const g=()=>!p&&!t.closed&&t.epoch===s&&t.job===h;function v(){return!p&&(p=!0,t.job===h&&(t.job=null,e.processing=!1),i([()=>clearTimeout(d),m,...b.splice(0)]),!0)}function w(t,n=!0){if(!p){try{v()}catch(r){t||(t=r)}if(n)try{e.emit("error",t?.message)}catch(o){t=o}u(t)}}function U(t){if(!g())return;f=!0,e.processing=!0;let r=0;const i=()=>{if(!g())return;if(r===t.points.length)return void function(){if(g())try{v(),e.emit("done"),l()}catch(t){let r=t;try{e.emit("error",t?.message)}catch(n){r=n}u(r)}}();const s=t.points[r];let a=!1,d=!1;const h=c.oncanplay,p=()=>{if(!(!g()||a||c.seeking||void 0!==c.readyState&&c.readyState<2)){a=!0;try{if(m(),t.context.drawImage(c,s.x,s.y,t.width,t.height),!g())return;t.canvas.toBlob(s=>{if(g()&&!d){d=!0;try{if(!s)throw new Error("Unable to create thumbnail image");const c=function(e,t,r){const i=n(e),s=URL.createObjectURL(t);if(i.thumbnailUrls.add(s),!r())return o(i.thumbnailUrls,s),null;const c=new Set([...i.thumbnailUrls,e.thumbnailUrl]);i.thumbnailUrl=s,e.thumbnailUrl=s;for(const n of c)n&&n!==s&&o(i.thumbnailUrls,n);return r()?s:null}(e,s,g);if(!c)return;e.emit("update",c,(r+1)/t.number),r++,Promise.resolve().then(i)}catch(c){w(c)}}})}catch(l){w(l)}}};m=()=>{c.oncanplay===p&&(c.oncanplay=h),c.removeEventListener("seeked",p)};try{c.oncanplay=p,c.addEventListener("seeked",p),c.currentTime=s.time,c.readyState>=2&&!c.seeking&&Promise.resolve().then(p)}catch(f){w(f)}};Promise.resolve().then(i)}function E(n=!1){if(g()&&!f)if(clearTimeout(d),!t.loading&&c.duration){f=!0;try{const t=function(e,t){const{video:n}=t,{width:r,number:o,begin:i,end:s}=e.option,c=n.videoHeight/n.videoWidth*r,l=(n,r)=>{try{e.errorHandle(n,r)}catch(o){throw t.reported=o,t.wasReported=!0,o}};e.option.height=c,e.option.begin=a(i,0,n.duration),e.option.end=a(s||n.duration,i,n.duration),l(e.option.end>e.option.begin,"End time must be greater than the start time"),l(Number.isFinite(n.duration),"Video duration must be finite"),e.duration=e.option.end-e.option.begin,e.density=o/e.duration,l(e.file&&n,"Please select the video file first"),l(!e.processing,"There is currently a task in progress, please wait a moment..."),l(e.density<=1,`The preview density cannot be greater than 1, but got ${e.density}`);const u=e.creatScreenshotDate(),d=e.creatCanvas(),h=d.getContext("2d");return e.emit("canvas",d),{width:r,height:c,number:o,points:u,canvas:d,context:h}}(e,h);U(t)}catch(r){if(w(r,!n&&!(h.wasReported&&r===h.reported)),n)throw r}}else d=setTimeout(()=>E(),1e3),g()||clearTimeout(d)}h={video:c,promise:y,ready:E,fail:w,cancel(e){w(r(e),!1)},adopt:t=>!p&&!f&&c===e.video&&(s=t,!0)},t.job=h;try{for(const[e,t]of[["error",()=>{g()&&w(new Error(`Unable to load video: media error ${c.error?.code||0}`))}],["loadedmetadata",()=>E()],["durationchange",()=>E()]])if(b.push(()=>c.removeEventListener(e,t)),c.addEventListener(e,t),!g())return i([()=>c.removeEventListener(e,t),...b.splice(0)]),y;c.error?w(new Error(`Unable to load video: media error ${c.error.code}`)):E(!0)}catch(j){throw w(j,!1),j}return y}const u=new WeakMap;function d(e){let t;for(const[r,o]of e.listeners.splice(0))try{e.input.removeEventListener(r,o)}catch(n){t||(t=n)}if(e.wrapper){try{e.input.parentNode&&e.input.parentNode.removeChild(e.input)}catch(n){t||(t=n)}"relative"===e.wrapper.style.position&&(e.wrapper.style.position=e.position)}if(t)throw t}function h(e,t){try{for(const[n,r]of t)e.listeners.push([n,r]),e.input.addEventListener(n,r)}catch(n){try{d(e)}catch{}throw n}}function p(e){const t=u.get(e);t&&(u.delete(e),d(t))}class f extends e{constructor(e={}){super(),this.processing=!1,this.option={};try{this.setup(Object.assign({},f.DEFAULTS,e)),this.video=f.creatVideo(),n(this).video=this.video,this.duration=0,this.inputChange=this.inputChange.bind(this),this.ondrop=this.ondrop.bind(this),function(e,t){const n=u.get(e);n.callbacks=[["change",e.inputChange],["dragover",t],["drop",e.ondrop]],h(n,n.callbacks)}(this,f.ondragover)}catch(t){s(this,"construction failed");try{p(this)}catch{}try{c(this)}catch{}throw t}}static get DEFAULTS(){return{number:60,width:160,height:90,column:10,begin:0,end:Number.NaN}}static ondragover(e){e.preventDefault()}ondrop(e){if(n(this).closed)return;e.preventDefault();const t=e.dataTransfer.files[0];this.loadVideo(t)}setup(e={}){return n(this).closed||function(e,t){const o=n(e),i=++o.inputEpoch,s=()=>!o.closed&&o.inputEpoch===i,c=Object.assign({},e.option,t),l=c.fileInput;e.errorHandle(l instanceof Element,"The 'fileInput' is not a Element");for(const n of["number","width","column","begin","end"])e.errorHandle("number"===typeof c[n],`The '${n}' is not a number`);if(c.number=a(c.number,10,1e3),c.width=a(c.width,10,1e3),c.column=a(c.column,1,1e3),!s())throw r("input setup superseded");const p=u.get(e);if(p&&(l===p.input||l===p.wrapper))return c.fileInput=p.input,e.option=c,c;const f={input:l,wrapper:null,position:"",listeners:[],callbacks:p?.callbacks||null};try{if("INPUT"===l.tagName&&"file"===l.type||(f.wrapper=l,f.position=l.style.position,f.input=document.createElement("input"),f.input.type="file",Object.assign(f.input.style,{position:"absolute",width:"100%",height:"100%",left:"0",top:"0",right:"0",bottom:"0",opacity:"0"}),l.style.position="relative",l.appendChild(f.input)),!s())throw r("input setup superseded");if(f.callbacks&&h(f,f.callbacks),!s())throw r("input setup superseded")}catch(m){try{d(f)}catch{}throw m}c.fileInput=f.input,u.set(e,f),e.option=c,p&&d(p)}(this,e),this}static creatVideo(){const e=document.createElement("video");e.style.position="absolute",e.style.top="-9999px",e.style.left="-9999px",e.muted=!0,e.controls=!0;try{document.body.appendChild(e)}catch(t){throw e.parentNode&&e.parentNode.removeChild(e),t}return e}inputChange(e){if(n(this).closed)return;const t=this.option.fileInput.files[0];this.loadVideo(t),e.target.value=""}loadVideo(e){!function(e,t){const r=n(e);if(!t||r.closed)return;const s=r.epoch,c=e.video,a=c.canPlayType(t.type);if(e.errorHandle("maybe"===a||"probably"===a,`Playback of this file format is not supported: ${t.type}`),r.closed||r.epoch!==s)return;const l=URL.createObjectURL(t);if(r.sourceUrls.add(l),r.closed||r.epoch!==s)return void o(r.sourceUrls,l);const u=++r.epoch;!r.sourceUrl&&!e.file&&r.job?.adopt(u)||r.job?.cancel("source changed"),r.loading=!0;const d=()=>!r.closed&&r.epoch===u,h=r.sourceListeners,p=[];r.sourceListeners=p;let f=()=>{},m=!1;const b=()=>{if(!d()||m||c.currentSrc&&c.currentSrc!==l)return;m=!0,r.loading=!1;const t=new Error(`Unable to load video: media error ${c.error?.code||0}`);r.job?r.job.fail(t):e.emit("error",t.message)},y=()=>{!d()||c.currentSrc&&c.currentSrc!==l||(r.loading=!1,r.job?.ready())};try{if(i(h.splice(0)),!d())return void o(r.sourceUrls,l);for(const[e,t]of[["error",b],["loadedmetadata",y]])if(f=()=>c.removeEventListener(e,t),p.push(f),c.addEventListener(e,t),!d())return i([f,...p.splice(0)]),void o(r.sourceUrls,l);if(e.videoUrl=l,e.file=t,e.emit("file",t),!d())return void o(r.sourceUrls,l);if(c.src=l,!d())return void o(r.sourceUrls,l);r.sourceUrl=l;for(const e of[...r.sourceUrls])e!==l&&o(r.sourceUrls,e);d()&&e.emit("video",c)}catch(g){d()&&(r.loading=!1,r.job?.cancel("source setup failed"));try{i([f,...p.splice(0)])}catch{}throw o(r.sourceUrls,l),g}}(this,e)}start(){return function(e){const t=n(e);if(t.closed){const e=Promise.reject(r("destroyed"));return e.catch(()=>{}),e}return e.errorHandle(!t.job,"There is currently a task in progress, please wait a moment..."),l(e,t)}(this)}creatScreenshotDate(){return function(e,t){const{number:n,width:r,height:o,column:i,begin:s}=e,c=t/n,a=[s+c];for(;a.length({time:e-c/2,x:t%i*r,y:Math.floor(t/i)*o}))}(this.option,this.duration)}creatCanvas(){return function(e){const{number:t,width:n,height:r,column:o}=e,i=document.createElement("canvas"),s=i.getContext("2d");return i.width=n*o,i.height=Math.ceil(t/o)*r+30,s.fillStyle="black",s.fillRect(0,0,i.width,i.height),s.font="14px Georgia",s.fillStyle="#fff",s.fillText(`From: https://artplayer.org/, Number: ${t}, Width: ${n}, Height: ${r}, Column: ${o}`,10,i.height-11),i}(this.option)}download(){if(n(this).closed)return this;this.errorHandle(this.file&&this.thumbnailUrl,"Download does not seem to be ready, please create preview first"),this.errorHandle(!this.processing,"There is currently a task in progress, please wait a moment...");const e=function(e,t){const n=document.createElement("a"),r=`${function(e){const t=e.split(".");return t.pop(),t.join(".")}(e.name)}.png`;n.download=r,n.href=t;try{document.body.appendChild(n),n.click()}finally{n.parentNode&&n.parentNode.removeChild(n)}return r}(this.file,this.thumbnailUrl);return this.emit("download",e),this}errorHandle(e,t){if(!e)throw this.emit("error",t),new Error(t)}destroy(){s(this)&&i([()=>p(this),()=>c(this),()=>this.emit("destroy")])}}return f}); +!function(e,t){"object"===typeof exports&&"undefined"!==typeof module?module.exports=t():"function"==typeof define&&define.amd?(e.ArtplayerToolThumbnail=t(),define(function(){return e.ArtplayerToolThumbnail})):(e="undefined"!==typeof globalThis?globalThis:e||self).ArtplayerToolThumbnail=t()}(this,function(){"use strict";const e=(e,t)=>Object.prototype.hasOwnProperty.call(e,t);class t{on(t,n,r){const o=this.e||(this.e={});let i=e(o,t)?o[t]:void 0;return i||(i=[],Object.defineProperty(o,t,{value:i,enumerable:!0,configurable:!0,writable:!0})),i.push({fn:n,ctx:r}),this}once(e,t,n){const r=this,o=t;let i=!1;function s(...t){i||(i=!0,r.off(e,s),o.apply(n,t))}return s._=t,this.on(e,s,n)}emit(t,...n){const r=this.e||(this.e={}),o=(e(r,t)&&r[t]||[]).slice();for(let e=0;er.pause?.(),()=>r.removeAttribute?.("src"),()=>r.load?.(),()=>r.parentNode?.removeChild(r));s([...t.sourceListeners.splice(0),...c,...[...n].map(e=>()=>i(t.sourceUrls,e)),...[...o].map(e=>()=>i(t.thumbnailUrls,e))])}function l(e,t,n){return Math.max(Math.min(e,Math.max(t,n)),Math.min(t,n))}function u(e,t){let n=t.epoch;const c=e.video;let a,u,d,h,p=!1,f=!1,m=()=>{};const b=[],y=new Promise((e,t)=>{a=e,u=t});y.catch(()=>{});const g=()=>!p&&!t.closed&&t.epoch===n&&t.job===h;function v(){return!p&&(p=!0,t.job===h&&(t.job=null,e.processing=!1),s([()=>clearTimeout(d),m,...b.splice(0)]),!0)}function w(t,n=!0){if(!p){try{v()}catch(r){t||(t=r)}if(n)try{e.emit("error",t?.message)}catch(o){t=o}u(t)}}function U(t){if(!g())return;f=!0,e.processing=!0;let n=0;const o=()=>{if(!g())return;if(n===t.points.length)return void function(){if(g())try{v(),e.emit("done"),a()}catch(t){let r=t;try{e.emit("error",t?.message)}catch(n){r=n}u(r)}}();const s=t.points[n];let l=!1,d=!1;const h=c.oncanplay,p=()=>{if(!(!g()||l||c.seeking||void 0!==c.readyState&&c.readyState<2)){l=!0;try{if(m(),t.context.drawImage(c,s.x,s.y,t.width,t.height),!g())return;t.canvas.toBlob(s=>{if(g()&&!d){d=!0;try{if(!s)throw new Error("Unable to create thumbnail image");const c=function(e,t,n){const o=r(e),s=URL.createObjectURL(t);if(o.thumbnailUrls.add(s),!n())return i(o.thumbnailUrls,s),null;const c=new Set([...o.thumbnailUrls,e.thumbnailUrl]);o.thumbnailUrl=s,e.thumbnailUrl=s;for(const r of c)r&&r!==s&&i(o.thumbnailUrls,r);return n()?s:null}(e,s,g);if(!c)return;e.emit("update",c,(n+1)/t.number),n++,Promise.resolve().then(o)}catch(c){w(c)}}})}catch(a){w(a)}}};m=()=>{c.oncanplay===p&&(c.oncanplay=h),c.removeEventListener("seeked",p)};try{c.oncanplay=p,c.addEventListener("seeked",p),c.currentTime=s.time,c.readyState>=2&&!c.seeking&&Promise.resolve().then(p)}catch(f){w(f)}};Promise.resolve().then(o)}function E(n=!1){if(g()&&!f)if(clearTimeout(d),!t.loading&&c.duration){f=!0;try{const t=function(e,t){const{video:n}=t,{width:r,number:o,begin:i,end:s}=e.option,c=n.videoHeight/n.videoWidth*r,a=(n,r)=>{try{e.errorHandle(n,r)}catch(o){throw t.reported=o,t.wasReported=!0,o}};e.option.height=c,e.option.begin=l(i,0,n.duration),e.option.end=l(s||n.duration,i,n.duration),a(e.option.end>e.option.begin,"End time must be greater than the start time"),a(Number.isFinite(n.duration),"Video duration must be finite"),e.duration=e.option.end-e.option.begin,e.density=o/e.duration,a(e.file&&n,"Please select the video file first"),a(!e.processing,"There is currently a task in progress, please wait a moment..."),a(e.density<=1,`The preview density cannot be greater than 1, but got ${e.density}`);const u=e.creatScreenshotDate(),d=e.creatCanvas(),h=d.getContext("2d");return e.emit("canvas",d),{width:r,height:c,number:o,points:u,canvas:d,context:h}}(e,h);U(t)}catch(r){if(w(r,!n&&!(h.wasReported&&r===h.reported)),n)throw r}}else d=setTimeout(()=>E(),1e3),g()||clearTimeout(d)}h={video:c,promise:y,ready:E,fail:w,cancel(e){w(o(e),!1)},adopt:t=>!p&&!f&&c===e.video&&(n=t,!0)},t.job=h;try{for(const[e,t]of[["error",()=>{g()&&w(new Error(`Unable to load video: media error ${c.error?.code||0}`))}],["loadedmetadata",()=>E()],["durationchange",()=>E()]])if(b.push(()=>c.removeEventListener(e,t)),c.addEventListener(e,t),!g())return s([()=>c.removeEventListener(e,t),...b.splice(0)]),y;c.error?w(new Error(`Unable to load video: media error ${c.error.code}`)):E(!0)}catch(j){throw w(j,!1),j}return y}const d=new WeakMap;function h(e){let t;for(const[r,o]of e.listeners.splice(0))try{e.input.removeEventListener(r,o)}catch(n){t||(t=n)}if(e.wrapper){try{e.input.parentNode&&e.input.parentNode.removeChild(e.input)}catch(n){t||(t=n)}"relative"===e.wrapper.style.position&&(e.wrapper.style.position=e.position)}if(t)throw t}function p(e,t){try{for(const[n,r]of t)e.listeners.push([n,r]),e.input.addEventListener(n,r)}catch(n){try{h(e)}catch{}throw n}}function f(e){const t=d.get(e);t&&(d.delete(e),h(t))}class m extends t{constructor(e={}){super(),this.processing=!1,this.option={};try{this.setup(Object.assign({},m.DEFAULTS,e)),this.video=m.creatVideo(),r(this).video=this.video,this.duration=0,this.inputChange=this.inputChange.bind(this),this.ondrop=this.ondrop.bind(this),function(e,t){const n=d.get(e);n.callbacks=[["change",e.inputChange],["dragover",t],["drop",e.ondrop]],p(n,n.callbacks)}(this,m.ondragover)}catch(t){c(this,"construction failed");try{f(this)}catch{}try{a(this)}catch{}throw t}}static get DEFAULTS(){return{number:60,width:160,height:90,column:10,begin:0,end:Number.NaN}}static ondragover(e){e.preventDefault()}ondrop(e){if(r(this).closed)return;e.preventDefault();const t=e.dataTransfer.files[0];this.loadVideo(t)}setup(e={}){return r(this).closed||function(e,t){const n=r(e),i=++n.inputEpoch,s=()=>!n.closed&&n.inputEpoch===i,c=Object.assign({},e.option,t),a=c.fileInput;e.errorHandle(a instanceof Element,"The 'fileInput' is not a Element");for(const r of["number","width","column","begin","end"])e.errorHandle("number"===typeof c[r],`The '${r}' is not a number`);if(c.number=l(c.number,10,1e3),c.width=l(c.width,10,1e3),c.column=l(c.column,1,1e3),!s())throw o("input setup superseded");const u=d.get(e);if(u&&(a===u.input||a===u.wrapper))return c.fileInput=u.input,e.option=c,c;const f={input:a,wrapper:null,position:"",listeners:[],callbacks:u?.callbacks||null};try{if("INPUT"===a.tagName&&"file"===a.type||(f.wrapper=a,f.position=a.style.position,f.input=document.createElement("input"),f.input.type="file",Object.assign(f.input.style,{position:"absolute",width:"100%",height:"100%",left:"0",top:"0",right:"0",bottom:"0",opacity:"0"}),a.style.position="relative",a.appendChild(f.input)),!s())throw o("input setup superseded");if(f.callbacks&&p(f,f.callbacks),!s())throw o("input setup superseded")}catch(m){try{h(f)}catch{}throw m}c.fileInput=f.input,d.set(e,f),e.option=c,u&&h(u)}(this,e),this}static creatVideo(){const e=document.createElement("video");e.style.position="absolute",e.style.top="-9999px",e.style.left="-9999px",e.muted=!0,e.controls=!0;try{document.body.appendChild(e)}catch(t){throw e.parentNode&&e.parentNode.removeChild(e),t}return e}inputChange(e){if(r(this).closed)return;const t=this.option.fileInput.files[0];this.loadVideo(t),e.target.value=""}loadVideo(e){!function(e,t){const n=r(e);if(!t||n.closed)return;const o=n.epoch,c=e.video,a=c.canPlayType(t.type);if(e.errorHandle("maybe"===a||"probably"===a,`Playback of this file format is not supported: ${t.type}`),n.closed||n.epoch!==o)return;const l=URL.createObjectURL(t);if(n.sourceUrls.add(l),n.closed||n.epoch!==o)return void i(n.sourceUrls,l);const u=++n.epoch;!n.sourceUrl&&!e.file&&n.job?.adopt(u)||n.job?.cancel("source changed"),n.loading=!0;const d=()=>!n.closed&&n.epoch===u,h=n.sourceListeners,p=[];n.sourceListeners=p;let f=()=>{},m=!1;const b=()=>{if(!d()||m||c.currentSrc&&c.currentSrc!==l)return;m=!0,n.loading=!1;const t=new Error(`Unable to load video: media error ${c.error?.code||0}`);n.job?n.job.fail(t):e.emit("error",t.message)},y=()=>{!d()||c.currentSrc&&c.currentSrc!==l||(n.loading=!1,n.job?.ready())};try{if(s(h.splice(0)),!d())return void i(n.sourceUrls,l);for(const[e,t]of[["error",b],["loadedmetadata",y]])if(f=()=>c.removeEventListener(e,t),p.push(f),c.addEventListener(e,t),!d())return s([f,...p.splice(0)]),void i(n.sourceUrls,l);if(e.videoUrl=l,e.file=t,e.emit("file",t),!d())return void i(n.sourceUrls,l);if(c.src=l,!d())return void i(n.sourceUrls,l);n.sourceUrl=l;for(const e of[...n.sourceUrls])e!==l&&i(n.sourceUrls,e);d()&&e.emit("video",c)}catch(g){d()&&(n.loading=!1,n.job?.cancel("source setup failed"));try{s([f,...p.splice(0)])}catch{}throw i(n.sourceUrls,l),g}}(this,e)}start(){return function(e){const t=r(e);if(t.closed){const e=Promise.reject(o("destroyed"));return e.catch(()=>{}),e}return e.errorHandle(!t.job,"There is currently a task in progress, please wait a moment..."),u(e,t)}(this)}creatScreenshotDate(){return function(e,t){const{number:n,width:r,height:o,column:i,begin:s}=e,c=t/n,a=[s+c];for(;a.length({time:e-c/2,x:t%i*r,y:Math.floor(t/i)*o}))}(this.option,this.duration)}creatCanvas(){return function(e){const{number:t,width:n,height:r,column:o}=e,i=document.createElement("canvas"),s=i.getContext("2d");return i.width=n*o,i.height=Math.ceil(t/o)*r+30,s.fillStyle="black",s.fillRect(0,0,i.width,i.height),s.font="14px Georgia",s.fillStyle="#fff",s.fillText(`From: https://artplayer.org/, Number: ${t}, Width: ${n}, Height: ${r}, Column: ${o}`,10,i.height-11),i}(this.option)}download(){if(r(this).closed)return this;this.errorHandle(this.file&&this.thumbnailUrl,"Download does not seem to be ready, please create preview first"),this.errorHandle(!this.processing,"There is currently a task in progress, please wait a moment...");const e=function(e,t){const n=document.createElement("a"),r=`${function(e){const t=e.split(".");return t.pop(),t.join(".")}(e.name)}.png`;n.download=r,n.href=t;try{document.body.appendChild(n),n.click()}finally{n.parentNode&&n.parentNode.removeChild(n)}return r}(this.file,this.thumbnailUrl);return this.emit("download",e),this}errorHandle(e,t){if(!e)throw this.emit("error",t),new Error(t)}destroy(){c(this)&&s([()=>f(this),()=>a(this),()=>this.emit("destroy")])}}return m}); diff --git a/packages/artplayer-tool-thumbnail/dist/artplayer-tool-thumbnail.legacy.js b/packages/artplayer-tool-thumbnail/dist/artplayer-tool-thumbnail.legacy.js index 86ca050d8..8a27da060 100644 --- a/packages/artplayer-tool-thumbnail/dist/artplayer-tool-thumbnail.legacy.js +++ b/packages/artplayer-tool-thumbnail/dist/artplayer-tool-thumbnail.legacy.js @@ -31,4 +31,4 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -!function(e,t){"object"===typeof exports&&"undefined"!==typeof module?module.exports=t():"function"==typeof define&&define.amd?(e.ArtplayerToolThumbnail=t(),define(function(){return e.ArtplayerToolThumbnail})):(e="undefined"!==typeof globalThis?globalThis:e||self).ArtplayerToolThumbnail=t()}(this,function(){"use strict";class e{on(e,t,n){const r=this.e||(this.e={});return(r[e]||(r[e]=[])).push({fn:t,ctx:n}),this}once(e,t,n){const r=this,o=t;function i(...t){r.off(e,i),o.apply(n,t)}return i._=t,this.on(e,i,n)}emit(e,...t){const n=((this.e||(this.e={}))[e]||[]).slice();for(let r=0;r{var e;return null==(e=n.pause)?void 0:e.call(n)},()=>{var e;return null==(e=n.removeAttribute)?void 0:e.call(n,"src")},()=>{var e;return null==(e=n.load)?void 0:e.call(n)},()=>{var e;return null==(e=n.parentNode)?void 0:e.removeChild(n)});i([...t.sourceListeners.splice(0),...c,...[...r].map(e=>()=>o(t.sourceUrls,e)),...[...s].map(e=>()=>o(t.thumbnailUrls,e))])}function l(e,t,n){return Math.max(Math.min(e,Math.max(t,n)),Math.min(t,n))}function a(e,t){let s=t.epoch;const c=e.video;let a,u,d,h,p=!1,f=!1,m=()=>{};const b=[],v=new Promise((e,t)=>{a=e,u=t});v.catch(()=>{});const y=()=>!p&&!t.closed&&t.epoch===s&&t.job===h;function g(){return!p&&(p=!0,t.job===h&&(t.job=null,e.processing=!1),i([()=>clearTimeout(d),m,...b.splice(0)]),!0)}function w(t,n=!0){if(!p){try{g()}catch(r){t||(t=r)}if(n)try{e.emit("error",null==t?void 0:t.message)}catch(o){t=o}u(t)}}function U(t){if(!y())return;f=!0,e.processing=!0;let r=0;const i=()=>{if(!y())return;if(r===t.points.length)return void function(){if(y())try{g(),e.emit("done"),a()}catch(t){let r=t;try{e.emit("error",null==t?void 0:t.message)}catch(n){r=n}u(r)}}();const s=t.points[r];let l=!1,d=!1;const h=c.oncanplay,p=()=>{if(!(!y()||l||c.seeking||void 0!==c.readyState&&c.readyState<2)){l=!0;try{if(m(),t.context.drawImage(c,s.x,s.y,t.width,t.height),!y())return;t.canvas.toBlob(s=>{if(y()&&!d){d=!0;try{if(!s)throw new Error("Unable to create thumbnail image");const c=function(e,t,r){const i=n(e),s=URL.createObjectURL(t);if(i.thumbnailUrls.add(s),!r())return o(i.thumbnailUrls,s),null;const c=new Set([...i.thumbnailUrls,e.thumbnailUrl]);i.thumbnailUrl=s,e.thumbnailUrl=s;for(const n of c)n&&n!==s&&o(i.thumbnailUrls,n);return r()?s:null}(e,s,y);if(!c)return;e.emit("update",c,(r+1)/t.number),r++,Promise.resolve().then(i)}catch(c){w(c)}}})}catch(a){w(a)}}};m=()=>{c.oncanplay===p&&(c.oncanplay=h),c.removeEventListener("seeked",p)};try{c.oncanplay=p,c.addEventListener("seeked",p),c.currentTime=s.time,c.readyState>=2&&!c.seeking&&Promise.resolve().then(p)}catch(f){w(f)}};Promise.resolve().then(i)}function E(n=!1){if(y()&&!f)if(clearTimeout(d),!t.loading&&c.duration){f=!0;try{const t=function(e,t){const{video:n}=t,{width:r,number:o,begin:i,end:s}=e.option,c=n.videoHeight/n.videoWidth*r,a=(n,r)=>{try{e.errorHandle(n,r)}catch(o){throw t.reported=o,t.wasReported=!0,o}};e.option.height=c,e.option.begin=l(i,0,n.duration),e.option.end=l(s||n.duration,i,n.duration),a(e.option.end>e.option.begin,"End time must be greater than the start time"),a(Number.isFinite(n.duration),"Video duration must be finite"),e.duration=e.option.end-e.option.begin,e.density=o/e.duration,a(e.file&&n,"Please select the video file first"),a(!e.processing,"There is currently a task in progress, please wait a moment..."),a(e.density<=1,`The preview density cannot be greater than 1, but got ${e.density}`);const u=e.creatScreenshotDate(),d=e.creatCanvas(),h=d.getContext("2d");return e.emit("canvas",d),{width:r,height:c,number:o,points:u,canvas:d,context:h}}(e,h);U(t)}catch(r){if(w(r,!n&&!(h.wasReported&&r===h.reported)),n)throw r}}else d=setTimeout(()=>E(),1e3),y()||clearTimeout(d)}h={video:c,promise:v,ready:E,fail:w,cancel(e){w(r(e),!1)},adopt:t=>!p&&!f&&c===e.video&&(s=t,!0)},t.job=h;try{for(const[e,t]of[["error",()=>{var e;y()&&w(new Error(`Unable to load video: media error ${(null==(e=c.error)?void 0:e.code)||0}`))}],["loadedmetadata",()=>E()],["durationchange",()=>E()]])if(b.push(()=>c.removeEventListener(e,t)),c.addEventListener(e,t),!y())return i([()=>c.removeEventListener(e,t),...b.splice(0)]),v;c.error?w(new Error(`Unable to load video: media error ${c.error.code}`)):E(!0)}catch(j){throw w(j,!1),j}return v}const u=new WeakMap;function d(e){let t;for(const[r,o]of e.listeners.splice(0))try{e.input.removeEventListener(r,o)}catch(n){t||(t=n)}if(e.wrapper){try{e.input.parentNode&&e.input.parentNode.removeChild(e.input)}catch(n){t||(t=n)}"relative"===e.wrapper.style.position&&(e.wrapper.style.position=e.position)}if(t)throw t}function h(e,t){try{for(const[n,r]of t)e.listeners.push([n,r]),e.input.addEventListener(n,r)}catch(n){try{d(e)}catch(r){}throw n}}function p(e){const t=u.get(e);t&&(u.delete(e),d(t))}class f extends e{constructor(e={}){super(),this.processing=!1,this.option={};try{this.setup(Object.assign({},f.DEFAULTS,e)),this.video=f.creatVideo(),n(this).video=this.video,this.duration=0,this.inputChange=this.inputChange.bind(this),this.ondrop=this.ondrop.bind(this),function(e,t){const n=u.get(e);n.callbacks=[["change",e.inputChange],["dragover",t],["drop",e.ondrop]],h(n,n.callbacks)}(this,f.ondragover)}catch(t){s(this,"construction failed");try{p(this)}catch(r){}try{c(this)}catch(r){}throw t}}static get DEFAULTS(){return{number:60,width:160,height:90,column:10,begin:0,end:Number.NaN}}static ondragover(e){e.preventDefault()}ondrop(e){if(n(this).closed)return;e.preventDefault();const t=e.dataTransfer.files[0];this.loadVideo(t)}setup(e={}){return n(this).closed||function(e,t){const o=n(e),i=++o.inputEpoch,s=()=>!o.closed&&o.inputEpoch===i,c=Object.assign({},e.option,t),a=c.fileInput;e.errorHandle(a instanceof Element,"The 'fileInput' is not a Element");for(const n of["number","width","column","begin","end"])e.errorHandle("number"===typeof c[n],`The '${n}' is not a number`);if(c.number=l(c.number,10,1e3),c.width=l(c.width,10,1e3),c.column=l(c.column,1,1e3),!s())throw r("input setup superseded");const p=u.get(e);if(p&&(a===p.input||a===p.wrapper))return c.fileInput=p.input,e.option=c,c;const f={input:a,wrapper:null,position:"",listeners:[],callbacks:(null==p?void 0:p.callbacks)||null};try{if("INPUT"===a.tagName&&"file"===a.type||(f.wrapper=a,f.position=a.style.position,f.input=document.createElement("input"),f.input.type="file",Object.assign(f.input.style,{position:"absolute",width:"100%",height:"100%",left:"0",top:"0",right:"0",bottom:"0",opacity:"0"}),a.style.position="relative",a.appendChild(f.input)),!s())throw r("input setup superseded");if(f.callbacks&&h(f,f.callbacks),!s())throw r("input setup superseded")}catch(m){try{d(f)}catch(b){}throw m}c.fileInput=f.input,u.set(e,f),e.option=c,p&&d(p)}(this,e),this}static creatVideo(){const e=document.createElement("video");e.style.position="absolute",e.style.top="-9999px",e.style.left="-9999px",e.muted=!0,e.controls=!0;try{document.body.appendChild(e)}catch(t){throw e.parentNode&&e.parentNode.removeChild(e),t}return e}inputChange(e){if(n(this).closed)return;const t=this.option.fileInput.files[0];this.loadVideo(t),e.target.value=""}loadVideo(e){!function(e,t){var r,s,c;const l=n(e);if(!t||l.closed)return;const a=l.epoch,u=e.video,d=u.canPlayType(t.type);if(e.errorHandle("maybe"===d||"probably"===d,`Playback of this file format is not supported: ${t.type}`),l.closed||l.epoch!==a)return;const h=URL.createObjectURL(t);if(l.sourceUrls.add(h),l.closed||l.epoch!==a)return void o(l.sourceUrls,h);const p=++l.epoch;!l.sourceUrl&&!e.file&&(null==(r=l.job)?void 0:r.adopt(p))||null==(s=l.job)||s.cancel("source changed"),l.loading=!0;const f=()=>!l.closed&&l.epoch===p,m=l.sourceListeners,b=[];l.sourceListeners=b;let v=()=>{},y=!1;const g=()=>{var t;if(!f()||y||u.currentSrc&&u.currentSrc!==h)return;y=!0,l.loading=!1;const n=new Error(`Unable to load video: media error ${(null==(t=u.error)?void 0:t.code)||0}`);l.job?l.job.fail(n):e.emit("error",n.message)},w=()=>{var e;!f()||u.currentSrc&&u.currentSrc!==h||(l.loading=!1,null==(e=l.job)||e.ready())};try{if(i(m.splice(0)),!f())return void o(l.sourceUrls,h);for(const[e,t]of[["error",g],["loadedmetadata",w]])if(v=()=>u.removeEventListener(e,t),b.push(v),u.addEventListener(e,t),!f())return i([v,...b.splice(0)]),void o(l.sourceUrls,h);if(e.videoUrl=h,e.file=t,e.emit("file",t),!f())return void o(l.sourceUrls,h);if(u.src=h,!f())return void o(l.sourceUrls,h);l.sourceUrl=h;for(const e of[...l.sourceUrls])e!==h&&o(l.sourceUrls,e);f()&&e.emit("video",u)}catch(U){f()&&(l.loading=!1,null==(c=l.job)||c.cancel("source setup failed"));try{i([v,...b.splice(0)])}catch(E){}throw o(l.sourceUrls,h),U}}(this,e)}start(){return function(e){const t=n(e);if(t.closed){const e=Promise.reject(r("destroyed"));return e.catch(()=>{}),e}return e.errorHandle(!t.job,"There is currently a task in progress, please wait a moment..."),a(e,t)}(this)}creatScreenshotDate(){return function(e,t){const{number:n,width:r,height:o,column:i,begin:s}=e,c=t/n,l=[s+c];for(;l.length({time:e-c/2,x:t%i*r,y:Math.floor(t/i)*o}))}(this.option,this.duration)}creatCanvas(){return function(e){const{number:t,width:n,height:r,column:o}=e,i=document.createElement("canvas"),s=i.getContext("2d");return i.width=n*o,i.height=Math.ceil(t/o)*r+30,s.fillStyle="black",s.fillRect(0,0,i.width,i.height),s.font="14px Georgia",s.fillStyle="#fff",s.fillText(`From: https://artplayer.org/, Number: ${t}, Width: ${n}, Height: ${r}, Column: ${o}`,10,i.height-11),i}(this.option)}download(){if(n(this).closed)return this;this.errorHandle(this.file&&this.thumbnailUrl,"Download does not seem to be ready, please create preview first"),this.errorHandle(!this.processing,"There is currently a task in progress, please wait a moment...");const e=function(e,t){const n=document.createElement("a"),r=`${function(e){const t=e.split(".");return t.pop(),t.join(".")}(e.name)}.png`;n.download=r,n.href=t;try{document.body.appendChild(n),n.click()}finally{n.parentNode&&n.parentNode.removeChild(n)}return r}(this.file,this.thumbnailUrl);return this.emit("download",e),this}errorHandle(e,t){if(!e)throw this.emit("error",t),new Error(t)}destroy(){s(this)&&i([()=>p(this),()=>c(this),()=>this.emit("destroy")])}}return f}); +!function(e,t){"object"===typeof exports&&"undefined"!==typeof module?module.exports=t():"function"==typeof define&&define.amd?(e.ArtplayerToolThumbnail=t(),define(function(){return e.ArtplayerToolThumbnail})):(e="undefined"!==typeof globalThis?globalThis:e||self).ArtplayerToolThumbnail=t()}(this,function(){"use strict";const e=(e,t)=>Object.prototype.hasOwnProperty.call(e,t);class t{on(t,n,r){const o=this.e||(this.e={});let i=e(o,t)?o[t]:void 0;return i||(i=[],Object.defineProperty(o,t,{value:i,enumerable:!0,configurable:!0,writable:!0})),i.push({fn:n,ctx:r}),this}once(e,t,n){const r=this,o=t;let i=!1;function s(...t){i||(i=!0,r.off(e,s),o.apply(n,t))}return s._=t,this.on(e,s,n)}emit(t,...n){const r=this.e||(this.e={}),o=(e(r,t)&&r[t]||[]).slice();for(let e=0;e{var e;return null==(e=r.pause)?void 0:e.call(r)},()=>{var e;return null==(e=r.removeAttribute)?void 0:e.call(r,"src")},()=>{var e;return null==(e=r.load)?void 0:e.call(r)},()=>{var e;return null==(e=r.parentNode)?void 0:e.removeChild(r)});s([...t.sourceListeners.splice(0),...c,...[...n].map(e=>()=>i(t.sourceUrls,e)),...[...o].map(e=>()=>i(t.thumbnailUrls,e))])}function a(e,t,n){return Math.max(Math.min(e,Math.max(t,n)),Math.min(t,n))}function u(e,t){let n=t.epoch;const c=e.video;let l,u,d,h,p=!1,f=!1,m=()=>{};const b=[],v=new Promise((e,t)=>{l=e,u=t});v.catch(()=>{});const y=()=>!p&&!t.closed&&t.epoch===n&&t.job===h;function g(){return!p&&(p=!0,t.job===h&&(t.job=null,e.processing=!1),s([()=>clearTimeout(d),m,...b.splice(0)]),!0)}function w(t,n=!0){if(!p){try{g()}catch(r){t||(t=r)}if(n)try{e.emit("error",null==t?void 0:t.message)}catch(o){t=o}u(t)}}function U(t){if(!y())return;f=!0,e.processing=!0;let n=0;const o=()=>{if(!y())return;if(n===t.points.length)return void function(){if(y())try{g(),e.emit("done"),l()}catch(t){let r=t;try{e.emit("error",null==t?void 0:t.message)}catch(n){r=n}u(r)}}();const s=t.points[n];let a=!1,d=!1;const h=c.oncanplay,p=()=>{if(!(!y()||a||c.seeking||void 0!==c.readyState&&c.readyState<2)){a=!0;try{if(m(),t.context.drawImage(c,s.x,s.y,t.width,t.height),!y())return;t.canvas.toBlob(s=>{if(y()&&!d){d=!0;try{if(!s)throw new Error("Unable to create thumbnail image");const c=function(e,t,n){const o=r(e),s=URL.createObjectURL(t);if(o.thumbnailUrls.add(s),!n())return i(o.thumbnailUrls,s),null;const c=new Set([...o.thumbnailUrls,e.thumbnailUrl]);o.thumbnailUrl=s,e.thumbnailUrl=s;for(const r of c)r&&r!==s&&i(o.thumbnailUrls,r);return n()?s:null}(e,s,y);if(!c)return;e.emit("update",c,(n+1)/t.number),n++,Promise.resolve().then(o)}catch(c){w(c)}}})}catch(l){w(l)}}};m=()=>{c.oncanplay===p&&(c.oncanplay=h),c.removeEventListener("seeked",p)};try{c.oncanplay=p,c.addEventListener("seeked",p),c.currentTime=s.time,c.readyState>=2&&!c.seeking&&Promise.resolve().then(p)}catch(f){w(f)}};Promise.resolve().then(o)}function E(n=!1){if(y()&&!f)if(clearTimeout(d),!t.loading&&c.duration){f=!0;try{const t=function(e,t){const{video:n}=t,{width:r,number:o,begin:i,end:s}=e.option,c=n.videoHeight/n.videoWidth*r,l=(n,r)=>{try{e.errorHandle(n,r)}catch(o){throw t.reported=o,t.wasReported=!0,o}};e.option.height=c,e.option.begin=a(i,0,n.duration),e.option.end=a(s||n.duration,i,n.duration),l(e.option.end>e.option.begin,"End time must be greater than the start time"),l(Number.isFinite(n.duration),"Video duration must be finite"),e.duration=e.option.end-e.option.begin,e.density=o/e.duration,l(e.file&&n,"Please select the video file first"),l(!e.processing,"There is currently a task in progress, please wait a moment..."),l(e.density<=1,`The preview density cannot be greater than 1, but got ${e.density}`);const u=e.creatScreenshotDate(),d=e.creatCanvas(),h=d.getContext("2d");return e.emit("canvas",d),{width:r,height:c,number:o,points:u,canvas:d,context:h}}(e,h);U(t)}catch(r){if(w(r,!n&&!(h.wasReported&&r===h.reported)),n)throw r}}else d=setTimeout(()=>E(),1e3),y()||clearTimeout(d)}h={video:c,promise:v,ready:E,fail:w,cancel(e){w(o(e),!1)},adopt:t=>!p&&!f&&c===e.video&&(n=t,!0)},t.job=h;try{for(const[e,t]of[["error",()=>{var e;y()&&w(new Error(`Unable to load video: media error ${(null==(e=c.error)?void 0:e.code)||0}`))}],["loadedmetadata",()=>E()],["durationchange",()=>E()]])if(b.push(()=>c.removeEventListener(e,t)),c.addEventListener(e,t),!y())return s([()=>c.removeEventListener(e,t),...b.splice(0)]),v;c.error?w(new Error(`Unable to load video: media error ${c.error.code}`)):E(!0)}catch(j){throw w(j,!1),j}return v}const d=new WeakMap;function h(e){let t;for(const[r,o]of e.listeners.splice(0))try{e.input.removeEventListener(r,o)}catch(n){t||(t=n)}if(e.wrapper){try{e.input.parentNode&&e.input.parentNode.removeChild(e.input)}catch(n){t||(t=n)}"relative"===e.wrapper.style.position&&(e.wrapper.style.position=e.position)}if(t)throw t}function p(e,t){try{for(const[n,r]of t)e.listeners.push([n,r]),e.input.addEventListener(n,r)}catch(n){try{h(e)}catch(r){}throw n}}function f(e){const t=d.get(e);t&&(d.delete(e),h(t))}class m extends t{constructor(e={}){super(),this.processing=!1,this.option={};try{this.setup(Object.assign({},m.DEFAULTS,e)),this.video=m.creatVideo(),r(this).video=this.video,this.duration=0,this.inputChange=this.inputChange.bind(this),this.ondrop=this.ondrop.bind(this),function(e,t){const n=d.get(e);n.callbacks=[["change",e.inputChange],["dragover",t],["drop",e.ondrop]],p(n,n.callbacks)}(this,m.ondragover)}catch(t){c(this,"construction failed");try{f(this)}catch(n){}try{l(this)}catch(n){}throw t}}static get DEFAULTS(){return{number:60,width:160,height:90,column:10,begin:0,end:Number.NaN}}static ondragover(e){e.preventDefault()}ondrop(e){if(r(this).closed)return;e.preventDefault();const t=e.dataTransfer.files[0];this.loadVideo(t)}setup(e={}){return r(this).closed||function(e,t){const n=r(e),i=++n.inputEpoch,s=()=>!n.closed&&n.inputEpoch===i,c=Object.assign({},e.option,t),l=c.fileInput;e.errorHandle(l instanceof Element,"The 'fileInput' is not a Element");for(const r of["number","width","column","begin","end"])e.errorHandle("number"===typeof c[r],`The '${r}' is not a number`);if(c.number=a(c.number,10,1e3),c.width=a(c.width,10,1e3),c.column=a(c.column,1,1e3),!s())throw o("input setup superseded");const u=d.get(e);if(u&&(l===u.input||l===u.wrapper))return c.fileInput=u.input,e.option=c,c;const f={input:l,wrapper:null,position:"",listeners:[],callbacks:(null==u?void 0:u.callbacks)||null};try{if("INPUT"===l.tagName&&"file"===l.type||(f.wrapper=l,f.position=l.style.position,f.input=document.createElement("input"),f.input.type="file",Object.assign(f.input.style,{position:"absolute",width:"100%",height:"100%",left:"0",top:"0",right:"0",bottom:"0",opacity:"0"}),l.style.position="relative",l.appendChild(f.input)),!s())throw o("input setup superseded");if(f.callbacks&&p(f,f.callbacks),!s())throw o("input setup superseded")}catch(m){try{h(f)}catch(b){}throw m}c.fileInput=f.input,d.set(e,f),e.option=c,u&&h(u)}(this,e),this}static creatVideo(){const e=document.createElement("video");e.style.position="absolute",e.style.top="-9999px",e.style.left="-9999px",e.muted=!0,e.controls=!0;try{document.body.appendChild(e)}catch(t){throw e.parentNode&&e.parentNode.removeChild(e),t}return e}inputChange(e){if(r(this).closed)return;const t=this.option.fileInput.files[0];this.loadVideo(t),e.target.value=""}loadVideo(e){!function(e,t){var n,o,c;const l=r(e);if(!t||l.closed)return;const a=l.epoch,u=e.video,d=u.canPlayType(t.type);if(e.errorHandle("maybe"===d||"probably"===d,`Playback of this file format is not supported: ${t.type}`),l.closed||l.epoch!==a)return;const h=URL.createObjectURL(t);if(l.sourceUrls.add(h),l.closed||l.epoch!==a)return void i(l.sourceUrls,h);const p=++l.epoch;!l.sourceUrl&&!e.file&&(null==(n=l.job)?void 0:n.adopt(p))||null==(o=l.job)||o.cancel("source changed"),l.loading=!0;const f=()=>!l.closed&&l.epoch===p,m=l.sourceListeners,b=[];l.sourceListeners=b;let v=()=>{},y=!1;const g=()=>{var t;if(!f()||y||u.currentSrc&&u.currentSrc!==h)return;y=!0,l.loading=!1;const n=new Error(`Unable to load video: media error ${(null==(t=u.error)?void 0:t.code)||0}`);l.job?l.job.fail(n):e.emit("error",n.message)},w=()=>{var e;!f()||u.currentSrc&&u.currentSrc!==h||(l.loading=!1,null==(e=l.job)||e.ready())};try{if(s(m.splice(0)),!f())return void i(l.sourceUrls,h);for(const[e,t]of[["error",g],["loadedmetadata",w]])if(v=()=>u.removeEventListener(e,t),b.push(v),u.addEventListener(e,t),!f())return s([v,...b.splice(0)]),void i(l.sourceUrls,h);if(e.videoUrl=h,e.file=t,e.emit("file",t),!f())return void i(l.sourceUrls,h);if(u.src=h,!f())return void i(l.sourceUrls,h);l.sourceUrl=h;for(const e of[...l.sourceUrls])e!==h&&i(l.sourceUrls,e);f()&&e.emit("video",u)}catch(U){f()&&(l.loading=!1,null==(c=l.job)||c.cancel("source setup failed"));try{s([v,...b.splice(0)])}catch(E){}throw i(l.sourceUrls,h),U}}(this,e)}start(){return function(e){const t=r(e);if(t.closed){const e=Promise.reject(o("destroyed"));return e.catch(()=>{}),e}return e.errorHandle(!t.job,"There is currently a task in progress, please wait a moment..."),u(e,t)}(this)}creatScreenshotDate(){return function(e,t){const{number:n,width:r,height:o,column:i,begin:s}=e,c=t/n,l=[s+c];for(;l.length({time:e-c/2,x:t%i*r,y:Math.floor(t/i)*o}))}(this.option,this.duration)}creatCanvas(){return function(e){const{number:t,width:n,height:r,column:o}=e,i=document.createElement("canvas"),s=i.getContext("2d");return i.width=n*o,i.height=Math.ceil(t/o)*r+30,s.fillStyle="black",s.fillRect(0,0,i.width,i.height),s.font="14px Georgia",s.fillStyle="#fff",s.fillText(`From: https://artplayer.org/, Number: ${t}, Width: ${n}, Height: ${r}, Column: ${o}`,10,i.height-11),i}(this.option)}download(){if(r(this).closed)return this;this.errorHandle(this.file&&this.thumbnailUrl,"Download does not seem to be ready, please create preview first"),this.errorHandle(!this.processing,"There is currently a task in progress, please wait a moment...");const e=function(e,t){const n=document.createElement("a"),r=`${function(e){const t=e.split(".");return t.pop(),t.join(".")}(e.name)}.png`;n.download=r,n.href=t;try{document.body.appendChild(n),n.click()}finally{n.parentNode&&n.parentNode.removeChild(n)}return r}(this.file,this.thumbnailUrl);return this.emit("download",e),this}errorHandle(e,t){if(!e)throw this.emit("error",t),new Error(t)}destroy(){c(this)&&s([()=>f(this),()=>l(this),()=>this.emit("destroy")])}}return m}); diff --git a/packages/artplayer-tool-thumbnail/dist/artplayer-tool-thumbnail.mjs b/packages/artplayer-tool-thumbnail/dist/artplayer-tool-thumbnail.mjs index 7fbe1017c..5adc54695 100644 --- a/packages/artplayer-tool-thumbnail/dist/artplayer-tool-thumbnail.mjs +++ b/packages/artplayer-tool-thumbnail/dist/artplayer-tool-thumbnail.mjs @@ -31,16 +31,26 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ +const owns = (object, name) => Object.prototype.hasOwnProperty.call(object, name); class Emitter { on(name, fn, ctx) { const e = this.e || (this.e = {}); - (e[name] || (e[name] = [])).push({ fn, ctx }); + let listeners = owns(e, name) ? e[name] : void 0; + if (!listeners) { + listeners = []; + Object.defineProperty(e, name, { value: listeners, enumerable: true, configurable: true, writable: true }); + } + listeners.push({ fn, ctx }); return this; } once(name, fn, ctx) { const self = this; const callback = fn; + let fired = false; function listener(...args) { + if (fired) + return; + fired = true; self.off(name, listener); callback.apply(ctx, args); } @@ -49,7 +59,7 @@ class Emitter { } emit(name, ...data) { const e = this.e || (this.e = {}); - const evtArr = (e[name] || []).slice(); + const evtArr = (owns(e, name) ? e[name] || [] : []).slice(); for (let i = 0; i < evtArr.length; i += 1) { evtArr[i].fn.apply(evtArr[i].ctx, data); } @@ -57,7 +67,7 @@ class Emitter { } off(name, callback) { const e = this.e || (this.e = {}); - const evts = e[name]; + const evts = owns(e, name) ? e[name] : void 0; const liveEvents = []; if (evts && callback) { for (let i = 0, len = evts.length; i < len; i += 1) { diff --git a/packages/artplayer-tool-thumbnail/src/emitter.ts b/packages/artplayer-tool-thumbnail/src/emitter.ts index debce874f..1de9c5005 100644 --- a/packages/artplayer-tool-thumbnail/src/emitter.ts +++ b/packages/artplayer-tool-thumbnail/src/emitter.ts @@ -7,13 +7,19 @@ interface Registration { ctx: unknown } type Registry = Partial[]>> +const owns = (object: object, name: PropertyKey) => Object.prototype.hasOwnProperty.call(object, name) export default class Emitter = Record> { declare e?: Registry on(name: Name, fn: (this: Context, ...args: EventArgs) => unknown, ctx?: Context): this { - const e: Registry = this.e || (this.e = {}); - (e[name] || (e[name] = [] as Registration[])).push({ fn, ctx }) + const e: Registry = this.e || (this.e = {}) + let listeners = owns(e, name) ? e[name] : undefined + if (!listeners) { + listeners = [] + Object.defineProperty(e, name, { value: listeners, enumerable: true, configurable: true, writable: true }) + } + listeners.push({ fn, ctx }) return this } @@ -21,7 +27,11 @@ export default class Emitter = Record> = fn + let fired = false function listener(...args: EventArgs) { + if (fired) + return + fired = true self.off(name, listener) callback.apply(ctx, args) } @@ -31,7 +41,7 @@ export default class Emitter = Record(name: Name, ...data: EventArgs): this { const e: Registry = this.e || (this.e = {}) - const evtArr = (e[name] || []).slice() + const evtArr = (owns(e, name) ? e[name] || [] : []).slice() for (let i = 0; i < evtArr.length; i += 1) { // The registry erases heterogeneous listener tuples; emit checks its own payload. (evtArr[i]!.fn as Listener).apply(evtArr[i]!.ctx, data) @@ -41,7 +51,7 @@ export default class Emitter = Record(name: Name, callback?: Listener>): this { const e: Registry = this.e || (this.e = {}) - const evts = e[name] + const evts = owns(e, name) ? e[name] : undefined const liveEvents: Registration[] = [] if (evts && callback) { for (let i = 0, len = evts.length; i < len; i += 1) { diff --git a/refactor/baselines/thumbnail-emitter-validation.json b/refactor/baselines/thumbnail-emitter-validation.json new file mode 100644 index 000000000..d2d1329ae --- /dev/null +++ b/refactor/baselines/thumbnail-emitter-validation.json @@ -0,0 +1,8727 @@ +{ + "schemaVersion": 1, + "task": "PKG-TOOL-THUMB-04", + "status": "doing", + "verifiedAt": "2026-09-12T21:29:00.885Z", + "startedFrom": "3bff236145805ecae46ce60ff5c5710189560755", + "nodeVersion": "v24.21.0", + "node": { + "before": { + "total": 6, + "passed": 1, + "failed": 5 + }, + "candidateSource": 53, + "candidateMain": 55, + "candidateLegacy": 55, + "sourceIncludesVendorChecksInCi": true + }, + "ci": { + "total": 1664, + "unit": 1456, + "engineering": 14, + "baseline": 194, + "productionTs": 339, + "imports": 3 + }, + "browser": { + "freshCases": 189, + "retries": 0, + "skips": 0, + "runs": [ + { + "variant": "source", + "counts": { + "actual-tool": 34, + "input": 3, + "blob-unavailable": 17, + "native-control": 3, + "editor": 3, + "emitter": 3 + }, + "cases": [ + { + "title": "thumbnail-editor-types.spec.js / Monaco consumes Thumbnail global declarations and runs the actual typed constructor and cleanup", + "project": "chromium", + "kind": "editor", + "status": "passed", + "durationMs": 1196, + "browser": "153.0.8010.12", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-editor", + "data": { + "candidate": "candidate-source", + "sha256": "e714d08f1cdb144bc8fd5ccf58c40a69b1e202ee8db91ff768777502c6eb915a", + "declarations": { + "artplayer": "652c0fdf605e87203526777a05851bd69c95c1660d5ff29eab19c00967c9a985", + "artplayer-tool-thumbnail": "9b72d10a1fe964d54fcf4774770074058ea739b4f734b52b8f4adebb37fc83e6", + "artplayer-proxy-mediabunny": "acbaa0a25f20ac15b3d79db00b3a16167f84452ab58933110f42dcb3262fbd3e" + }, + "result": { + "syntax": [], + "semantic": [], + "declaration": [], + "invalid": [ + 2322 + ], + "executed": true, + "actual": { + "last": 7, + "connected": false, + "inputConnected": false, + "processing": false + } + }, + "scope": "Real local Monaco worker, typed constructor/custom event and native DOM cleanup; MediaBunny relative type bundle also compiles. This does not test file decoding or the complete demo/player integration." + } + } + ] + }, + { + "title": "thumbnail-editor-types.spec.js / Monaco consumes Thumbnail global declarations and runs the actual typed constructor and cleanup", + "project": "firefox", + "kind": "editor", + "status": "passed", + "durationMs": 2626, + "browser": "155.0", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-editor", + "data": { + "candidate": "candidate-source", + "sha256": "e714d08f1cdb144bc8fd5ccf58c40a69b1e202ee8db91ff768777502c6eb915a", + "declarations": { + "artplayer": "652c0fdf605e87203526777a05851bd69c95c1660d5ff29eab19c00967c9a985", + "artplayer-tool-thumbnail": "9b72d10a1fe964d54fcf4774770074058ea739b4f734b52b8f4adebb37fc83e6", + "artplayer-proxy-mediabunny": "acbaa0a25f20ac15b3d79db00b3a16167f84452ab58933110f42dcb3262fbd3e" + }, + "result": { + "syntax": [], + "semantic": [], + "declaration": [], + "invalid": [ + 2322 + ], + "executed": true, + "actual": { + "last": 7, + "connected": false, + "inputConnected": false, + "processing": false + } + }, + "scope": "Real local Monaco worker, typed constructor/custom event and native DOM cleanup; MediaBunny relative type bundle also compiles. This does not test file decoding or the complete demo/player integration." + } + } + ] + }, + { + "title": "thumbnail-editor-types.spec.js / Monaco consumes Thumbnail global declarations and runs the actual typed constructor and cleanup", + "project": "webkit", + "kind": "editor", + "status": "passed", + "durationMs": 2025, + "browser": "26.6", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-editor", + "data": { + "candidate": "candidate-source", + "sha256": "e714d08f1cdb144bc8fd5ccf58c40a69b1e202ee8db91ff768777502c6eb915a", + "declarations": { + "artplayer": "652c0fdf605e87203526777a05851bd69c95c1660d5ff29eab19c00967c9a985", + "artplayer-tool-thumbnail": "9b72d10a1fe964d54fcf4774770074058ea739b4f734b52b8f4adebb37fc83e6", + "artplayer-proxy-mediabunny": "acbaa0a25f20ac15b3d79db00b3a16167f84452ab58933110f42dcb3262fbd3e" + }, + "result": { + "syntax": [], + "semantic": [], + "declaration": [], + "invalid": [ + 2322 + ], + "executed": true, + "actual": { + "last": 7, + "connected": false, + "inputConnected": false, + "processing": false + } + }, + "scope": "Real local Monaco worker, typed constructor/custom event and native DOM cleanup; MediaBunny relative type bundle also compiles. This does not test file decoding or the complete demo/player integration." + } + } + ] + }, + { + "title": "thumbnail-emitter.spec.js / Thumbnail native browser event registry handles special names and nested once without changing DOM cleanup", + "project": "chromium", + "kind": "emitter", + "status": "passed", + "durationMs": 307, + "browser": "153.0.8010.12", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-emitter", + "data": { + "candidate": "candidate-source", + "sha256": "e714d08f1cdb144bc8fd5ccf58c40a69b1e202ee8db91ff768777502c6eb915a", + "result": { + "keys": [ + { + "name": "__proto__", + "calls": 3, + "prototypePreserved": true, + "removed": true + }, + { + "name": "constructor", + "calls": 3, + "prototypePreserved": true, + "removed": true + }, + { + "name": "toString", + "calls": 3, + "prototypePreserved": true, + "removed": true + }, + { + "name": "hasOwnProperty", + "calls": 3, + "prototypePreserved": true, + "removed": true + } + ], + "seen": [ + "on:inner", + "once:inner", + "on:outer" + ], + "videoConnected": false, + "inputConnected": false, + "processing": false + } + } + } + ] + }, + { + "title": "thumbnail-emitter.spec.js / Thumbnail native browser event registry handles special names and nested once without changing DOM cleanup", + "project": "firefox", + "kind": "emitter", + "status": "passed", + "durationMs": 1602, + "browser": "155.0", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-emitter", + "data": { + "candidate": "candidate-source", + "sha256": "e714d08f1cdb144bc8fd5ccf58c40a69b1e202ee8db91ff768777502c6eb915a", + "result": { + "keys": [ + { + "name": "__proto__", + "calls": 3, + "prototypePreserved": true, + "removed": true + }, + { + "name": "constructor", + "calls": 3, + "prototypePreserved": true, + "removed": true + }, + { + "name": "toString", + "calls": 3, + "prototypePreserved": true, + "removed": true + }, + { + "name": "hasOwnProperty", + "calls": 3, + "prototypePreserved": true, + "removed": true + } + ], + "seen": [ + "on:inner", + "once:inner", + "on:outer" + ], + "videoConnected": false, + "inputConnected": false, + "processing": false + } + } + } + ] + }, + { + "title": "thumbnail-emitter.spec.js / Thumbnail native browser event registry handles special names and nested once without changing DOM cleanup", + "project": "webkit", + "kind": "emitter", + "status": "passed", + "durationMs": 843, + "browser": "26.6", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-emitter", + "data": { + "candidate": "candidate-source", + "sha256": "e714d08f1cdb144bc8fd5ccf58c40a69b1e202ee8db91ff768777502c6eb915a", + "result": { + "keys": [ + { + "name": "__proto__", + "calls": 3, + "prototypePreserved": true, + "removed": true + }, + { + "name": "constructor", + "calls": 3, + "prototypePreserved": true, + "removed": true + }, + { + "name": "toString", + "calls": 3, + "prototypePreserved": true, + "removed": true + }, + { + "name": "hasOwnProperty", + "calls": 3, + "prototypePreserved": true, + "removed": true + } + ], + "seen": [ + "on:inner", + "once:inner", + "on:outer" + ], + "videoConnected": false, + "inputConnected": false, + "processing": false + } + } + } + ] + }, + { + "title": "thumbnail-input.spec.js / Thumbnail input ownership replaces real DOM listeners and generated inputs", + "project": "chromium", + "kind": "input", + "status": "passed", + "durationMs": 434, + "browser": "153.0.8010.12", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-input-ownership", + "data": { + "implementation": "candidate-source", + "sha256": "e714d08f1cdb144bc8fd5ccf58c40a69b1e202ee8db91ff768777502c6eb915a", + "scope": "Native file input/DOM/listeners only; does not claim media decoding.", + "switched": { + "value": "", + "events": [ + "thumbnail-pattern.mp4" + ], + "firstPosition": "sticky", + "disconnected": true + }, + "result": { + "events": [ + "thumbnail-pattern.mp4", + "thumbnail-pattern.mp4" + ], + "sameError": true, + "notifications": 1, + "inputConnected": false, + "videoConnected": false, + "secondPosition": "absolute", + "inputs": 0 + } + } + } + ] + }, + { + "title": "thumbnail-input.spec.js / Thumbnail input ownership replaces real DOM listeners and generated inputs", + "project": "firefox", + "kind": "input", + "status": "passed", + "durationMs": 616, + "browser": "155.0", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-input-ownership", + "data": { + "implementation": "candidate-source", + "sha256": "e714d08f1cdb144bc8fd5ccf58c40a69b1e202ee8db91ff768777502c6eb915a", + "scope": "Native file input/DOM/listeners only; does not claim media decoding.", + "switched": { + "value": "", + "events": [ + "thumbnail-pattern.mp4" + ], + "firstPosition": "sticky", + "disconnected": true + }, + "result": { + "events": [ + "thumbnail-pattern.mp4", + "thumbnail-pattern.mp4" + ], + "sameError": true, + "notifications": 1, + "inputConnected": false, + "videoConnected": false, + "secondPosition": "absolute", + "inputs": 0 + } + } + } + ] + }, + { + "title": "thumbnail-input.spec.js / Thumbnail input ownership replaces real DOM listeners and generated inputs", + "project": "webkit", + "kind": "input", + "status": "passed", + "durationMs": 874, + "browser": "26.6", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-input-ownership", + "data": { + "implementation": "candidate-source", + "sha256": "e714d08f1cdb144bc8fd5ccf58c40a69b1e202ee8db91ff768777502c6eb915a", + "scope": "Native file input/DOM/listeners only; does not claim media decoding.", + "switched": { + "value": "", + "events": [ + "thumbnail-pattern.mp4" + ], + "firstPosition": "sticky", + "disconnected": true + }, + "result": { + "events": [ + "thumbnail-pattern.mp4", + "thumbnail-pattern.mp4" + ], + "sameError": true, + "notifications": 1, + "inputConnected": false, + "videoConnected": false, + "secondPosition": "absolute", + "inputs": 0 + } + } + } + ] + }, + { + "title": "thumbnail-native.spec.js / Thumbnail native HTTP and Blob media controls", + "project": "chromium", + "kind": "native-control", + "status": "passed", + "durationMs": 233, + "browser": "153.0.8010.12", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-native-media-controls", + "data": [ + { + "url": "/test/pattern.mp4", + "type": "video/mp4", + "transport": "http", + "outcome": "loaded", + "readyState": 4, + "error": 0, + "duration": 8, + "width": 320 + }, + { + "url": "/test/pattern.mp4", + "type": "video/mp4", + "transport": "blob", + "outcome": "loaded", + "readyState": 4, + "error": 0, + "duration": 8, + "width": 320 + }, + { + "url": "/test/thumbnail-pattern.mp4", + "type": "video/mp4", + "transport": "http", + "outcome": "loaded", + "readyState": 4, + "error": 0, + "duration": 24, + "width": 320 + }, + { + "url": "/test/thumbnail-pattern.mp4", + "type": "video/mp4", + "transport": "blob", + "outcome": "loaded", + "readyState": 4, + "error": 0, + "duration": 24, + "width": 320 + }, + { + "url": "/assets/sample/video.mp4", + "type": "video/mp4", + "transport": "http", + "outcome": "loaded", + "readyState": 4, + "error": 0, + "duration": 90.046009, + "width": 640 + }, + { + "url": "/assets/sample/video.mp4", + "type": "video/mp4", + "transport": "blob", + "outcome": "loaded", + "readyState": 4, + "error": 0, + "duration": 90.046009, + "width": 640 + } + ] + } + ] + }, + { + "title": "thumbnail-native.spec.js / Thumbnail native HTTP and Blob media controls", + "project": "firefox", + "kind": "native-control", + "status": "passed", + "durationMs": 654, + "browser": "155.0", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-native-media-controls", + "data": [ + { + "url": "/test/pattern.mp4", + "type": "video/mp4", + "transport": "http", + "outcome": "loaded", + "readyState": 4, + "error": 0, + "duration": 8, + "width": 320 + }, + { + "url": "/test/pattern.mp4", + "type": "video/mp4", + "transport": "blob", + "outcome": "loaded", + "readyState": 4, + "error": 0, + "duration": 8, + "width": 320 + }, + { + "url": "/test/thumbnail-pattern.mp4", + "type": "video/mp4", + "transport": "http", + "outcome": "loaded", + "readyState": 4, + "error": 0, + "duration": 24, + "width": 320 + }, + { + "url": "/test/thumbnail-pattern.mp4", + "type": "video/mp4", + "transport": "blob", + "outcome": "loaded", + "readyState": 4, + "error": 0, + "duration": 24, + "width": 320 + }, + { + "url": "/assets/sample/video.mp4", + "type": "video/mp4", + "transport": "http", + "outcome": "loaded", + "readyState": 4, + "error": 0, + "duration": 90.045986, + "width": 640 + }, + { + "url": "/assets/sample/video.mp4", + "type": "video/mp4", + "transport": "blob", + "outcome": "loaded", + "readyState": 4, + "error": 0, + "duration": 90.045986, + "width": 640 + } + ] + } + ] + }, + { + "title": "thumbnail-native.spec.js / Thumbnail native HTTP and Blob media controls", + "project": "webkit", + "kind": "native-control", + "status": "passed", + "durationMs": 2167, + "browser": "26.6", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-native-media-controls", + "data": [ + { + "url": "/test/pattern.mp4", + "type": "video/mp4", + "transport": "http", + "outcome": "loaded", + "readyState": 4, + "error": 0, + "duration": 8, + "width": 320 + }, + { + "url": "/test/pattern.mp4", + "type": "video/mp4", + "transport": "blob", + "outcome": "error", + "readyState": 0, + "error": 4, + "duration": null, + "width": 0 + }, + { + "url": "/test/thumbnail-pattern.mp4", + "type": "video/mp4", + "transport": "http", + "outcome": "loaded", + "readyState": 4, + "error": 0, + "duration": 24, + "width": 320 + }, + { + "url": "/test/thumbnail-pattern.mp4", + "type": "video/mp4", + "transport": "blob", + "outcome": "error", + "readyState": 0, + "error": 4, + "duration": null, + "width": 0 + }, + { + "url": "/assets/sample/video.mp4", + "type": "video/mp4", + "transport": "http", + "outcome": "loaded", + "readyState": 2, + "error": 0, + "duration": 90.094, + "width": 640 + }, + { + "url": "/assets/sample/video.mp4", + "type": "video/mp4", + "transport": "blob", + "outcome": "error", + "readyState": 0, + "error": 4, + "duration": null, + "width": 0 + } + ] + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool cdn-3.5.31 / native file selection, seek extraction, PNG pixels and repeat generation", + "project": "chromium", + "kind": "actual-tool", + "status": "passed", + "durationMs": 8111, + "browser": "153.0.8010.12", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "cdn-3.5.31", + "sha256": "27fdc506c9b228c40df39f8508b3a42c145571c06c0b9cc60327a007a2064c87", + "recoveredIndividualFile": true, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-extraction", + "data": { + "width": 240, + "height": 150, + "sourceWidth": 320, + "sourceHeight": 180, + "cellHeight": 30, + "mime": "image/png", + "bytes": 26427, + "nonBlack": 2400, + "createdCount": 21, + "revokedCount": 21, + "doneCount": 2, + "updates": [ + 0.1, + 0.2, + 0.3, + 0.4, + 0.5, + 0.6, + 0.7, + 0.8, + 0.9, + 1 + ], + "retiredReadyState": 4, + "retiredSrc": "blob:http://127.0.0.1:8084/5ecd1f50-5c1c-4cfc-b646-d8d11c4e0490", + "connected": false + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool cdn-3.5.31 / DOM drop dispatch exposes missing listener while callable ondrop works", + "project": "chromium", + "kind": "actual-tool", + "status": "passed", + "durationMs": 228, + "browser": "153.0.8010.12", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "cdn-3.5.31", + "sha256": "27fdc506c9b228c40df39f8508b3a42c145571c06c0b9cc60327a007a2064c87", + "recoveredIndividualFile": true, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool cdn-3.5.31 / corrupt media, replacement URLs and repeated destroy reproduce baseline gaps", + "project": "chromium", + "kind": "actual-tool", + "status": "passed", + "durationMs": 261, + "browser": "153.0.8010.12", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "cdn-3.5.31", + "sha256": "27fdc506c9b228c40df39f8508b3a42c145571c06c0b9cc60327a007a2064c87", + "recoveredIndividualFile": true, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-media-failure", + "data": { + "failed": { + "code": 4, + "toolErrors": 0, + "first": "blob:http://127.0.0.1:8084/9fa5e32f-9023-4452-95f8-0302d9807dd8" + }, + "result": { + "value": "C:\\fakepath\\thumbnail-pattern.mp4", + "created": [ + "blob:http://127.0.0.1:8084/9fa5e32f-9023-4452-95f8-0302d9807dd8", + "blob:http://127.0.0.1:8084/9afe5ef7-7290-47d6-8d0b-2342f12825b5" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/9afe5ef7-7290-47d6-8d0b-2342f12825b5" + ], + "repeated": "NotFoundError" + } + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool cdn-3.5.31 / native PNG callback delivered after destroy still emits update and creates a URL", + "project": "chromium", + "kind": "actual-tool", + "status": "passed", + "durationMs": 659, + "browser": "153.0.8010.12", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "cdn-3.5.31", + "sha256": "27fdc506c9b228c40df39f8508b3a42c145571c06c0b9cc60327a007a2064c87", + "recoveredIndividualFile": true, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-late-native-blob", + "data": { + "count": 1, + "created": [ + "blob:http://127.0.0.1:8084/a231d12b-d7ba-402a-8459-2f5125f26ac6", + "blob:http://127.0.0.1:8084/540eef65-98d2-4465-8e16-c666c21e3d66" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/a231d12b-d7ba-402a-8459-2f5125f26ac6" + ], + "pendingState": "pending", + "events": [ + "file", + "canvas", + "video", + "destroy", + "update" + ], + "connected": false, + "bytes": 7540 + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool cdn-3.5.31 / native file selection, seek extraction, PNG pixels and repeat generation", + "project": "firefox", + "kind": "actual-tool", + "status": "passed", + "durationMs": 8394, + "browser": "155.0", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "cdn-3.5.31", + "sha256": "27fdc506c9b228c40df39f8508b3a42c145571c06c0b9cc60327a007a2064c87", + "recoveredIndividualFile": true, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-extraction", + "data": { + "width": 240, + "height": 150, + "sourceWidth": 320, + "sourceHeight": 180, + "cellHeight": 30, + "mime": "image/png", + "bytes": 18224, + "nonBlack": 2400, + "createdCount": 21, + "revokedCount": 21, + "doneCount": 2, + "updates": [ + 0.1, + 0.2, + 0.3, + 0.4, + 0.5, + 0.6, + 0.7, + 0.8, + 0.9, + 1 + ], + "retiredReadyState": 4, + "retiredSrc": "blob:http://127.0.0.1:8084/8930c6ec-7799-4237-a547-ccf77ad357a6", + "connected": false + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool cdn-3.5.31 / DOM drop dispatch exposes missing listener while callable ondrop works", + "project": "firefox", + "kind": "actual-tool", + "status": "passed", + "durationMs": 1733, + "browser": "155.0", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "cdn-3.5.31", + "sha256": "27fdc506c9b228c40df39f8508b3a42c145571c06c0b9cc60327a007a2064c87", + "recoveredIndividualFile": true, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool cdn-3.5.31 / corrupt media, replacement URLs and repeated destroy reproduce baseline gaps", + "project": "firefox", + "kind": "actual-tool", + "status": "passed", + "durationMs": 716, + "browser": "155.0", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "cdn-3.5.31", + "sha256": "27fdc506c9b228c40df39f8508b3a42c145571c06c0b9cc60327a007a2064c87", + "recoveredIndividualFile": true, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-media-failure", + "data": { + "failed": { + "code": 4, + "toolErrors": 0, + "first": "blob:http://127.0.0.1:8084/3ce6f1d3-edd5-4284-a23b-e16d86eccbee" + }, + "result": { + "value": "C:\\fakepath\\thumbnail-pattern.mp4", + "created": [ + "blob:http://127.0.0.1:8084/3ce6f1d3-edd5-4284-a23b-e16d86eccbee", + "blob:http://127.0.0.1:8084/dc7942fa-3f0e-4bc4-8923-2fd54e2fbf1f" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/dc7942fa-3f0e-4bc4-8923-2fd54e2fbf1f" + ], + "repeated": "NotFoundError" + } + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool cdn-3.5.31 / native PNG callback delivered after destroy still emits update and creates a URL", + "project": "firefox", + "kind": "actual-tool", + "status": "passed", + "durationMs": 1038, + "browser": "155.0", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "cdn-3.5.31", + "sha256": "27fdc506c9b228c40df39f8508b3a42c145571c06c0b9cc60327a007a2064c87", + "recoveredIndividualFile": true, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-late-native-blob", + "data": { + "count": 1, + "created": [ + "blob:http://127.0.0.1:8084/d5268189-203c-4d2b-a6dd-6dc2da94286d", + "blob:http://127.0.0.1:8084/4d04291a-1fd9-4a5a-894b-05a54d69d032" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/d5268189-203c-4d2b-a6dd-6dc2da94286d" + ], + "pendingState": "pending", + "events": [ + "file", + "canvas", + "video", + "destroy", + "update" + ], + "connected": false, + "bytes": 4980 + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool cdn-3.5.31 / native file selection, seek extraction, PNG pixels and repeat generation", + "project": "webkit", + "kind": "blob-unavailable", + "status": "passed", + "durationMs": 1609, + "browser": "26.6", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "cdn-3.5.31", + "sha256": "27fdc506c9b228c40df39f8508b3a42c145571c06c0b9cc60327a007a2064c87", + "recoveredIndividualFile": true, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-blob-unavailable", + "data": { + "native": { + "http": "loaded", + "error": 4, + "blob": "error" + }, + "tool": { + "errors": 0, + "processing": false, + "duration": null + } + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool cdn-3.5.31 / DOM drop dispatch exposes missing listener while callable ondrop works", + "project": "webkit", + "kind": "blob-unavailable", + "status": "passed", + "durationMs": 1542, + "browser": "26.6", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "cdn-3.5.31", + "sha256": "27fdc506c9b228c40df39f8508b3a42c145571c06c0b9cc60327a007a2064c87", + "recoveredIndividualFile": true, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-blob-unavailable", + "data": { + "native": { + "http": "loaded", + "error": 4, + "blob": "error" + }, + "tool": { + "errors": 0, + "processing": false, + "duration": null + } + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool cdn-3.5.31 / corrupt media, replacement URLs and repeated destroy reproduce baseline gaps", + "project": "webkit", + "kind": "blob-unavailable", + "status": "passed", + "durationMs": 1433, + "browser": "26.6", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "cdn-3.5.31", + "sha256": "27fdc506c9b228c40df39f8508b3a42c145571c06c0b9cc60327a007a2064c87", + "recoveredIndividualFile": true, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-blob-unavailable", + "data": { + "native": { + "http": "loaded", + "error": 4, + "blob": "error" + }, + "tool": { + "errors": 0, + "processing": false, + "duration": null + } + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool cdn-3.5.31 / native PNG callback delivered after destroy still emits update and creates a URL", + "project": "webkit", + "kind": "blob-unavailable", + "status": "passed", + "durationMs": 1336, + "browser": "26.6", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "cdn-3.5.31", + "sha256": "27fdc506c9b228c40df39f8508b3a42c145571c06c0b9cc60327a007a2064c87", + "recoveredIndividualFile": true, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-blob-unavailable", + "data": { + "native": { + "http": "loaded", + "error": 4, + "blob": "error" + }, + "tool": { + "errors": 0, + "processing": false, + "duration": null + } + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool workspace.js / native file selection, seek extraction, PNG pixels and repeat generation", + "project": "chromium", + "kind": "actual-tool", + "status": "passed", + "durationMs": 623, + "browser": "153.0.8010.12", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "workspace.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "recoveredIndividualFile": false, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-extraction", + "data": { + "width": 240, + "height": 210, + "sourceWidth": 320, + "sourceHeight": 180, + "cellHeight": 45, + "mime": "image/png", + "bytes": 44055, + "nonBlack": 3600, + "createdCount": 21, + "revokedCount": 21, + "doneCount": 2, + "updates": [ + 0.1, + 0.2, + 0.3, + 0.4, + 0.5, + 0.6, + 0.7, + 0.8, + 0.9, + 1 + ], + "retiredReadyState": 4, + "retiredSrc": "blob:http://127.0.0.1:8084/5e581191-4122-40dd-8bbe-e7c1c5d81cfc", + "connected": false + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool workspace.js / DOM drop dispatch exposes missing listener while callable ondrop works", + "project": "chromium", + "kind": "actual-tool", + "status": "passed", + "durationMs": 235, + "browser": "153.0.8010.12", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "workspace.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "recoveredIndividualFile": false, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool workspace.js / corrupt media, replacement URLs and repeated destroy reproduce baseline gaps", + "project": "chromium", + "kind": "actual-tool", + "status": "passed", + "durationMs": 274, + "browser": "153.0.8010.12", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "workspace.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "recoveredIndividualFile": false, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-media-failure", + "data": { + "failed": { + "code": 4, + "toolErrors": 0, + "first": "blob:http://127.0.0.1:8084/30829811-9468-4539-bed9-23783287c4f7" + }, + "result": { + "value": "", + "created": [ + "blob:http://127.0.0.1:8084/30829811-9468-4539-bed9-23783287c4f7", + "blob:http://127.0.0.1:8084/70cdacb7-934d-4e6e-9027-02ee438902fe" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/70cdacb7-934d-4e6e-9027-02ee438902fe" + ], + "repeated": "NotFoundError" + } + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool workspace.js / native PNG callback delivered after destroy still emits update and creates a URL", + "project": "chromium", + "kind": "actual-tool", + "status": "passed", + "durationMs": 382, + "browser": "153.0.8010.12", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "workspace.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "recoveredIndividualFile": false, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-late-native-blob", + "data": { + "count": 1, + "created": [ + "blob:http://127.0.0.1:8084/b088fe5b-177c-4836-8957-1b9a37651a66", + "blob:http://127.0.0.1:8084/725b914e-5575-4f3f-94c0-3669839d14d7" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/b088fe5b-177c-4836-8957-1b9a37651a66" + ], + "pendingState": "pending", + "events": [ + "file", + "video", + "canvas", + "destroy", + "update" + ], + "connected": false, + "bytes": 9120 + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool workspace.js / native file selection, seek extraction, PNG pixels and repeat generation", + "project": "firefox", + "kind": "actual-tool", + "status": "passed", + "durationMs": 1746, + "browser": "155.0", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "workspace.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "recoveredIndividualFile": false, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-extraction", + "data": { + "width": 240, + "height": 210, + "sourceWidth": 320, + "sourceHeight": 180, + "cellHeight": 45, + "mime": "image/png", + "bytes": 38895, + "nonBlack": 3600, + "createdCount": 21, + "revokedCount": 21, + "doneCount": 2, + "updates": [ + 0.1, + 0.2, + 0.3, + 0.4, + 0.5, + 0.6, + 0.7, + 0.8, + 0.9, + 1 + ], + "retiredReadyState": 4, + "retiredSrc": "blob:http://127.0.0.1:8084/a9815dbb-ae63-4b6b-a217-03f61ec7e2b7", + "connected": false + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool workspace.js / DOM drop dispatch exposes missing listener while callable ondrop works", + "project": "firefox", + "kind": "actual-tool", + "status": "passed", + "durationMs": 460, + "browser": "155.0", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "workspace.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "recoveredIndividualFile": false, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool workspace.js / corrupt media, replacement URLs and repeated destroy reproduce baseline gaps", + "project": "firefox", + "kind": "actual-tool", + "status": "passed", + "durationMs": 619, + "browser": "155.0", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "workspace.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "recoveredIndividualFile": false, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-media-failure", + "data": { + "failed": { + "code": 4, + "toolErrors": 0, + "first": "blob:http://127.0.0.1:8084/947f7244-3864-41a8-8620-9bb1cd254e02" + }, + "result": { + "value": "", + "created": [ + "blob:http://127.0.0.1:8084/947f7244-3864-41a8-8620-9bb1cd254e02", + "blob:http://127.0.0.1:8084/1eda1dd3-90ab-4ea2-8a1b-17d3c7c353a2" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/1eda1dd3-90ab-4ea2-8a1b-17d3c7c353a2" + ], + "repeated": "NotFoundError" + } + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool workspace.js / native PNG callback delivered after destroy still emits update and creates a URL", + "project": "firefox", + "kind": "actual-tool", + "status": "passed", + "durationMs": 597, + "browser": "155.0", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "workspace.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "recoveredIndividualFile": false, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-late-native-blob", + "data": { + "count": 1, + "created": [ + "blob:http://127.0.0.1:8084/5fb29f5d-335c-4641-b204-0469ba9fa0f0", + "blob:http://127.0.0.1:8084/6eee226f-999c-4b9c-b7b5-ba7cd6f2ae6b" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/5fb29f5d-335c-4641-b204-0469ba9fa0f0" + ], + "pendingState": "pending", + "events": [ + "file", + "video", + "canvas", + "destroy", + "update" + ], + "connected": false, + "bytes": 6496 + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool workspace.js / native file selection, seek extraction, PNG pixels and repeat generation", + "project": "webkit", + "kind": "blob-unavailable", + "status": "passed", + "durationMs": 1810, + "browser": "26.6", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "workspace.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "recoveredIndividualFile": false, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-blob-unavailable", + "data": { + "native": { + "http": "loaded", + "error": 4, + "blob": "error" + }, + "tool": { + "errors": 0, + "processing": false, + "duration": null + } + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool workspace.js / DOM drop dispatch exposes missing listener while callable ondrop works", + "project": "webkit", + "kind": "blob-unavailable", + "status": "passed", + "durationMs": 1358, + "browser": "26.6", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "workspace.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "recoveredIndividualFile": false, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-blob-unavailable", + "data": { + "native": { + "http": "loaded", + "error": 4, + "blob": "error" + }, + "tool": { + "errors": 0, + "processing": false, + "duration": null + } + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool workspace.js / corrupt media, replacement URLs and repeated destroy reproduce baseline gaps", + "project": "webkit", + "kind": "blob-unavailable", + "status": "passed", + "durationMs": 1395, + "browser": "26.6", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "workspace.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "recoveredIndividualFile": false, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-blob-unavailable", + "data": { + "native": { + "http": "loaded", + "error": 4, + "blob": "error" + }, + "tool": { + "errors": 0, + "processing": false, + "duration": null + } + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool workspace.js / native PNG callback delivered after destroy still emits update and creates a URL", + "project": "webkit", + "kind": "blob-unavailable", + "status": "passed", + "durationMs": 1447, + "browser": "26.6", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "workspace.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "recoveredIndividualFile": false, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-blob-unavailable", + "data": { + "native": { + "http": "loaded", + "error": 4, + "blob": "error" + }, + "tool": { + "errors": 0, + "processing": false, + "duration": null + } + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool workspace.legacy.js / native file selection, seek extraction, PNG pixels and repeat generation", + "project": "chromium", + "kind": "actual-tool", + "status": "passed", + "durationMs": 592, + "browser": "153.0.8010.12", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "workspace.legacy.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "recoveredIndividualFile": false, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-extraction", + "data": { + "width": 240, + "height": 210, + "sourceWidth": 320, + "sourceHeight": 180, + "cellHeight": 45, + "mime": "image/png", + "bytes": 44055, + "nonBlack": 3600, + "createdCount": 21, + "revokedCount": 21, + "doneCount": 2, + "updates": [ + 0.1, + 0.2, + 0.3, + 0.4, + 0.5, + 0.6, + 0.7, + 0.8, + 0.9, + 1 + ], + "retiredReadyState": 4, + "retiredSrc": "blob:http://127.0.0.1:8084/c8bed336-f7b4-4665-9135-ca360ffc273b", + "connected": false + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool workspace.legacy.js / DOM drop dispatch exposes missing listener while callable ondrop works", + "project": "chromium", + "kind": "actual-tool", + "status": "passed", + "durationMs": 206, + "browser": "153.0.8010.12", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "workspace.legacy.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "recoveredIndividualFile": false, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool workspace.legacy.js / corrupt media, replacement URLs and repeated destroy reproduce baseline gaps", + "project": "chromium", + "kind": "actual-tool", + "status": "passed", + "durationMs": 273, + "browser": "153.0.8010.12", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "workspace.legacy.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "recoveredIndividualFile": false, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-media-failure", + "data": { + "failed": { + "code": 4, + "toolErrors": 0, + "first": "blob:http://127.0.0.1:8084/548f6940-92f3-420b-a321-bd9dd2eb2b54" + }, + "result": { + "value": "", + "created": [ + "blob:http://127.0.0.1:8084/548f6940-92f3-420b-a321-bd9dd2eb2b54", + "blob:http://127.0.0.1:8084/eae9d0a7-bd35-43b8-9e11-50154422cd95" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/eae9d0a7-bd35-43b8-9e11-50154422cd95" + ], + "repeated": "NotFoundError" + } + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool workspace.legacy.js / native PNG callback delivered after destroy still emits update and creates a URL", + "project": "chromium", + "kind": "actual-tool", + "status": "passed", + "durationMs": 367, + "browser": "153.0.8010.12", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "workspace.legacy.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "recoveredIndividualFile": false, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-late-native-blob", + "data": { + "count": 1, + "created": [ + "blob:http://127.0.0.1:8084/647e9c70-e52f-4cfc-bdaa-a90538219382", + "blob:http://127.0.0.1:8084/c5dd3e23-47fe-4463-8266-d1d8d2d48e60" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/647e9c70-e52f-4cfc-bdaa-a90538219382" + ], + "pendingState": "pending", + "events": [ + "file", + "video", + "canvas", + "destroy", + "update" + ], + "connected": false, + "bytes": 9120 + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool workspace.legacy.js / native file selection, seek extraction, PNG pixels and repeat generation", + "project": "firefox", + "kind": "actual-tool", + "status": "passed", + "durationMs": 1670, + "browser": "155.0", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "workspace.legacy.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "recoveredIndividualFile": false, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-extraction", + "data": { + "width": 240, + "height": 210, + "sourceWidth": 320, + "sourceHeight": 180, + "cellHeight": 45, + "mime": "image/png", + "bytes": 38895, + "nonBlack": 3600, + "createdCount": 21, + "revokedCount": 21, + "doneCount": 2, + "updates": [ + 0.1, + 0.2, + 0.3, + 0.4, + 0.5, + 0.6, + 0.7, + 0.8, + 0.9, + 1 + ], + "retiredReadyState": 4, + "retiredSrc": "blob:http://127.0.0.1:8084/abfb5971-25fd-4595-88e0-44d87430ac16", + "connected": false + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool workspace.legacy.js / DOM drop dispatch exposes missing listener while callable ondrop works", + "project": "firefox", + "kind": "actual-tool", + "status": "passed", + "durationMs": 516, + "browser": "155.0", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "workspace.legacy.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "recoveredIndividualFile": false, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool workspace.legacy.js / corrupt media, replacement URLs and repeated destroy reproduce baseline gaps", + "project": "firefox", + "kind": "actual-tool", + "status": "passed", + "durationMs": 492, + "browser": "155.0", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "workspace.legacy.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "recoveredIndividualFile": false, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-media-failure", + "data": { + "failed": { + "code": 4, + "toolErrors": 0, + "first": "blob:http://127.0.0.1:8084/bd431428-c0c7-45b7-93dc-630dc9e557f4" + }, + "result": { + "value": "", + "created": [ + "blob:http://127.0.0.1:8084/bd431428-c0c7-45b7-93dc-630dc9e557f4", + "blob:http://127.0.0.1:8084/96d0894f-eeb4-4cff-9fe9-b212baa48d78" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/96d0894f-eeb4-4cff-9fe9-b212baa48d78" + ], + "repeated": "NotFoundError" + } + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool workspace.legacy.js / native PNG callback delivered after destroy still emits update and creates a URL", + "project": "firefox", + "kind": "actual-tool", + "status": "passed", + "durationMs": 703, + "browser": "155.0", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "workspace.legacy.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "recoveredIndividualFile": false, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-late-native-blob", + "data": { + "count": 1, + "created": [ + "blob:http://127.0.0.1:8084/ec845d8b-c711-4fc9-89da-cf71ce775930", + "blob:http://127.0.0.1:8084/0368fec3-55b3-4120-9882-ce53cb07fec0" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/ec845d8b-c711-4fc9-89da-cf71ce775930" + ], + "pendingState": "pending", + "events": [ + "file", + "video", + "canvas", + "destroy", + "update" + ], + "connected": false, + "bytes": 6496 + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool workspace.legacy.js / native file selection, seek extraction, PNG pixels and repeat generation", + "project": "webkit", + "kind": "blob-unavailable", + "status": "passed", + "durationMs": 1462, + "browser": "26.6", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "workspace.legacy.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "recoveredIndividualFile": false, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-blob-unavailable", + "data": { + "native": { + "http": "loaded", + "error": 4, + "blob": "error" + }, + "tool": { + "errors": 0, + "processing": false, + "duration": null + } + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool workspace.legacy.js / DOM drop dispatch exposes missing listener while callable ondrop works", + "project": "webkit", + "kind": "blob-unavailable", + "status": "passed", + "durationMs": 1507, + "browser": "26.6", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "workspace.legacy.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "recoveredIndividualFile": false, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-blob-unavailable", + "data": { + "native": { + "http": "loaded", + "error": 4, + "blob": "error" + }, + "tool": { + "errors": 0, + "processing": false, + "duration": null + } + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool workspace.legacy.js / corrupt media, replacement URLs and repeated destroy reproduce baseline gaps", + "project": "webkit", + "kind": "blob-unavailable", + "status": "passed", + "durationMs": 1455, + "browser": "26.6", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "workspace.legacy.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "recoveredIndividualFile": false, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-blob-unavailable", + "data": { + "native": { + "http": "loaded", + "error": 4, + "blob": "error" + }, + "tool": { + "errors": 0, + "processing": false, + "duration": null + } + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool workspace.legacy.js / native PNG callback delivered after destroy still emits update and creates a URL", + "project": "webkit", + "kind": "blob-unavailable", + "status": "passed", + "durationMs": 1404, + "browser": "26.6", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "workspace.legacy.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "recoveredIndividualFile": false, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-blob-unavailable", + "data": { + "native": { + "http": "loaded", + "error": 4, + "blob": "error" + }, + "tool": { + "errors": 0, + "processing": false, + "duration": null + } + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool candidate-source / native file selection, seek extraction, PNG pixels and repeat generation", + "project": "chromium", + "kind": "actual-tool", + "status": "passed", + "durationMs": 722, + "browser": "153.0.8010.12", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "candidate-source", + "sha256": "e714d08f1cdb144bc8fd5ccf58c40a69b1e202ee8db91ff768777502c6eb915a", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-extraction", + "data": { + "width": 240, + "height": 210, + "sourceWidth": 320, + "sourceHeight": 180, + "cellHeight": 45, + "mime": "image/png", + "bytes": 44055, + "nonBlack": 3600, + "createdCount": 21, + "revokedCount": 21, + "doneCount": 2, + "updates": [ + 0.1, + 0.2, + 0.3, + 0.4, + 0.5, + 0.6, + 0.7, + 0.8, + 0.9, + 1 + ], + "retiredReadyState": 0, + "retiredSrc": null, + "connected": false + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool candidate-source / DOM drop dispatch uses the registered handler while callable ondrop works", + "project": "chromium", + "kind": "actual-tool", + "status": "passed", + "durationMs": 257, + "browser": "153.0.8010.12", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "candidate-source", + "sha256": "e714d08f1cdb144bc8fd5ccf58c40a69b1e202ee8db91ff768777502c6eb915a", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool candidate-source / corrupt media, replacement URLs and repeated destroy reproduce baseline gaps", + "project": "chromium", + "kind": "actual-tool", + "status": "passed", + "durationMs": 301, + "browser": "153.0.8010.12", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "candidate-source", + "sha256": "e714d08f1cdb144bc8fd5ccf58c40a69b1e202ee8db91ff768777502c6eb915a", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-media-failure", + "data": { + "failed": { + "code": 4, + "toolErrors": 1, + "first": "blob:http://127.0.0.1:8084/3eaeada2-4d2b-4b83-b6fe-a38098e963bf" + }, + "result": { + "value": "", + "created": [ + "blob:http://127.0.0.1:8084/3eaeada2-4d2b-4b83-b6fe-a38098e963bf", + "blob:http://127.0.0.1:8084/46009813-0d44-467d-bb5a-938ecc80834c" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/3eaeada2-4d2b-4b83-b6fe-a38098e963bf", + "blob:http://127.0.0.1:8084/46009813-0d44-467d-bb5a-938ecc80834c" + ] + } + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool candidate-source / native PNG callback delivered after destroy is ignored after cancellation", + "project": "chromium", + "kind": "actual-tool", + "status": "passed", + "durationMs": 372, + "browser": "153.0.8010.12", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "candidate-source", + "sha256": "e714d08f1cdb144bc8fd5ccf58c40a69b1e202ee8db91ff768777502c6eb915a", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-late-native-blob", + "data": { + "count": 1, + "created": [ + "blob:http://127.0.0.1:8084/28ec9023-34e4-453a-af77-575af88c3ad5" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/28ec9023-34e4-453a-af77-575af88c3ad5" + ], + "pendingState": "rejected", + "events": [ + "file", + "video", + "canvas", + "destroy" + ], + "connected": false, + "bytes": 9120 + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool candidate-source / source replacement cancels held native encoding and completes only the latest job", + "project": "chromium", + "kind": "actual-tool", + "status": "passed", + "durationMs": 587, + "browser": "153.0.8010.12", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "candidate-source", + "sha256": "e714d08f1cdb144bc8fd5ccf58c40a69b1e202ee8db91ff768777502c6eb915a", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-native-source-cancellation", + "data": { + "oldState": "AbortError", + "firstEncodedBytes": 9120, + "source": "blob:http://127.0.0.1:8084/ffe8d5da-7e13-48b6-a1d5-bc8cc106aadc", + "updateSources": [ + "blob:http://127.0.0.1:8084/ffe8d5da-7e13-48b6-a1d5-bc8cc106aadc", + "blob:http://127.0.0.1:8084/ffe8d5da-7e13-48b6-a1d5-bc8cc106aadc", + "blob:http://127.0.0.1:8084/ffe8d5da-7e13-48b6-a1d5-bc8cc106aadc", + "blob:http://127.0.0.1:8084/ffe8d5da-7e13-48b6-a1d5-bc8cc106aadc", + "blob:http://127.0.0.1:8084/ffe8d5da-7e13-48b6-a1d5-bc8cc106aadc", + "blob:http://127.0.0.1:8084/ffe8d5da-7e13-48b6-a1d5-bc8cc106aadc", + "blob:http://127.0.0.1:8084/ffe8d5da-7e13-48b6-a1d5-bc8cc106aadc", + "blob:http://127.0.0.1:8084/ffe8d5da-7e13-48b6-a1d5-bc8cc106aadc", + "blob:http://127.0.0.1:8084/ffe8d5da-7e13-48b6-a1d5-bc8cc106aadc", + "blob:http://127.0.0.1:8084/ffe8d5da-7e13-48b6-a1d5-bc8cc106aadc" + ], + "created": [ + "blob:http://127.0.0.1:8084/6fea3587-b7f5-4ac1-a8a8-40f8d7d23773", + "blob:http://127.0.0.1:8084/ffe8d5da-7e13-48b6-a1d5-bc8cc106aadc", + "blob:http://127.0.0.1:8084/ba8fe4cd-c1d7-4cf0-acd8-5ab9e0d95fe0", + "blob:http://127.0.0.1:8084/53156dbb-f1f4-4857-ba65-9bedc6261f9d", + "blob:http://127.0.0.1:8084/68e07c47-e9d8-4435-a530-d4501129adbe", + "blob:http://127.0.0.1:8084/3f97c027-eb51-4554-961c-9e40d563f0b0", + "blob:http://127.0.0.1:8084/0c1dcc9c-8c86-4ca0-a063-49f61aec886a", + "blob:http://127.0.0.1:8084/9d051ce5-3a5f-43b9-a746-edcd0b4aeda2", + "blob:http://127.0.0.1:8084/191bd97f-e707-4aa9-b97c-a1992757713c", + "blob:http://127.0.0.1:8084/fe73fee2-5c2b-40dc-a7d8-2c6dfca9e5e6", + "blob:http://127.0.0.1:8084/2e0fa910-ee09-4217-8eea-f4f9bad95b8c", + "blob:http://127.0.0.1:8084/8ab3cc12-b8c3-4c89-a3ac-d49c7382ca8e" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/6fea3587-b7f5-4ac1-a8a8-40f8d7d23773", + "blob:http://127.0.0.1:8084/ba8fe4cd-c1d7-4cf0-acd8-5ab9e0d95fe0", + "blob:http://127.0.0.1:8084/53156dbb-f1f4-4857-ba65-9bedc6261f9d", + "blob:http://127.0.0.1:8084/68e07c47-e9d8-4435-a530-d4501129adbe", + "blob:http://127.0.0.1:8084/3f97c027-eb51-4554-961c-9e40d563f0b0", + "blob:http://127.0.0.1:8084/0c1dcc9c-8c86-4ca0-a063-49f61aec886a", + "blob:http://127.0.0.1:8084/9d051ce5-3a5f-43b9-a746-edcd0b4aeda2", + "blob:http://127.0.0.1:8084/191bd97f-e707-4aa9-b97c-a1992757713c", + "blob:http://127.0.0.1:8084/fe73fee2-5c2b-40dc-a7d8-2c6dfca9e5e6", + "blob:http://127.0.0.1:8084/2e0fa910-ee09-4217-8eea-f4f9bad95b8c", + "blob:http://127.0.0.1:8084/ffe8d5da-7e13-48b6-a1d5-bc8cc106aadc", + "blob:http://127.0.0.1:8084/8ab3cc12-b8c3-4c89-a3ac-d49c7382ca8e" + ], + "events": [ + "file", + "video", + "canvas", + "file", + "video", + "canvas", + "update", + "update", + "update", + "update", + "update", + "update", + "update", + "update", + "update", + "update", + "done", + "destroy" + ], + "connected": false + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool candidate-source / native file selection, seek extraction, PNG pixels and repeat generation", + "project": "firefox", + "kind": "actual-tool", + "status": "passed", + "durationMs": 1668, + "browser": "155.0", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "candidate-source", + "sha256": "e714d08f1cdb144bc8fd5ccf58c40a69b1e202ee8db91ff768777502c6eb915a", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-extraction", + "data": { + "width": 240, + "height": 210, + "sourceWidth": 320, + "sourceHeight": 180, + "cellHeight": 45, + "mime": "image/png", + "bytes": 38895, + "nonBlack": 3600, + "createdCount": 21, + "revokedCount": 21, + "doneCount": 2, + "updates": [ + 0.1, + 0.2, + 0.3, + 0.4, + 0.5, + 0.6, + 0.7, + 0.8, + 0.9, + 1 + ], + "retiredReadyState": 0, + "retiredSrc": null, + "connected": false + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool candidate-source / DOM drop dispatch uses the registered handler while callable ondrop works", + "project": "firefox", + "kind": "actual-tool", + "status": "passed", + "durationMs": 1380, + "browser": "155.0", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "candidate-source", + "sha256": "e714d08f1cdb144bc8fd5ccf58c40a69b1e202ee8db91ff768777502c6eb915a", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool candidate-source / corrupt media, replacement URLs and repeated destroy reproduce baseline gaps", + "project": "firefox", + "kind": "actual-tool", + "status": "passed", + "durationMs": 715, + "browser": "155.0", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "candidate-source", + "sha256": "e714d08f1cdb144bc8fd5ccf58c40a69b1e202ee8db91ff768777502c6eb915a", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-media-failure", + "data": { + "failed": { + "code": 4, + "toolErrors": 1, + "first": "blob:http://127.0.0.1:8084/e566aca5-e800-4349-ae45-a6820fcea40a" + }, + "result": { + "value": "", + "created": [ + "blob:http://127.0.0.1:8084/e566aca5-e800-4349-ae45-a6820fcea40a", + "blob:http://127.0.0.1:8084/39ab0726-5aa9-40d4-bf92-e65899df3e17" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/e566aca5-e800-4349-ae45-a6820fcea40a", + "blob:http://127.0.0.1:8084/39ab0726-5aa9-40d4-bf92-e65899df3e17" + ] + } + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool candidate-source / native PNG callback delivered after destroy is ignored after cancellation", + "project": "firefox", + "kind": "actual-tool", + "status": "passed", + "durationMs": 794, + "browser": "155.0", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "candidate-source", + "sha256": "e714d08f1cdb144bc8fd5ccf58c40a69b1e202ee8db91ff768777502c6eb915a", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-late-native-blob", + "data": { + "count": 1, + "created": [ + "blob:http://127.0.0.1:8084/1bf71312-8b6b-45fc-86ce-dafe12646e91" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/1bf71312-8b6b-45fc-86ce-dafe12646e91" + ], + "pendingState": "rejected", + "events": [ + "file", + "video", + "canvas", + "destroy" + ], + "connected": false, + "bytes": 6496 + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool candidate-source / source replacement cancels held native encoding and completes only the latest job", + "project": "firefox", + "kind": "actual-tool", + "status": "passed", + "durationMs": 1334, + "browser": "155.0", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "candidate-source", + "sha256": "e714d08f1cdb144bc8fd5ccf58c40a69b1e202ee8db91ff768777502c6eb915a", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-native-source-cancellation", + "data": { + "oldState": "AbortError", + "firstEncodedBytes": 6496, + "source": "blob:http://127.0.0.1:8084/5935352f-0f5a-4d6e-a641-f69c915084be", + "updateSources": [ + "blob:http://127.0.0.1:8084/5935352f-0f5a-4d6e-a641-f69c915084be", + "blob:http://127.0.0.1:8084/5935352f-0f5a-4d6e-a641-f69c915084be", + "blob:http://127.0.0.1:8084/5935352f-0f5a-4d6e-a641-f69c915084be", + "blob:http://127.0.0.1:8084/5935352f-0f5a-4d6e-a641-f69c915084be", + "blob:http://127.0.0.1:8084/5935352f-0f5a-4d6e-a641-f69c915084be", + "blob:http://127.0.0.1:8084/5935352f-0f5a-4d6e-a641-f69c915084be", + "blob:http://127.0.0.1:8084/5935352f-0f5a-4d6e-a641-f69c915084be", + "blob:http://127.0.0.1:8084/5935352f-0f5a-4d6e-a641-f69c915084be", + "blob:http://127.0.0.1:8084/5935352f-0f5a-4d6e-a641-f69c915084be", + "blob:http://127.0.0.1:8084/5935352f-0f5a-4d6e-a641-f69c915084be" + ], + "created": [ + "blob:http://127.0.0.1:8084/21705d5c-fda1-4175-adc4-7ac019bb4690", + "blob:http://127.0.0.1:8084/5935352f-0f5a-4d6e-a641-f69c915084be", + "blob:http://127.0.0.1:8084/3cf017bc-9f1e-43ae-aaf8-f36d1730abd7", + "blob:http://127.0.0.1:8084/5155a9d9-a5d5-4119-aebd-c0b57f713b8f", + "blob:http://127.0.0.1:8084/dcfb34cd-8b7b-4a09-b4a9-0afeeec28ac7", + "blob:http://127.0.0.1:8084/a29b840d-376c-4cfc-91aa-37d0b0bf4c45", + "blob:http://127.0.0.1:8084/65d3acad-eeec-457d-92f5-094838f0f970", + "blob:http://127.0.0.1:8084/5ace0559-5e8f-4a87-b054-67f22dd1f8b0", + "blob:http://127.0.0.1:8084/7f909870-87b3-4973-a2c1-c4af834abe6e", + "blob:http://127.0.0.1:8084/472bd1c3-d8e8-4560-bb3b-99fa00871f8e", + "blob:http://127.0.0.1:8084/726d743b-997d-45d1-83e8-19bfa85d8af0", + "blob:http://127.0.0.1:8084/b22dc480-4e5d-4c18-b9c6-bcd6036a5e8c" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/21705d5c-fda1-4175-adc4-7ac019bb4690", + "blob:http://127.0.0.1:8084/3cf017bc-9f1e-43ae-aaf8-f36d1730abd7", + "blob:http://127.0.0.1:8084/5155a9d9-a5d5-4119-aebd-c0b57f713b8f", + "blob:http://127.0.0.1:8084/dcfb34cd-8b7b-4a09-b4a9-0afeeec28ac7", + "blob:http://127.0.0.1:8084/a29b840d-376c-4cfc-91aa-37d0b0bf4c45", + "blob:http://127.0.0.1:8084/65d3acad-eeec-457d-92f5-094838f0f970", + "blob:http://127.0.0.1:8084/5ace0559-5e8f-4a87-b054-67f22dd1f8b0", + "blob:http://127.0.0.1:8084/7f909870-87b3-4973-a2c1-c4af834abe6e", + "blob:http://127.0.0.1:8084/472bd1c3-d8e8-4560-bb3b-99fa00871f8e", + "blob:http://127.0.0.1:8084/726d743b-997d-45d1-83e8-19bfa85d8af0", + "blob:http://127.0.0.1:8084/5935352f-0f5a-4d6e-a641-f69c915084be", + "blob:http://127.0.0.1:8084/b22dc480-4e5d-4c18-b9c6-bcd6036a5e8c" + ], + "events": [ + "file", + "video", + "canvas", + "file", + "video", + "canvas", + "update", + "update", + "update", + "update", + "update", + "update", + "update", + "update", + "update", + "update", + "done", + "destroy" + ], + "connected": false + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool candidate-source / native file selection, seek extraction, PNG pixels and repeat generation", + "project": "webkit", + "kind": "blob-unavailable", + "status": "passed", + "durationMs": 1372, + "browser": "26.6", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "candidate-source", + "sha256": "e714d08f1cdb144bc8fd5ccf58c40a69b1e202ee8db91ff768777502c6eb915a", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-blob-unavailable", + "data": { + "native": { + "http": "loaded", + "error": 4, + "blob": "error" + }, + "tool": { + "errors": 1, + "processing": false, + "duration": null + } + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool candidate-source / DOM drop dispatch uses the registered handler while callable ondrop works", + "project": "webkit", + "kind": "blob-unavailable", + "status": "passed", + "durationMs": 1478, + "browser": "26.6", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "candidate-source", + "sha256": "e714d08f1cdb144bc8fd5ccf58c40a69b1e202ee8db91ff768777502c6eb915a", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-blob-unavailable", + "data": { + "native": { + "http": "loaded", + "error": 4, + "blob": "error" + }, + "tool": { + "errors": 1, + "processing": false, + "duration": null + } + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool candidate-source / corrupt media, replacement URLs and repeated destroy reproduce baseline gaps", + "project": "webkit", + "kind": "blob-unavailable", + "status": "passed", + "durationMs": 1473, + "browser": "26.6", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "candidate-source", + "sha256": "e714d08f1cdb144bc8fd5ccf58c40a69b1e202ee8db91ff768777502c6eb915a", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-blob-unavailable", + "data": { + "native": { + "http": "loaded", + "error": 4, + "blob": "error" + }, + "tool": { + "errors": 1, + "processing": false, + "duration": null + } + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool candidate-source / native PNG callback delivered after destroy is ignored after cancellation", + "project": "webkit", + "kind": "blob-unavailable", + "status": "passed", + "durationMs": 1515, + "browser": "26.6", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "candidate-source", + "sha256": "e714d08f1cdb144bc8fd5ccf58c40a69b1e202ee8db91ff768777502c6eb915a", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-blob-unavailable", + "data": { + "native": { + "http": "loaded", + "error": 4, + "blob": "error" + }, + "tool": { + "errors": 1, + "processing": false, + "duration": null + } + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool candidate-source / source replacement cancels held native encoding and completes only the latest job", + "project": "webkit", + "kind": "blob-unavailable", + "status": "passed", + "durationMs": 1304, + "browser": "26.6", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "candidate-source", + "sha256": "e714d08f1cdb144bc8fd5ccf58c40a69b1e202ee8db91ff768777502c6eb915a", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-blob-unavailable", + "data": { + "native": { + "http": "loaded", + "error": 4, + "blob": "error" + }, + "tool": { + "errors": 1, + "processing": false, + "duration": null + } + } + } + ] + } + ] + }, + { + "variant": "main", + "counts": { + "actual-tool": 34, + "input": 3, + "blob-unavailable": 17, + "native-control": 3, + "editor": 3, + "emitter": 3 + }, + "cases": [ + { + "title": "thumbnail-editor-types.spec.js / Monaco consumes Thumbnail global declarations and runs the actual typed constructor and cleanup", + "project": "chromium", + "kind": "editor", + "status": "passed", + "durationMs": 2163, + "browser": "153.0.8010.12", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-editor", + "data": { + "candidate": "candidate-artifact", + "sha256": "ef72d277e817c2f7312c10815ee91a74dbb9334285476d14fae7f4449b3d2758", + "declarations": { + "artplayer": "652c0fdf605e87203526777a05851bd69c95c1660d5ff29eab19c00967c9a985", + "artplayer-tool-thumbnail": "9b72d10a1fe964d54fcf4774770074058ea739b4f734b52b8f4adebb37fc83e6", + "artplayer-proxy-mediabunny": "acbaa0a25f20ac15b3d79db00b3a16167f84452ab58933110f42dcb3262fbd3e" + }, + "result": { + "syntax": [], + "semantic": [], + "declaration": [], + "invalid": [ + 2322 + ], + "executed": true, + "actual": { + "last": 7, + "connected": false, + "inputConnected": false, + "processing": false + } + }, + "scope": "Real local Monaco worker, typed constructor/custom event and native DOM cleanup; MediaBunny relative type bundle also compiles. This does not test file decoding or the complete demo/player integration." + } + } + ] + }, + { + "title": "thumbnail-editor-types.spec.js / Monaco consumes Thumbnail global declarations and runs the actual typed constructor and cleanup", + "project": "firefox", + "kind": "editor", + "status": "passed", + "durationMs": 7623, + "browser": "155.0", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-editor", + "data": { + "candidate": "candidate-artifact", + "sha256": "ef72d277e817c2f7312c10815ee91a74dbb9334285476d14fae7f4449b3d2758", + "declarations": { + "artplayer": "652c0fdf605e87203526777a05851bd69c95c1660d5ff29eab19c00967c9a985", + "artplayer-tool-thumbnail": "9b72d10a1fe964d54fcf4774770074058ea739b4f734b52b8f4adebb37fc83e6", + "artplayer-proxy-mediabunny": "acbaa0a25f20ac15b3d79db00b3a16167f84452ab58933110f42dcb3262fbd3e" + }, + "result": { + "syntax": [], + "semantic": [], + "declaration": [], + "invalid": [ + 2322 + ], + "executed": true, + "actual": { + "last": 7, + "connected": false, + "inputConnected": false, + "processing": false + } + }, + "scope": "Real local Monaco worker, typed constructor/custom event and native DOM cleanup; MediaBunny relative type bundle also compiles. This does not test file decoding or the complete demo/player integration." + } + } + ] + }, + { + "title": "thumbnail-editor-types.spec.js / Monaco consumes Thumbnail global declarations and runs the actual typed constructor and cleanup", + "project": "webkit", + "kind": "editor", + "status": "passed", + "durationMs": 3084, + "browser": "26.6", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-editor", + "data": { + "candidate": "candidate-artifact", + "sha256": "ef72d277e817c2f7312c10815ee91a74dbb9334285476d14fae7f4449b3d2758", + "declarations": { + "artplayer": "652c0fdf605e87203526777a05851bd69c95c1660d5ff29eab19c00967c9a985", + "artplayer-tool-thumbnail": "9b72d10a1fe964d54fcf4774770074058ea739b4f734b52b8f4adebb37fc83e6", + "artplayer-proxy-mediabunny": "acbaa0a25f20ac15b3d79db00b3a16167f84452ab58933110f42dcb3262fbd3e" + }, + "result": { + "syntax": [], + "semantic": [], + "declaration": [], + "invalid": [ + 2322 + ], + "executed": true, + "actual": { + "last": 7, + "connected": false, + "inputConnected": false, + "processing": false + } + }, + "scope": "Real local Monaco worker, typed constructor/custom event and native DOM cleanup; MediaBunny relative type bundle also compiles. This does not test file decoding or the complete demo/player integration." + } + } + ] + }, + { + "title": "thumbnail-emitter.spec.js / Thumbnail native browser event registry handles special names and nested once without changing DOM cleanup", + "project": "chromium", + "kind": "emitter", + "status": "passed", + "durationMs": 859, + "browser": "153.0.8010.12", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-emitter", + "data": { + "candidate": "candidate-artifact", + "sha256": "ef72d277e817c2f7312c10815ee91a74dbb9334285476d14fae7f4449b3d2758", + "result": { + "keys": [ + { + "name": "__proto__", + "calls": 3, + "prototypePreserved": true, + "removed": true + }, + { + "name": "constructor", + "calls": 3, + "prototypePreserved": true, + "removed": true + }, + { + "name": "toString", + "calls": 3, + "prototypePreserved": true, + "removed": true + }, + { + "name": "hasOwnProperty", + "calls": 3, + "prototypePreserved": true, + "removed": true + } + ], + "seen": [ + "on:inner", + "once:inner", + "on:outer" + ], + "videoConnected": false, + "inputConnected": false, + "processing": false + } + } + } + ] + }, + { + "title": "thumbnail-emitter.spec.js / Thumbnail native browser event registry handles special names and nested once without changing DOM cleanup", + "project": "firefox", + "kind": "emitter", + "status": "passed", + "durationMs": 3022, + "browser": "155.0", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-emitter", + "data": { + "candidate": "candidate-artifact", + "sha256": "ef72d277e817c2f7312c10815ee91a74dbb9334285476d14fae7f4449b3d2758", + "result": { + "keys": [ + { + "name": "__proto__", + "calls": 3, + "prototypePreserved": true, + "removed": true + }, + { + "name": "constructor", + "calls": 3, + "prototypePreserved": true, + "removed": true + }, + { + "name": "toString", + "calls": 3, + "prototypePreserved": true, + "removed": true + }, + { + "name": "hasOwnProperty", + "calls": 3, + "prototypePreserved": true, + "removed": true + } + ], + "seen": [ + "on:inner", + "once:inner", + "on:outer" + ], + "videoConnected": false, + "inputConnected": false, + "processing": false + } + } + } + ] + }, + { + "title": "thumbnail-emitter.spec.js / Thumbnail native browser event registry handles special names and nested once without changing DOM cleanup", + "project": "webkit", + "kind": "emitter", + "status": "passed", + "durationMs": 1111, + "browser": "26.6", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-emitter", + "data": { + "candidate": "candidate-artifact", + "sha256": "ef72d277e817c2f7312c10815ee91a74dbb9334285476d14fae7f4449b3d2758", + "result": { + "keys": [ + { + "name": "__proto__", + "calls": 3, + "prototypePreserved": true, + "removed": true + }, + { + "name": "constructor", + "calls": 3, + "prototypePreserved": true, + "removed": true + }, + { + "name": "toString", + "calls": 3, + "prototypePreserved": true, + "removed": true + }, + { + "name": "hasOwnProperty", + "calls": 3, + "prototypePreserved": true, + "removed": true + } + ], + "seen": [ + "on:inner", + "once:inner", + "on:outer" + ], + "videoConnected": false, + "inputConnected": false, + "processing": false + } + } + } + ] + }, + { + "title": "thumbnail-input.spec.js / Thumbnail input ownership replaces real DOM listeners and generated inputs", + "project": "chromium", + "kind": "input", + "status": "passed", + "durationMs": 1055, + "browser": "153.0.8010.12", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-input-ownership", + "data": { + "implementation": "candidate-artifact", + "sha256": "ef72d277e817c2f7312c10815ee91a74dbb9334285476d14fae7f4449b3d2758", + "scope": "Native file input/DOM/listeners only; does not claim media decoding.", + "switched": { + "value": "", + "events": [ + "thumbnail-pattern.mp4" + ], + "firstPosition": "sticky", + "disconnected": true + }, + "result": { + "events": [ + "thumbnail-pattern.mp4", + "thumbnail-pattern.mp4" + ], + "sameError": true, + "notifications": 1, + "inputConnected": false, + "videoConnected": false, + "secondPosition": "absolute", + "inputs": 0 + } + } + } + ] + }, + { + "title": "thumbnail-input.spec.js / Thumbnail input ownership replaces real DOM listeners and generated inputs", + "project": "firefox", + "kind": "input", + "status": "passed", + "durationMs": 4173, + "browser": "155.0", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-input-ownership", + "data": { + "implementation": "candidate-artifact", + "sha256": "ef72d277e817c2f7312c10815ee91a74dbb9334285476d14fae7f4449b3d2758", + "scope": "Native file input/DOM/listeners only; does not claim media decoding.", + "switched": { + "value": "", + "events": [ + "thumbnail-pattern.mp4" + ], + "firstPosition": "sticky", + "disconnected": true + }, + "result": { + "events": [ + "thumbnail-pattern.mp4", + "thumbnail-pattern.mp4" + ], + "sameError": true, + "notifications": 1, + "inputConnected": false, + "videoConnected": false, + "secondPosition": "absolute", + "inputs": 0 + } + } + } + ] + }, + { + "title": "thumbnail-input.spec.js / Thumbnail input ownership replaces real DOM listeners and generated inputs", + "project": "webkit", + "kind": "input", + "status": "passed", + "durationMs": 1528, + "browser": "26.6", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-input-ownership", + "data": { + "implementation": "candidate-artifact", + "sha256": "ef72d277e817c2f7312c10815ee91a74dbb9334285476d14fae7f4449b3d2758", + "scope": "Native file input/DOM/listeners only; does not claim media decoding.", + "switched": { + "value": "", + "events": [ + "thumbnail-pattern.mp4" + ], + "firstPosition": "sticky", + "disconnected": true + }, + "result": { + "events": [ + "thumbnail-pattern.mp4", + "thumbnail-pattern.mp4" + ], + "sameError": true, + "notifications": 1, + "inputConnected": false, + "videoConnected": false, + "secondPosition": "absolute", + "inputs": 0 + } + } + } + ] + }, + { + "title": "thumbnail-native.spec.js / Thumbnail native HTTP and Blob media controls", + "project": "chromium", + "kind": "native-control", + "status": "passed", + "durationMs": 381, + "browser": "153.0.8010.12", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-native-media-controls", + "data": [ + { + "url": "/test/pattern.mp4", + "type": "video/mp4", + "transport": "http", + "outcome": "loaded", + "readyState": 4, + "error": 0, + "duration": 8, + "width": 320 + }, + { + "url": "/test/pattern.mp4", + "type": "video/mp4", + "transport": "blob", + "outcome": "loaded", + "readyState": 4, + "error": 0, + "duration": 8, + "width": 320 + }, + { + "url": "/test/thumbnail-pattern.mp4", + "type": "video/mp4", + "transport": "http", + "outcome": "loaded", + "readyState": 4, + "error": 0, + "duration": 24, + "width": 320 + }, + { + "url": "/test/thumbnail-pattern.mp4", + "type": "video/mp4", + "transport": "blob", + "outcome": "loaded", + "readyState": 4, + "error": 0, + "duration": 24, + "width": 320 + }, + { + "url": "/assets/sample/video.mp4", + "type": "video/mp4", + "transport": "http", + "outcome": "loaded", + "readyState": 4, + "error": 0, + "duration": 90.046009, + "width": 640 + }, + { + "url": "/assets/sample/video.mp4", + "type": "video/mp4", + "transport": "blob", + "outcome": "loaded", + "readyState": 4, + "error": 0, + "duration": 90.046009, + "width": 640 + } + ] + } + ] + }, + { + "title": "thumbnail-native.spec.js / Thumbnail native HTTP and Blob media controls", + "project": "firefox", + "kind": "native-control", + "status": "passed", + "durationMs": 2390, + "browser": "155.0", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-native-media-controls", + "data": [ + { + "url": "/test/pattern.mp4", + "type": "video/mp4", + "transport": "http", + "outcome": "loaded", + "readyState": 4, + "error": 0, + "duration": 8, + "width": 320 + }, + { + "url": "/test/pattern.mp4", + "type": "video/mp4", + "transport": "blob", + "outcome": "loaded", + "readyState": 4, + "error": 0, + "duration": 8, + "width": 320 + }, + { + "url": "/test/thumbnail-pattern.mp4", + "type": "video/mp4", + "transport": "http", + "outcome": "loaded", + "readyState": 4, + "error": 0, + "duration": 24, + "width": 320 + }, + { + "url": "/test/thumbnail-pattern.mp4", + "type": "video/mp4", + "transport": "blob", + "outcome": "loaded", + "readyState": 4, + "error": 0, + "duration": 24, + "width": 320 + }, + { + "url": "/assets/sample/video.mp4", + "type": "video/mp4", + "transport": "http", + "outcome": "loaded", + "readyState": 4, + "error": 0, + "duration": 90.045986, + "width": 640 + }, + { + "url": "/assets/sample/video.mp4", + "type": "video/mp4", + "transport": "blob", + "outcome": "loaded", + "readyState": 4, + "error": 0, + "duration": 90.045986, + "width": 640 + } + ] + } + ] + }, + { + "title": "thumbnail-native.spec.js / Thumbnail native HTTP and Blob media controls", + "project": "webkit", + "kind": "native-control", + "status": "passed", + "durationMs": 3558, + "browser": "26.6", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-native-media-controls", + "data": [ + { + "url": "/test/pattern.mp4", + "type": "video/mp4", + "transport": "http", + "outcome": "loaded", + "readyState": 4, + "error": 0, + "duration": 8, + "width": 320 + }, + { + "url": "/test/pattern.mp4", + "type": "video/mp4", + "transport": "blob", + "outcome": "error", + "readyState": 0, + "error": 4, + "duration": null, + "width": 0 + }, + { + "url": "/test/thumbnail-pattern.mp4", + "type": "video/mp4", + "transport": "http", + "outcome": "loaded", + "readyState": 3, + "error": 0, + "duration": 24, + "width": 320 + }, + { + "url": "/test/thumbnail-pattern.mp4", + "type": "video/mp4", + "transport": "blob", + "outcome": "error", + "readyState": 0, + "error": 4, + "duration": null, + "width": 0 + }, + { + "url": "/assets/sample/video.mp4", + "type": "video/mp4", + "transport": "http", + "outcome": "loaded", + "readyState": 2, + "error": 0, + "duration": 90.094, + "width": 640 + }, + { + "url": "/assets/sample/video.mp4", + "type": "video/mp4", + "transport": "blob", + "outcome": "error", + "readyState": 0, + "error": 4, + "duration": null, + "width": 0 + } + ] + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool cdn-3.5.31 / native file selection, seek extraction, PNG pixels and repeat generation", + "project": "chromium", + "kind": "actual-tool", + "status": "passed", + "durationMs": 8585, + "browser": "153.0.8010.12", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "cdn-3.5.31", + "sha256": "27fdc506c9b228c40df39f8508b3a42c145571c06c0b9cc60327a007a2064c87", + "recoveredIndividualFile": true, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-extraction", + "data": { + "width": 240, + "height": 150, + "sourceWidth": 320, + "sourceHeight": 180, + "cellHeight": 30, + "mime": "image/png", + "bytes": 26427, + "nonBlack": 2400, + "createdCount": 21, + "revokedCount": 21, + "doneCount": 2, + "updates": [ + 0.1, + 0.2, + 0.3, + 0.4, + 0.5, + 0.6, + 0.7, + 0.8, + 0.9, + 1 + ], + "retiredReadyState": 4, + "retiredSrc": "blob:http://127.0.0.1:8084/e704e721-e054-4111-9ea0-6173306cf4b4", + "connected": false + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool cdn-3.5.31 / DOM drop dispatch exposes missing listener while callable ondrop works", + "project": "chromium", + "kind": "actual-tool", + "status": "passed", + "durationMs": 408, + "browser": "153.0.8010.12", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "cdn-3.5.31", + "sha256": "27fdc506c9b228c40df39f8508b3a42c145571c06c0b9cc60327a007a2064c87", + "recoveredIndividualFile": true, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool cdn-3.5.31 / corrupt media, replacement URLs and repeated destroy reproduce baseline gaps", + "project": "chromium", + "kind": "actual-tool", + "status": "passed", + "durationMs": 460, + "browser": "153.0.8010.12", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "cdn-3.5.31", + "sha256": "27fdc506c9b228c40df39f8508b3a42c145571c06c0b9cc60327a007a2064c87", + "recoveredIndividualFile": true, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-media-failure", + "data": { + "failed": { + "code": 4, + "toolErrors": 0, + "first": "blob:http://127.0.0.1:8084/26924bc0-cab0-4960-b18b-38c37d4794bb" + }, + "result": { + "value": "C:\\fakepath\\thumbnail-pattern.mp4", + "created": [ + "blob:http://127.0.0.1:8084/26924bc0-cab0-4960-b18b-38c37d4794bb", + "blob:http://127.0.0.1:8084/bf3b032c-b178-4481-af57-d3ba68b57e8d" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/bf3b032c-b178-4481-af57-d3ba68b57e8d" + ], + "repeated": "NotFoundError" + } + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool cdn-3.5.31 / native PNG callback delivered after destroy still emits update and creates a URL", + "project": "chromium", + "kind": "actual-tool", + "status": "passed", + "durationMs": 831, + "browser": "153.0.8010.12", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "cdn-3.5.31", + "sha256": "27fdc506c9b228c40df39f8508b3a42c145571c06c0b9cc60327a007a2064c87", + "recoveredIndividualFile": true, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-late-native-blob", + "data": { + "count": 1, + "created": [ + "blob:http://127.0.0.1:8084/dcafa77b-76c7-43ee-becc-61bb58d27ecf", + "blob:http://127.0.0.1:8084/968276c5-5231-410d-8ba7-db7fac3146b9" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/dcafa77b-76c7-43ee-becc-61bb58d27ecf" + ], + "pendingState": "pending", + "events": [ + "file", + "canvas", + "video", + "destroy", + "update" + ], + "connected": false, + "bytes": 7540 + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool cdn-3.5.31 / native file selection, seek extraction, PNG pixels and repeat generation", + "project": "firefox", + "kind": "actual-tool", + "status": "passed", + "durationMs": 8970, + "browser": "155.0", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "cdn-3.5.31", + "sha256": "27fdc506c9b228c40df39f8508b3a42c145571c06c0b9cc60327a007a2064c87", + "recoveredIndividualFile": true, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-extraction", + "data": { + "width": 240, + "height": 150, + "sourceWidth": 320, + "sourceHeight": 180, + "cellHeight": 30, + "mime": "image/png", + "bytes": 18224, + "nonBlack": 2400, + "createdCount": 21, + "revokedCount": 21, + "doneCount": 2, + "updates": [ + 0.1, + 0.2, + 0.3, + 0.4, + 0.5, + 0.6, + 0.7, + 0.8, + 0.9, + 1 + ], + "retiredReadyState": 4, + "retiredSrc": "blob:http://127.0.0.1:8084/9c9b9b2f-a8d1-420b-850c-5158f02ab472", + "connected": false + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool cdn-3.5.31 / DOM drop dispatch exposes missing listener while callable ondrop works", + "project": "firefox", + "kind": "actual-tool", + "status": "passed", + "durationMs": 925, + "browser": "155.0", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "cdn-3.5.31", + "sha256": "27fdc506c9b228c40df39f8508b3a42c145571c06c0b9cc60327a007a2064c87", + "recoveredIndividualFile": true, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool cdn-3.5.31 / corrupt media, replacement URLs and repeated destroy reproduce baseline gaps", + "project": "firefox", + "kind": "actual-tool", + "status": "passed", + "durationMs": 1134, + "browser": "155.0", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "cdn-3.5.31", + "sha256": "27fdc506c9b228c40df39f8508b3a42c145571c06c0b9cc60327a007a2064c87", + "recoveredIndividualFile": true, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-media-failure", + "data": { + "failed": { + "code": 4, + "toolErrors": 0, + "first": "blob:http://127.0.0.1:8084/aa98a179-d8ba-4996-a3be-541055358733" + }, + "result": { + "value": "C:\\fakepath\\thumbnail-pattern.mp4", + "created": [ + "blob:http://127.0.0.1:8084/aa98a179-d8ba-4996-a3be-541055358733", + "blob:http://127.0.0.1:8084/87d89a26-fbf4-4035-87ca-894a1070b9e9" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/87d89a26-fbf4-4035-87ca-894a1070b9e9" + ], + "repeated": "NotFoundError" + } + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool cdn-3.5.31 / native PNG callback delivered after destroy still emits update and creates a URL", + "project": "firefox", + "kind": "actual-tool", + "status": "passed", + "durationMs": 1350, + "browser": "155.0", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "cdn-3.5.31", + "sha256": "27fdc506c9b228c40df39f8508b3a42c145571c06c0b9cc60327a007a2064c87", + "recoveredIndividualFile": true, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-late-native-blob", + "data": { + "count": 1, + "created": [ + "blob:http://127.0.0.1:8084/5d9278b0-7bd8-4208-aceb-24cdfbfe84e5", + "blob:http://127.0.0.1:8084/fe41ae2b-3186-4902-be3f-b2f6fbdb8124" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/5d9278b0-7bd8-4208-aceb-24cdfbfe84e5" + ], + "pendingState": "pending", + "events": [ + "file", + "canvas", + "video", + "destroy", + "update" + ], + "connected": false, + "bytes": 4980 + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool cdn-3.5.31 / native file selection, seek extraction, PNG pixels and repeat generation", + "project": "webkit", + "kind": "blob-unavailable", + "status": "passed", + "durationMs": 2320, + "browser": "26.6", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "cdn-3.5.31", + "sha256": "27fdc506c9b228c40df39f8508b3a42c145571c06c0b9cc60327a007a2064c87", + "recoveredIndividualFile": true, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-blob-unavailable", + "data": { + "native": { + "http": "loaded", + "error": 4, + "blob": "error" + }, + "tool": { + "errors": 0, + "processing": false, + "duration": null + } + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool cdn-3.5.31 / DOM drop dispatch exposes missing listener while callable ondrop works", + "project": "webkit", + "kind": "blob-unavailable", + "status": "passed", + "durationMs": 1975, + "browser": "26.6", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "cdn-3.5.31", + "sha256": "27fdc506c9b228c40df39f8508b3a42c145571c06c0b9cc60327a007a2064c87", + "recoveredIndividualFile": true, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-blob-unavailable", + "data": { + "native": { + "http": "loaded", + "error": 4, + "blob": "error" + }, + "tool": { + "errors": 0, + "processing": false, + "duration": null + } + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool cdn-3.5.31 / corrupt media, replacement URLs and repeated destroy reproduce baseline gaps", + "project": "webkit", + "kind": "blob-unavailable", + "status": "passed", + "durationMs": 1959, + "browser": "26.6", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "cdn-3.5.31", + "sha256": "27fdc506c9b228c40df39f8508b3a42c145571c06c0b9cc60327a007a2064c87", + "recoveredIndividualFile": true, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-blob-unavailable", + "data": { + "native": { + "http": "loaded", + "error": 4, + "blob": "error" + }, + "tool": { + "errors": 0, + "processing": false, + "duration": null + } + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool cdn-3.5.31 / native PNG callback delivered after destroy still emits update and creates a URL", + "project": "webkit", + "kind": "blob-unavailable", + "status": "passed", + "durationMs": 2136, + "browser": "26.6", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "cdn-3.5.31", + "sha256": "27fdc506c9b228c40df39f8508b3a42c145571c06c0b9cc60327a007a2064c87", + "recoveredIndividualFile": true, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-blob-unavailable", + "data": { + "native": { + "http": "loaded", + "error": 4, + "blob": "error" + }, + "tool": { + "errors": 0, + "processing": false, + "duration": null + } + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool workspace.js / native file selection, seek extraction, PNG pixels and repeat generation", + "project": "chromium", + "kind": "actual-tool", + "status": "passed", + "durationMs": 1103, + "browser": "153.0.8010.12", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "workspace.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "recoveredIndividualFile": false, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-extraction", + "data": { + "width": 240, + "height": 210, + "sourceWidth": 320, + "sourceHeight": 180, + "cellHeight": 45, + "mime": "image/png", + "bytes": 44055, + "nonBlack": 3600, + "createdCount": 21, + "revokedCount": 21, + "doneCount": 2, + "updates": [ + 0.1, + 0.2, + 0.3, + 0.4, + 0.5, + 0.6, + 0.7, + 0.8, + 0.9, + 1 + ], + "retiredReadyState": 4, + "retiredSrc": "blob:http://127.0.0.1:8084/cc7dbb50-4976-420b-addb-1c9b8d8791c0", + "connected": false + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool workspace.js / DOM drop dispatch exposes missing listener while callable ondrop works", + "project": "chromium", + "kind": "actual-tool", + "status": "passed", + "durationMs": 389, + "browser": "153.0.8010.12", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "workspace.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "recoveredIndividualFile": false, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool workspace.js / corrupt media, replacement URLs and repeated destroy reproduce baseline gaps", + "project": "chromium", + "kind": "actual-tool", + "status": "passed", + "durationMs": 404, + "browser": "153.0.8010.12", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "workspace.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "recoveredIndividualFile": false, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-media-failure", + "data": { + "failed": { + "code": 4, + "toolErrors": 0, + "first": "blob:http://127.0.0.1:8084/d985e906-846d-49db-9512-74844cd142f1" + }, + "result": { + "value": "", + "created": [ + "blob:http://127.0.0.1:8084/d985e906-846d-49db-9512-74844cd142f1", + "blob:http://127.0.0.1:8084/7a9bdf5b-52b0-4187-80e4-aa839b048d66" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/7a9bdf5b-52b0-4187-80e4-aa839b048d66" + ], + "repeated": "NotFoundError" + } + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool workspace.js / native PNG callback delivered after destroy still emits update and creates a URL", + "project": "chromium", + "kind": "actual-tool", + "status": "passed", + "durationMs": 682, + "browser": "153.0.8010.12", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "workspace.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "recoveredIndividualFile": false, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-late-native-blob", + "data": { + "count": 1, + "created": [ + "blob:http://127.0.0.1:8084/31b4ab9b-45bc-4749-8bab-6db591b4b40d", + "blob:http://127.0.0.1:8084/5a5da371-e584-42f8-9745-a57b0a298be1" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/31b4ab9b-45bc-4749-8bab-6db591b4b40d" + ], + "pendingState": "pending", + "events": [ + "file", + "video", + "canvas", + "destroy", + "update" + ], + "connected": false, + "bytes": 9120 + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool workspace.js / native file selection, seek extraction, PNG pixels and repeat generation", + "project": "firefox", + "kind": "actual-tool", + "status": "passed", + "durationMs": 2584, + "browser": "155.0", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "workspace.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "recoveredIndividualFile": false, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-extraction", + "data": { + "width": 240, + "height": 210, + "sourceWidth": 320, + "sourceHeight": 180, + "cellHeight": 45, + "mime": "image/png", + "bytes": 38895, + "nonBlack": 3600, + "createdCount": 21, + "revokedCount": 21, + "doneCount": 2, + "updates": [ + 0.1, + 0.2, + 0.3, + 0.4, + 0.5, + 0.6, + 0.7, + 0.8, + 0.9, + 1 + ], + "retiredReadyState": 4, + "retiredSrc": "blob:http://127.0.0.1:8084/ef1a2714-7144-4765-aaae-b6390414ac3b", + "connected": false + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool workspace.js / DOM drop dispatch exposes missing listener while callable ondrop works", + "project": "firefox", + "kind": "actual-tool", + "status": "passed", + "durationMs": 763, + "browser": "155.0", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "workspace.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "recoveredIndividualFile": false, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool workspace.js / corrupt media, replacement URLs and repeated destroy reproduce baseline gaps", + "project": "firefox", + "kind": "actual-tool", + "status": "passed", + "durationMs": 788, + "browser": "155.0", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "workspace.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "recoveredIndividualFile": false, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-media-failure", + "data": { + "failed": { + "code": 4, + "toolErrors": 0, + "first": "blob:http://127.0.0.1:8084/2b40ffab-58c2-409e-82ae-b06d51b26c94" + }, + "result": { + "value": "", + "created": [ + "blob:http://127.0.0.1:8084/2b40ffab-58c2-409e-82ae-b06d51b26c94", + "blob:http://127.0.0.1:8084/640478a6-109a-4de0-b29c-9d238808a6a0" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/640478a6-109a-4de0-b29c-9d238808a6a0" + ], + "repeated": "NotFoundError" + } + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool workspace.js / native PNG callback delivered after destroy still emits update and creates a URL", + "project": "firefox", + "kind": "actual-tool", + "status": "passed", + "durationMs": 1020, + "browser": "155.0", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "workspace.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "recoveredIndividualFile": false, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-late-native-blob", + "data": { + "count": 1, + "created": [ + "blob:http://127.0.0.1:8084/de5ac81a-1ff0-4090-8ff3-c401af6d2f29", + "blob:http://127.0.0.1:8084/86023697-cd60-4669-aad5-129e8535a07c" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/de5ac81a-1ff0-4090-8ff3-c401af6d2f29" + ], + "pendingState": "pending", + "events": [ + "file", + "video", + "canvas", + "destroy", + "update" + ], + "connected": false, + "bytes": 6496 + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool workspace.js / native file selection, seek extraction, PNG pixels and repeat generation", + "project": "webkit", + "kind": "blob-unavailable", + "status": "passed", + "durationMs": 2034, + "browser": "26.6", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "workspace.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "recoveredIndividualFile": false, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-blob-unavailable", + "data": { + "native": { + "http": "loaded", + "error": 4, + "blob": "error" + }, + "tool": { + "errors": 0, + "processing": false, + "duration": null + } + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool workspace.js / DOM drop dispatch exposes missing listener while callable ondrop works", + "project": "webkit", + "kind": "blob-unavailable", + "status": "passed", + "durationMs": 1950, + "browser": "26.6", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "workspace.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "recoveredIndividualFile": false, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-blob-unavailable", + "data": { + "native": { + "http": "loaded", + "error": 4, + "blob": "error" + }, + "tool": { + "errors": 0, + "processing": false, + "duration": null + } + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool workspace.js / corrupt media, replacement URLs and repeated destroy reproduce baseline gaps", + "project": "webkit", + "kind": "blob-unavailable", + "status": "passed", + "durationMs": 1861, + "browser": "26.6", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "workspace.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "recoveredIndividualFile": false, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-blob-unavailable", + "data": { + "native": { + "http": "loaded", + "error": 4, + "blob": "error" + }, + "tool": { + "errors": 0, + "processing": false, + "duration": null + } + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool workspace.js / native PNG callback delivered after destroy still emits update and creates a URL", + "project": "webkit", + "kind": "blob-unavailable", + "status": "passed", + "durationMs": 1962, + "browser": "26.6", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "workspace.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "recoveredIndividualFile": false, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-blob-unavailable", + "data": { + "native": { + "http": "loaded", + "error": 4, + "blob": "error" + }, + "tool": { + "errors": 0, + "processing": false, + "duration": null + } + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool workspace.legacy.js / native file selection, seek extraction, PNG pixels and repeat generation", + "project": "chromium", + "kind": "actual-tool", + "status": "passed", + "durationMs": 1111, + "browser": "153.0.8010.12", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "workspace.legacy.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "recoveredIndividualFile": false, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-extraction", + "data": { + "width": 240, + "height": 210, + "sourceWidth": 320, + "sourceHeight": 180, + "cellHeight": 45, + "mime": "image/png", + "bytes": 44055, + "nonBlack": 3600, + "createdCount": 21, + "revokedCount": 21, + "doneCount": 2, + "updates": [ + 0.1, + 0.2, + 0.3, + 0.4, + 0.5, + 0.6, + 0.7, + 0.8, + 0.9, + 1 + ], + "retiredReadyState": 4, + "retiredSrc": "blob:http://127.0.0.1:8084/621e40c8-c44c-46b5-9a6b-1fc8824e1593", + "connected": false + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool workspace.legacy.js / DOM drop dispatch exposes missing listener while callable ondrop works", + "project": "chromium", + "kind": "actual-tool", + "status": "passed", + "durationMs": 379, + "browser": "153.0.8010.12", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "workspace.legacy.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "recoveredIndividualFile": false, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool workspace.legacy.js / corrupt media, replacement URLs and repeated destroy reproduce baseline gaps", + "project": "chromium", + "kind": "actual-tool", + "status": "passed", + "durationMs": 391, + "browser": "153.0.8010.12", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "workspace.legacy.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "recoveredIndividualFile": false, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-media-failure", + "data": { + "failed": { + "code": 4, + "toolErrors": 0, + "first": "blob:http://127.0.0.1:8084/4c2acad1-496b-4fa3-a74e-5015ba4ef025" + }, + "result": { + "value": "", + "created": [ + "blob:http://127.0.0.1:8084/4c2acad1-496b-4fa3-a74e-5015ba4ef025", + "blob:http://127.0.0.1:8084/6fb97ca3-276b-4660-8d96-5604f70d33f1" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/6fb97ca3-276b-4660-8d96-5604f70d33f1" + ], + "repeated": "NotFoundError" + } + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool workspace.legacy.js / native PNG callback delivered after destroy still emits update and creates a URL", + "project": "chromium", + "kind": "actual-tool", + "status": "passed", + "durationMs": 588, + "browser": "153.0.8010.12", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "workspace.legacy.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "recoveredIndividualFile": false, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-late-native-blob", + "data": { + "count": 1, + "created": [ + "blob:http://127.0.0.1:8084/d75b2d1d-02b4-4815-a8a1-9ff0fca482e1", + "blob:http://127.0.0.1:8084/755a1c12-37bc-4e72-b807-351d9bec8fe6" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/d75b2d1d-02b4-4815-a8a1-9ff0fca482e1" + ], + "pendingState": "pending", + "events": [ + "file", + "video", + "canvas", + "destroy", + "update" + ], + "connected": false, + "bytes": 9120 + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool workspace.legacy.js / native file selection, seek extraction, PNG pixels and repeat generation", + "project": "firefox", + "kind": "actual-tool", + "status": "passed", + "durationMs": 2434, + "browser": "155.0", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "workspace.legacy.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "recoveredIndividualFile": false, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-extraction", + "data": { + "width": 240, + "height": 210, + "sourceWidth": 320, + "sourceHeight": 180, + "cellHeight": 45, + "mime": "image/png", + "bytes": 38560, + "nonBlack": 3600, + "createdCount": 21, + "revokedCount": 21, + "doneCount": 2, + "updates": [ + 0.1, + 0.2, + 0.3, + 0.4, + 0.5, + 0.6, + 0.7, + 0.8, + 0.9, + 1 + ], + "retiredReadyState": 4, + "retiredSrc": "blob:http://127.0.0.1:8084/a37382df-1dcc-4c15-a527-0844a742c662", + "connected": false + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool workspace.legacy.js / DOM drop dispatch exposes missing listener while callable ondrop works", + "project": "firefox", + "kind": "actual-tool", + "status": "passed", + "durationMs": 927, + "browser": "155.0", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "workspace.legacy.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "recoveredIndividualFile": false, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool workspace.legacy.js / corrupt media, replacement URLs and repeated destroy reproduce baseline gaps", + "project": "firefox", + "kind": "actual-tool", + "status": "passed", + "durationMs": 906, + "browser": "155.0", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "workspace.legacy.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "recoveredIndividualFile": false, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-media-failure", + "data": { + "failed": { + "code": 4, + "toolErrors": 0, + "first": "blob:http://127.0.0.1:8084/85c10533-8357-4353-87dd-02624e685111" + }, + "result": { + "value": "", + "created": [ + "blob:http://127.0.0.1:8084/85c10533-8357-4353-87dd-02624e685111", + "blob:http://127.0.0.1:8084/f291eea3-239e-490d-a6a0-ad1afcb70bc8" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/f291eea3-239e-490d-a6a0-ad1afcb70bc8" + ], + "repeated": "NotFoundError" + } + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool workspace.legacy.js / native PNG callback delivered after destroy still emits update and creates a URL", + "project": "firefox", + "kind": "actual-tool", + "status": "passed", + "durationMs": 1010, + "browser": "155.0", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "workspace.legacy.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "recoveredIndividualFile": false, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-late-native-blob", + "data": { + "count": 1, + "created": [ + "blob:http://127.0.0.1:8084/2c72a98f-e590-4371-92d9-6c6bf793768a", + "blob:http://127.0.0.1:8084/55b606bb-0825-4b16-9bd1-85697b821865" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/2c72a98f-e590-4371-92d9-6c6bf793768a" + ], + "pendingState": "pending", + "events": [ + "file", + "video", + "canvas", + "destroy", + "update" + ], + "connected": false, + "bytes": 6496 + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool workspace.legacy.js / native file selection, seek extraction, PNG pixels and repeat generation", + "project": "webkit", + "kind": "blob-unavailable", + "status": "passed", + "durationMs": 1896, + "browser": "26.6", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "workspace.legacy.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "recoveredIndividualFile": false, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-blob-unavailable", + "data": { + "native": { + "http": "loaded", + "error": 4, + "blob": "error" + }, + "tool": { + "errors": 0, + "processing": false, + "duration": null + } + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool workspace.legacy.js / DOM drop dispatch exposes missing listener while callable ondrop works", + "project": "webkit", + "kind": "blob-unavailable", + "status": "passed", + "durationMs": 1974, + "browser": "26.6", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "workspace.legacy.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "recoveredIndividualFile": false, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-blob-unavailable", + "data": { + "native": { + "http": "loaded", + "error": 4, + "blob": "error" + }, + "tool": { + "errors": 0, + "processing": false, + "duration": null + } + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool workspace.legacy.js / corrupt media, replacement URLs and repeated destroy reproduce baseline gaps", + "project": "webkit", + "kind": "blob-unavailable", + "status": "passed", + "durationMs": 1782, + "browser": "26.6", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "workspace.legacy.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "recoveredIndividualFile": false, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-blob-unavailable", + "data": { + "native": { + "http": "loaded", + "error": 4, + "blob": "error" + }, + "tool": { + "errors": 0, + "processing": false, + "duration": null + } + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool workspace.legacy.js / native PNG callback delivered after destroy still emits update and creates a URL", + "project": "webkit", + "kind": "blob-unavailable", + "status": "passed", + "durationMs": 1992, + "browser": "26.6", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "workspace.legacy.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "recoveredIndividualFile": false, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-blob-unavailable", + "data": { + "native": { + "http": "loaded", + "error": 4, + "blob": "error" + }, + "tool": { + "errors": 0, + "processing": false, + "duration": null + } + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool candidate-artifact / native file selection, seek extraction, PNG pixels and repeat generation", + "project": "chromium", + "kind": "actual-tool", + "status": "passed", + "durationMs": 947, + "browser": "153.0.8010.12", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "candidate-artifact", + "sha256": "ef72d277e817c2f7312c10815ee91a74dbb9334285476d14fae7f4449b3d2758", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-extraction", + "data": { + "width": 240, + "height": 210, + "sourceWidth": 320, + "sourceHeight": 180, + "cellHeight": 45, + "mime": "image/png", + "bytes": 44055, + "nonBlack": 3600, + "createdCount": 21, + "revokedCount": 21, + "doneCount": 2, + "updates": [ + 0.1, + 0.2, + 0.3, + 0.4, + 0.5, + 0.6, + 0.7, + 0.8, + 0.9, + 1 + ], + "retiredReadyState": 0, + "retiredSrc": null, + "connected": false + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool candidate-artifact / DOM drop dispatch uses the registered handler while callable ondrop works", + "project": "chromium", + "kind": "actual-tool", + "status": "passed", + "durationMs": 558, + "browser": "153.0.8010.12", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "candidate-artifact", + "sha256": "ef72d277e817c2f7312c10815ee91a74dbb9334285476d14fae7f4449b3d2758", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool candidate-artifact / corrupt media, replacement URLs and repeated destroy reproduce baseline gaps", + "project": "chromium", + "kind": "actual-tool", + "status": "passed", + "durationMs": 402, + "browser": "153.0.8010.12", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "candidate-artifact", + "sha256": "ef72d277e817c2f7312c10815ee91a74dbb9334285476d14fae7f4449b3d2758", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-media-failure", + "data": { + "failed": { + "code": 4, + "toolErrors": 1, + "first": "blob:http://127.0.0.1:8084/fada52b4-a93c-4299-aae2-f53baf3713c9" + }, + "result": { + "value": "", + "created": [ + "blob:http://127.0.0.1:8084/fada52b4-a93c-4299-aae2-f53baf3713c9", + "blob:http://127.0.0.1:8084/c6e9c21d-7dfa-4540-9b03-2945643e38d7" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/fada52b4-a93c-4299-aae2-f53baf3713c9", + "blob:http://127.0.0.1:8084/c6e9c21d-7dfa-4540-9b03-2945643e38d7" + ] + } + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool candidate-artifact / native PNG callback delivered after destroy is ignored after cancellation", + "project": "chromium", + "kind": "actual-tool", + "status": "passed", + "durationMs": 652, + "browser": "153.0.8010.12", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "candidate-artifact", + "sha256": "ef72d277e817c2f7312c10815ee91a74dbb9334285476d14fae7f4449b3d2758", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-late-native-blob", + "data": { + "count": 1, + "created": [ + "blob:http://127.0.0.1:8084/419791ad-9d9f-4e5e-9b7b-a7057f6b9f3f" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/419791ad-9d9f-4e5e-9b7b-a7057f6b9f3f" + ], + "pendingState": "rejected", + "events": [ + "file", + "video", + "canvas", + "destroy" + ], + "connected": false, + "bytes": 9120 + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool candidate-artifact / source replacement cancels held native encoding and completes only the latest job", + "project": "chromium", + "kind": "actual-tool", + "status": "passed", + "durationMs": 853, + "browser": "153.0.8010.12", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "candidate-artifact", + "sha256": "ef72d277e817c2f7312c10815ee91a74dbb9334285476d14fae7f4449b3d2758", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-native-source-cancellation", + "data": { + "oldState": "AbortError", + "firstEncodedBytes": 9120, + "source": "blob:http://127.0.0.1:8084/b98c1bdb-6a2b-4b46-aee6-9a26c253ed4a", + "updateSources": [ + "blob:http://127.0.0.1:8084/b98c1bdb-6a2b-4b46-aee6-9a26c253ed4a", + "blob:http://127.0.0.1:8084/b98c1bdb-6a2b-4b46-aee6-9a26c253ed4a", + "blob:http://127.0.0.1:8084/b98c1bdb-6a2b-4b46-aee6-9a26c253ed4a", + "blob:http://127.0.0.1:8084/b98c1bdb-6a2b-4b46-aee6-9a26c253ed4a", + "blob:http://127.0.0.1:8084/b98c1bdb-6a2b-4b46-aee6-9a26c253ed4a", + "blob:http://127.0.0.1:8084/b98c1bdb-6a2b-4b46-aee6-9a26c253ed4a", + "blob:http://127.0.0.1:8084/b98c1bdb-6a2b-4b46-aee6-9a26c253ed4a", + "blob:http://127.0.0.1:8084/b98c1bdb-6a2b-4b46-aee6-9a26c253ed4a", + "blob:http://127.0.0.1:8084/b98c1bdb-6a2b-4b46-aee6-9a26c253ed4a", + "blob:http://127.0.0.1:8084/b98c1bdb-6a2b-4b46-aee6-9a26c253ed4a" + ], + "created": [ + "blob:http://127.0.0.1:8084/72cccd44-4237-42ae-9b86-a494af79a5f4", + "blob:http://127.0.0.1:8084/b98c1bdb-6a2b-4b46-aee6-9a26c253ed4a", + "blob:http://127.0.0.1:8084/8d7cf8c6-1d4c-4d77-8b90-616df1b4d45e", + "blob:http://127.0.0.1:8084/f045f41e-292b-42a8-b9c4-672cc12667be", + "blob:http://127.0.0.1:8084/899e2e9b-80c0-4184-9a08-1cfb4133a105", + "blob:http://127.0.0.1:8084/d3327321-e757-4747-9b65-940bb5a383fc", + "blob:http://127.0.0.1:8084/ab982923-a799-4daf-adc2-ad9a8612ca7e", + "blob:http://127.0.0.1:8084/695aea19-9539-4999-a9ca-2fb8d518f5b7", + "blob:http://127.0.0.1:8084/157defeb-50dd-4176-a2ab-ec0fc2c8b3a5", + "blob:http://127.0.0.1:8084/393faa42-3b20-407e-8f71-0025d8bd1aae", + "blob:http://127.0.0.1:8084/df345acd-eb19-454d-aada-8f23fab7c891", + "blob:http://127.0.0.1:8084/7e9794a7-d02b-46d7-b5a1-d2e661b4da5a" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/72cccd44-4237-42ae-9b86-a494af79a5f4", + "blob:http://127.0.0.1:8084/8d7cf8c6-1d4c-4d77-8b90-616df1b4d45e", + "blob:http://127.0.0.1:8084/f045f41e-292b-42a8-b9c4-672cc12667be", + "blob:http://127.0.0.1:8084/899e2e9b-80c0-4184-9a08-1cfb4133a105", + "blob:http://127.0.0.1:8084/d3327321-e757-4747-9b65-940bb5a383fc", + "blob:http://127.0.0.1:8084/ab982923-a799-4daf-adc2-ad9a8612ca7e", + "blob:http://127.0.0.1:8084/695aea19-9539-4999-a9ca-2fb8d518f5b7", + "blob:http://127.0.0.1:8084/157defeb-50dd-4176-a2ab-ec0fc2c8b3a5", + "blob:http://127.0.0.1:8084/393faa42-3b20-407e-8f71-0025d8bd1aae", + "blob:http://127.0.0.1:8084/df345acd-eb19-454d-aada-8f23fab7c891", + "blob:http://127.0.0.1:8084/b98c1bdb-6a2b-4b46-aee6-9a26c253ed4a", + "blob:http://127.0.0.1:8084/7e9794a7-d02b-46d7-b5a1-d2e661b4da5a" + ], + "events": [ + "file", + "video", + "canvas", + "file", + "video", + "canvas", + "update", + "update", + "update", + "update", + "update", + "update", + "update", + "update", + "update", + "update", + "done", + "destroy" + ], + "connected": false + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool candidate-artifact / native file selection, seek extraction, PNG pixels and repeat generation", + "project": "firefox", + "kind": "actual-tool", + "status": "passed", + "durationMs": 2831, + "browser": "155.0", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "candidate-artifact", + "sha256": "ef72d277e817c2f7312c10815ee91a74dbb9334285476d14fae7f4449b3d2758", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-extraction", + "data": { + "width": 240, + "height": 210, + "sourceWidth": 320, + "sourceHeight": 180, + "cellHeight": 45, + "mime": "image/png", + "bytes": 38895, + "nonBlack": 3600, + "createdCount": 21, + "revokedCount": 21, + "doneCount": 2, + "updates": [ + 0.1, + 0.2, + 0.3, + 0.4, + 0.5, + 0.6, + 0.7, + 0.8, + 0.9, + 1 + ], + "retiredReadyState": 0, + "retiredSrc": null, + "connected": false + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool candidate-artifact / DOM drop dispatch uses the registered handler while callable ondrop works", + "project": "firefox", + "kind": "actual-tool", + "status": "passed", + "durationMs": 681, + "browser": "155.0", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "candidate-artifact", + "sha256": "ef72d277e817c2f7312c10815ee91a74dbb9334285476d14fae7f4449b3d2758", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool candidate-artifact / corrupt media, replacement URLs and repeated destroy reproduce baseline gaps", + "project": "firefox", + "kind": "actual-tool", + "status": "passed", + "durationMs": 1036, + "browser": "155.0", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "candidate-artifact", + "sha256": "ef72d277e817c2f7312c10815ee91a74dbb9334285476d14fae7f4449b3d2758", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-media-failure", + "data": { + "failed": { + "code": 4, + "toolErrors": 1, + "first": "blob:http://127.0.0.1:8084/fb2e1281-01e5-45ab-95dd-416dcae3aa2a" + }, + "result": { + "value": "", + "created": [ + "blob:http://127.0.0.1:8084/fb2e1281-01e5-45ab-95dd-416dcae3aa2a", + "blob:http://127.0.0.1:8084/053f17bc-0709-4dd0-985f-a73576bb317e" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/fb2e1281-01e5-45ab-95dd-416dcae3aa2a", + "blob:http://127.0.0.1:8084/053f17bc-0709-4dd0-985f-a73576bb317e" + ] + } + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool candidate-artifact / native PNG callback delivered after destroy is ignored after cancellation", + "project": "firefox", + "kind": "actual-tool", + "status": "passed", + "durationMs": 1074, + "browser": "155.0", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "candidate-artifact", + "sha256": "ef72d277e817c2f7312c10815ee91a74dbb9334285476d14fae7f4449b3d2758", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-late-native-blob", + "data": { + "count": 1, + "created": [ + "blob:http://127.0.0.1:8084/6a49c341-8d5e-4b1c-98d8-308b74639d7e" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/6a49c341-8d5e-4b1c-98d8-308b74639d7e" + ], + "pendingState": "rejected", + "events": [ + "file", + "video", + "canvas", + "destroy" + ], + "connected": false, + "bytes": 6496 + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool candidate-artifact / source replacement cancels held native encoding and completes only the latest job", + "project": "firefox", + "kind": "actual-tool", + "status": "passed", + "durationMs": 1830, + "browser": "155.0", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "candidate-artifact", + "sha256": "ef72d277e817c2f7312c10815ee91a74dbb9334285476d14fae7f4449b3d2758", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-native-source-cancellation", + "data": { + "oldState": "AbortError", + "firstEncodedBytes": 6496, + "source": "blob:http://127.0.0.1:8084/6c03b7d7-7dbc-4cc6-803c-1b67128b49ee", + "updateSources": [ + "blob:http://127.0.0.1:8084/6c03b7d7-7dbc-4cc6-803c-1b67128b49ee", + "blob:http://127.0.0.1:8084/6c03b7d7-7dbc-4cc6-803c-1b67128b49ee", + "blob:http://127.0.0.1:8084/6c03b7d7-7dbc-4cc6-803c-1b67128b49ee", + "blob:http://127.0.0.1:8084/6c03b7d7-7dbc-4cc6-803c-1b67128b49ee", + "blob:http://127.0.0.1:8084/6c03b7d7-7dbc-4cc6-803c-1b67128b49ee", + "blob:http://127.0.0.1:8084/6c03b7d7-7dbc-4cc6-803c-1b67128b49ee", + "blob:http://127.0.0.1:8084/6c03b7d7-7dbc-4cc6-803c-1b67128b49ee", + "blob:http://127.0.0.1:8084/6c03b7d7-7dbc-4cc6-803c-1b67128b49ee", + "blob:http://127.0.0.1:8084/6c03b7d7-7dbc-4cc6-803c-1b67128b49ee", + "blob:http://127.0.0.1:8084/6c03b7d7-7dbc-4cc6-803c-1b67128b49ee" + ], + "created": [ + "blob:http://127.0.0.1:8084/851d05e3-327a-47c0-a64a-afadf108de78", + "blob:http://127.0.0.1:8084/6c03b7d7-7dbc-4cc6-803c-1b67128b49ee", + "blob:http://127.0.0.1:8084/c5836cbc-b0bd-4a55-8e9d-c46f1c1c1e53", + "blob:http://127.0.0.1:8084/81875705-8df2-47f3-be95-cf3288b6b73f", + "blob:http://127.0.0.1:8084/59193e96-de1f-440e-8dd6-d3b6f4f5032b", + "blob:http://127.0.0.1:8084/4406a433-fd8a-49d9-9be5-975d6ea75e88", + "blob:http://127.0.0.1:8084/dbccd945-70d5-4d7d-96e8-4a54a0007704", + "blob:http://127.0.0.1:8084/62925c6d-c6af-424c-a605-c19c7dea57ef", + "blob:http://127.0.0.1:8084/de32a697-a799-46fa-9a22-d68be0bb2cf0", + "blob:http://127.0.0.1:8084/229af163-f481-46e0-b14a-529badb1f3cf", + "blob:http://127.0.0.1:8084/98875e32-4a25-45c3-b8ba-1d891b44d9ba", + "blob:http://127.0.0.1:8084/33426d22-d124-4e1d-80ad-a27057312924" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/851d05e3-327a-47c0-a64a-afadf108de78", + "blob:http://127.0.0.1:8084/c5836cbc-b0bd-4a55-8e9d-c46f1c1c1e53", + "blob:http://127.0.0.1:8084/81875705-8df2-47f3-be95-cf3288b6b73f", + "blob:http://127.0.0.1:8084/59193e96-de1f-440e-8dd6-d3b6f4f5032b", + "blob:http://127.0.0.1:8084/4406a433-fd8a-49d9-9be5-975d6ea75e88", + "blob:http://127.0.0.1:8084/dbccd945-70d5-4d7d-96e8-4a54a0007704", + "blob:http://127.0.0.1:8084/62925c6d-c6af-424c-a605-c19c7dea57ef", + "blob:http://127.0.0.1:8084/de32a697-a799-46fa-9a22-d68be0bb2cf0", + "blob:http://127.0.0.1:8084/229af163-f481-46e0-b14a-529badb1f3cf", + "blob:http://127.0.0.1:8084/98875e32-4a25-45c3-b8ba-1d891b44d9ba", + "blob:http://127.0.0.1:8084/6c03b7d7-7dbc-4cc6-803c-1b67128b49ee", + "blob:http://127.0.0.1:8084/33426d22-d124-4e1d-80ad-a27057312924" + ], + "events": [ + "file", + "video", + "canvas", + "file", + "video", + "canvas", + "update", + "update", + "update", + "update", + "update", + "update", + "update", + "update", + "update", + "update", + "done", + "destroy" + ], + "connected": false + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool candidate-artifact / native file selection, seek extraction, PNG pixels and repeat generation", + "project": "webkit", + "kind": "blob-unavailable", + "status": "passed", + "durationMs": 1675, + "browser": "26.6", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "candidate-artifact", + "sha256": "ef72d277e817c2f7312c10815ee91a74dbb9334285476d14fae7f4449b3d2758", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-blob-unavailable", + "data": { + "native": { + "http": "loaded", + "error": 4, + "blob": "error" + }, + "tool": { + "errors": 1, + "processing": false, + "duration": null + } + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool candidate-artifact / DOM drop dispatch uses the registered handler while callable ondrop works", + "project": "webkit", + "kind": "blob-unavailable", + "status": "passed", + "durationMs": 1665, + "browser": "26.6", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "candidate-artifact", + "sha256": "ef72d277e817c2f7312c10815ee91a74dbb9334285476d14fae7f4449b3d2758", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-blob-unavailable", + "data": { + "native": { + "http": "loaded", + "error": 4, + "blob": "error" + }, + "tool": { + "errors": 1, + "processing": false, + "duration": null + } + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool candidate-artifact / corrupt media, replacement URLs and repeated destroy reproduce baseline gaps", + "project": "webkit", + "kind": "blob-unavailable", + "status": "passed", + "durationMs": 1615, + "browser": "26.6", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "candidate-artifact", + "sha256": "ef72d277e817c2f7312c10815ee91a74dbb9334285476d14fae7f4449b3d2758", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-blob-unavailable", + "data": { + "native": { + "http": "loaded", + "error": 4, + "blob": "error" + }, + "tool": { + "errors": 1, + "processing": false, + "duration": null + } + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool candidate-artifact / native PNG callback delivered after destroy is ignored after cancellation", + "project": "webkit", + "kind": "blob-unavailable", + "status": "passed", + "durationMs": 1653, + "browser": "26.6", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "candidate-artifact", + "sha256": "ef72d277e817c2f7312c10815ee91a74dbb9334285476d14fae7f4449b3d2758", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-blob-unavailable", + "data": { + "native": { + "http": "loaded", + "error": 4, + "blob": "error" + }, + "tool": { + "errors": 1, + "processing": false, + "duration": null + } + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool candidate-artifact / source replacement cancels held native encoding and completes only the latest job", + "project": "webkit", + "kind": "blob-unavailable", + "status": "passed", + "durationMs": 1625, + "browser": "26.6", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "candidate-artifact", + "sha256": "ef72d277e817c2f7312c10815ee91a74dbb9334285476d14fae7f4449b3d2758", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-blob-unavailable", + "data": { + "native": { + "http": "loaded", + "error": 4, + "blob": "error" + }, + "tool": { + "errors": 1, + "processing": false, + "duration": null + } + } + } + ] + } + ] + }, + { + "variant": "legacy", + "counts": { + "actual-tool": 34, + "input": 3, + "blob-unavailable": 17, + "native-control": 3, + "editor": 3, + "emitter": 3 + }, + "cases": [ + { + "title": "thumbnail-editor-types.spec.js / Monaco consumes Thumbnail global declarations and runs the actual typed constructor and cleanup", + "project": "chromium", + "kind": "editor", + "status": "passed", + "durationMs": 1099, + "browser": "153.0.8010.12", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-editor", + "data": { + "candidate": "candidate-artifact", + "sha256": "f1d62e3cee7e060e7ecff4a21e7589e2befb2e405976efcdee73719857d0be03", + "declarations": { + "artplayer": "652c0fdf605e87203526777a05851bd69c95c1660d5ff29eab19c00967c9a985", + "artplayer-tool-thumbnail": "9b72d10a1fe964d54fcf4774770074058ea739b4f734b52b8f4adebb37fc83e6", + "artplayer-proxy-mediabunny": "acbaa0a25f20ac15b3d79db00b3a16167f84452ab58933110f42dcb3262fbd3e" + }, + "result": { + "syntax": [], + "semantic": [], + "declaration": [], + "invalid": [ + 2322 + ], + "executed": true, + "actual": { + "last": 7, + "connected": false, + "inputConnected": false, + "processing": false + } + }, + "scope": "Real local Monaco worker, typed constructor/custom event and native DOM cleanup; MediaBunny relative type bundle also compiles. This does not test file decoding or the complete demo/player integration." + } + } + ] + }, + { + "title": "thumbnail-editor-types.spec.js / Monaco consumes Thumbnail global declarations and runs the actual typed constructor and cleanup", + "project": "firefox", + "kind": "editor", + "status": "passed", + "durationMs": 2357, + "browser": "155.0", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-editor", + "data": { + "candidate": "candidate-artifact", + "sha256": "f1d62e3cee7e060e7ecff4a21e7589e2befb2e405976efcdee73719857d0be03", + "declarations": { + "artplayer": "652c0fdf605e87203526777a05851bd69c95c1660d5ff29eab19c00967c9a985", + "artplayer-tool-thumbnail": "9b72d10a1fe964d54fcf4774770074058ea739b4f734b52b8f4adebb37fc83e6", + "artplayer-proxy-mediabunny": "acbaa0a25f20ac15b3d79db00b3a16167f84452ab58933110f42dcb3262fbd3e" + }, + "result": { + "syntax": [], + "semantic": [], + "declaration": [], + "invalid": [ + 2322 + ], + "executed": true, + "actual": { + "last": 7, + "connected": false, + "inputConnected": false, + "processing": false + } + }, + "scope": "Real local Monaco worker, typed constructor/custom event and native DOM cleanup; MediaBunny relative type bundle also compiles. This does not test file decoding or the complete demo/player integration." + } + } + ] + }, + { + "title": "thumbnail-editor-types.spec.js / Monaco consumes Thumbnail global declarations and runs the actual typed constructor and cleanup", + "project": "webkit", + "kind": "editor", + "status": "passed", + "durationMs": 1619, + "browser": "26.6", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-editor", + "data": { + "candidate": "candidate-artifact", + "sha256": "f1d62e3cee7e060e7ecff4a21e7589e2befb2e405976efcdee73719857d0be03", + "declarations": { + "artplayer": "652c0fdf605e87203526777a05851bd69c95c1660d5ff29eab19c00967c9a985", + "artplayer-tool-thumbnail": "9b72d10a1fe964d54fcf4774770074058ea739b4f734b52b8f4adebb37fc83e6", + "artplayer-proxy-mediabunny": "acbaa0a25f20ac15b3d79db00b3a16167f84452ab58933110f42dcb3262fbd3e" + }, + "result": { + "syntax": [], + "semantic": [], + "declaration": [], + "invalid": [ + 2322 + ], + "executed": true, + "actual": { + "last": 7, + "connected": false, + "inputConnected": false, + "processing": false + } + }, + "scope": "Real local Monaco worker, typed constructor/custom event and native DOM cleanup; MediaBunny relative type bundle also compiles. This does not test file decoding or the complete demo/player integration." + } + } + ] + }, + { + "title": "thumbnail-emitter.spec.js / Thumbnail native browser event registry handles special names and nested once without changing DOM cleanup", + "project": "chromium", + "kind": "emitter", + "status": "passed", + "durationMs": 286, + "browser": "153.0.8010.12", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-emitter", + "data": { + "candidate": "candidate-artifact", + "sha256": "f1d62e3cee7e060e7ecff4a21e7589e2befb2e405976efcdee73719857d0be03", + "result": { + "keys": [ + { + "name": "__proto__", + "calls": 3, + "prototypePreserved": true, + "removed": true + }, + { + "name": "constructor", + "calls": 3, + "prototypePreserved": true, + "removed": true + }, + { + "name": "toString", + "calls": 3, + "prototypePreserved": true, + "removed": true + }, + { + "name": "hasOwnProperty", + "calls": 3, + "prototypePreserved": true, + "removed": true + } + ], + "seen": [ + "on:inner", + "once:inner", + "on:outer" + ], + "videoConnected": false, + "inputConnected": false, + "processing": false + } + } + } + ] + }, + { + "title": "thumbnail-emitter.spec.js / Thumbnail native browser event registry handles special names and nested once without changing DOM cleanup", + "project": "firefox", + "kind": "emitter", + "status": "passed", + "durationMs": 1399, + "browser": "155.0", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-emitter", + "data": { + "candidate": "candidate-artifact", + "sha256": "f1d62e3cee7e060e7ecff4a21e7589e2befb2e405976efcdee73719857d0be03", + "result": { + "keys": [ + { + "name": "__proto__", + "calls": 3, + "prototypePreserved": true, + "removed": true + }, + { + "name": "constructor", + "calls": 3, + "prototypePreserved": true, + "removed": true + }, + { + "name": "toString", + "calls": 3, + "prototypePreserved": true, + "removed": true + }, + { + "name": "hasOwnProperty", + "calls": 3, + "prototypePreserved": true, + "removed": true + } + ], + "seen": [ + "on:inner", + "once:inner", + "on:outer" + ], + "videoConnected": false, + "inputConnected": false, + "processing": false + } + } + } + ] + }, + { + "title": "thumbnail-emitter.spec.js / Thumbnail native browser event registry handles special names and nested once without changing DOM cleanup", + "project": "webkit", + "kind": "emitter", + "status": "passed", + "durationMs": 582, + "browser": "26.6", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-emitter", + "data": { + "candidate": "candidate-artifact", + "sha256": "f1d62e3cee7e060e7ecff4a21e7589e2befb2e405976efcdee73719857d0be03", + "result": { + "keys": [ + { + "name": "__proto__", + "calls": 3, + "prototypePreserved": true, + "removed": true + }, + { + "name": "constructor", + "calls": 3, + "prototypePreserved": true, + "removed": true + }, + { + "name": "toString", + "calls": 3, + "prototypePreserved": true, + "removed": true + }, + { + "name": "hasOwnProperty", + "calls": 3, + "prototypePreserved": true, + "removed": true + } + ], + "seen": [ + "on:inner", + "once:inner", + "on:outer" + ], + "videoConnected": false, + "inputConnected": false, + "processing": false + } + } + } + ] + }, + { + "title": "thumbnail-input.spec.js / Thumbnail input ownership replaces real DOM listeners and generated inputs", + "project": "chromium", + "kind": "input", + "status": "passed", + "durationMs": 347, + "browser": "153.0.8010.12", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-input-ownership", + "data": { + "implementation": "candidate-artifact", + "sha256": "f1d62e3cee7e060e7ecff4a21e7589e2befb2e405976efcdee73719857d0be03", + "scope": "Native file input/DOM/listeners only; does not claim media decoding.", + "switched": { + "value": "", + "events": [ + "thumbnail-pattern.mp4" + ], + "firstPosition": "sticky", + "disconnected": true + }, + "result": { + "events": [ + "thumbnail-pattern.mp4", + "thumbnail-pattern.mp4" + ], + "sameError": true, + "notifications": 1, + "inputConnected": false, + "videoConnected": false, + "secondPosition": "absolute", + "inputs": 0 + } + } + } + ] + }, + { + "title": "thumbnail-input.spec.js / Thumbnail input ownership replaces real DOM listeners and generated inputs", + "project": "firefox", + "kind": "input", + "status": "passed", + "durationMs": 510, + "browser": "155.0", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-input-ownership", + "data": { + "implementation": "candidate-artifact", + "sha256": "f1d62e3cee7e060e7ecff4a21e7589e2befb2e405976efcdee73719857d0be03", + "scope": "Native file input/DOM/listeners only; does not claim media decoding.", + "switched": { + "value": "", + "events": [ + "thumbnail-pattern.mp4" + ], + "firstPosition": "sticky", + "disconnected": true + }, + "result": { + "events": [ + "thumbnail-pattern.mp4", + "thumbnail-pattern.mp4" + ], + "sameError": true, + "notifications": 1, + "inputConnected": false, + "videoConnected": false, + "secondPosition": "absolute", + "inputs": 0 + } + } + } + ] + }, + { + "title": "thumbnail-input.spec.js / Thumbnail input ownership replaces real DOM listeners and generated inputs", + "project": "webkit", + "kind": "input", + "status": "passed", + "durationMs": 789, + "browser": "26.6", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-input-ownership", + "data": { + "implementation": "candidate-artifact", + "sha256": "f1d62e3cee7e060e7ecff4a21e7589e2befb2e405976efcdee73719857d0be03", + "scope": "Native file input/DOM/listeners only; does not claim media decoding.", + "switched": { + "value": "", + "events": [ + "thumbnail-pattern.mp4" + ], + "firstPosition": "sticky", + "disconnected": true + }, + "result": { + "events": [ + "thumbnail-pattern.mp4", + "thumbnail-pattern.mp4" + ], + "sameError": true, + "notifications": 1, + "inputConnected": false, + "videoConnected": false, + "secondPosition": "absolute", + "inputs": 0 + } + } + } + ] + }, + { + "title": "thumbnail-native.spec.js / Thumbnail native HTTP and Blob media controls", + "project": "chromium", + "kind": "native-control", + "status": "passed", + "durationMs": 196, + "browser": "153.0.8010.12", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-native-media-controls", + "data": [ + { + "url": "/test/pattern.mp4", + "type": "video/mp4", + "transport": "http", + "outcome": "loaded", + "readyState": 4, + "error": 0, + "duration": 8, + "width": 320 + }, + { + "url": "/test/pattern.mp4", + "type": "video/mp4", + "transport": "blob", + "outcome": "loaded", + "readyState": 4, + "error": 0, + "duration": 8, + "width": 320 + }, + { + "url": "/test/thumbnail-pattern.mp4", + "type": "video/mp4", + "transport": "http", + "outcome": "loaded", + "readyState": 4, + "error": 0, + "duration": 24, + "width": 320 + }, + { + "url": "/test/thumbnail-pattern.mp4", + "type": "video/mp4", + "transport": "blob", + "outcome": "loaded", + "readyState": 4, + "error": 0, + "duration": 24, + "width": 320 + }, + { + "url": "/assets/sample/video.mp4", + "type": "video/mp4", + "transport": "http", + "outcome": "loaded", + "readyState": 4, + "error": 0, + "duration": 90.046009, + "width": 640 + }, + { + "url": "/assets/sample/video.mp4", + "type": "video/mp4", + "transport": "blob", + "outcome": "loaded", + "readyState": 4, + "error": 0, + "duration": 90.046009, + "width": 640 + } + ] + } + ] + }, + { + "title": "thumbnail-native.spec.js / Thumbnail native HTTP and Blob media controls", + "project": "firefox", + "kind": "native-control", + "status": "passed", + "durationMs": 487, + "browser": "155.0", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-native-media-controls", + "data": [ + { + "url": "/test/pattern.mp4", + "type": "video/mp4", + "transport": "http", + "outcome": "loaded", + "readyState": 4, + "error": 0, + "duration": 8, + "width": 320 + }, + { + "url": "/test/pattern.mp4", + "type": "video/mp4", + "transport": "blob", + "outcome": "loaded", + "readyState": 4, + "error": 0, + "duration": 8, + "width": 320 + }, + { + "url": "/test/thumbnail-pattern.mp4", + "type": "video/mp4", + "transport": "http", + "outcome": "loaded", + "readyState": 3, + "error": 0, + "duration": 24, + "width": 320 + }, + { + "url": "/test/thumbnail-pattern.mp4", + "type": "video/mp4", + "transport": "blob", + "outcome": "loaded", + "readyState": 4, + "error": 0, + "duration": 24, + "width": 320 + }, + { + "url": "/assets/sample/video.mp4", + "type": "video/mp4", + "transport": "http", + "outcome": "loaded", + "readyState": 4, + "error": 0, + "duration": 90.045986, + "width": 640 + }, + { + "url": "/assets/sample/video.mp4", + "type": "video/mp4", + "transport": "blob", + "outcome": "loaded", + "readyState": 4, + "error": 0, + "duration": 90.045986, + "width": 640 + } + ] + } + ] + }, + { + "title": "thumbnail-native.spec.js / Thumbnail native HTTP and Blob media controls", + "project": "webkit", + "kind": "native-control", + "status": "passed", + "durationMs": 2755, + "browser": "26.6", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-native-media-controls", + "data": [ + { + "url": "/test/pattern.mp4", + "type": "video/mp4", + "transport": "http", + "outcome": "loaded", + "readyState": 3, + "error": 0, + "duration": 8, + "width": 320 + }, + { + "url": "/test/pattern.mp4", + "type": "video/mp4", + "transport": "blob", + "outcome": "error", + "readyState": 0, + "error": 4, + "duration": null, + "width": 0 + }, + { + "url": "/test/thumbnail-pattern.mp4", + "type": "video/mp4", + "transport": "http", + "outcome": "loaded", + "readyState": 2, + "error": 0, + "duration": 24, + "width": 320 + }, + { + "url": "/test/thumbnail-pattern.mp4", + "type": "video/mp4", + "transport": "blob", + "outcome": "error", + "readyState": 0, + "error": 4, + "duration": null, + "width": 0 + }, + { + "url": "/assets/sample/video.mp4", + "type": "video/mp4", + "transport": "http", + "outcome": "loaded", + "readyState": 2, + "error": 0, + "duration": 90.094, + "width": 640 + }, + { + "url": "/assets/sample/video.mp4", + "type": "video/mp4", + "transport": "blob", + "outcome": "error", + "readyState": 0, + "error": 4, + "duration": null, + "width": 0 + } + ] + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool cdn-3.5.31 / native file selection, seek extraction, PNG pixels and repeat generation", + "project": "chromium", + "kind": "actual-tool", + "status": "passed", + "durationMs": 8054, + "browser": "153.0.8010.12", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "cdn-3.5.31", + "sha256": "27fdc506c9b228c40df39f8508b3a42c145571c06c0b9cc60327a007a2064c87", + "recoveredIndividualFile": true, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-extraction", + "data": { + "width": 240, + "height": 150, + "sourceWidth": 320, + "sourceHeight": 180, + "cellHeight": 30, + "mime": "image/png", + "bytes": 26427, + "nonBlack": 2400, + "createdCount": 21, + "revokedCount": 21, + "doneCount": 2, + "updates": [ + 0.1, + 0.2, + 0.3, + 0.4, + 0.5, + 0.6, + 0.7, + 0.8, + 0.9, + 1 + ], + "retiredReadyState": 4, + "retiredSrc": "blob:http://127.0.0.1:8084/019f7feb-4785-4eb1-bcc1-029035e5b8da", + "connected": false + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool cdn-3.5.31 / DOM drop dispatch exposes missing listener while callable ondrop works", + "project": "chromium", + "kind": "actual-tool", + "status": "passed", + "durationMs": 229, + "browser": "153.0.8010.12", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "cdn-3.5.31", + "sha256": "27fdc506c9b228c40df39f8508b3a42c145571c06c0b9cc60327a007a2064c87", + "recoveredIndividualFile": true, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool cdn-3.5.31 / corrupt media, replacement URLs and repeated destroy reproduce baseline gaps", + "project": "chromium", + "kind": "actual-tool", + "status": "passed", + "durationMs": 234, + "browser": "153.0.8010.12", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "cdn-3.5.31", + "sha256": "27fdc506c9b228c40df39f8508b3a42c145571c06c0b9cc60327a007a2064c87", + "recoveredIndividualFile": true, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-media-failure", + "data": { + "failed": { + "code": 4, + "toolErrors": 0, + "first": "blob:http://127.0.0.1:8084/94f9aaf7-8f71-48a2-9f09-903ecfa3a1d3" + }, + "result": { + "value": "C:\\fakepath\\thumbnail-pattern.mp4", + "created": [ + "blob:http://127.0.0.1:8084/94f9aaf7-8f71-48a2-9f09-903ecfa3a1d3", + "blob:http://127.0.0.1:8084/956171cf-ed2d-47ad-9432-c80b733cef39" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/956171cf-ed2d-47ad-9432-c80b733cef39" + ], + "repeated": "NotFoundError" + } + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool cdn-3.5.31 / native PNG callback delivered after destroy still emits update and creates a URL", + "project": "chromium", + "kind": "actual-tool", + "status": "passed", + "durationMs": 611, + "browser": "153.0.8010.12", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "cdn-3.5.31", + "sha256": "27fdc506c9b228c40df39f8508b3a42c145571c06c0b9cc60327a007a2064c87", + "recoveredIndividualFile": true, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-late-native-blob", + "data": { + "count": 1, + "created": [ + "blob:http://127.0.0.1:8084/8fb5d378-c31b-4c6b-99bc-7d8df8bea7f9", + "blob:http://127.0.0.1:8084/a68547aa-472d-4eab-9961-c4830a84e2e6" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/8fb5d378-c31b-4c6b-99bc-7d8df8bea7f9" + ], + "pendingState": "pending", + "events": [ + "file", + "canvas", + "video", + "destroy", + "update" + ], + "connected": false, + "bytes": 7540 + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool cdn-3.5.31 / native file selection, seek extraction, PNG pixels and repeat generation", + "project": "firefox", + "kind": "actual-tool", + "status": "passed", + "durationMs": 8345, + "browser": "155.0", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "cdn-3.5.31", + "sha256": "27fdc506c9b228c40df39f8508b3a42c145571c06c0b9cc60327a007a2064c87", + "recoveredIndividualFile": true, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-extraction", + "data": { + "width": 240, + "height": 150, + "sourceWidth": 320, + "sourceHeight": 180, + "cellHeight": 30, + "mime": "image/png", + "bytes": 18224, + "nonBlack": 2400, + "createdCount": 21, + "revokedCount": 21, + "doneCount": 2, + "updates": [ + 0.1, + 0.2, + 0.3, + 0.4, + 0.5, + 0.6, + 0.7, + 0.8, + 0.9, + 1 + ], + "retiredReadyState": 4, + "retiredSrc": "blob:http://127.0.0.1:8084/cf7c4b86-7d57-428c-b3bf-b8b4df5d437b", + "connected": false + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool cdn-3.5.31 / DOM drop dispatch exposes missing listener while callable ondrop works", + "project": "firefox", + "kind": "actual-tool", + "status": "passed", + "durationMs": 434, + "browser": "155.0", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "cdn-3.5.31", + "sha256": "27fdc506c9b228c40df39f8508b3a42c145571c06c0b9cc60327a007a2064c87", + "recoveredIndividualFile": true, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool cdn-3.5.31 / corrupt media, replacement URLs and repeated destroy reproduce baseline gaps", + "project": "firefox", + "kind": "actual-tool", + "status": "passed", + "durationMs": 1657, + "browser": "155.0", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "cdn-3.5.31", + "sha256": "27fdc506c9b228c40df39f8508b3a42c145571c06c0b9cc60327a007a2064c87", + "recoveredIndividualFile": true, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-media-failure", + "data": { + "failed": { + "code": 4, + "toolErrors": 0, + "first": "blob:http://127.0.0.1:8084/e4546514-4a03-4da2-a960-68d4969c7195" + }, + "result": { + "value": "C:\\fakepath\\thumbnail-pattern.mp4", + "created": [ + "blob:http://127.0.0.1:8084/e4546514-4a03-4da2-a960-68d4969c7195", + "blob:http://127.0.0.1:8084/cba05fb7-2e3d-47cf-a281-98591a89d0b2" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/cba05fb7-2e3d-47cf-a281-98591a89d0b2" + ], + "repeated": "NotFoundError" + } + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool cdn-3.5.31 / native PNG callback delivered after destroy still emits update and creates a URL", + "project": "firefox", + "kind": "actual-tool", + "status": "passed", + "durationMs": 852, + "browser": "155.0", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "cdn-3.5.31", + "sha256": "27fdc506c9b228c40df39f8508b3a42c145571c06c0b9cc60327a007a2064c87", + "recoveredIndividualFile": true, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-late-native-blob", + "data": { + "count": 1, + "created": [ + "blob:http://127.0.0.1:8084/c3726257-e796-43f6-b925-869623b406d8", + "blob:http://127.0.0.1:8084/30dce9f6-7aa9-46b8-a2f2-489ad57e00ff" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/c3726257-e796-43f6-b925-869623b406d8" + ], + "pendingState": "pending", + "events": [ + "file", + "canvas", + "video", + "destroy", + "update" + ], + "connected": false, + "bytes": 4980 + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool cdn-3.5.31 / native file selection, seek extraction, PNG pixels and repeat generation", + "project": "webkit", + "kind": "blob-unavailable", + "status": "passed", + "durationMs": 1282, + "browser": "26.6", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "cdn-3.5.31", + "sha256": "27fdc506c9b228c40df39f8508b3a42c145571c06c0b9cc60327a007a2064c87", + "recoveredIndividualFile": true, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-blob-unavailable", + "data": { + "native": { + "http": "loaded", + "error": 4, + "blob": "error" + }, + "tool": { + "errors": 0, + "processing": false, + "duration": null + } + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool cdn-3.5.31 / DOM drop dispatch exposes missing listener while callable ondrop works", + "project": "webkit", + "kind": "blob-unavailable", + "status": "passed", + "durationMs": 1246, + "browser": "26.6", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "cdn-3.5.31", + "sha256": "27fdc506c9b228c40df39f8508b3a42c145571c06c0b9cc60327a007a2064c87", + "recoveredIndividualFile": true, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-blob-unavailable", + "data": { + "native": { + "http": "loaded", + "error": 4, + "blob": "error" + }, + "tool": { + "errors": 0, + "processing": false, + "duration": null + } + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool cdn-3.5.31 / corrupt media, replacement URLs and repeated destroy reproduce baseline gaps", + "project": "webkit", + "kind": "blob-unavailable", + "status": "passed", + "durationMs": 1301, + "browser": "26.6", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "cdn-3.5.31", + "sha256": "27fdc506c9b228c40df39f8508b3a42c145571c06c0b9cc60327a007a2064c87", + "recoveredIndividualFile": true, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-blob-unavailable", + "data": { + "native": { + "http": "loaded", + "error": 4, + "blob": "error" + }, + "tool": { + "errors": 0, + "processing": false, + "duration": null + } + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool cdn-3.5.31 / native PNG callback delivered after destroy still emits update and creates a URL", + "project": "webkit", + "kind": "blob-unavailable", + "status": "passed", + "durationMs": 1321, + "browser": "26.6", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "cdn-3.5.31", + "sha256": "27fdc506c9b228c40df39f8508b3a42c145571c06c0b9cc60327a007a2064c87", + "recoveredIndividualFile": true, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-blob-unavailable", + "data": { + "native": { + "http": "loaded", + "error": 4, + "blob": "error" + }, + "tool": { + "errors": 0, + "processing": false, + "duration": null + } + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool workspace.js / native file selection, seek extraction, PNG pixels and repeat generation", + "project": "chromium", + "kind": "actual-tool", + "status": "passed", + "durationMs": 559, + "browser": "153.0.8010.12", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "workspace.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "recoveredIndividualFile": false, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-extraction", + "data": { + "width": 240, + "height": 210, + "sourceWidth": 320, + "sourceHeight": 180, + "cellHeight": 45, + "mime": "image/png", + "bytes": 44055, + "nonBlack": 3600, + "createdCount": 21, + "revokedCount": 21, + "doneCount": 2, + "updates": [ + 0.1, + 0.2, + 0.3, + 0.4, + 0.5, + 0.6, + 0.7, + 0.8, + 0.9, + 1 + ], + "retiredReadyState": 4, + "retiredSrc": "blob:http://127.0.0.1:8084/56b3caab-3396-4aa0-9771-40e44cec5a9f", + "connected": false + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool workspace.js / DOM drop dispatch exposes missing listener while callable ondrop works", + "project": "chromium", + "kind": "actual-tool", + "status": "passed", + "durationMs": 199, + "browser": "153.0.8010.12", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "workspace.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "recoveredIndividualFile": false, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool workspace.js / corrupt media, replacement URLs and repeated destroy reproduce baseline gaps", + "project": "chromium", + "kind": "actual-tool", + "status": "passed", + "durationMs": 239, + "browser": "153.0.8010.12", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "workspace.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "recoveredIndividualFile": false, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-media-failure", + "data": { + "failed": { + "code": 4, + "toolErrors": 0, + "first": "blob:http://127.0.0.1:8084/6b034064-291c-4e2d-9713-92513f65e7a3" + }, + "result": { + "value": "", + "created": [ + "blob:http://127.0.0.1:8084/6b034064-291c-4e2d-9713-92513f65e7a3", + "blob:http://127.0.0.1:8084/41c42d02-0264-4bc0-9e36-2126340544e8" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/41c42d02-0264-4bc0-9e36-2126340544e8" + ], + "repeated": "NotFoundError" + } + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool workspace.js / native PNG callback delivered after destroy still emits update and creates a URL", + "project": "chromium", + "kind": "actual-tool", + "status": "passed", + "durationMs": 340, + "browser": "153.0.8010.12", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "workspace.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "recoveredIndividualFile": false, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-late-native-blob", + "data": { + "count": 1, + "created": [ + "blob:http://127.0.0.1:8084/3296d2e4-9658-4210-801c-535575b2f756", + "blob:http://127.0.0.1:8084/d89cc4ae-6687-4eb1-9e15-d857a330a779" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/3296d2e4-9658-4210-801c-535575b2f756" + ], + "pendingState": "pending", + "events": [ + "file", + "video", + "canvas", + "destroy", + "update" + ], + "connected": false, + "bytes": 9120 + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool workspace.js / native file selection, seek extraction, PNG pixels and repeat generation", + "project": "firefox", + "kind": "actual-tool", + "status": "passed", + "durationMs": 1691, + "browser": "155.0", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "workspace.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "recoveredIndividualFile": false, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-extraction", + "data": { + "width": 240, + "height": 210, + "sourceWidth": 320, + "sourceHeight": 180, + "cellHeight": 45, + "mime": "image/png", + "bytes": 38895, + "nonBlack": 3600, + "createdCount": 21, + "revokedCount": 21, + "doneCount": 2, + "updates": [ + 0.1, + 0.2, + 0.3, + 0.4, + 0.5, + 0.6, + 0.7, + 0.8, + 0.9, + 1 + ], + "retiredReadyState": 4, + "retiredSrc": "blob:http://127.0.0.1:8084/d724331a-a2f9-4900-a6a9-5b2793acb084", + "connected": false + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool workspace.js / DOM drop dispatch exposes missing listener while callable ondrop works", + "project": "firefox", + "kind": "actual-tool", + "status": "passed", + "durationMs": 525, + "browser": "155.0", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "workspace.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "recoveredIndividualFile": false, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool workspace.js / corrupt media, replacement URLs and repeated destroy reproduce baseline gaps", + "project": "firefox", + "kind": "actual-tool", + "status": "passed", + "durationMs": 624, + "browser": "155.0", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "workspace.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "recoveredIndividualFile": false, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-media-failure", + "data": { + "failed": { + "code": 4, + "toolErrors": 0, + "first": "blob:http://127.0.0.1:8084/1d6c9f78-d277-4048-9897-c45d7ad77976" + }, + "result": { + "value": "", + "created": [ + "blob:http://127.0.0.1:8084/1d6c9f78-d277-4048-9897-c45d7ad77976", + "blob:http://127.0.0.1:8084/ec3595bf-8b29-4e03-9dc8-1a78fc3c9c6a" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/ec3595bf-8b29-4e03-9dc8-1a78fc3c9c6a" + ], + "repeated": "NotFoundError" + } + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool workspace.js / native PNG callback delivered after destroy still emits update and creates a URL", + "project": "firefox", + "kind": "actual-tool", + "status": "passed", + "durationMs": 574, + "browser": "155.0", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "workspace.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "recoveredIndividualFile": false, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-late-native-blob", + "data": { + "count": 1, + "created": [ + "blob:http://127.0.0.1:8084/36b16309-35dd-426d-b91e-c90b1de5f70e", + "blob:http://127.0.0.1:8084/1a0d32b9-7e84-43ea-b56b-8e8846d180ef" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/36b16309-35dd-426d-b91e-c90b1de5f70e" + ], + "pendingState": "pending", + "events": [ + "file", + "video", + "canvas", + "destroy", + "update" + ], + "connected": false, + "bytes": 6496 + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool workspace.js / native file selection, seek extraction, PNG pixels and repeat generation", + "project": "webkit", + "kind": "blob-unavailable", + "status": "passed", + "durationMs": 1302, + "browser": "26.6", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "workspace.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "recoveredIndividualFile": false, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-blob-unavailable", + "data": { + "native": { + "http": "loaded", + "error": 4, + "blob": "error" + }, + "tool": { + "errors": 0, + "processing": false, + "duration": null + } + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool workspace.js / DOM drop dispatch exposes missing listener while callable ondrop works", + "project": "webkit", + "kind": "blob-unavailable", + "status": "passed", + "durationMs": 1316, + "browser": "26.6", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "workspace.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "recoveredIndividualFile": false, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-blob-unavailable", + "data": { + "native": { + "http": "loaded", + "error": 4, + "blob": "error" + }, + "tool": { + "errors": 0, + "processing": false, + "duration": null + } + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool workspace.js / corrupt media, replacement URLs and repeated destroy reproduce baseline gaps", + "project": "webkit", + "kind": "blob-unavailable", + "status": "passed", + "durationMs": 1312, + "browser": "26.6", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "workspace.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "recoveredIndividualFile": false, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-blob-unavailable", + "data": { + "native": { + "http": "loaded", + "error": 4, + "blob": "error" + }, + "tool": { + "errors": 0, + "processing": false, + "duration": null + } + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool workspace.js / native PNG callback delivered after destroy still emits update and creates a URL", + "project": "webkit", + "kind": "blob-unavailable", + "status": "passed", + "durationMs": 1306, + "browser": "26.6", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "workspace.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "recoveredIndividualFile": false, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-blob-unavailable", + "data": { + "native": { + "http": "loaded", + "error": 4, + "blob": "error" + }, + "tool": { + "errors": 0, + "processing": false, + "duration": null + } + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool workspace.legacy.js / native file selection, seek extraction, PNG pixels and repeat generation", + "project": "chromium", + "kind": "actual-tool", + "status": "passed", + "durationMs": 553, + "browser": "153.0.8010.12", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "workspace.legacy.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "recoveredIndividualFile": false, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-extraction", + "data": { + "width": 240, + "height": 210, + "sourceWidth": 320, + "sourceHeight": 180, + "cellHeight": 45, + "mime": "image/png", + "bytes": 44055, + "nonBlack": 3600, + "createdCount": 21, + "revokedCount": 21, + "doneCount": 2, + "updates": [ + 0.1, + 0.2, + 0.3, + 0.4, + 0.5, + 0.6, + 0.7, + 0.8, + 0.9, + 1 + ], + "retiredReadyState": 4, + "retiredSrc": "blob:http://127.0.0.1:8084/36cc3944-b2e5-45aa-904a-eee24589e8e5", + "connected": false + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool workspace.legacy.js / DOM drop dispatch exposes missing listener while callable ondrop works", + "project": "chromium", + "kind": "actual-tool", + "status": "passed", + "durationMs": 193, + "browser": "153.0.8010.12", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "workspace.legacy.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "recoveredIndividualFile": false, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool workspace.legacy.js / corrupt media, replacement URLs and repeated destroy reproduce baseline gaps", + "project": "chromium", + "kind": "actual-tool", + "status": "passed", + "durationMs": 237, + "browser": "153.0.8010.12", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "workspace.legacy.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "recoveredIndividualFile": false, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-media-failure", + "data": { + "failed": { + "code": 4, + "toolErrors": 0, + "first": "blob:http://127.0.0.1:8084/5bbd2b65-02b1-4aad-86e3-9f85b7c37983" + }, + "result": { + "value": "", + "created": [ + "blob:http://127.0.0.1:8084/5bbd2b65-02b1-4aad-86e3-9f85b7c37983", + "blob:http://127.0.0.1:8084/0f7bf361-58d8-4a4e-ae42-1728ee00828f" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/0f7bf361-58d8-4a4e-ae42-1728ee00828f" + ], + "repeated": "NotFoundError" + } + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool workspace.legacy.js / native PNG callback delivered after destroy still emits update and creates a URL", + "project": "chromium", + "kind": "actual-tool", + "status": "passed", + "durationMs": 334, + "browser": "153.0.8010.12", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "workspace.legacy.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "recoveredIndividualFile": false, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-late-native-blob", + "data": { + "count": 1, + "created": [ + "blob:http://127.0.0.1:8084/ebf878a6-8f80-4b33-8c8f-795270031d17", + "blob:http://127.0.0.1:8084/c89cc8d8-61d5-45c5-8cc2-bb1067dcff37" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/ebf878a6-8f80-4b33-8c8f-795270031d17" + ], + "pendingState": "pending", + "events": [ + "file", + "video", + "canvas", + "destroy", + "update" + ], + "connected": false, + "bytes": 9120 + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool workspace.legacy.js / native file selection, seek extraction, PNG pixels and repeat generation", + "project": "firefox", + "kind": "actual-tool", + "status": "passed", + "durationMs": 1617, + "browser": "155.0", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "workspace.legacy.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "recoveredIndividualFile": false, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-extraction", + "data": { + "width": 240, + "height": 210, + "sourceWidth": 320, + "sourceHeight": 180, + "cellHeight": 45, + "mime": "image/png", + "bytes": 38895, + "nonBlack": 3600, + "createdCount": 21, + "revokedCount": 21, + "doneCount": 2, + "updates": [ + 0.1, + 0.2, + 0.3, + 0.4, + 0.5, + 0.6, + 0.7, + 0.8, + 0.9, + 1 + ], + "retiredReadyState": 4, + "retiredSrc": "blob:http://127.0.0.1:8084/0990e61a-2614-4a8b-bb3e-aec4238be2a6", + "connected": false + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool workspace.legacy.js / DOM drop dispatch exposes missing listener while callable ondrop works", + "project": "firefox", + "kind": "actual-tool", + "status": "passed", + "durationMs": 446, + "browser": "155.0", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "workspace.legacy.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "recoveredIndividualFile": false, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool workspace.legacy.js / corrupt media, replacement URLs and repeated destroy reproduce baseline gaps", + "project": "firefox", + "kind": "actual-tool", + "status": "passed", + "durationMs": 596, + "browser": "155.0", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "workspace.legacy.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "recoveredIndividualFile": false, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-media-failure", + "data": { + "failed": { + "code": 4, + "toolErrors": 0, + "first": "blob:http://127.0.0.1:8084/8275667a-7c2c-4804-b02b-64ee459b7ae2" + }, + "result": { + "value": "", + "created": [ + "blob:http://127.0.0.1:8084/8275667a-7c2c-4804-b02b-64ee459b7ae2", + "blob:http://127.0.0.1:8084/dcb27e0c-e5ea-4317-aadd-dd96f454e0ef" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/dcb27e0c-e5ea-4317-aadd-dd96f454e0ef" + ], + "repeated": "NotFoundError" + } + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool workspace.legacy.js / native PNG callback delivered after destroy still emits update and creates a URL", + "project": "firefox", + "kind": "actual-tool", + "status": "passed", + "durationMs": 572, + "browser": "155.0", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "workspace.legacy.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "recoveredIndividualFile": false, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-late-native-blob", + "data": { + "count": 1, + "created": [ + "blob:http://127.0.0.1:8084/4c906932-7ab6-4fdf-874c-66bfc0a03ef8", + "blob:http://127.0.0.1:8084/ffed46be-a938-4b5c-88b5-995b07ee41f3" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/4c906932-7ab6-4fdf-874c-66bfc0a03ef8" + ], + "pendingState": "pending", + "events": [ + "file", + "video", + "canvas", + "destroy", + "update" + ], + "connected": false, + "bytes": 6496 + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool workspace.legacy.js / native file selection, seek extraction, PNG pixels and repeat generation", + "project": "webkit", + "kind": "blob-unavailable", + "status": "passed", + "durationMs": 1305, + "browser": "26.6", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "workspace.legacy.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "recoveredIndividualFile": false, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-blob-unavailable", + "data": { + "native": { + "http": "loaded", + "error": 4, + "blob": "error" + }, + "tool": { + "errors": 0, + "processing": false, + "duration": null + } + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool workspace.legacy.js / DOM drop dispatch exposes missing listener while callable ondrop works", + "project": "webkit", + "kind": "blob-unavailable", + "status": "passed", + "durationMs": 1254, + "browser": "26.6", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "workspace.legacy.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "recoveredIndividualFile": false, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-blob-unavailable", + "data": { + "native": { + "http": "loaded", + "error": 4, + "blob": "error" + }, + "tool": { + "errors": 0, + "processing": false, + "duration": null + } + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool workspace.legacy.js / corrupt media, replacement URLs and repeated destroy reproduce baseline gaps", + "project": "webkit", + "kind": "blob-unavailable", + "status": "passed", + "durationMs": 1304, + "browser": "26.6", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "workspace.legacy.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "recoveredIndividualFile": false, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-blob-unavailable", + "data": { + "native": { + "http": "loaded", + "error": 4, + "blob": "error" + }, + "tool": { + "errors": 0, + "processing": false, + "duration": null + } + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool workspace.legacy.js / native PNG callback delivered after destroy still emits update and creates a URL", + "project": "webkit", + "kind": "blob-unavailable", + "status": "passed", + "durationMs": 1320, + "browser": "26.6", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "workspace.legacy.js", + "sha256": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "recoveredIndividualFile": false, + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-blob-unavailable", + "data": { + "native": { + "http": "loaded", + "error": 4, + "blob": "error" + }, + "tool": { + "errors": 0, + "processing": false, + "duration": null + } + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool candidate-artifact / native file selection, seek extraction, PNG pixels and repeat generation", + "project": "chromium", + "kind": "actual-tool", + "status": "passed", + "durationMs": 527, + "browser": "153.0.8010.12", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "candidate-artifact", + "sha256": "f1d62e3cee7e060e7ecff4a21e7589e2befb2e405976efcdee73719857d0be03", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-extraction", + "data": { + "width": 240, + "height": 210, + "sourceWidth": 320, + "sourceHeight": 180, + "cellHeight": 45, + "mime": "image/png", + "bytes": 44055, + "nonBlack": 3600, + "createdCount": 21, + "revokedCount": 21, + "doneCount": 2, + "updates": [ + 0.1, + 0.2, + 0.3, + 0.4, + 0.5, + 0.6, + 0.7, + 0.8, + 0.9, + 1 + ], + "retiredReadyState": 0, + "retiredSrc": null, + "connected": false + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool candidate-artifact / DOM drop dispatch uses the registered handler while callable ondrop works", + "project": "chromium", + "kind": "actual-tool", + "status": "passed", + "durationMs": 221, + "browser": "153.0.8010.12", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "candidate-artifact", + "sha256": "f1d62e3cee7e060e7ecff4a21e7589e2befb2e405976efcdee73719857d0be03", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool candidate-artifact / corrupt media, replacement URLs and repeated destroy reproduce baseline gaps", + "project": "chromium", + "kind": "actual-tool", + "status": "passed", + "durationMs": 222, + "browser": "153.0.8010.12", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "candidate-artifact", + "sha256": "f1d62e3cee7e060e7ecff4a21e7589e2befb2e405976efcdee73719857d0be03", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-media-failure", + "data": { + "failed": { + "code": 4, + "toolErrors": 1, + "first": "blob:http://127.0.0.1:8084/a8b00a4b-65ee-4894-b9d8-e357094b7d7c" + }, + "result": { + "value": "", + "created": [ + "blob:http://127.0.0.1:8084/a8b00a4b-65ee-4894-b9d8-e357094b7d7c", + "blob:http://127.0.0.1:8084/51d46ae9-391f-4a9a-8cd1-717231e23338" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/a8b00a4b-65ee-4894-b9d8-e357094b7d7c", + "blob:http://127.0.0.1:8084/51d46ae9-391f-4a9a-8cd1-717231e23338" + ] + } + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool candidate-artifact / native PNG callback delivered after destroy is ignored after cancellation", + "project": "chromium", + "kind": "actual-tool", + "status": "passed", + "durationMs": 339, + "browser": "153.0.8010.12", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "candidate-artifact", + "sha256": "f1d62e3cee7e060e7ecff4a21e7589e2befb2e405976efcdee73719857d0be03", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-late-native-blob", + "data": { + "count": 1, + "created": [ + "blob:http://127.0.0.1:8084/ea5fffd1-40bf-4aee-9ce5-1095f94bb910" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/ea5fffd1-40bf-4aee-9ce5-1095f94bb910" + ], + "pendingState": "rejected", + "events": [ + "file", + "video", + "canvas", + "destroy" + ], + "connected": false, + "bytes": 9120 + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool candidate-artifact / source replacement cancels held native encoding and completes only the latest job", + "project": "chromium", + "kind": "actual-tool", + "status": "passed", + "durationMs": 490, + "browser": "153.0.8010.12", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "candidate-artifact", + "sha256": "f1d62e3cee7e060e7ecff4a21e7589e2befb2e405976efcdee73719857d0be03", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-native-source-cancellation", + "data": { + "oldState": "AbortError", + "firstEncodedBytes": 9120, + "source": "blob:http://127.0.0.1:8084/106421c1-e5a1-4f3e-88ac-1d4579d53b7c", + "updateSources": [ + "blob:http://127.0.0.1:8084/106421c1-e5a1-4f3e-88ac-1d4579d53b7c", + "blob:http://127.0.0.1:8084/106421c1-e5a1-4f3e-88ac-1d4579d53b7c", + "blob:http://127.0.0.1:8084/106421c1-e5a1-4f3e-88ac-1d4579d53b7c", + "blob:http://127.0.0.1:8084/106421c1-e5a1-4f3e-88ac-1d4579d53b7c", + "blob:http://127.0.0.1:8084/106421c1-e5a1-4f3e-88ac-1d4579d53b7c", + "blob:http://127.0.0.1:8084/106421c1-e5a1-4f3e-88ac-1d4579d53b7c", + "blob:http://127.0.0.1:8084/106421c1-e5a1-4f3e-88ac-1d4579d53b7c", + "blob:http://127.0.0.1:8084/106421c1-e5a1-4f3e-88ac-1d4579d53b7c", + "blob:http://127.0.0.1:8084/106421c1-e5a1-4f3e-88ac-1d4579d53b7c", + "blob:http://127.0.0.1:8084/106421c1-e5a1-4f3e-88ac-1d4579d53b7c" + ], + "created": [ + "blob:http://127.0.0.1:8084/6f40ee99-0ff4-41d1-998d-6f1c7acef587", + "blob:http://127.0.0.1:8084/106421c1-e5a1-4f3e-88ac-1d4579d53b7c", + "blob:http://127.0.0.1:8084/20f2f951-d007-4ce9-9955-3ef1b1deb857", + "blob:http://127.0.0.1:8084/4a98843f-a179-4494-87c3-d6bacf55ac66", + "blob:http://127.0.0.1:8084/e2cf611d-f47e-4b53-a631-4078679fd2b7", + "blob:http://127.0.0.1:8084/aa8521f6-234e-4aa9-b2ad-3be293b060a9", + "blob:http://127.0.0.1:8084/7a22387d-a075-4c83-8314-a4b3e6098da3", + "blob:http://127.0.0.1:8084/cfef45c8-ed9b-406e-89a2-f36a27907eca", + "blob:http://127.0.0.1:8084/ec7f20ad-8d5b-4798-a095-138124a52fbe", + "blob:http://127.0.0.1:8084/dcc5ef14-3b19-44f7-9aa4-6188af7685a4", + "blob:http://127.0.0.1:8084/301755be-5c1d-41be-a7b6-275ff5048b14", + "blob:http://127.0.0.1:8084/8ddc8a8d-f00d-4e15-bfa5-d62086bfbf7d" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/6f40ee99-0ff4-41d1-998d-6f1c7acef587", + "blob:http://127.0.0.1:8084/20f2f951-d007-4ce9-9955-3ef1b1deb857", + "blob:http://127.0.0.1:8084/4a98843f-a179-4494-87c3-d6bacf55ac66", + "blob:http://127.0.0.1:8084/e2cf611d-f47e-4b53-a631-4078679fd2b7", + "blob:http://127.0.0.1:8084/aa8521f6-234e-4aa9-b2ad-3be293b060a9", + "blob:http://127.0.0.1:8084/7a22387d-a075-4c83-8314-a4b3e6098da3", + "blob:http://127.0.0.1:8084/cfef45c8-ed9b-406e-89a2-f36a27907eca", + "blob:http://127.0.0.1:8084/ec7f20ad-8d5b-4798-a095-138124a52fbe", + "blob:http://127.0.0.1:8084/dcc5ef14-3b19-44f7-9aa4-6188af7685a4", + "blob:http://127.0.0.1:8084/301755be-5c1d-41be-a7b6-275ff5048b14", + "blob:http://127.0.0.1:8084/106421c1-e5a1-4f3e-88ac-1d4579d53b7c", + "blob:http://127.0.0.1:8084/8ddc8a8d-f00d-4e15-bfa5-d62086bfbf7d" + ], + "events": [ + "file", + "video", + "canvas", + "file", + "video", + "canvas", + "update", + "update", + "update", + "update", + "update", + "update", + "update", + "update", + "update", + "update", + "done", + "destroy" + ], + "connected": false + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool candidate-artifact / native file selection, seek extraction, PNG pixels and repeat generation", + "project": "firefox", + "kind": "actual-tool", + "status": "passed", + "durationMs": 2318, + "browser": "155.0", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "candidate-artifact", + "sha256": "f1d62e3cee7e060e7ecff4a21e7589e2befb2e405976efcdee73719857d0be03", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-extraction", + "data": { + "width": 240, + "height": 210, + "sourceWidth": 320, + "sourceHeight": 180, + "cellHeight": 45, + "mime": "image/png", + "bytes": 38895, + "nonBlack": 3600, + "createdCount": 21, + "revokedCount": 21, + "doneCount": 2, + "updates": [ + 0.1, + 0.2, + 0.3, + 0.4, + 0.5, + 0.6, + 0.7, + 0.8, + 0.9, + 1 + ], + "retiredReadyState": 0, + "retiredSrc": null, + "connected": false + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool candidate-artifact / DOM drop dispatch uses the registered handler while callable ondrop works", + "project": "firefox", + "kind": "actual-tool", + "status": "passed", + "durationMs": 455, + "browser": "155.0", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "candidate-artifact", + "sha256": "f1d62e3cee7e060e7ecff4a21e7589e2befb2e405976efcdee73719857d0be03", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool candidate-artifact / corrupt media, replacement URLs and repeated destroy reproduce baseline gaps", + "project": "firefox", + "kind": "actual-tool", + "status": "passed", + "durationMs": 656, + "browser": "155.0", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "candidate-artifact", + "sha256": "f1d62e3cee7e060e7ecff4a21e7589e2befb2e405976efcdee73719857d0be03", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-media-failure", + "data": { + "failed": { + "code": 4, + "toolErrors": 1, + "first": "blob:http://127.0.0.1:8084/9f170137-5ec1-4839-938d-4959cb7d85c5" + }, + "result": { + "value": "", + "created": [ + "blob:http://127.0.0.1:8084/9f170137-5ec1-4839-938d-4959cb7d85c5", + "blob:http://127.0.0.1:8084/b8f87f78-de83-4b3e-83f4-4865d651b18d" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/9f170137-5ec1-4839-938d-4959cb7d85c5", + "blob:http://127.0.0.1:8084/b8f87f78-de83-4b3e-83f4-4865d651b18d" + ] + } + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool candidate-artifact / native PNG callback delivered after destroy is ignored after cancellation", + "project": "firefox", + "kind": "actual-tool", + "status": "passed", + "durationMs": 792, + "browser": "155.0", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "candidate-artifact", + "sha256": "f1d62e3cee7e060e7ecff4a21e7589e2befb2e405976efcdee73719857d0be03", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-late-native-blob", + "data": { + "count": 1, + "created": [ + "blob:http://127.0.0.1:8084/46ba63d9-9520-4199-bb36-a890024ab69b" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/46ba63d9-9520-4199-bb36-a890024ab69b" + ], + "pendingState": "rejected", + "events": [ + "file", + "video", + "canvas", + "destroy" + ], + "connected": false, + "bytes": 6496 + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool candidate-artifact / source replacement cancels held native encoding and completes only the latest job", + "project": "firefox", + "kind": "actual-tool", + "status": "passed", + "durationMs": 1280, + "browser": "155.0", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "candidate-artifact", + "sha256": "f1d62e3cee7e060e7ecff4a21e7589e2befb2e405976efcdee73719857d0be03", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-native-source-cancellation", + "data": { + "oldState": "AbortError", + "firstEncodedBytes": 6496, + "source": "blob:http://127.0.0.1:8084/aa8a196a-d6d5-4981-8fee-ee7cd96e147d", + "updateSources": [ + "blob:http://127.0.0.1:8084/aa8a196a-d6d5-4981-8fee-ee7cd96e147d", + "blob:http://127.0.0.1:8084/aa8a196a-d6d5-4981-8fee-ee7cd96e147d", + "blob:http://127.0.0.1:8084/aa8a196a-d6d5-4981-8fee-ee7cd96e147d", + "blob:http://127.0.0.1:8084/aa8a196a-d6d5-4981-8fee-ee7cd96e147d", + "blob:http://127.0.0.1:8084/aa8a196a-d6d5-4981-8fee-ee7cd96e147d", + "blob:http://127.0.0.1:8084/aa8a196a-d6d5-4981-8fee-ee7cd96e147d", + "blob:http://127.0.0.1:8084/aa8a196a-d6d5-4981-8fee-ee7cd96e147d", + "blob:http://127.0.0.1:8084/aa8a196a-d6d5-4981-8fee-ee7cd96e147d", + "blob:http://127.0.0.1:8084/aa8a196a-d6d5-4981-8fee-ee7cd96e147d", + "blob:http://127.0.0.1:8084/aa8a196a-d6d5-4981-8fee-ee7cd96e147d" + ], + "created": [ + "blob:http://127.0.0.1:8084/262f9f87-4a97-4d22-8305-e21b64c4ad89", + "blob:http://127.0.0.1:8084/aa8a196a-d6d5-4981-8fee-ee7cd96e147d", + "blob:http://127.0.0.1:8084/d374d867-3026-4ae1-aee3-1c2661296058", + "blob:http://127.0.0.1:8084/5a6798c4-f2f2-489f-a8ed-093d18c30c97", + "blob:http://127.0.0.1:8084/8c4b749b-c9d6-457c-8bb5-18ad22384c6a", + "blob:http://127.0.0.1:8084/3b6acdbd-e1e4-49ca-ac79-cc632c5db37f", + "blob:http://127.0.0.1:8084/3bef7cfc-008e-4db5-bf8c-7a08db5bdc3c", + "blob:http://127.0.0.1:8084/207af236-1a8c-4ddf-b3e5-8ae2b646cce2", + "blob:http://127.0.0.1:8084/fb202622-854c-4a80-aafc-ad0b8520c973", + "blob:http://127.0.0.1:8084/c339e4bb-0955-4d7e-b472-2ba86e0b2f99", + "blob:http://127.0.0.1:8084/26f8e001-3096-4dff-900a-9f6bf4bf03b2", + "blob:http://127.0.0.1:8084/52c344b3-834d-4e7d-9f0a-89e8096f8244" + ], + "revoked": [ + "blob:http://127.0.0.1:8084/262f9f87-4a97-4d22-8305-e21b64c4ad89", + "blob:http://127.0.0.1:8084/d374d867-3026-4ae1-aee3-1c2661296058", + "blob:http://127.0.0.1:8084/5a6798c4-f2f2-489f-a8ed-093d18c30c97", + "blob:http://127.0.0.1:8084/8c4b749b-c9d6-457c-8bb5-18ad22384c6a", + "blob:http://127.0.0.1:8084/3b6acdbd-e1e4-49ca-ac79-cc632c5db37f", + "blob:http://127.0.0.1:8084/3bef7cfc-008e-4db5-bf8c-7a08db5bdc3c", + "blob:http://127.0.0.1:8084/207af236-1a8c-4ddf-b3e5-8ae2b646cce2", + "blob:http://127.0.0.1:8084/fb202622-854c-4a80-aafc-ad0b8520c973", + "blob:http://127.0.0.1:8084/c339e4bb-0955-4d7e-b472-2ba86e0b2f99", + "blob:http://127.0.0.1:8084/26f8e001-3096-4dff-900a-9f6bf4bf03b2", + "blob:http://127.0.0.1:8084/aa8a196a-d6d5-4981-8fee-ee7cd96e147d", + "blob:http://127.0.0.1:8084/52c344b3-834d-4e7d-9f0a-89e8096f8244" + ], + "events": [ + "file", + "video", + "canvas", + "file", + "video", + "canvas", + "update", + "update", + "update", + "update", + "update", + "update", + "update", + "update", + "update", + "update", + "done", + "destroy" + ], + "connected": false + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool candidate-artifact / native file selection, seek extraction, PNG pixels and repeat generation", + "project": "webkit", + "kind": "blob-unavailable", + "status": "passed", + "durationMs": 1331, + "browser": "26.6", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "candidate-artifact", + "sha256": "f1d62e3cee7e060e7ecff4a21e7589e2befb2e405976efcdee73719857d0be03", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-blob-unavailable", + "data": { + "native": { + "http": "loaded", + "error": 4, + "blob": "error" + }, + "tool": { + "errors": 1, + "processing": false, + "duration": null + } + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool candidate-artifact / DOM drop dispatch uses the registered handler while callable ondrop works", + "project": "webkit", + "kind": "blob-unavailable", + "status": "passed", + "durationMs": 1317, + "browser": "26.6", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "candidate-artifact", + "sha256": "f1d62e3cee7e060e7ecff4a21e7589e2befb2e405976efcdee73719857d0be03", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-blob-unavailable", + "data": { + "native": { + "http": "loaded", + "error": 4, + "blob": "error" + }, + "tool": { + "errors": 1, + "processing": false, + "duration": null + } + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool candidate-artifact / corrupt media, replacement URLs and repeated destroy reproduce baseline gaps", + "project": "webkit", + "kind": "blob-unavailable", + "status": "passed", + "durationMs": 1306, + "browser": "26.6", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "candidate-artifact", + "sha256": "f1d62e3cee7e060e7ecff4a21e7589e2befb2e405976efcdee73719857d0be03", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-blob-unavailable", + "data": { + "native": { + "http": "loaded", + "error": 4, + "blob": "error" + }, + "tool": { + "errors": 1, + "processing": false, + "duration": null + } + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool candidate-artifact / native PNG callback delivered after destroy is ignored after cancellation", + "project": "webkit", + "kind": "blob-unavailable", + "status": "passed", + "durationMs": 1265, + "browser": "26.6", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "candidate-artifact", + "sha256": "f1d62e3cee7e060e7ecff4a21e7589e2befb2e405976efcdee73719857d0be03", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-blob-unavailable", + "data": { + "native": { + "http": "loaded", + "error": 4, + "blob": "error" + }, + "tool": { + "errors": 1, + "processing": false, + "duration": null + } + } + } + ] + }, + { + "title": "thumbnail-tool.spec.js / Thumbnail tool candidate-artifact / source replacement cancels held native encoding and completes only the latest job", + "project": "webkit", + "kind": "blob-unavailable", + "status": "passed", + "durationMs": 1222, + "browser": "26.6", + "platform": "win32", + "measurements": [ + { + "name": "thumbnail-runtime", + "data": { + "name": "candidate-artifact", + "sha256": "f1d62e3cee7e060e7ecff4a21e7589e2befb2e405976efcdee73719857d0be03", + "originalNpmArchive": false, + "media": "test\\browser\\media\\thumbnail-pattern.mp4" + } + }, + { + "name": "thumbnail-blob-unavailable", + "data": { + "native": { + "http": "loaded", + "error": 4, + "blob": "error" + }, + "tool": { + "errors": 1, + "processing": false, + "duration": null + } + } + } + ] + } + ] + } + ] + }, + "installed": { + "task": "PKG-TOOL-THUMB-04", + "scope": "Frozen Git input fixture and candidate Yarn pack installed outside workspace. The frozen fixture is not an original npm archive; no recovered 3.5.31 complete archive is claimed. Candidate root/legacy runtime, offline frozen install, all files and strict consumers verified.", + "packages": [ + { + "label": "workspace-fixture", + "name": "artplayer-tool-thumbnail", + "archive": "D:\\github\\ArtPlayer\\refactor\\.cache\\thumbnail-package-types-MOwl7S\\workspace-fixture.tgz", + "sha256": "b79f5247929a5e4ff18fbb8ea2226596f9255e7d9335eaf231ab830a288d5fae", + "manifest": { + "name": "artplayer-tool-thumbnail", + "version": "4.4.0", + "description": "Thumbnail tool for ArtPlayer", + "author": "Harvey Zhao ", + "license": "MIT", + "legacy": "dist/artplayer-tool-thumbnail.legacy.js", + "homepage": "https://artplayer.org", + "repository": { + "type": "git", + "url": "git+https://github.com/zhw2590582/ArtPlayer.git" + }, + "bugs": { + "url": "https://github.com/zhw2590582/ArtPlayer/issues" + }, + "keywords": [ + "html5", + "video", + "player" + ], + "exports": { + "import": "./dist/artplayer-tool-thumbnail.esm.js", + "require": "./dist/artplayer-tool-thumbnail.js", + "default": "./dist/artplayer-tool-thumbnail.js" + }, + "module": "dist/artplayer-tool-thumbnail.esm.js", + "types": "types/artplayer-tool-thumbnail.d.ts", + "browserslist": "last 1 Chrome version" + }, + "files": { + "package/README.md": "91477fe542a44713089e384308ae6f311f887cfe63aafe1483b778a37acfa458", + "package/dist/artplayer-tool-thumbnail.js": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "package/dist/artplayer-tool-thumbnail.legacy.js": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "package/dist/artplayer-tool-thumbnail.mjs": "4d46df8cccdc92d18efc6f12abedf85e8881737023f4ead28697c8b1e85eead1", + "package/package.json": "9f4f9c6628ee48068f380bc52f4058c1f6aac31c20de6ed069a2c96ba635e23f", + "package/src/emitter.js": "5aa71e4317037c06b94e259beed2e964bbbb7117edbe78d29cc941c4916fd2ff", + "package/src/index.js": "67bc1b6a58faf11b14c8d410ae829edd64b84dc9e7b4b388b2e2dd60788bcbe4", + "package/src/utils.js": "dc19fe2f338df7058084eb2301d37db59ec4c914a85ba819a856d270c1267105" + } + }, + { + "label": "candidate", + "name": "artplayer-tool-thumbnail", + "archive": "D:\\github\\ArtPlayer\\refactor\\.cache\\thumbnail-package-types-MOwl7S\\candidate.tgz", + "sha256": "65804615ed2fd8a8f855116e89592ad27b005288d2f3b19b51ffce4452fc462c", + "manifest": { + "name": "artplayer-tool-thumbnail", + "version": "4.4.0", + "description": "Thumbnail tool for ArtPlayer", + "author": "Harvey Zhao ", + "license": "MIT", + "legacy": "dist/artplayer-tool-thumbnail.legacy.js", + "homepage": "https://artplayer.org", + "repository": { + "type": "git", + "url": "git+https://github.com/zhw2590582/ArtPlayer.git" + }, + "bugs": { + "url": "https://github.com/zhw2590582/ArtPlayer/issues" + }, + "keywords": [ + "html5", + "video", + "player" + ], + "exports": { + ".": { + "import": { + "types": "./types/artplayer-tool-thumbnail.d.mts", + "default": "./dist/artplayer-tool-thumbnail.mjs" + }, + "require": { + "types": "./types/artplayer-tool-thumbnail.d.cts", + "default": "./dist/artplayer-tool-thumbnail.js" + }, + "default": "./dist/artplayer-tool-thumbnail.js" + }, + "./legacy": { + "types": "./types/artplayer-tool-thumbnail.d.cts", + "import": "./dist/artplayer-tool-thumbnail.legacy.js", + "require": "./dist/artplayer-tool-thumbnail.legacy.js", + "default": "./dist/artplayer-tool-thumbnail.legacy.js" + } + }, + "main": "./dist/artplayer-tool-thumbnail.js", + "module": "./dist/artplayer-tool-thumbnail.mjs", + "types": "./types/artplayer-tool-thumbnail.d.ts", + "browserslist": "last 1 Chrome version", + "typesVersions": { + "*": { + "legacy": [ + "types/artplayer-tool-thumbnail.d.ts" + ] + } + }, + "files": [ + "ARCHITECTURE.md", + "README.md", + "THIRD_PARTY_NOTICES", + "dist", + "types" + ] + }, + "files": { + "package/ARCHITECTURE.md": "de99b1ed090c0de4c496aa97dbcdd312e3333b57b41ee5f6cef1c02fea206a15", + "package/README.md": "24ed417415653307342281e31f6bf507c5d27f1cd8ab0be81af11ca50e1f3950", + "package/THIRD_PARTY_NOTICES": "7bea0832808c3d37b5a79f2ed6c65ef2ebdf7dbcbba124c469ebb90e2ec7c013", + "package/dist/artplayer-tool-thumbnail.js": "ef72d277e817c2f7312c10815ee91a74dbb9334285476d14fae7f4449b3d2758", + "package/dist/artplayer-tool-thumbnail.legacy.js": "f1d62e3cee7e060e7ecff4a21e7589e2befb2e405976efcdee73719857d0be03", + "package/dist/artplayer-tool-thumbnail.mjs": "abdbc7c28bcccc1a8dee543f1bd784f850c147856fc148c3bf4b8ea863f3d6ea", + "package/package.json": "dafbe13cddee908b2932ab2f529b0eef86ad9d9de8f7ab7585ed073c221cdbe1", + "package/types/README.md": "5a6071256c2d810ca77d6022bf81479ec3d48666977fa0706444f9a514834215", + "package/types/artplayer-tool-thumbnail.d.cts": "f0573326178b57a6e22a08b53247eb15ef4d20a1652e40d0cae638ac7aaf8092", + "package/types/artplayer-tool-thumbnail.d.mts": "e96e0123bff89e109a00c1c169da99741c8bf66d3f4fd53b8e8ecf7c3f440533", + "package/types/artplayer-tool-thumbnail.d.ts": "ec2a39f3e2ac5ef1717d4da56546486c614a9bb6291c14c05723c1990546e79f" + } + } + ], + "matrix": [ + { + "package": "workspace-fixture", + "compiler": "5.9.3", + "mode": "node10-commonjs", + "diagnostics": [ + { + "code": 2307, + "message": "Cannot find module 'artplayer-tool-thumbnail' or its corresponding type declarations." + } + ], + "negative": [] + }, + { + "package": "workspace-fixture", + "compiler": "5.9.3", + "mode": "nodenext-cjs", + "diagnostics": [ + { + "code": 7016, + "message": "Could not find a declaration file for module 'artplayer-tool-thumbnail'. '/node_modules/artplayer-tool-thumbnail/dist/artplayer-tool-thumbnail.js' implicitly has an 'any' type.\n Try `npm i --save-dev @types/artplayer-tool-thumbnail` if it exists or add a new declaration (.d.ts) file containing `declare module 'artplayer-tool-thumbnail';`" + } + ], + "negative": [] + }, + { + "package": "workspace-fixture", + "compiler": "5.9.3", + "mode": "nodenext-esm", + "diagnostics": [ + { + "code": 7016, + "message": "Could not find a declaration file for module 'artplayer-tool-thumbnail'. '/node_modules/artplayer-tool-thumbnail/dist/artplayer-tool-thumbnail.js' implicitly has an 'any' type.\n Try `npm i --save-dev @types/artplayer-tool-thumbnail` if it exists or add a new declaration (.d.ts) file containing `declare module 'artplayer-tool-thumbnail';`" + } + ], + "negative": [] + }, + { + "package": "workspace-fixture", + "compiler": "5.9.3", + "mode": "bundler-esm", + "diagnostics": [ + { + "code": 7016, + "message": "Could not find a declaration file for module 'artplayer-tool-thumbnail'. '/node_modules/artplayer-tool-thumbnail/dist/artplayer-tool-thumbnail.js' implicitly has an 'any' type.\n Try `npm i --save-dev @types/artplayer-tool-thumbnail` if it exists or add a new declaration (.d.ts) file containing `declare module 'artplayer-tool-thumbnail';`" + } + ], + "negative": [] + }, + { + "package": "workspace-fixture", + "compiler": "4.3.5", + "mode": "node10-commonjs", + "diagnostics": [ + { + "code": 2307, + "message": "Cannot find module 'artplayer-tool-thumbnail' or its corresponding type declarations." + } + ], + "negative": [] + }, + { + "package": "candidate", + "compiler": "5.9.3", + "mode": "node10-commonjs", + "diagnostics": [], + "negative": [ + { + "code": 2322, + "message": "Type 'string' is not assignable to type 'number'." + }, + { + "code": 2322, + "message": "Type 'string' is not assignable to type 'Element'." + }, + { + "code": 2345, + "message": "Argument of type 'string' is not assignable to parameter of type 'File'." + }, + { + "code": 2345, + "message": "Argument of type 'number' is not assignable to parameter of type 'string'." + }, + { + "code": 2345, + "message": "Argument of type '(value: HTMLVideoElement) => number' is not assignable to parameter of type '(this: unknown, file: File) => unknown'.\n Types of parameters 'value' and 'file' are incompatible.\n Type 'File' is missing the following properties from type 'HTMLVideoElement': disablePictureInPicture, height, onenterpictureinpicture, onleavepictureinpicture, and 376 more." + }, + { + "code": 2322, + "message": "Type 'Promise' is not assignable to type 'Promise'.\n Type 'void' is not assignable to type 'string'." + }, + { + "code": 2551, + "message": "Property 'default' does not exist on type 'typeof ArtplayerToolThumbnail'. Did you mean 'DEFAULTS'?" + }, + { + "code": 2322, + "message": "Type 'number | undefined' is not assignable to type 'number'.\n Type 'undefined' is not assignable to type 'number'." + }, + { + "code": 2345, + "message": "Argument of type '(message: string) => number' is not assignable to parameter of type '(this: unknown, message: unknown) => unknown'.\n Types of parameters 'message' and 'message' are incompatible.\n Type 'unknown' is not assignable to type 'string'." + }, + { + "code": 2554, + "message": "Expected 1 arguments, but got 2." + } + ] + }, + { + "package": "candidate", + "compiler": "5.9.3", + "mode": "nodenext-cjs", + "diagnostics": [], + "negative": [ + { + "code": 2322, + "message": "Type 'string' is not assignable to type 'number'." + }, + { + "code": 2322, + "message": "Type 'string' is not assignable to type 'Element'." + }, + { + "code": 2345, + "message": "Argument of type 'string' is not assignable to parameter of type 'File'." + }, + { + "code": 2345, + "message": "Argument of type 'number' is not assignable to parameter of type 'string'." + }, + { + "code": 2345, + "message": "Argument of type '(value: HTMLVideoElement) => number' is not assignable to parameter of type '(this: unknown, file: File) => unknown'.\n Types of parameters 'value' and 'file' are incompatible.\n Type 'File' is missing the following properties from type 'HTMLVideoElement': disablePictureInPicture, height, onenterpictureinpicture, onleavepictureinpicture, and 376 more." + }, + { + "code": 2322, + "message": "Type 'Promise' is not assignable to type 'Promise'.\n Type 'void' is not assignable to type 'string'." + }, + { + "code": 2551, + "message": "Property 'default' does not exist on type 'typeof ArtplayerToolThumbnail'. Did you mean 'DEFAULTS'?" + }, + { + "code": 2322, + "message": "Type 'number | undefined' is not assignable to type 'number'.\n Type 'undefined' is not assignable to type 'number'." + }, + { + "code": 2345, + "message": "Argument of type '(message: string) => number' is not assignable to parameter of type '(this: unknown, message: unknown) => unknown'.\n Types of parameters 'message' and 'message' are incompatible.\n Type 'unknown' is not assignable to type 'string'." + }, + { + "code": 2554, + "message": "Expected 1 arguments, but got 2." + } + ] + }, + { + "package": "candidate", + "compiler": "5.9.3", + "mode": "nodenext-esm", + "diagnostics": [], + "negative": [ + { + "code": 2322, + "message": "Type 'string' is not assignable to type 'number'." + }, + { + "code": 2322, + "message": "Type 'string' is not assignable to type 'Element'." + }, + { + "code": 2345, + "message": "Argument of type 'string' is not assignable to parameter of type 'File'." + }, + { + "code": 2345, + "message": "Argument of type 'number' is not assignable to parameter of type 'string'." + }, + { + "code": 2345, + "message": "Argument of type '(value: HTMLVideoElement) => number' is not assignable to parameter of type '(this: unknown, file: File) => unknown'.\n Types of parameters 'value' and 'file' are incompatible.\n Type 'File' is missing the following properties from type 'HTMLVideoElement': disablePictureInPicture, height, onenterpictureinpicture, onleavepictureinpicture, and 376 more." + }, + { + "code": 2322, + "message": "Type 'Promise' is not assignable to type 'Promise'.\n Type 'void' is not assignable to type 'string'." + }, + { + "code": 2551, + "message": "Property 'default' does not exist on type 'typeof ArtplayerToolThumbnail'. Did you mean 'DEFAULTS'?" + }, + { + "code": 2322, + "message": "Type 'number | undefined' is not assignable to type 'number'.\n Type 'undefined' is not assignable to type 'number'." + }, + { + "code": 2345, + "message": "Argument of type '(message: string) => number' is not assignable to parameter of type '(this: unknown, message: unknown) => unknown'.\n Types of parameters 'message' and 'message' are incompatible.\n Type 'unknown' is not assignable to type 'string'." + }, + { + "code": 2554, + "message": "Expected 1 arguments, but got 2." + } + ] + }, + { + "package": "candidate", + "compiler": "5.9.3", + "mode": "bundler-esm", + "diagnostics": [], + "negative": [ + { + "code": 2322, + "message": "Type 'string' is not assignable to type 'number'." + }, + { + "code": 2322, + "message": "Type 'string' is not assignable to type 'Element'." + }, + { + "code": 2345, + "message": "Argument of type 'string' is not assignable to parameter of type 'File'." + }, + { + "code": 2345, + "message": "Argument of type 'number' is not assignable to parameter of type 'string'." + }, + { + "code": 2345, + "message": "Argument of type '(value: HTMLVideoElement) => number' is not assignable to parameter of type '(this: unknown, file: File) => unknown'.\n Types of parameters 'value' and 'file' are incompatible.\n Type 'File' is missing the following properties from type 'HTMLVideoElement': disablePictureInPicture, height, onenterpictureinpicture, onleavepictureinpicture, and 376 more." + }, + { + "code": 2322, + "message": "Type 'Promise' is not assignable to type 'Promise'.\n Type 'void' is not assignable to type 'string'." + }, + { + "code": 2551, + "message": "Property 'default' does not exist on type 'typeof ArtplayerToolThumbnail'. Did you mean 'DEFAULTS'?" + }, + { + "code": 2322, + "message": "Type 'number | undefined' is not assignable to type 'number'.\n Type 'undefined' is not assignable to type 'number'." + }, + { + "code": 2345, + "message": "Argument of type '(message: string) => number' is not assignable to parameter of type '(this: unknown, message: unknown) => unknown'.\n Types of parameters 'message' and 'message' are incompatible.\n Type 'unknown' is not assignable to type 'string'." + }, + { + "code": 2554, + "message": "Expected 1 arguments, but got 2." + } + ] + }, + { + "package": "candidate", + "compiler": "4.3.5", + "mode": "node10-commonjs", + "diagnostics": [], + "negative": [ + { + "code": 2322, + "message": "Type 'string' is not assignable to type 'number | undefined'." + }, + { + "code": 2322, + "message": "Type 'string' is not assignable to type 'Element | undefined'." + }, + { + "code": 2345, + "message": "Argument of type '\"video.mp4\"' is not assignable to parameter of type 'File | null | undefined'." + }, + { + "code": 2345, + "message": "Argument of type 'number' is not assignable to parameter of type 'string'." + }, + { + "code": 2345, + "message": "Argument of type '(value: HTMLVideoElement) => number' is not assignable to parameter of type '(this: unknown, file: File) => unknown'.\n Types of parameters 'value' and 'file' are incompatible.\n Type 'File' is missing the following properties from type 'HTMLVideoElement': height, playsInline, poster, videoHeight, and 285 more." + }, + { + "code": 2322, + "message": "Type 'Promise' is not assignable to type 'Promise'.\n Type 'void' is not assignable to type 'string'." + }, + { + "code": 2551, + "message": "Property 'default' does not exist on type 'typeof ArtplayerToolThumbnail'. Did you mean 'DEFAULTS'?" + }, + { + "code": 2322, + "message": "Type 'number | undefined' is not assignable to type 'number'.\n Type 'undefined' is not assignable to type 'number'." + }, + { + "code": 2345, + "message": "Argument of type '(message: string) => number' is not assignable to parameter of type '(this: unknown, message: unknown) => unknown'.\n Types of parameters 'message' and 'message' are incompatible.\n Type 'unknown' is not assignable to type 'string'." + }, + { + "code": 2554, + "message": "Expected 1 arguments, but got 2." + } + ] + }, + { + "package": "candidate", + "compiler": "5.9.3", + "mode": "nodenext-cjs-no-interop", + "diagnostics": [], + "negative": [ + { + "code": 2551, + "message": "Property 'default' does not exist on type 'typeof ArtplayerToolThumbnail'. Did you mean 'DEFAULTS'?" + } + ] + }, + { + "package": "candidate", + "compiler": "4.3.5", + "mode": "node10-commonjs-no-interop", + "diagnostics": [], + "negative": [ + { + "code": 2551, + "message": "Property 'default' does not exist on type 'typeof ArtplayerToolThumbnail'. Did you mean 'DEFAULTS'?" + } + ] + } + ], + "runtime": [ + { + "package": "workspace-fixture", + "commonjs": "function", + "esm": "ERR_MODULE_NOT_FOUND", + "typesMissing": true + }, + { + "package": "candidate", + "commonjs": "function", + "esm": "function", + "legacy": "function", + "ssrImport": true + } + ] + }, + "files": [ + { + "file": "package.json", + "sha256": "6ec4a99ba9b3e65fafa1e5839501934888e62e907d0e3ffefac432f19b5f5e5a" + }, + { + "file": "packages/artplayer-tool-thumbnail/package.json", + "sha256": "dafbe13cddee908b2932ab2f529b0eef86ad9d9de8f7ab7585ed073c221cdbe1" + }, + { + "file": "packages/artplayer-tool-thumbnail/ARCHITECTURE.md", + "sha256": "de99b1ed090c0de4c496aa97dbcdd312e3333b57b41ee5f6cef1c02fea206a15" + }, + { + "file": "packages/artplayer-tool-thumbnail/src/emitter.ts", + "sha256": "a083e029dd1104912d3e9a0ba23a6dcc160d9ccde6a55be40db74367a647a3cb" + }, + { + "file": "packages/artplayer-tool-thumbnail/src/extraction.ts", + "sha256": "9464d34523291be3c46babd8753dbb7e8f881c5956adf4831f3c8a60d969362c" + }, + { + "file": "packages/artplayer-tool-thumbnail/src/index.ts", + "sha256": "24713364030cfca8201bb564794b612047a517cb9b233889b71bd8b9e14c49a9" + }, + { + "file": "packages/artplayer-tool-thumbnail/src/input.ts", + "sha256": "cafbef270a2c4edbbf2ac3814a9fccf95d805b7158ffd25a42ebc211bf1834ce" + }, + { + "file": "packages/artplayer-tool-thumbnail/src/lifecycle.ts", + "sha256": "3c30d5ad20d89abe80a39abb95270e340c47534e11065047ab191bd5fbecec4a" + }, + { + "file": "packages/artplayer-tool-thumbnail/src/sheet.ts", + "sha256": "af423e53fbd6aec2c54ae74410cf84f99eb8c125605b064aaf6fa1f0bb8f0d45" + }, + { + "file": "packages/artplayer-tool-thumbnail/src/source.ts", + "sha256": "e10348712f5edbdfe1012a527f7c6af83f44f9a7e1dee69a6353c4c095458e54" + }, + { + "file": "packages/artplayer-tool-thumbnail/src/types.ts", + "sha256": "15d97bf41d87c4ccb7cf290e663b73390ed5bf40196adfda9bee1faa1b082196" + }, + { + "file": "packages/artplayer-tool-thumbnail/src/utils.ts", + "sha256": "90d2e1195f7de6c4a5930172baf23b95c2de1cc729375b0d29e30fe43e4dfd13" + }, + { + "file": "packages/artplayer-tool-thumbnail/dist/artplayer-tool-thumbnail.js", + "sha256": "ef72d277e817c2f7312c10815ee91a74dbb9334285476d14fae7f4449b3d2758" + }, + { + "file": "docs/compiled/artplayer-tool-thumbnail.js", + "sha256": "ef72d277e817c2f7312c10815ee91a74dbb9334285476d14fae7f4449b3d2758" + }, + { + "file": "packages/artplayer-tool-thumbnail/dist/artplayer-tool-thumbnail.legacy.js", + "sha256": "f1d62e3cee7e060e7ecff4a21e7589e2befb2e405976efcdee73719857d0be03" + }, + { + "file": "docs/compiled/artplayer-tool-thumbnail.legacy.js", + "sha256": "f1d62e3cee7e060e7ecff4a21e7589e2befb2e405976efcdee73719857d0be03" + }, + { + "file": "packages/artplayer-tool-thumbnail/dist/artplayer-tool-thumbnail.mjs", + "sha256": "abdbc7c28bcccc1a8dee543f1bd784f850c147856fc148c3bf4b8ea863f3d6ea" + }, + { + "file": "docs/compiled/artplayer-tool-thumbnail.mjs", + "sha256": "abdbc7c28bcccc1a8dee543f1bd784f850c147856fc148c3bf4b8ea863f3d6ea" + }, + { + "file": "test/thumbnail-emitter.test.js", + "sha256": "16d33bac016319ce1796c33e2959093521499d1e69328a2ce25a5a3d0ffd5e70" + }, + { + "file": "test/thumbnail-runtime.test.js", + "sha256": "b8541ad8073e247becb3b90cb7eab517785dea5e1bbf5c06132796f8148c6a96" + }, + { + "file": "test/thumbnail-lifecycle.test.js", + "sha256": "decc5f06f343412815f06d30fff5263580ff106eb266eda7c73fb3eec3dc4403" + }, + { + "file": "test/thumbnail-input.test.js", + "sha256": "08da28330bcd4ef83bddcea3aa10a95ad9ef075bdf3bb7daee734c0807f4b51a" + }, + { + "file": "test/thumbnail-vendor.test.js", + "sha256": "b9dc2ea786aa89f334d0f06efc708bcee817c8449ef2f4af84c5ff525e59ed18" + }, + { + "file": "test/browser/thumbnail-tool.spec.js", + "sha256": "04f322adf9f505f8705253f9225cc92f3bfa8bb1ba9495926a23811b39277089" + }, + { + "file": "test/browser/thumbnail-native.spec.js", + "sha256": "50d2724c4903c2281cf7a5fa8577e4a8174742ac30b48cd3c679eaecc2d27f3e" + }, + { + "file": "test/browser/thumbnail-input.spec.js", + "sha256": "df82785958860b550552507dde59a334d13d916985c0ac85673e210eeaaf3a38" + }, + { + "file": "test/browser/thumbnail-editor-types.spec.js", + "sha256": "12c944134e503f46a773a5c97daa6995a7ec365d0a9185a1b9c830c63bdd8d8c" + }, + { + "file": "test/browser/thumbnail-emitter.spec.js", + "sha256": "05ae55b9588afba97555e9870ebc42ab7b42eff71b0f15cb1f410b6c2f16d416" + } + ], + "logs": [ + { + "file": "refactor/.cache/thumbnail04-emitter-before.log", + "sha256": "166414f4ddc1e768a911b4fdbce78bbce9f027ddc89b7e7fff6b8d8365af72df" + }, + { + "file": "refactor/.cache/thumbnail04-emitter-source.log", + "sha256": "9bca27c09586bd4c444ab3f18cd901acdea1d78a2cd97ec06fb4281eff452868" + }, + { + "file": "refactor/.cache/thumbnail04-emitter-main.log", + "sha256": "bd98637eb86424bbcd15ca8f3bf98ddbe822e1997ba7f801c575533fdaed0c73" + }, + { + "file": "refactor/.cache/thumbnail04-emitter-legacy.log", + "sha256": "4d69e90fdabc9fd827b869a0e8ac2e84bd8cb5d45d9e4553d5326edbdb2ae878" + }, + { + "file": "refactor/.cache/thumbnail04-emitter-build.log", + "sha256": "fae5621cad26fc88ed89a803ee1841ff4384429dd5a52ce15dcd36a3bf9276c9" + }, + { + "file": "refactor/.cache/thumbnail04-emitter-ci.log", + "sha256": "83f8724f2b7aa3287dd9db8bad34b515b2a699eac96e7e891071c24b56ffe517" + }, + { + "file": "refactor/.cache/thumbnail04-emitter-installed.log", + "sha256": "d1f6a4a4585b22c9e703f1fa4c03b5fb3e9bde61ac8a3578158449f41068e161" + }, + { + "file": "refactor/.cache/thumbnail04-emitter-imports.log", + "sha256": "7d739ef8c2a732977a76b56c24fff43caf568c11fd6a123827ad140c8effe601" + }, + { + "file": "refactor/.cache/thumbnail04-emitter-browser-source.log", + "sha256": "6214cc235f64358f0a77832732df3867484af920398831742b6ddbfbcca8955e" + }, + { + "file": "refactor/.cache/thumbnail04-emitter-browser-source.json", + "sha256": "b1a7d2622cd93ce92ae8a0097d3f32d2d239463498eeda8a267dec9eae854cc6" + }, + { + "file": "refactor/.cache/thumbnail04-emitter-browser-main.log", + "sha256": "f814dc703e5a07ee4737bd1265f10d86a55e2f7c167c16c94cc9c93415efe6a1" + }, + { + "file": "refactor/.cache/thumbnail04-emitter-browser-main.json", + "sha256": "753f25de8715af64c3e3f403ebb3c9e9af284ee4a35296fd356499b7c8bb1531" + }, + { + "file": "refactor/.cache/thumbnail04-emitter-browser-legacy.log", + "sha256": "2850dbbc9b2c666e03734cda1788c50775fd5293141f8c3b50762901c9aba129" + }, + { + "file": "refactor/.cache/thumbnail04-emitter-browser-legacy.json", + "sha256": "7142e8133980a9dd91b18ad31c0f675c2c0b04912a4ea4123e95dd4819a4ffda" + } + ], + "limitations": [ + "Default 3.5.31/4.4.0 policy question remains pending and runtime defaults unchanged.", + "Each run includes 17 Windows WebKit Blob-unavailable controls, not extraction success.", + "Original npm archive/revision and historical CSS entry limitations remain documented; full demo/core and physical Safari integration remain 05/06.", + "No new coverage percentage, hosted CI, version bump, push, tag or npm publication." + ] +} diff --git a/refactor/changes/2026-09-13-PKG-TOOL-THUMB-04-emitter.md b/refactor/changes/2026-09-13-PKG-TOOL-THUMB-04-emitter.md new file mode 100644 index 000000000..a15dfc5fa --- /dev/null +++ b/refactor/changes/2026-09-13-PKG-TOOL-THUMB-04-emitter.md @@ -0,0 +1,39 @@ +# PKG-TOOL-THUMB-04 emitter behavior checkpoint + +Starts from 3bff236145805ecae46ce60ff5c5710189560755. This closes reproduced event +registry defects independently of the pending 3.5.31/4.4.0 default-policy choice. +04 remains doing; no default options, extraction timing or public types change. + +## Before and after + +The inherited tiny-emitter structure treats Object.prototype members as event +arrays. emit/on/off can fail on names such as __proto__, constructor and toString; +inherited getters/setters can intercept a replaced public e registry. Nested emits +take separate snapshots, allowing one once wrapper to run in an inner dispatch +and then again when the outer snapshot resumes, even after the inner throw was +caught. Re-registering its original callback does not make that duplicate correct. + +The candidate checks own keys and creates new entries with data descriptors, +retaining the registry object/prototype and ordinary descriptor flags. Each once +closure is consumed before removal/callback invocation. Normal listener order, +snapshot behavior, callback ctx, symbols/numbers, duplicate independent +registrations, original-function removal and synchronous exception identity remain. +The design matches the already-tested core approach, without adding a runtime +dependency on the core or erasing this tool's declaration-specific tuple boundary. + +Six new Node cases pass 1/fail 5 on the frozen workspace. Candidate source and +artifacts repeat them together with the unchanged input/extraction/facade tests. +The new real-browser case verifies special names, nested once and native DOM +cleanup. Existing extraction, native media controls and Monaco cases are rerun +for source/main/legacy because the runtime bundle has changed. Windows WebKit +Blob failures remain explicit unavailable controls, not successful extraction. +Installed package checks repeat after the final package documentation/build. + +The upstream attribution remains in every bundle and pack. The provenance ledger +records these intentional local fixes separately from the previous type-only +adaptation. THUMB-EVENT-01 tracks closure evidence; default-policy, historical CSS +and actual supported Safari/core integration gates are unaffected. + +Revert this checkpoint to roll back only the emitter fixes and their records; +run the normal package build and installed checks again. Keep earlier lifecycle, +public declaration and distribution fixes. No push, tag or publication. diff --git a/refactor/plan.md b/refactor/plan.md index c6c70000b..5be1d6978 100644 --- a/refactor/plan.md +++ b/refactor/plan.md @@ -538,5 +538,5 @@ - PKG-TOOL-THUMB-01: [记录](baselines/thumbnail-release.json) [记录](baselines/thumbnail-contract.md) [记录](changes/2026-09-13-PKG-TOOL-THUMB-01-contract.md) [记录](baselines/thumbnail-contract-validation.json) - PKG-TOOL-THUMB-02: [记录](baselines/thumbnail-behavior-validation.json) [记录](changes/2026-09-13-PKG-TOOL-THUMB-02-behavior.md) - PKG-TOOL-THUMB-03: [记录](changes/2026-09-13-PKG-TOOL-THUMB-03-input-checkpoint.md) [记录](baselines/thumbnail-input-checkpoint.json) [记录](changes/2026-09-13-PKG-TOOL-THUMB-03-lifecycle.md) [记录](baselines/thumbnail-lifecycle-validation.json) -- PKG-TOOL-THUMB-04: [记录](changes/2026-09-13-PKG-TOOL-THUMB-04-runtime-types.md) [记录](baselines/thumbnail-runtime-types-validation.json) [记录](changes/2026-09-13-PKG-TOOL-THUMB-04-public-types.md) [记录](baselines/thumbnail-public-types-validation.json) +- PKG-TOOL-THUMB-04: [记录](changes/2026-09-13-PKG-TOOL-THUMB-04-runtime-types.md) [记录](baselines/thumbnail-runtime-types-validation.json) [记录](changes/2026-09-13-PKG-TOOL-THUMB-04-public-types.md) [记录](baselines/thumbnail-public-types-validation.json) [记录](changes/2026-09-13-PKG-TOOL-THUMB-04-emitter.md) [记录](baselines/thumbnail-emitter-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) diff --git a/refactor/progress.md b/refactor/progress.md index 085f36fba..690ca3274 100644 --- a/refactor/progress.md +++ b/refactor/progress.md @@ -1,5 +1,17 @@ # 进度与证据 +## PKG-TOOL-THUMB-04 事件注册表修复检查点(doing) + +复现并修复特殊事件名碰撞对象原型、继承getter/setter干扰,以及嵌套emit导致once重复执行。 +保留普通派发快照/ctx/off原回调/独立重复注册语义,使用own key/data descriptor及每次 +注册独立的once消费标记。新6项旧版1通过/5失败;候选源码53、main/legacy各55通过。 +重新构建并重跑三份真实浏览器各63项,共189项:每份34工具、3输入、17Blob不可用、 +3原生对照、3Monaco、3事件注册表。没有沿用运行文件改变前的媒体证据。 +完整CI1664、339生产TS、3导入及实际安装7种模式/52项类型反例通过,关闭THUMB-EVENT-01。 +默认行为策略问题继续等待用户选择;04不标完成。见[变更](changes/2026-09-13-PKG-TOOL-THUMB-04-emitter.md) +与[验证](baselines/thumbnail-emitter-validation.json)。222项:112 done、11 doing、99 todo。 + + ## PKG-TOOL-THUMB-04 公开类型与真实安装检查点(doing) 补齐class/namespace及d.cts/d.mts,修复root ESM并保留直接CJS构造器,新增legacy类型路径。 diff --git a/refactor/risk-table.md b/refactor/risk-table.md index cd30b3b77..1e931b706 100644 --- a/refactor/risk-table.md +++ b/refactor/risk-table.md @@ -182,3 +182,4 @@ | THUMB-PROVENANCE-01 | resolved / 源码/产物事实 | Thumbnail local emitter attribution and historical distribution completeness remain unverified | PKG-TOOL-THUMB-04, PKG-TOOL-THUMB-06 | | THUMB-MEDIA-01 | open / 已复现 | Windows WebKit native Blob URLs cannot load tested MP4 media despite successful HTTP controls | PKG-TOOL-THUMB-05, PKG-TOOL-THUMB-06 | | VENDOR-09 | resolved / 源码/产物事实 | Thumbnail tiny-emitter 改写来源与最终分发许可 | PKG-TOOL-THUMB-04, PKG-TOOL-THUMB-06 | +| THUMB-EVENT-01 | resolved / 已复现 | Thumbnail 原型事件名与嵌套 once 派发缺陷 | PKG-TOOL-THUMB-04 | diff --git a/refactor/risks.json b/refactor/risks.json index c6e55f724..3b32a5fdd 100644 --- a/refactor/risks.json +++ b/refactor/risks.json @@ -3919,12 +3919,14 @@ "refactor/baselines/thumbnail-contract.md", "refactor/baselines/thumbnail-release.json", "refactor/scripts/thumbnail-contract.test.mjs", - "refactor/baselines/thumbnail-public-types-validation.json" + "refactor/baselines/thumbnail-public-types-validation.json", + "refactor/baselines/thumbnail-emitter-validation.json" ], "resolutionEvidence": [ "refactor/baselines/thumbnail-public-types-validation.json", "refactor/baselines/thumbnail-vendor/sources.json", - "packages/artplayer-tool-thumbnail/THIRD_PARTY_NOTICES" + "packages/artplayer-tool-thumbnail/THIRD_PARTY_NOTICES", + "refactor/baselines/thumbnail-emitter-validation.json" ], "resolutionRationale": "Fixed 2.1.0 upstream comparison and local adaptation reviewed; complete MIT notice verified in every standalone bundle and every byte of the actual installed candidate pack. Exact original copied revision and complete historical npm archive remain unknown and are explicitly bounded, not claimed recovered. Historical CSS/paths remain independently open under BASE-DIST-01.", "workspaceState": "Current candidate source/bundle/packed attribution verified; repeat installed artifact verification when release contents change." @@ -3961,7 +3963,8 @@ "packages/artplayer-tool-thumbnail/src/emitter.ts", "packages/artplayer-tool-thumbnail/THIRD_PARTY_NOTICES", "test/thumbnail-vendor.test.js", - "refactor/baselines/thumbnail-public-types-validation.json" + "refactor/baselines/thumbnail-public-types-validation.json", + "refactor/baselines/thumbnail-emitter-validation.json" ], "compatibleResolution": "Preserve original attribution, typed local dispatch compatibility and complete MIT notice in source/package/standalone bundles.", "closureCriteria": "Pinned upstream reference and local adaptation reviewed; all bundle and actual packed distribution notices verified. Do not claim the exact original copied revision or a recovered complete historical archive.", @@ -3969,9 +3972,32 @@ "resolutionEvidence": [ "refactor/baselines/thumbnail-public-types-validation.json", "refactor/baselines/thumbnail-vendor/sources.json", - "packages/artplayer-tool-thumbnail/THIRD_PARTY_NOTICES" + "packages/artplayer-tool-thumbnail/THIRD_PARTY_NOTICES", + "refactor/baselines/thumbnail-emitter-validation.json" ], "resolutionRationale": "Fixed 2.1.0 upstream comparison and local adaptation reviewed; complete MIT notice verified in every standalone bundle and every byte of the actual installed candidate pack. Exact original copied revision and complete historical npm archive remain unknown and are explicitly bounded, not claimed recovered. Historical CSS/paths remain independently open under BASE-DIST-01." + }, + { + "id": "THUMB-EVENT-01", + "title": "Thumbnail 原型事件名与嵌套 once 派发缺陷", + "confirmation": "reproduced", + "status": "resolved", + "owners": [ + "PKG-TOOL-THUMB-04" + ], + "evidence": [ + "test/thumbnail-emitter.test.js", + "refactor/changes/2026-09-13-PKG-TOOL-THUMB-04-emitter.md", + "refactor/baselines/thumbnail-emitter-validation.json" + ], + "compatibleResolution": "Use own event keys and preserve registry descriptors/prototype; consume each once registration before removal and invocation across nested snapshots.", + "closureCriteria": "Frozen failure/candidate success, ordinary event and lifecycle parity, all runtime formats and actual browser plus installed candidate evidence.", + "resolutionEvidence": [ + "refactor/baselines/thumbnail-emitter-validation.json", + "test/thumbnail-emitter.test.js", + "test/browser/thumbnail-emitter.spec.js" + ], + "resolutionRationale": "Frozen workspace reproduces five failures. Own-key/data-descriptor registry and once-per-registration consumption pass source/main/legacy, actual browser and installed candidate checks while ordinary snapshot/ctx/off/duplicate behavior remains tested." } ] } diff --git a/refactor/tasks.json b/refactor/tasks.json index 5687ec4a7..b8d6c205a 100644 --- a/refactor/tasks.json +++ b/refactor/tasks.json @@ -3817,7 +3817,9 @@ "changes/2026-09-13-PKG-TOOL-THUMB-04-runtime-types.md", "baselines/thumbnail-runtime-types-validation.json", "changes/2026-09-13-PKG-TOOL-THUMB-04-public-types.md", - "baselines/thumbnail-public-types-validation.json" + "baselines/thumbnail-public-types-validation.json", + "changes/2026-09-13-PKG-TOOL-THUMB-04-emitter.md", + "baselines/thumbnail-emitter-validation.json" ] }, { diff --git a/refactor/third-party.json b/refactor/third-party.json index 61cb29ae7..6708bb6f8 100644 --- a/refactor/third-party.json +++ b/refactor/third-party.json @@ -1036,7 +1036,7 @@ "PKG-TOOL-THUMB-04", "PKG-TOOL-THUMB-06" ], - "sourceStatus": "Local on/once/emit/off structurally correspond to the fixed tiny-emitter 2.1.0 reference; recovered 3.5.31 declared ^2.1.0. Exact original copied revision is unknown. This migration adds types while preserving the local class and dispatch behavior.", + "sourceStatus": "Local on/once/emit/off structurally correspond to the fixed tiny-emitter 2.1.0 reference; recovered 3.5.31 declared ^2.1.0. Exact original copied revision is unknown. This migration adds types while preserving the local class and dispatch behavior. Subsequent reviewed fixes use own event keys/data descriptors and once-per-registration consumption across nested snapshots, preserving ordinary dispatch behavior.", "licenseStatus": "Full pinned upstream MIT notice verified in package, all standalone bundles and actual installed candidate tarball. Repeat on changed release candidates; original copy revision remains unknown.", "upstreamSources": [ "https://raw.githubusercontent.com/scottcorgan/tiny-emitter/2.1.0/index.js", @@ -1049,7 +1049,7 @@ { "path": "packages/artplayer-tool-thumbnail/src/emitter.ts", "algorithm": "sha256-lf", - "sha256": "bd4d5472361edd2e31a6ae1aecce01e310e4086edebe434733d0f5b5eea37a0b" + "sha256": "a083e029dd1104912d3e9a0ba23a6dcc160d9ccde6a55be40db74367a647a3cb" } ], "reviewEvidence": [ @@ -1058,7 +1058,9 @@ "test/thumbnail-vendor.test.js", "refactor/changes/2026-09-13-PKG-TOOL-THUMB-04-runtime-types.md", "refactor/changes/2026-09-13-PKG-TOOL-THUMB-04-public-types.md", - "refactor/baselines/thumbnail-public-types-validation.json" + "refactor/baselines/thumbnail-public-types-validation.json", + "refactor/changes/2026-09-13-PKG-TOOL-THUMB-04-emitter.md", + "refactor/baselines/thumbnail-emitter-validation.json" ] } ], diff --git a/test/browser/thumbnail-emitter.spec.js b/test/browser/thumbnail-emitter.spec.js new file mode 100644 index 000000000..dd0c9a9aa --- /dev/null +++ b/test/browser/thumbnail-emitter.spec.js @@ -0,0 +1,47 @@ +import { hash } from '../../refactor/scripts/releases.mjs' +import { thumbnailCandidate } from '../helpers/thumbnail.js' +import { expect, test } from './fixtures.js' + +const candidate = await thumbnailCandidate() + +test('Thumbnail native browser event registry handles special names and nested once without changing DOM cleanup', async ({ page }, testInfo) => { + await page.goto('/test/player.html?core=candidate&chapter=published') + await page.addScriptTag({ content: candidate.code }) + const result = await page.evaluate(() => { + const input = document.createElement('input') + input.type = 'file' + document.body.append(input) + const tool = new ArtplayerToolThumbnail({ fileInput: input }) + const keys = [] + for (const name of ['__proto__', 'constructor', 'toString', 'hasOwnProperty']) { + let calls = 0 + tool.emit(name) + const prototype = Object.getPrototypeOf(tool.e) + tool.on(name, () => calls++).once(name, () => calls++) + tool.emit(name).emit(name).off(name) + keys.push({ name, calls, prototypePreserved: Object.getPrototypeOf(tool.e) === prototype, removed: !Object.hasOwn(tool.e, name) }) + } + let nested = false + const seen = [] + tool.on('custom', value => { + if (!nested) { + nested = true + tool.emit('custom', 'inner') + } + seen.push(`on:${value}`) + }) + tool.once('custom', value => seen.push(`once:${value}`)) + tool.emit('custom', 'outer') + const video = tool.video + tool.destroy() + tool.destroy() + input.remove() + return { keys, seen, videoConnected: video.isConnected, inputConnected: input.isConnected, processing: tool.processing } + }) + expect(result.keys).toEqual(['__proto__', 'constructor', 'toString', 'hasOwnProperty'].map(name => ({ name, calls: 3, prototypePreserved: true, removed: true }))) + expect(result.seen).toEqual(['on:inner', 'once:inner', 'on:outer']) + expect(result.videoConnected).toBe(false) + expect(result.inputConnected).toBe(false) + expect(result.processing).toBe(false) + await testInfo.attach('thumbnail-emitter', { contentType: 'application/json', body: JSON.stringify({ candidate: candidate.name, sha256: hash(candidate.code), result }) }) +}) diff --git a/test/thumbnail-emitter.test.js b/test/thumbnail-emitter.test.js new file mode 100644 index 000000000..78fac4011 --- /dev/null +++ b/test/thumbnail-emitter.test.js @@ -0,0 +1,137 @@ +import assert from 'node:assert/strict' +// eslint-disable-next-line test/no-import-node-test -- Candidate event regressions are also run against the frozen workspace. +import test from 'node:test' +import { thumbnailCandidate, thumbnailEnvironment } from './helpers/thumbnail.js' + +const implementation = await thumbnailCandidate() +function create() { + const env = thumbnailEnvironment(implementation) + return new env.Factory({ fileInput: new env.Element('input') }) +} + +test('Thumbnail emitter accepts prototype-colliding event names without changing its registry prototype', () => { + for (const name of ['__proto__', 'constructor', 'toString', 'valueOf', 'hasOwnProperty', '__defineGetter__']) { + const tool = create() + assert.equal(tool.emit(name), tool) + assert.equal(tool.off(name, () => {}), tool) + const prototype = Object.getPrototypeOf(tool.e) + let calls = 0 + const callback = () => calls++ + tool.on(name, callback).once(name, callback) + const descriptor = Object.getOwnPropertyDescriptor(tool.e, name) + assert.equal(descriptor.enumerable, true) + assert.equal(descriptor.configurable, true) + assert.equal(descriptor.writable, true) + tool.emit(name).emit(name) + assert.equal(calls, 3) + tool.off(name, callback) + assert.equal(Object.hasOwn(tool.e, name), false) + assert.equal(Object.getPrototypeOf(tool.e), prototype) + tool.destroy() + } +}) + +test('Thumbnail emitter does not read inherited listener getters or trigger inherited setters', () => { + const tool = create() + let reads = 0 + let writes = 0 + const prototype = Object.create(null, { + value: { + get() { + reads++ + return [] + }, + set() { writes++ }, + }, + }) + tool.e = Object.create(prototype) + tool.emit('value').off('value', () => {}) + let calls = 0 + tool.on('value', () => calls++).emit('value') + assert.equal(calls, 1) + assert.equal(reads, 0) + assert.equal(writes, 0) + assert.equal(Object.getPrototypeOf(tool.e), prototype) + tool.off('value') + assert.equal(Object.hasOwn(tool.e, 'value'), false) + tool.destroy() +}) + +test('Thumbnail once is consumed once across nested dispatch snapshots', () => { + const tool = create() + let nesting = false + const seen = [] + tool.on('value', (value) => { + if (!nesting) { + nesting = true + tool.emit('value', 'inner') + } + seen.push(`on:${value}`) + }) + tool.once('value', value => seen.push(`once:${value}`)) + tool.emit('value', 'outer') + assert.deepEqual(seen, ['on:inner', 'once:inner', 'on:outer']) + tool.destroy() +}) + +test('Thumbnail nested once callback failure stays consumed when its caller catches the error', () => { + const tool = create() + let nesting = false + let calls = 0 + const failure = { reason: 'once listener' } + tool.on('value', () => { + if (nesting) + return + nesting = true + assert.throws(() => tool.emit('value'), error => error === failure) + }) + tool.once('value', () => { + calls++ + throw failure + }) + assert.doesNotThrow(() => tool.emit('value')) + assert.equal(calls, 1) + tool.emit('value') + assert.equal(calls, 1) + tool.destroy() +}) + +test('Thumbnail once re-registration is independent from a consumed outer registration', () => { + const tool = create() + let nesting = false + const seen = [] + tool.on('value', () => { + if (!nesting) { + nesting = true + tool.emit('value', 'inner') + } + }) + function callback(value) { + seen.push(value) + if (value === 'inner') + tool.once('value', callback) + } + tool.once('value', callback).emit('value', 'outer').emit('value', 'next') + assert.deepEqual(seen, ['inner', 'next']) + tool.destroy() +}) + +test('Thumbnail keeps duplicate registrations, symbol/numeric names and independent instance registries', () => { + const first = create() + const second = create() + for (const name of [0, Symbol('event')]) { + let firstCalls = 0 + let secondCalls = 0 + const callback = () => firstCalls++ + first.once(name, callback).once(name, callback) + second.on(name, () => secondCalls++) + first.emit(name).emit(name) + assert.equal(firstCalls, 2) + assert.equal(secondCalls, 0) + second.emit(name) + assert.equal(secondCalls, 1) + assert.notEqual(first.e, second.e) + } + first.destroy() + second.destroy() +})