ferridriver 0.5.0

Browser automation in Rust with a Playwright-compatible API. Four pluggable backends: CDP pipe, CDP WebSocket, Playwright WebKit, Firefox BiDi.
Documentation
1
(()=>{function I(z){if(z.__pwWebSocketDispatch)return;function J(){let j=new Uint8Array(32);z.crypto.getRandomValues(j);let _="0123456789abcdef";return[...j].map((E)=>{let P=Math.floor(E/16),Q=E%16;return _[P]+_[Q]}).join("")}function B(j){let _="";for(let E=0;E<j.length;E++)_+=String.fromCharCode(j[E]);return{data:z.btoa(_),isBase64:!0}}function K(j){j=z.atob(j);let _=new Uint8Array(j.length);for(let E=0;E<j.length;E++)_[E]=j.charCodeAt(E);return _.buffer}function G(j,_){if(j instanceof z.Blob)return j.arrayBuffer().then((E)=>_(B(new Uint8Array(E))));if(typeof j==="string")return _({data:j,isBase64:!1});if(ArrayBuffer.isView(j))return _(B(new Uint8Array(j.buffer,j.byteOffset,j.byteLength)));return _(B(new Uint8Array(j)))}function H(j,_){if(!j.isBase64)return j.data;let E=K(j.data);return _==="arraybuffer"?E:new Blob([E])}let{__pwWebSocketBinding:A,WebSocket:O}=z,F=new Map;z.__pwWebSocketDispatch=(j)=>{let _=F.get(j.id);if(!_)return;if(j.type==="connect")_._apiConnect();if(j.type==="passthrough")_._apiPassThrough();if(j.type==="ensureOpened")_._apiEnsureOpened();if(j.type==="sendToPage")_._apiSendToPage(H(j.data,_.binaryType));if(j.type==="closePage")_._apiClosePage(j.code,j.reason,j.wasClean);if(j.type==="sendToServer")_._apiSendToServer(H(j.data,_.binaryType));if(j.type==="closeServer")_._apiCloseServer(j.code,j.reason,j.wasClean)};class R extends EventTarget{static CONNECTING=0;static OPEN=1;static CLOSING=2;static CLOSED=3;CONNECTING=0;OPEN=1;CLOSING=2;CLOSED=3;_oncloseListener=null;_onerrorListener=null;_onmessageListener=null;_onopenListener=null;bufferedAmount=0;extensions="";protocol="";readyState=0;url;_id;_origin="";_protocols;_ws;_passthrough=!1;_wsBufferedMessages=[];_binaryType="blob";constructor(j,_){super();this.url=new URL(j,z.window.document.baseURI).href.replace(/^http/,"ws"),this._origin=URL.parse(this.url)?.origin??"",this._protocols=_,this._id=J(),F.set(this._id,this);let E=Array.isArray(_)?[..._]:_?[_]:[];A({type:"onCreate",id:this._id,url:this.url,protocols:E})}get binaryType(){return this._binaryType}set binaryType(j){if(this._binaryType=j,this._ws)this._ws.binaryType=j}get onclose(){return this._oncloseListener}set onclose(j){if(this._oncloseListener)this.removeEventListener("close",this._oncloseListener);if(this._oncloseListener=j,this._oncloseListener)this.addEventListener("close",this._oncloseListener)}get onerror(){return this._onerrorListener}set onerror(j){if(this._onerrorListener)this.removeEventListener("error",this._onerrorListener);if(this._onerrorListener=j,this._onerrorListener)this.addEventListener("error",this._onerrorListener)}get onopen(){return this._onopenListener}set onopen(j){if(this._onopenListener)this.removeEventListener("open",this._onopenListener);if(this._onopenListener=j,this._onopenListener)this.addEventListener("open",this._onopenListener)}get onmessage(){return this._onmessageListener}set onmessage(j){if(this._onmessageListener)this.removeEventListener("message",this._onmessageListener);if(this._onmessageListener=j,this._onmessageListener)this.addEventListener("message",this._onmessageListener)}send(j){if(this.readyState===R.CONNECTING)throw new DOMException("Failed to execute 'send' on 'WebSocket': Still in CONNECTING state.");if(this.readyState!==R.OPEN)throw new DOMException("WebSocket is already in CLOSING or CLOSED state.");if(this._passthrough){if(this._ws)this._apiSendToServer(j)}else G(j,(_)=>A({type:"onMessageFromPage",id:this._id,data:_}))}close(j,_){if(j!==void 0&&j!==1000&&(j<3000||j>4999))throw new DOMException(`Failed to execute 'close' on 'WebSocket': The close code must be either 1000, or between 3000 and 4999. ${j} is neither.`);if(this.readyState===R.OPEN||this.readyState===R.CONNECTING)this.readyState=R.CLOSING;if(this._passthrough)this._apiCloseServer(j,_,!0);else A({type:"onClosePage",id:this._id,code:j,reason:_,wasClean:!0})}_apiEnsureOpened(){if(!this._ws)this._ensureOpened()}_apiSendToPage(j){if(this._ensureOpened(),this.readyState!==R.OPEN)throw new DOMException("WebSocket is already in CLOSING or CLOSED state.");this.dispatchEvent(new MessageEvent("message",{data:j,origin:this._origin,cancelable:!0}))}_apiSendToServer(j){if(!this._ws)throw Error("Cannot send a message before connecting to the server");if(this._ws.readyState===R.CONNECTING)this._wsBufferedMessages.push(j);else this._ws.send(j)}_apiConnect(){if(this._ws)throw Error("Can only connect to the server once");this._ws=new O(this.url,this._protocols),this._ws.binaryType=this._binaryType,this._ws.onopen=()=>{for(let j of this._wsBufferedMessages)this._ws.send(j);this._wsBufferedMessages=[],this._ensureOpened()},this._ws.onclose=(j)=>{this._onWSClose(j.code,j.reason,j.wasClean)},this._ws.onmessage=(j)=>{if(this._passthrough)this._apiSendToPage(j.data);else G(j.data,(_)=>A({type:"onMessageFromServer",id:this._id,data:_}))},this._ws.onerror=()=>{let j=new Event("error",{cancelable:!0});this.dispatchEvent(j)}}_apiPassThrough(){this._passthrough=!0,this._apiConnect()}_apiCloseServer(j,_,E){if(!this._ws){this._onWSClose(j,_,E);return}if(this._ws.readyState===R.CONNECTING||this._ws.readyState===R.OPEN)this._ws.close(j,_)}_apiClosePage(j,_,E){if(this.readyState===R.CLOSED)return;if(this.readyState=R.CLOSED,this.dispatchEvent(new CloseEvent("close",{code:j,reason:_,wasClean:E,cancelable:!0})),this._maybeCleanup(),this._passthrough)this._apiCloseServer(j,_,E);else A({type:"onClosePage",id:this._id,code:j,reason:_,wasClean:E})}_ensureOpened(){if(this.readyState!==R.CONNECTING)return;if(this.extensions=this._ws?.extensions||"",this._ws)this.protocol=this._ws.protocol;else if(Array.isArray(this._protocols))this.protocol=this._protocols[0]||"";else this.protocol=this._protocols||"";this.readyState=R.OPEN,this.dispatchEvent(new Event("open",{cancelable:!0}))}_onWSClose(j,_,E){if(this._passthrough)this._apiClosePage(j,_,E);else A({type:"onCloseServer",id:this._id,code:j,reason:_,wasClean:E});if(this._ws)this._ws.onopen=null,this._ws.onclose=null,this._ws.onmessage=null,this._ws.onerror=null,this._ws=void 0,this._wsBufferedMessages=[];this._maybeCleanup()}_maybeCleanup(){if(this.readyState===R.CLOSED&&!this._ws)F.delete(this._id)}}z.WebSocket=class extends R{}}I(globalThis);})();