@snagset/server 0.1.12 → 0.1.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
const ue={root:"data-snag-root"},_=e=>ue[e],he={anchor:"data-snag-anchor",ignore:"data-snag-ignore",mask:"data-snag-mask",nomask:"data-snag-nomask",src:"data-snag-src",component:"data-snag-component"},fe=e=>he[e],ge={mask:"snag-mask",block:"snag-block"},te=e=>ge[e],T=(e,t,i)=>e===1?t:i;function j(e){const t=[],i=e.corsImages??0;i>0&&t.push(`${i} ${T(i,"image may appear as a placeholder","images may appear as placeholders")} (served without CORS headers)`);const n=e.failedImages??0;n>0&&t.push(`${n} ${T(n,"image","images")} could not be included in the screenshot`);const o=e.shadowRoots??0;o>0&&t.push(o===1?"1 web component could not be captured (its contents are in a shadow root)":`${o} web components could not be captured (their contents are in shadow roots)`);const s=e.iframeHosts??[];if(s.length>0){const g=[...new Set(s.filter(Boolean))].join(", ");t.push(`${s.length} cross-origin ${T(s.length,"iframe","iframes")} not captured${g?`: ${g}`:""}`)}const r=e.taintedCanvases??0;r>0&&t.push(r===1?"1 canvas could not be read (tainted by a cross-origin image)":`${r} canvases could not be read (tainted by cross-origin images)`);const f=e.blankCanvases??0;f>0&&t.push(`${f} ${T(f,"canvas","canvases")} may appear blank`),(e.videos??0)>0&&t.push("Video frames are not captured");const d=e.failedOverlays??0;return d>0&&t.push(`${d} overlay ${T(d,"element","elements")} could not be captured`),t}const me=["display","flex-direction","align-items","justify-content","gap","grid-template-columns","width","height","margin","padding","border","border-radius","background-color","background","color","font-family","font-size","font-weight","font-style","line-height","letter-spacing","text-decoration","text-align","white-space","text-wrap","overflow","opacity","box-shadow","max-width","list-style"],ne=200;function ie(e,t){try{return new URL(e,t.baseURI).origin===t.location.origin}catch{return!1}}function V(e){return new Promise(t=>{const i=new FileReader;i.onload=()=>t(typeof i.result=="string"?i.result:null),i.onerror=()=>t(null),i.readAsDataURL(e)})}async function we(e,t){try{const i=await fetch(new URL(e,t.baseURI).href,{credentials:"same-origin"});return i.ok?await V(await i.blob()):null}catch{return null}}async function pe(e,t,i){const n=new URL(e,t.baseURI).href;try{const s=await fetch(n,{mode:"cors",credentials:"omit"});if(s.ok)return await V(await s.blob())}catch{}if(!i)return null;const o=await i(n).catch(()=>null);return o?await V(o):null}function be(e,t){try{return/^https?:$/.test(new URL(e,t.baseURI).protocol)}catch{return!1}}const W="data:image/gif;base64,R0lGODlhAQABAIAAALu7uwAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==";async function z(e,t){const i=e.ownerDocument,n=i.defaultView??window,o=e.getBoundingClientRect();if(o.width<4||o.height<4)throw new Error("raster:too-small");const s=e.cloneNode(!0),r=[e,...e.querySelectorAll("*")].slice(0,ne),f=[s,...s.querySelectorAll("*")].slice(0,ne),d=/^(?:SCRIPT|IFRAME|VIDEO|CANVAS|NOSCRIPT)$/;let g=0,u=0,w=0;const l=[],p=[];for(let v=0;v<Math.min(r.length,f.length);v++){const k=r[v],M=f[v];if(d.test(k.tagName)||t.drop?.(k)){p.push(M);continue}const S=n.getComputedStyle(k);let E=me.map(m=>`${m}:${S.getPropertyValue(m)}`).join(";");if(t.hide?.(k)&&(E+=";visibility:hidden"),M.setAttribute("style",E),k.shadowRoot){const m=k.getBoundingClientRect();m.width>=1&&m.height>=1&&S.visibility!=="hidden"&&(g++,M.setAttribute("style",E+";background:repeating-linear-gradient(45deg,#eee,#eee 6px,#ddd 6px,#ddd 12px)"))}if(M instanceof n.HTMLImageElement||M.tagName==="IMG"){const m=M,R=k.currentSrc||m.getAttribute("src")||"";t.inlineSameOriginAssets&&R&&ie(R,i)?l.push(we(R,i).then(x=>{x?m.setAttribute("src",x):(m.setAttribute("src",W),w++)})):t.inlineSameOriginAssets&&R&&be(R,i)?(m.setAttribute("src",W),l.push(pe(R,i,t.fetchForeignImage).then(x=>{x?m.setAttribute("src",x):u++}))):R&&(m.setAttribute("src",W),ie(R,i)||u++)}}for(const v of p)v.remove();for(const v of s.querySelectorAll("script, iframe, video, canvas, noscript"))v.remove();await Promise.all(l);const C=j({corsImages:u,failedImages:w,shadowRoots:g}),h=`<svg xmlns="http://www.w3.org/2000/svg" width="${Math.ceil(o.width)}" height="${Math.ceil(o.height)}"><foreignObject width="100%" height="100%"><div xmlns="http://www.w3.org/1999/xhtml">`+new XMLSerializer().serializeToString(s)+"</div></foreignObject></svg>",A=new n.Image,O=new Promise((v,k)=>{A.onload=()=>v(),A.onerror=()=>k(new Error("raster:img-error"))});return A.src="data:image/svg+xml;charset=utf-8,"+encodeURIComponent(h),await O,{image:A,width:o.width,height:o.height,blindSpots:C}}let q=null;function oe(e){return q||(q=new Promise(t=>{const i=new(e.defaultView??window).Image,n=setTimeout(()=>t(!1),1e3);i.onload=()=>{clearTimeout(n),t(!0)},i.onerror=()=>{clearTimeout(n),t(!1)},i.src="data:image/svg+xml;charset=utf-8,"+encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" width="1" height="1"/>')}),q)}const G=256e3,X=1600,ye=2048,ae=.82,Ae=5e5;let P=null;function se(e){if(P!==null)return P;try{const t=e.createElement("canvas");t.width=1,t.height=1,P=t.toDataURL("image/webp").startsWith("data:image/webp")}catch{P=!1}return P}function F(e,t,i){return new Promise((n,o)=>{try{e.toBlob(s=>n(s),t,i)}catch(s){s.name==="SecurityError"?o(s):n(null)}})}async function re(e,t,i){const n=se(t)?await F(e,"image/webp",i):await F(e,"image/jpeg",Math.max(i,.85));return n&&(n.type==="image/webp"||n.type==="image/jpeg")?n:F(e,"image/png")}async function le(e,t,i=X){const n=Math.min(Math.max(1,i),ye);let o=e;const s=Math.max(o.width,o.height);s>n&&(o=ce(e,t,n/s));let r=ae,f=!1;for(let g=0;g<4;g++){let u=await re(o,t,r);if(!u)return null;if(o.width*o.height<=Ae){const w=await F(o,"image/png");w&&w.size<u.size&&(u=w)}if(u.size<=G)return{blob:u,quality:r,rescaled:f};r>.55?r-=.12:(o=ce(o,t,.75),r=ae,f=!0)}const d=await re(o,t,r);return d&&d.size<=G?{blob:d,quality:r,rescaled:f}:null}function ce(e,t,i){const n=t.createElement("canvas");return n.width=Math.max(1,Math.round(e.width*i)),n.height=Math.max(1,Math.round(e.height*i)),n.getContext("2d").drawImage(e,0,0,n.width,n.height),n}function ve(e){const t=[`[${fe("mask")}]`,`.${te("mask")}`,`.${te("block")}`,'input[type="password"]','input[autocomplete*="password" i]','[autocomplete*="cc-" i]','[autocomplete="one-time-code"]','input[type="email"]','input[type="tel"]'];return e.maskAllInputs&&t.push("input","textarea","select","[contenteditable]"),[...t,...e.maskSelectors??[]]}function ke(e,t){const i=e.defaultView??window,n=performance.now(),o=[],s=[],r=`[${_("root")}]`,f=e.querySelectorAll("body *"),d=f.length>5e3,g=d?e.querySelectorAll("body > *, [class*=fixed], [class*=sticky], header, nav, dialog, [popover], [role=dialog], [role=tooltip], [role=menu]"):f;d&&o.push("Overlay scan was reduced on this very large page; some fixed elements may be missing.");for(const l of g){if(performance.now()-n>8)return o.push("Overlay scan ran out of time; fixed and sticky elements are not composited."),{overlays:[],notes:o};if(l.closest(r))continue;let p=!1,C=!1;try{l.matches(":popover-open")&&(p=!0)}catch{}if(l instanceof i.HTMLDialogElement&&l.open){p=!0;try{C=l.matches(":modal")}catch{C=!1}}if(!p){const A=i.getComputedStyle(l).position;(A==="fixed"||A==="sticky")&&(p=!0)}if(!p)continue;const h=l.getBoundingClientRect();h.width<1||h.height<1||h.bottom<0||h.right<0||h.top>i.innerHeight||h.left>i.innerWidth||s.push({el:l,rect:h,modal:C})}const u=s.filter(l=>!s.some(p=>p!==l&&p.el.contains(l.el))),w=(l,p,C)=>l===1?p:C;if(u.length>t){const l=u.length-t;o.push(`${l} overlay ${w(l,"element","elements")} (fixed/sticky/menus) ${w(l,"was","were")} not captured`)}if(u.length>0){const l=Math.min(u.length,t);o.push(`${l} overlay ${w(l,"element","elements")} ${w(l,"was","were")} re-composited; ${w(l,"its","their")} stacking order is approximate`)}return{overlays:u.slice(0,t),notes:o}}function Se(e,t){const i=performance.now(),n=[],o=e.querySelectorAll("iframe, canvas, video");if(o.length>5e3)return t.push("Blind-spot scan skipped on this very large page."),n;const s={iframeHosts:[],taintedCanvases:0,blankCanvases:0,videos:0};for(const r of o){if(performance.now()-i>5)break;if(r.closest(`[${_("root")}]`))continue;const f=r.getBoundingClientRect();if(!(f.width<2||f.height<2))if(r.tagName==="IFRAME"){const d=r;let g=!1;try{d.contentDocument||(g=!0)}catch{g=!0}if(g){let u="";try{u=new URL(d.src,e.baseURI).host}catch{}s.iframeHosts.push(u),n.push({el:r,rect:f,label:"iframe not captured"})}}else if(r.tagName==="CANVAS"){const d=r;let g=!1;try{d.toDataURL()}catch(u){g=u.name==="SecurityError"}g?s.taintedCanvases++:s.blankCanvases++,n.push({el:r,rect:f,label:"canvas not captured"})}else s.videos++,n.push({el:r,rect:f,label:"video not captured"})}return t.push(...j(s)),n}function Re(e,t,i,n,o,s){e.save(),e.beginPath(),e.rect(t,i,n,o),e.clip(),e.fillStyle="#f3f3f3",e.fillRect(t,i,n,o),e.strokeStyle="#d0d0d0",e.lineWidth=1,e.beginPath();for(let r=-o;r<n;r+=12)e.moveTo(t+r,i+o),e.lineTo(t+r+o,i);e.stroke(),e.strokeRect(t+.5,i+.5,n-1,o-1),n>=120&&o>=20&&(e.fillStyle="#8a8f98",e.font="11px system-ui, sans-serif",e.textAlign="center",e.fillText(s,t+n/2,i+o/2+4)),e.restore()}function Me(e,t,i,n,o){e.save(),e.fillStyle="#5b5f66",e.fillRect(t,i,n,o),e.strokeStyle="#8a8f98",e.lineWidth=1,e.strokeRect(t+.5,i+.5,n-1,o-1),n>=80&&o>=24&&(e.fillStyle="#c9cdd3",e.font="11px system-ui, sans-serif",e.textAlign="center",e.fillText("masked",t+n/2,i+o/2+4)),e.restore()}function Ce(e){const t=e.defaultView??window;for(const i of[e.documentElement,e.body]){if(!i)continue;const n=t.getComputedStyle(i).backgroundColor;if(n&&n!=="rgba(0, 0, 0, 0)"&&n!=="transparent")return n}return"#ffffff"}async function N(e,t,i,n,o){const s=e.doc,r=s.defaultView??window,f=`[${_("root")}]`,d=[];if(await Promise.race([new Promise(a=>r.requestAnimationFrame(()=>r.requestAnimationFrame(a))),new Promise(a=>r.setTimeout(a,50))]),s.hidden&&await new Promise(a=>{const c=r.setTimeout(a,500);s.addEventListener("visibilitychange",()=>{r.clearTimeout(c),a()},{once:!0})}),e.signal?.aborted)throw new Error("navigated");let g,u;try{const a=ve(e).join(", "),c=[...s.querySelectorAll(a)].filter(b=>!b.closest(f));u=new Set(c),g=c.map(b=>b.getBoundingClientRect()).filter(b=>b.width>0&&b.height>0)}catch(a){throw Object.assign(new Error("masking-failed"),{cause:a})}const{overlays:w,notes:l}=n?ke(s,e.maxOverlays??8):{overlays:[],notes:[]};d.push(...l);const p=Se(s,d),C=new Set([...w.map(a=>a.el),...u]);let h=Math.min(r.devicePixelRatio||1,e.maxDpr??2);const A=s.documentElement,O=t==="viewport"?A.scrollWidth:0,v=t==="viewport"?A.scrollHeight:0,k=e.maxRasterPixels??4e7;if(t==="viewport"){const a=O*v*h*h;if(a>k&&(h=Math.max(1,h*Math.sqrt(k/a))),O*v>k)return N(e,"element",i,n,!0)}let M=A,S,E={x:0,y:0};if(t==="viewport")S={x:0,y:0,w:r.innerWidth,h:r.innerHeight};else{const a=e.target&&e.target.isConnected?e.target:A,c=a.getBoundingClientRect(),b=200,B=Math.max(0,c.left-b),U=Math.max(0,c.top-b),K=Math.min(r.innerWidth,c.right+b),Z=Math.min(r.innerHeight,c.bottom+b);let $=a;for(;$.parentElement&&$!==A;){const D=$.getBoundingClientRect();if(D.left<=B&&D.top<=U&&D.right>=K&&D.bottom>=Z)break;$=$.parentElement}M=$;const ee=$.getBoundingClientRect();S={x:B-ee.left,y:U-ee.top,w:K-B,h:Z-U},E={x:B,y:U}}const m=await z(M,{drop:a=>a.closest?.(f)!=null,hide:a=>C.has(a),inlineSameOriginAssets:i,...e.fetchForeignImage?{fetchForeignImage:e.fetchForeignImage}:{}});if(d.push(...m.blindSpots),e.signal?.aborted)throw m.image.src="",new Error("navigated");const R=M.getBoundingClientRect(),x=t==="viewport"?-R.left:S.x,de=t==="viewport"?-R.top:S.y,y=s.createElement("canvas");y.width=Math.max(1,Math.round(S.w*h)),y.height=Math.max(1,Math.round(S.h*h));const I=y.getContext("2d");I.fillStyle=Ce(s),I.fillRect(0,0,y.width,y.height),I.drawImage(m.image,x,de,S.w,S.h,0,0,y.width,y.height),m.image.src="",w.some(a=>a.modal)&&(I.fillStyle="rgba(0,0,0,0.35)",I.fillRect(0,0,y.width,y.height));let Q=0;for(const a of w)try{const c=await z(a.el,{drop:b=>b.closest?.(f)!=null,hide:b=>u.has(b),inlineSameOriginAssets:i,...e.fetchForeignImage?{fetchForeignImage:e.fetchForeignImage}:{}});I.drawImage(c.image,Math.round(a.rect.left*h),Math.round(a.rect.top*h),Math.round(a.rect.width*h),Math.round(a.rect.height*h)),c.image.src=""}catch{Q++}d.push(...j({failedOverlays:Q}));const J=a=>({x:Math.round((a.left-E.x)*h),y:Math.round((a.top-E.y)*h),w:Math.round(a.width*h),h:Math.round(a.height*h)});for(const a of p){const c=J(a.rect);c.w>0&&c.h>0&&Re(I,c.x,c.y,c.w,c.h,a.label)}let H=0;try{for(const a of g){const c=J(a);c.w<=0||c.h<=0||(Me(I,c.x,c.y,c.w,c.h),H++)}}catch(a){throw Object.assign(new Error("masking-failed"),{cause:a})}if(e.signal?.aborted)throw new Error("navigated");let L;try{L=await le(y,s,e.maxLongestSide??X)}catch(a){throw y.width=y.height=0,a.name==="SecurityError"?new Error("tainted"):a}return y.width=y.height=0,L?{situation:o?"scope-forced":"ok",blob:L.blob,quality:L.quality,width:Math.round(S.w),height:Math.round(S.h),blindSpots:d,maskedCount:H}:{situation:"engine-failed",blindSpots:d,maskedCount:H}}function Y(e,t){return Promise.race([e.catch(i=>i.message==="masking-failed"?{situation:"masking-failed",blindSpots:[],maskedCount:0}:i.message==="navigated"?{situation:"navigated",blindSpots:[],maskedCount:0}:i.message==="tainted"?{situation:"tainted",blindSpots:[],maskedCount:0}:{situation:"engine-failed",blindSpots:[],maskedCount:0}),new Promise(i=>setTimeout(()=>i({situation:"timeout",blindSpots:[],maskedCount:0}),t))])}async function Ie(e){if(!await oe(e.doc))return{situation:"csp-blocked",blindSpots:[],maskedCount:0};const t=e.scope??"viewport",i=[e.timeoutMs??3e3,1500,1500];let n=await Y(N(e,t,!0,!0,!1),Math.min(i[0],1e4));if(n.situation==="ok"||n.situation==="scope-forced"||n.situation==="masking-failed"||n.situation==="navigated"||n.situation==="tainted"||(n=await Y(N(e,t,!1,!1,!1),i[1]),n.situation==="ok"||n.situation==="scope-forced"||n.situation==="masking-failed"||n.situation==="navigated"||n.situation==="tainted"))return n;const o=await Y(N(e,"element",!1,!1,!0),i[2]);return o.situation==="ok"||o.situation==="scope-forced"?o:o.situation==="engine-failed"&&n.situation==="timeout"?n:o}const Ee="screenshot";export{Ee as CHUNK,X as MAX_LONGEST_SIDE,G as TARGET_BYTES,se as canEncodeWebp,Ie as captureScreenshot,le as encodeToBudget,oe as probeDataImages,z as rasterise};
|
|
1
|
+
const dt={root:"data-snag-root"},V=t=>dt[t],ut={anchor:"data-snag-anchor",ignore:"data-snag-ignore",mask:"data-snag-mask",nomask:"data-snag-nomask",src:"data-snag-src",component:"data-snag-component"},ht=t=>ut[t],ft={mask:"snag-mask",block:"snag-block"},nt=t=>ft[t],L=(t,e,n)=>t===1?e:n;function z(t){const e=[],n=t.corsImages??0;n>0&&e.push(`${n} ${L(n,"image may appear as a placeholder","images may appear as placeholders")} (served without CORS headers)`);const i=t.failedImages??0;i>0&&e.push(`${i} ${L(i,"image","images")} could not be included in the screenshot`);const o=t.shadowRoots??0;o>0&&e.push(o===1?"1 web component could not be captured (its contents are in a shadow root)":`${o} web components could not be captured (their contents are in shadow roots)`);const s=t.iframeHosts??[];if(s.length>0){const w=[...new Set(s.filter(Boolean))].join(", ");e.push(`${s.length} cross-origin ${L(s.length,"iframe","iframes")} not captured${w?`: ${w}`:""}`)}const c=t.taintedCanvases??0;c>0&&e.push(c===1?"1 canvas could not be read (tainted by a cross-origin image)":`${c} canvases could not be read (tainted by cross-origin images)`);const u=t.blankCanvases??0;u>0&&e.push(`${u} ${L(u,"canvas","canvases")} may appear blank`),(t.videos??0)>0&&e.push("Video frames are not captured");const f=t.failedOverlays??0;return f>0&&e.push(`${f} overlay ${L(f,"element","elements")} could not be captured`),e}const gt=["display","flex-direction","flex-wrap","flex-grow","flex-shrink","flex-basis","order","gap","align-items","align-content","align-self","justify-content","justify-items","justify-self","grid-template-columns","grid-template-rows","grid-column","grid-row","box-sizing","width","height","margin","padding","border","border-radius","position","top","right","bottom","left","z-index","isolation","transform","transform-origin","object-fit","object-position","vertical-align","text-transform","background-color","background","color","font-family","font-size","font-weight","font-style","line-height","letter-spacing","text-decoration","text-align","white-space","text-wrap","overflow","opacity","box-shadow","max-width","list-style"],mt=6e3;function it(t,e){try{return new URL(t,e.baseURI).origin===e.location.origin}catch{return!1}}function G(t){return new Promise(e=>{const n=new FileReader;n.onload=()=>e(typeof n.result=="string"?n.result:null),n.onerror=()=>e(null),n.readAsDataURL(t)})}async function wt(t,e){try{const n=await fetch(new URL(t,e.baseURI).href,{credentials:"same-origin"});return n.ok?await G(await n.blob()):null}catch{return null}}async function pt(t,e,n){const i=new URL(t,e.baseURI).href;try{const s=await fetch(i,{mode:"cors",credentials:"omit"});if(s.ok)return await G(await s.blob())}catch{}if(!n)return null;const o=await n(i).catch(()=>null);return o?await G(o):null}function bt(t,e){try{return/^https?:$/.test(new URL(t,e.baseURI).protocol)}catch{return!1}}const X="data:image/gif;base64,R0lGODlhAQABAIAAALu7uwAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==",At="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";async function Y(t,e){const n=t.ownerDocument,i=n.defaultView??window,o=t.getBoundingClientRect();if(o.width<4||o.height<4)throw new Error("raster:too-small");const s=t.cloneNode(!0);let c=0,u=0,f=0;const w=[],g=[],m=i.innerWidth,h=i.innerHeight,k=l=>{const d=l.getBoundingClientRect();return d.width>0&&d.height>0&&d.right>0&&d.bottom>0&&d.left<m&&d.top<h},M=l=>{const d=i.getComputedStyle(l),C=l.getBoundingClientRect();return`display:${d.display==="inline"?"inline-block":d.display};box-sizing:border-box;width:${C.width}px;height:${C.height}px;margin:${d.margin};border:${d.border};padding:${d.padding};vertical-align:${d.verticalAlign}`},p="img, canvas, iframe, video",R=[...t.querySelectorAll(p)],A=[...s.querySelectorAll(p)];for(let l=0;l<Math.min(R.length,A.length);l++){const d=R[l],C=A[l];if(e.drop?.(d))continue;if(d.tagName!=="IMG"){let a="";if(d.tagName==="CANVAS"&&e.inlineSameOriginAssets&&k(d))try{a=d.toDataURL()}catch{}const r=n.createElement(a?"img":"span");a&&r.setAttribute("src",a);const b=d.getAttribute("class");b&&r.setAttribute("class",b),r.setAttribute("style",M(d)),C.replaceWith(r);continue}const y=C;y.removeAttribute("srcset"),y.removeAttribute("sizes"),y.removeAttribute("loading");const S=d.currentSrc||d.getAttribute("src")||"";S&&(k(d)?e.inlineSameOriginAssets&&it(S,n)?w.push(wt(S,n).then(a=>{a?y.setAttribute("src",a):(y.setAttribute("src",X),f++)})):e.inlineSameOriginAssets&&bt(S,n)?(y.setAttribute("src",X),w.push(pt(S,n,e.fetchForeignImage).then(a=>{a?y.setAttribute("src",a):u++}))):S.startsWith("data:")||(y.setAttribute("src",X),it(S,n)||u++):y.setAttribute("src",At))}for(const l of s.querySelectorAll("picture > source"))l.remove();const E=64,P=l=>l.bottom<-E||l.top>h+E||l.right<-E||l.left>m+E,T=/^(?:SCRIPT|NOSCRIPT)$/,x=/^(?:IFRAME|VIDEO|CANVAS)$/;let B=0;const I=[[t,s]];for(;I.length>0&&B<mt;){const[l,d]=I.pop();if(T.test(l.tagName)||e.drop?.(l)){g.push(d);continue}if(x.test(l.tagName))continue;B++;const C=i.getComputedStyle(l);let y=gt.map(r=>`${r}:${C.getPropertyValue(r)}`).join(";");if(e.hide?.(l)&&(y+=";visibility:hidden"),d.setAttribute("style",y),l.shadowRoot){const r=l.getBoundingClientRect();r.width>=1&&r.height>=1&&C.visibility!=="hidden"&&(c++,d.setAttribute("style",y+";background:repeating-linear-gradient(45deg,#eee,#eee 6px,#ddd 6px,#ddd 12px)"))}if(C.display==="none")continue;if(l!==t&&C.position!=="fixed"&&P(l.getBoundingClientRect())){const r=[...d.querySelectorAll("style")];d.replaceChildren(...r);continue}const S=l.children,a=d.children;for(let r=Math.min(S.length,a.length)-1;r>=0;r--)I.push([S[r],a[r]])}for(const l of g)l.remove();for(const l of s.querySelectorAll("script, iframe, video, canvas, noscript"))l.remove();await Promise.all(w);const D=z({corsImages:u,failedImages:f,shadowRoots:c}),_=`<svg xmlns="http://www.w3.org/2000/svg" width="${Math.ceil(o.width)}" height="${Math.ceil(o.height)}"><foreignObject width="100%" height="100%"><div xmlns="http://www.w3.org/1999/xhtml">`+new XMLSerializer().serializeToString(s)+"</div></foreignObject></svg>",O=new i.Image,v=new Promise((l,d)=>{O.onload=()=>l(),O.onerror=()=>d(new Error("raster:img-error"))});return O.src="data:image/svg+xml;charset=utf-8,"+encodeURIComponent(_),await v,{image:O,width:o.width,height:o.height,blindSpots:D}}let F=null;function ot(t){return F||(F=new Promise(e=>{const n=new(t.defaultView??window).Image,i=setTimeout(()=>e(!1),1e3);n.onload=()=>{clearTimeout(i),e(!0)},n.onerror=()=>{clearTimeout(i),e(!1)},n.src="data:image/svg+xml;charset=utf-8,"+encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" width="1" height="1"/>')}),F)}const Q=256e3,J=1600,yt=2048,at=.82,vt=5e5;let U=null;function st(t){if(U!==null)return U;try{const e=t.createElement("canvas");e.width=1,e.height=1,U=e.toDataURL("image/webp").startsWith("data:image/webp")}catch{U=!1}return U}function H(t,e,n){return new Promise((i,o)=>{try{t.toBlob(s=>i(s),e,n)}catch(s){s.name==="SecurityError"?o(s):i(null)}})}async function rt(t,e,n){const i=st(e)?await H(t,"image/webp",n):await H(t,"image/jpeg",Math.max(n,.85));return i&&(i.type==="image/webp"||i.type==="image/jpeg")?i:H(t,"image/png")}async function lt(t,e,n=J){const i=Math.min(Math.max(1,n),yt);let o=t;const s=Math.max(o.width,o.height);s>i&&(o=ct(t,e,i/s));let c=at,u=!1;for(let w=0;w<4;w++){let g=await rt(o,e,c);if(!g)return null;if(o.width*o.height<=vt){const m=await H(o,"image/png");m&&m.size<g.size&&(g=m)}if(g.size<=Q)return{blob:g,quality:c,rescaled:u};c>.55?c-=.12:(o=ct(o,e,.75),c=at,u=!0)}const f=await rt(o,e,c);return f&&f.size<=Q?{blob:f,quality:c,rescaled:u}:null}function ct(t,e,n){const i=e.createElement("canvas");return i.width=Math.max(1,Math.round(t.width*n)),i.height=Math.max(1,Math.round(t.height*n)),i.getContext("2d").drawImage(t,0,0,i.width,i.height),i}function kt(t){const e=[`[${ht("mask")}]`,`.${nt("mask")}`,`.${nt("block")}`,'input[type="password"]','input[autocomplete*="password" i]','[autocomplete*="cc-" i]','[autocomplete="one-time-code"]','input[type="email"]','input[type="tel"]'];return t.maskAllInputs&&e.push("input","textarea","select","[contenteditable]"),[...e,...t.maskSelectors??[]]}function St(t,e){const n=t.defaultView??window,i=performance.now(),o=[],s=[],c=`[${V("root")}]`,u=t.querySelectorAll("body *"),f=u.length>5e3,w=f?t.querySelectorAll("body > *, [class*=fixed], [class*=sticky], header, nav, dialog, [popover], [role=dialog], [role=tooltip], [role=menu]"):u;f&&o.push("Overlay scan was reduced on this very large page; some fixed elements may be missing.");for(const h of w){if(performance.now()-i>8)return o.push("Overlay scan ran out of time; fixed and sticky elements are not composited."),{overlays:[],notes:o};if(h.closest(c))continue;let k=!1,M=!1;try{h.matches(":popover-open")&&(k=!0)}catch{}if(h instanceof n.HTMLDialogElement&&h.open){k=!0;try{M=h.matches(":modal")}catch{M=!1}}if(!k){const A=n.getComputedStyle(h).position;(A==="fixed"||A==="sticky")&&(k=!0)}if(!k)continue;const p=A=>A.width>=1&&A.height>=1&&A.bottom>=0&&A.right>=0&&A.top<=n.innerHeight&&A.left<=n.innerWidth,R=h.getBoundingClientRect();if(R.width<1||R.height<1){for(const A of h.children){const E=A.getBoundingClientRect();p(E)&&s.push({el:A,rect:E,modal:M})}continue}p(R)&&s.push({el:h,rect:R,modal:M})}const g=s.filter(h=>!s.some(k=>k!==h&&k.el.contains(h.el))),m=(h,k,M)=>h===1?k:M;if(g.length>e){const h=g.length-e;o.push(`${h} overlay ${m(h,"element","elements")} (fixed/sticky/menus) ${m(h,"was","were")} not captured`)}if(g.length>0){const h=Math.min(g.length,e);o.push(`${h} overlay ${m(h,"element","elements")} ${m(h,"was","were")} re-composited; ${m(h,"its","their")} stacking order is approximate`)}return{overlays:g.slice(0,e),notes:o}}function Rt(t,e){if(t.width===0||t.height===0)return!1;try{const n=e.createElement("canvas");n.width=n.height=16;const i=n.getContext("2d",{willReadFrequently:!0});if(!i)return!1;i.drawImage(t,0,0,16,16);const o=i.getImageData(0,0,16,16).data;for(let s=3;s<o.length;s+=4)if(o[s]>0)return!0;return!1}catch{return!1}}function Ct(t,e,n){const i=performance.now(),o=[],s=t.querySelectorAll("iframe, canvas, video");if(s.length>5e3)return e.push("Blind-spot scan skipped on this very large page."),o;const c={iframeHosts:[],taintedCanvases:0,blankCanvases:0,videos:0};for(const u of s){if(performance.now()-i>5)break;if(u.closest(`[${V("root")}]`))continue;const f=u.getBoundingClientRect();if(!(f.width<2||f.height<2))if(u.tagName==="IFRAME"){const w=u;let g=!1;try{w.contentDocument||(g=!0)}catch{g=!0}if(g){let m="";try{m=new URL(w.src,t.baseURI).host}catch{}c.iframeHosts.push(m),o.push({el:u,rect:f,label:"iframe not captured"})}}else if(u.tagName==="CANVAS"){const w=u;let g=!1;try{w.toDataURL()}catch(m){g=m.name==="SecurityError"}if(!g&&n&&Rt(w,t))continue;g?c.taintedCanvases++:c.blankCanvases++,o.push({el:u,rect:f,label:"canvas not captured"})}else c.videos++,o.push({el:u,rect:f,label:"video not captured"})}return e.push(...z(c)),o}function xt(t,e,n,i,o,s){t.save(),t.beginPath(),t.rect(e,n,i,o),t.clip(),t.fillStyle="#f3f3f3",t.fillRect(e,n,i,o),t.strokeStyle="#d0d0d0",t.lineWidth=1,t.beginPath();for(let c=-o;c<i;c+=12)t.moveTo(e+c,n+o),t.lineTo(e+c+o,n);t.stroke(),t.strokeRect(e+.5,n+.5,i-1,o-1),i>=120&&o>=20&&(t.fillStyle="#8a8f98",t.font="11px system-ui, sans-serif",t.textAlign="center",t.fillText(s,e+i/2,n+o/2+4)),t.restore()}function Mt(t,e,n,i,o){t.save(),t.fillStyle="#5b5f66",t.fillRect(e,n,i,o),t.strokeStyle="#8a8f98",t.lineWidth=1,t.strokeRect(e+.5,n+.5,i-1,o-1),i>=80&&o>=24&&(t.fillStyle="#c9cdd3",t.font="11px system-ui, sans-serif",t.textAlign="center",t.fillText("masked",e+i/2,n+o/2+4)),t.restore()}function Et(t){const e=t.defaultView??window;for(const n of[t.documentElement,t.body]){if(!n)continue;const i=e.getComputedStyle(n).backgroundColor;if(i&&i!=="rgba(0, 0, 0, 0)"&&i!=="transparent")return i}return"#ffffff"}async function W(t,e,n,i,o){const s=t.doc,c=s.defaultView??window,u=`[${V("root")}]`,f=[];if(await Promise.race([new Promise(a=>c.requestAnimationFrame(()=>c.requestAnimationFrame(a))),new Promise(a=>c.setTimeout(a,50))]),s.hidden&&await new Promise(a=>{const r=c.setTimeout(a,500);s.addEventListener("visibilitychange",()=>{c.clearTimeout(r),a()},{once:!0})}),t.signal?.aborted)throw new Error("navigated");let w,g;try{const a=kt(t).join(", "),r=[...s.querySelectorAll(a)].filter(b=>!b.closest(u));g=new Set(r),w=r.map(b=>b.getBoundingClientRect()).filter(b=>b.width>0&&b.height>0)}catch(a){throw Object.assign(new Error("masking-failed"),{cause:a})}const{overlays:m,notes:h}=i?St(s,t.maxOverlays??8):{overlays:[],notes:[]};f.push(...h);const k=Ct(s,f,n),M=new Set([...m.map(a=>a.el),...g]);let p=Math.min(c.devicePixelRatio||1,t.maxDpr??2);const R=s.documentElement,A=e==="viewport"?R.scrollWidth:0,E=e==="viewport"?R.scrollHeight:0,P=t.maxRasterPixels??4e7;if(e==="viewport"){const a=A*E*p*p;if(a>P&&(p=Math.max(1,p*Math.sqrt(P/a))),A*E>P)return W(t,"element",n,i,!0)}let T=R,x,B={x:0,y:0};if(e==="viewport")x={x:0,y:0,w:c.innerWidth,h:c.innerHeight};else{const a=t.target&&t.target.isConnected?t.target:R,r=a.getBoundingClientRect(),b=200,N=Math.max(0,r.left-b),q=Math.max(0,r.top-b),Z=Math.min(c.innerWidth,r.right+b),tt=Math.min(c.innerHeight,r.bottom+b);let $=a;for(;$.parentElement&&$!==R;){const j=$.getBoundingClientRect();if(j.left<=N&&j.top<=q&&j.right>=Z&&j.bottom>=tt)break;$=$.parentElement}T=$;const et=$.getBoundingClientRect();x={x:N-et.left,y:q-et.top,w:Z-N,h:tt-q},B={x:N,y:q}}const I=await Y(T,{drop:a=>a.closest?.(u)!=null,hide:a=>M.has(a),inlineSameOriginAssets:n,...t.fetchForeignImage?{fetchForeignImage:t.fetchForeignImage}:{}});if(f.push(...I.blindSpots),t.signal?.aborted)throw I.image.src="",new Error("navigated");const D=T.getBoundingClientRect(),_=e==="viewport"?-D.left:x.x,O=e==="viewport"?-D.top:x.y,v=s.createElement("canvas");v.width=Math.max(1,Math.round(x.w*p)),v.height=Math.max(1,Math.round(x.h*p));const l=v.getContext("2d");l.fillStyle=Et(s),l.fillRect(0,0,v.width,v.height),l.drawImage(I.image,_,O,x.w,x.h,0,0,v.width,v.height),I.image.src="",m.some(a=>a.modal)&&(l.fillStyle="rgba(0,0,0,0.35)",l.fillRect(0,0,v.width,v.height));let d=0;for(const a of m)try{const r=await Y(a.el,{drop:b=>b.closest?.(u)!=null,hide:b=>g.has(b),inlineSameOriginAssets:n,...t.fetchForeignImage?{fetchForeignImage:t.fetchForeignImage}:{}});l.drawImage(r.image,Math.round(a.rect.left*p),Math.round(a.rect.top*p),Math.round(a.rect.width*p),Math.round(a.rect.height*p)),r.image.src=""}catch{d++}f.push(...z({failedOverlays:d}));const C=a=>({x:Math.round((a.left-B.x)*p),y:Math.round((a.top-B.y)*p),w:Math.round(a.width*p),h:Math.round(a.height*p)});for(const a of k){const r=C(a.rect);r.w>0&&r.h>0&&xt(l,r.x,r.y,r.w,r.h,a.label)}let y=0;try{for(const a of w){const r=C(a);r.w<=0||r.h<=0||(Mt(l,r.x,r.y,r.w,r.h),y++)}}catch(a){throw Object.assign(new Error("masking-failed"),{cause:a})}if(t.signal?.aborted)throw new Error("navigated");let S;try{S=await lt(v,s,t.maxLongestSide??J)}catch(a){throw v.width=v.height=0,a.name==="SecurityError"?new Error("tainted"):a}return v.width=v.height=0,S?{situation:o?"scope-forced":"ok",blob:S.blob,quality:S.quality,width:Math.round(x.w),height:Math.round(x.h),blindSpots:f,maskedCount:y}:{situation:"engine-failed",blindSpots:f,maskedCount:y}}function K(t,e){return Promise.race([t.catch(n=>n.message==="masking-failed"?{situation:"masking-failed",blindSpots:[],maskedCount:0}:n.message==="navigated"?{situation:"navigated",blindSpots:[],maskedCount:0}:n.message==="tainted"?{situation:"tainted",blindSpots:[],maskedCount:0}:{situation:"engine-failed",blindSpots:[],maskedCount:0}),new Promise(n=>setTimeout(()=>n({situation:"timeout",blindSpots:[],maskedCount:0}),e))])}async function It(t){if(!await ot(t.doc))return{situation:"csp-blocked",blindSpots:[],maskedCount:0};const e=t.scope??"viewport",n=[t.timeoutMs??3e3,1500,1500];let i=await K(W(t,e,!0,!0,!1),Math.min(n[0],1e4));if(i.situation==="ok"||i.situation==="scope-forced"||i.situation==="masking-failed"||i.situation==="navigated"||i.situation==="tainted"||(i=await K(W(t,e,!1,!1,!1),n[1]),i.situation==="ok"||i.situation==="scope-forced"||i.situation==="masking-failed"||i.situation==="navigated"||i.situation==="tainted"))return i;const o=await K(W(t,"element",!1,!1,!0),n[2]);return o.situation==="ok"||o.situation==="scope-forced"?o:o.situation==="engine-failed"&&i.situation==="timeout"?i:o}const $t="screenshot";export{$t as CHUNK,J as MAX_LONGEST_SIDE,Q as TARGET_BYTES,st as canEncodeWebp,It as captureScreenshot,lt as encodeToBudget,ot as probeDataImages,Y as rasterise};
|