Render a modified MHTML file in an iframe from a content script

151 views
Skip to first unread message

Tamar Y

unread,
Jul 29, 2025, 2:53:29 PMJul 29
to Chromium Extensions

I'm working on a Chrome extension (Manifest V3) where I:

  1. Capture the current tab using chrome.pageCapture.saveAsMHTML from the background page.

  2. Send the MHTML data to an offscreen document, where I:

    • Parse and extract the HTML from the MHTML.

    • Modify the DOM (e.g. replacing <h1> content).

    • Reconstruct the MHTML with the modified HTML.

    • Create a Blob from the new MHTML and generate a blob: URL.

  3. I then send the new blob: URL back to the content script and try to embed it in an iframe:

    However, the iframe loads but its contentDocument.body is null — it doesn’t render anything.

Is there any way to render an MHTML file (a modified one) inside an <iframe> in a content script or extension page?

  • Is the restriction on iframes absolute (even within chrome-extension:// contexts)?

Background Page (snippet)

chrome.pageCapture.saveAsMHTML({ tabId }, mhtmlData => {
  const url = chrome.runtime.getURL('offscreen/mhtml-offscreen.html');

  setupOffscreenDocument(url).then(async () => {
    const client = (await (globalThis.clients as any).matchAll({ includeUncontrolled: true }))
      .find(c => c.url === url);

    const mc = new MessageChannel();
    chrome.runtime.onMessage.addListener((responseMsg) => {
      if (responseMsg.type === 'MHTML_PROCESSED') {
        chrome.runtime.sendMessage({
          type: 'SEND_TO_CONTENT_SCRIPT',
          blobURL: responseMsg.data.blobURL
        });
      }
    });

    client.postMessage(mhtmlData, [mc.port2]);
  });
});

Offscreen Document:

navigator.serviceWorker.onmessage = async e => {
  const blobUrl = URL.createObjectURL(e.data);
  const mhtmlText = await fetch(blobUrl).then(r => r.text());
  const htmlContent = extractHTMLFromMHTML(mhtmlText);

  const doc = new DOMParser().parseFromString(htmlContent, 'text/html');

  // Modify the DOM

  const modifiedHTML = doc.documentElement.outerHTML;
  const modifiedMHTML = reconstructMHTMLWithModifiedHTML(mhtmlText, modifiedHTML);

  const modifiedBlob = new Blob([modifiedMHTML], {
    type: 'application/x-mimearchive'
  });
  const modifiedBlobURL = URL.createObjectURL(modifiedBlob);

  chrome.runtime.sendMessage({
    type: 'MHTML_PROCESSED',
    data: {
      success: true,
      blobURL: modifiedBlobURL
    }
  });
};

The rest of the code sends a message from the bg page to the content script that creates an iframe with iframe src = blob URL. 

(My purpose is to persist the modified MHTML blob, for example by uploading it to S3, and later load and render it again (e.g. in an iframe), but first want to check that I can load it inside an iframe.)

Is there a supported way to render MHTML in an iframe or a workaround? I'd be happy to hear. 

Thanks in advance!

woxxom

unread,
Jul 30, 2025, 3:06:04 AMJul 30
to Chromium Extensions, Tamar Y
Show your code in the content script that creates/modifies the iframe. So far it sounds like you're trying to to access a cross-origin contentDocument or contentWindow directly, which is not possible, in which case you need to set the html either when creating the iframe via its srcdoc attribute or by having a content script instance running inside that iframe, which will be able to access its `document` directly.

Tamar Y

unread,
Jul 30, 2025, 6:15:21 AMJul 30
to Chromium Extensions, woxxom, Tamar Y
This is the code in the content script that creats the iframe with the modified blob:

chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
    if (message.type === 'MHTML_PROCESSED') {
        const { blobURL } = message.data;

        // Create an iframe and set its src to the blob URL
        const iframe = document.createElement('iframe');
        iframe.src = blobURL;
        iframe.style.width = '100%';
        iframe.style.height = '100vh';
        iframe.style.border = 'none';
        iframe.style.zIndex = '9999';
        iframe.style.position = 'fixed';
        iframe.style.top = '0';
        iframe.style.left = '0';
        iframe.style.backgroundColor = 'white';

        // Append the iframe to the body
        document.body.appendChild(iframe);
    }
});

 I tried using iframe.srcdoc with the processed HTML, but the page rendered without styling — likely because external CSS and resources from the original MHTML weren’t included. (I got a broken page)  

woxxom

unread,
Jul 30, 2025, 9:04:32 AMJul 30
to Chromium Extensions, Tamar Y, woxxom
So yeah, my guess was correct: this is a cross-origin iframe so you need to run a separate content script inside which you can do using executeScript with `allFrames:true` parameter and either `files` or `func` to inject a function's code with arguments passed via `args`. This injected code will be see `document` directly as it'll run inside the iframe.
Reply all
Reply to author
Forward
0 new messages