Render file to HTML with Firefox extension if Content-Type matches

Viewed 80

I'm trying to define a new filetype for the web, which is rendered to HTML via a browser extension. What I would like to do is have the server return files in my filetype with the HTTP header "Content-Type: text/mytype". The extension will inspect the response headers, and if the content-type matches text/mytype, it will intercept the response body and replace it with the rendered HTML. This gives us a filetype that interacts nicely with the web if you have the plugin installed.

As a proof of concept, I created a plugin working which intercepts the headers and renders them into the response, but it only works if the content-type is text/html. If I change the response content type to text/mytype on the server, Firefox downloads the file, seemingly without running my extension code at all.

Is there a different event I should be listening to, in order to intercept/change headers before and run extension code instead of just downloading the file?

Here's my code:

manifest.json:

{

  "description": "Altering HTTP responses",
  "manifest_version": 2,
  "name": "http-response-filter",
  "version": "1.0",
  "homepage_url": "https://github.com/mdn/webextensions-examples/tree/master/http-response",
  "icons": {
    "48": "pen.svg"
  },

  "permissions": [
    "webRequest", "webRequestBlocking", "<all_urls>"
  ],

  "background": {
    "scripts": ["background.js"]
  },

  "browser_specific_settings": {
    "gecko": {
      "strict_min_version": "57.0a1"
    }
  }

}

background.js:

var contentType;

function contentListener(details) {
  //if(contentType === 'text/mytype') {
    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});

      // Later I want to replace this line with code that parses the
      // document and renders it to HTML
      str = str.replace(/Example/g, 'Content-Type: ' + contentType);

      filter.write(encoder.encode(str));
      filter.disconnect();
    }
  //}

  return {};
}

browser.webRequest.onBeforeRequest.addListener(
  contentListener,
  {urls: ["<all_urls>"], types: ["main_frame"]},
  ["blocking"]
);

function headersListener(details) {
  let headers = details.responseHeaders;
  for(var i = 0; i < headers.length; i++) {
    if(headers[i].name.toLowerCase() === 'content-type') {
      contentType = headers[i].value;

      // Try setting the content type to HTML to prevent downloading--
      // doesn't work
      headers[i].value = 'text/html; charset=utf-8';
    }
  }

browser.webRequest.onBeforeRequest.addListener(
  contentListener,
  {urls: ["<all_urls>"], types: ["main_frame"]},
  ["blocking"]
);
}

browser.webRequest.onHeadersReceived.addListener(
  headersListener, {
    urls: ["<all_urls>"],
    types: ["main_frame"]
  }, ["blocking", "responseHeaders"]
);
1 Answers

You may modify response headers only in onHeadersReceived handler.

If you can't execute onHeadersReceived for your file, then you can't change Content-Type header.

But anyway you could catch a url by content type with use documentUrl in onBeforeRequest handler, fetch data by url in background script and then open new window of your addon where you can render fetched content as you wish.

In this case page with content will have url like moz-extension://blah-blah-blah/pageRenderer.html#https://example.com/fileWithCustomType

Related