I'm not sure that the original asker still cares about a question from 7 years ago, but it is possible to redirect (or cancel) a web request and return HTML from browser.storage
, preventing the network request.
manifest.json
...
"background": {
"scripts": [
"background.js"
]
},
"permissions": [
"storage",
"webRequest",
"webRequestBlocking",
"*://*/*"
],
"web_accessible_resources": [
"content.htm"
]
...
background.js
( function () {
'use strict';
var
baseURL = browser.runtime.getURL( 'content.htm' ) + '?q=';
// filter requests by target
// redirect into extension if hit
function onRequest( e ) {
var
rc = {};
// in practice, compare e.url to more than one value
if ( e.url === 'https://www.youtube.com/watch?v=9xp1XWmJ_Wo' ) {
rc = { redirectUrl: baseURL + encodeURIComponent( e.url ) };
}
return rc;
}
browser.webRequest.onBeforeRequest.addListener(
onRequest, {
types: [ 'main_frame' ],
urls: [ '*://*/*' ]
}, [
'blocking'
]
);
} () );
content.htm
<!DOCTYPE html><html lang="en"><head>
<title>Placeholder</title>
<script src="content.js"></script>
</head><body>
</body></html>
content.js
( function () {
'use strict';
var
url = decodeURIComponent( location.search.replace( /^\?q=/, '' ) );
// storage is keyed on the original url
browser.storage.local.get( url ).then( function ( o ) {
var
html = o[ url ],
newDOM;
if ( html ) {
document.documentElement.textContent = '';
newDOM = new DOMParser().parseFromString( html, 'text/html' );
newDOM = newDOM.firstElementChild; // <html>
while ( newDOM.children.length ) {
document.documentElement.appendChild( newDOM.firstElementChild );
}
} else {
document.body.textContent =
'No content found for: ' + url;
}
}, function ( err ) {
document.body.textContent =
'Error fetching storage: ' + err.name + ' ' + err.message;
console.error( err );
} );
} () );
In background.js
, intercept the web requests and compare the URL to known URLs. (This example code uses only one URL, a particular YouTube video.) If there's a match, redirect the web request into the extension, using the original URL as a query. The HTML document in the extension uses the query to look in storage for replacement HTML, which it parses and transfers into the DOM.
For this example, set the extension storage (e.g., in the debug console) for the URL and replacement HTML with:
browser.storage.local.set( { 'https://www.youtube.com/watch?v=9xp1XWmJ_Wo':
'<head><title>Stack Overflow Answer</title></head><body><p>This is not a YouTube video.</p></body>' } )
It's everything that would go inside the <html>
tags, without the <html>
tags. The Parser puts them in, anyway, but the code has to throw them away, because document.documentElement
cannot be replaced.
Then go to https://www.youtube.com/watch?v=9xp1XWmJ_Wo
. The replacement HTML is displayed, instead.
The code above only works for HTML in a main frame, but it proves the concept. It relies on the fact that JavaScript in a page served from the extension runs with elevated privilege, so it has access to browser.storage
.