Working
This commit is contained in:
182
page-probe.js
Normal file
182
page-probe.js
Normal file
@@ -0,0 +1,182 @@
|
||||
"use strict";
|
||||
|
||||
(function initPageProbe() {
|
||||
const EVENT_SOURCE = "S3_CAPTURE_PROBE";
|
||||
let lastAudioResponseUrl = "";
|
||||
|
||||
function emit(kind, payload) {
|
||||
try {
|
||||
window.postMessage(
|
||||
{
|
||||
source: EVENT_SOURCE,
|
||||
kind,
|
||||
payload: payload || {}
|
||||
},
|
||||
"*"
|
||||
);
|
||||
} catch (error) {
|
||||
// Ignore probe emission errors.
|
||||
}
|
||||
}
|
||||
|
||||
function safeString(value) {
|
||||
return typeof value === "string" ? value : "";
|
||||
}
|
||||
|
||||
function markLastAudioUrl(url, contentType) {
|
||||
const maybeUrl = safeString(url);
|
||||
const maybeType = safeString(contentType).toLowerCase();
|
||||
if (!maybeUrl) {
|
||||
return;
|
||||
}
|
||||
if (maybeType.startsWith("audio/") || maybeUrl.includes("audio_samples/")) {
|
||||
lastAudioResponseUrl = maybeUrl;
|
||||
}
|
||||
}
|
||||
|
||||
function patchFetch() {
|
||||
if (typeof window.fetch !== "function") {
|
||||
return;
|
||||
}
|
||||
const currentFetch = window.fetch;
|
||||
if (currentFetch.__s3CapturePatched) {
|
||||
return;
|
||||
}
|
||||
|
||||
const wrappedFetch = function wrappedFetch(input, init) {
|
||||
const requestUrl =
|
||||
typeof input === "string"
|
||||
? input
|
||||
: input && typeof input.url === "string"
|
||||
? input.url
|
||||
: "";
|
||||
|
||||
emit("fetch-request", { requestUrl });
|
||||
|
||||
return currentFetch.call(this, input, init).then(
|
||||
(response) => {
|
||||
const responseUrl = response && typeof response.url === "string" ? response.url : "";
|
||||
const contentType = response && response.headers ? response.headers.get("content-type") : "";
|
||||
markLastAudioUrl(responseUrl, contentType);
|
||||
emit("fetch-response", {
|
||||
requestUrl,
|
||||
responseUrl,
|
||||
status: response ? response.status : 0,
|
||||
contentType: contentType || ""
|
||||
});
|
||||
return response;
|
||||
},
|
||||
(error) => {
|
||||
emit("fetch-error", {
|
||||
requestUrl,
|
||||
error: error && error.message ? error.message : "fetch failed"
|
||||
});
|
||||
throw error;
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
wrappedFetch.__s3CapturePatched = true;
|
||||
window.fetch = wrappedFetch;
|
||||
}
|
||||
|
||||
function patchXhr() {
|
||||
if (!window.XMLHttpRequest || !window.XMLHttpRequest.prototype) {
|
||||
return;
|
||||
}
|
||||
|
||||
const proto = window.XMLHttpRequest.prototype;
|
||||
if (proto.open.__s3CapturePatched) {
|
||||
return;
|
||||
}
|
||||
|
||||
const originalOpen = proto.open;
|
||||
const originalSend = proto.send;
|
||||
|
||||
proto.open = function patchedOpen(method, url) {
|
||||
this.__s3CaptureRequestUrl = safeString(url);
|
||||
emit("xhr-open", {
|
||||
method: safeString(method),
|
||||
requestUrl: this.__s3CaptureRequestUrl
|
||||
});
|
||||
return originalOpen.apply(this, arguments);
|
||||
};
|
||||
proto.open.__s3CapturePatched = true;
|
||||
|
||||
proto.send = function patchedSend() {
|
||||
const xhr = this;
|
||||
xhr.addEventListener(
|
||||
"loadend",
|
||||
function onLoadEnd() {
|
||||
const responseUrl = safeString(xhr.responseURL);
|
||||
const contentType = safeString(xhr.getResponseHeader("content-type"));
|
||||
markLastAudioUrl(responseUrl || xhr.__s3CaptureRequestUrl, contentType);
|
||||
emit("xhr-response", {
|
||||
requestUrl: safeString(xhr.__s3CaptureRequestUrl),
|
||||
responseUrl,
|
||||
status: xhr.status || 0,
|
||||
contentType
|
||||
});
|
||||
},
|
||||
{ once: true }
|
||||
);
|
||||
return originalSend.apply(this, arguments);
|
||||
};
|
||||
}
|
||||
|
||||
function patchCreateObjectURL() {
|
||||
if (!window.URL || typeof window.URL.createObjectURL !== "function") {
|
||||
return;
|
||||
}
|
||||
const current = window.URL.createObjectURL;
|
||||
if (current.__s3CapturePatched) {
|
||||
return;
|
||||
}
|
||||
|
||||
const wrapped = function wrappedCreateObjectURL(blob) {
|
||||
const objectUrl = current.call(this, blob);
|
||||
const blobType = blob && typeof blob.type === "string" ? blob.type : "";
|
||||
const blobSize = blob && typeof blob.size === "number" ? blob.size : 0;
|
||||
emit("blob-created", {
|
||||
blobUrl: objectUrl,
|
||||
blobType,
|
||||
blobSize,
|
||||
linkedUrl: lastAudioResponseUrl
|
||||
});
|
||||
return objectUrl;
|
||||
};
|
||||
|
||||
wrapped.__s3CapturePatched = true;
|
||||
window.URL.createObjectURL = wrapped;
|
||||
}
|
||||
|
||||
function patchDecodeAudioData(ctor) {
|
||||
if (!ctor || !ctor.prototype || typeof ctor.prototype.decodeAudioData !== "function") {
|
||||
return;
|
||||
}
|
||||
const current = ctor.prototype.decodeAudioData;
|
||||
if (current.__s3CapturePatched) {
|
||||
return;
|
||||
}
|
||||
|
||||
const wrapped = function wrappedDecodeAudioData(audioData) {
|
||||
const byteLength =
|
||||
audioData && typeof audioData.byteLength === "number" ? audioData.byteLength : 0;
|
||||
emit("decode-audio-data", {
|
||||
byteLength,
|
||||
linkedUrl: lastAudioResponseUrl
|
||||
});
|
||||
return current.apply(this, arguments);
|
||||
};
|
||||
|
||||
wrapped.__s3CapturePatched = true;
|
||||
ctor.prototype.decodeAudioData = wrapped;
|
||||
}
|
||||
|
||||
patchFetch();
|
||||
patchXhr();
|
||||
patchCreateObjectURL();
|
||||
patchDecodeAudioData(window.AudioContext);
|
||||
patchDecodeAudioData(window.OfflineAudioContext);
|
||||
emit("probe-ready", { href: window.location.href });
|
||||
})();
|
||||
Reference in New Issue
Block a user