An event handler that will be called repeatedly when response data is available. The handler is passed an event
object which contains a data
property, which contains a chunk of the response data as an ArrayBuffer
.
To decode the data use either TextDecoder
or Blob
.
This example adds an ondata
listener which replaces "WebExtension Example" in the response with "WebExtension WebExtension Example" using the replace()
method.
Note: This example only works for occurrences of "WebExtension Example" that are entirely contained within a data chunk, and not ones that straddle two chunks (which might happen ~0.1% of the time for large documents). Additionally it only deals with UTF-8-coded documents. A real implementation of this would have to be more complex.
function listener(details) { let filter = browser.webRequest.filterResponseData(details.requestId); let decoder = new TextDecoder("utf-8"); let encoder = new TextEncoder(); filter.ondata = event => { let str = decoder.decode(event.data, {stream: true}); // Just change any instance of WebExtension Example in the HTTP response // to WebExtension WebExtension Example. str = str.replace(/WebExtension Example/g, 'WebExtension WebExtension Example'); filter.write(encoder.encode(str)); // Doing filter.disconnect(); here would make us process only // the first chunk, and let the rest through unchanged. Note // that this would break multi-byte characters that occur on // the chunk boundary! } filter.onstop = event => { filter.close(); } //return {}; // not needed } browser.webRequest.onBeforeRequest.addListener( listener, {urls: ["https://example.com/*"], types: ["main_frame"]}, ["blocking"] );
Another example for handling large documents:
function listener(details) { let filter = browser.webRequest.filterResponseData(details.requestId); let decoder = new TextDecoder("utf-8"); let encoder = new TextEncoder(); let data = []; filter.ondata = event => { data.push(event.data); }; filter.onstop = event => { let str = ""; if (data.length == 1) { str = decoder.decode(data[0]); } else { for (let i = 0; i < data.length; i++) { let stream = (i == data.length - 1) ? false : true; str += decoder.decode(data[i], {stream}); } } // Just change any instance of WebExtension Example in the HTTP response // to WebExtension WebExtension Example. str = str.replace(/WebExtension Example/g, 'WebExtension $&'); filter.write(encoder.encode(str)); filter.close(); }; } browser.webRequest.onBeforeRequest.addListener( listener, {urls: ["https://example.com/"], types: ["main_frame"]}, ["blocking"] );
Here's another version:
function listener(details) { let filter = browser.webRequest.filterResponseData(details.requestId); let decoder = new TextDecoder("utf-8"); let encoder = new TextEncoder(); let data = []; filter.ondata = event => { data.push(event.data); }; filter.onstop = event => { let str = ""; for (let buffer of data) { str += decoder.decode(buffer, {stream: true}); } str += decoder.decode(); // end-of-stream // Just change any instance of WebExtension Example in the HTTP response // to WebExtension WebExtension Example. str = str.replace(/WebExtension Example/g, 'WebExtension $&'); filter.write(encoder.encode(str)); filter.close(); }; } browser.webRequest.onBeforeRequest.addListener( listener, {urls: ["https://example.com/"], types: ["main_frame"]}, ["blocking"] );
The above example can also be written like so:
function listener(details) { let filter = browser.webRequest.filterResponseData(details.requestId); let decoder = new TextDecoder("utf-8"); let encoder = new TextEncoder(); let data = []; filter.ondata = event => { data.push(decoder.decode(event.data, {stream: true})); }; filter.onstop = event => { data.push(decoder.decode()); let str = data.join(""); // Just change any instance of WebExtension Example in the HTTP response // to WebExtension WebExtension Example. str = str.replace(/WebExtension Example/g, 'WebExtension $&'); filter.write(encoder.encode(str)); filter.close(); }; } browser.webRequest.onBeforeRequest.addListener( listener, {urls: ["https://example.com/"], types: ["main_frame"]}, ["blocking"] );
This example uses a Blob
:
function listener(details) { let filter = browser.webRequest.filterResponseData(details.requestId); let encoder = new TextEncoder(); let data = []; filter.ondata = event => { data.push(event.data); }; filter.onstop = async event => { let blob = new Blob(data, {type: 'text/html'}); let str = await blob.text(); // Just change any instance of WebExtension Example in the HTTP response // to WebExtension WebExtension Example. str = str.replace(/WebExtension Example/g, 'WebExtension $&'); filter.write(encoder.encode(str)); filter.close(); }; } browser.webRequest.onBeforeRequest.addListener( listener, {urls: ["https://example.com/"], types: ["main_frame"]}, ["blocking"] );
This example makes use of the DOMParser
interface:
function listener(details) { let filter = browser.webRequest.filterResponseData(details.requestId); let encoder = new TextEncoder(); let parser = new DOMParser(); let data = []; filter.ondata = event => { data.push(event.data); }; filter.onstop = async event => { let blob = new Blob(data, {type: 'text/html'}); let str = await blob.text(); let doc = parser.parseFromString(str, blob.type); let nodes = doc.querySelectorAll("title, h1"); for (let node of nodes) { node.innerText = node.innerText.replace('WebExtension Example', 'WebExtension $&'); } filter.write(encoder.encode(doc.documentElement.outerHTML)); filter.close(); }; } browser.webRequest.onBeforeRequest.addListener( listener, {urls: ["https://example.com/"], types: ["main_frame"]}, ["blocking"] );
This example combines all buffers into a single buffer:
function listener(details) { let filter = browser.webRequest.filterResponseData(details.requestId); let decoder = new TextDecoder("utf-8"); let encoder = new TextEncoder(); let data = []; filter.ondata = event => { data.push(new Uint8Array(event.data)); }; filter.onstop = event => { let combinedLength = 0; for (let buffer of data) { combinedLength += buffer.length; } let combinedArray = new Uint8Array(combinedLength); let writeOffset = 0; for (let buffer of data) { combinedArray.set(buffer, writeOffset); writeOffset += buffer.length; } let str = decoder.decode(combinedArray); // Just change any instance of WebExtension Example in the HTTP response // to WebExtension WebExtension Example. str = str.replace(/WebExtension Example/g, 'WebExtension $&'); filter.write(encoder.encode(str)); filter.close(); }; } browser.webRequest.onBeforeRequest.addListener( listener, {urls: ["https://example.com/"], types: ["main_frame"]}, ["blocking"] );
The above example can also be written like so:
function listener(details) { let filter = browser.webRequest.filterResponseData(details.requestId); let decoder = new TextDecoder('utf-8'); let encoder = new TextEncoder(); let data = []; filter.ondata = event => { data.push(event.data); }; filter.onstop = async event => { let blob = new Blob(data, {type: 'text/html'}); let buffer = await blob.arrayBuffer(); let str = decoder.decode(buffer); str = str.replace(/WebExtension Example/g, 'WebExtension $&'); filter.write(encoder.encode(str)); filter.close(); }; } browser.webRequest.onBeforeRequest.addListener( listener, {urls: ["https://example.com/"], types: ["main_frame"]}, ["blocking"] );
This example demonstrates, how one can detect, if it's the final chunk in the response:
function listener(details) { let filter = browser.webRequest.filterResponseData(details.requestId); let encoder = new TextEncoder(); let decoder = new TextDecoder("utf-8"); let str = ""; filter.ondata = event => { let stream = true; let data = new Uint8Array(event.data.slice(-8, -1)); if (String.fromCharCode(...data) == "</html>") { stream = false; // end-of-stream } str += decoder.decode(event.data, {stream}); }; filter.onstop = event => { // Just change any instance of WebExtension Example in the HTTP response // to WebExtension WebExtension Example. str = str.replace(/WebExtension Example/g, 'WebExtension $&'); filter.write(encoder.encode(str)); filter.close(); }; } browser.webRequest.onBeforeRequest.addListener( listener, {urls: ["https://example.com/"], types: ["main_frame"]}, ["blocking"] );
Desktop | Mobile | |||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|
Chrome | Edge | Firefox | Internet Explorer | Opera | Safari | WebView Android | Chrome Android | Firefox for Android | Opera Android | Safari on IOS | Samsung Internet | |
ondata |
No |
No |
57 |
? |
No |
No |
? |
? |
57 |
? |
? |
? |
© 2005–2021 MDN contributors.
Licensed under the Creative Commons Attribution-ShareAlike License v2.5 or later.
https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/webRequest/StreamFilter/ondata