title: Chrome extension - how to access local file:// with manifest v3
body: <p>I have a Chrome extension that can (if you allow access to file URLs) grab your local pdf file that you have open in chrome and send it on to our API for processing. This is done by fetching the pdf with <code>XMLHttpRequest</code> to <code>file:///Users/user/whatever/testfile.pdf</code> from the background script.</p>
  <p>When migrating to manifest v3 for a Chrome extension the background script becomes a service worker. In a service worker only <code>fetch</code> is available, not <code>XMLHttpRequest</code>. Problem is, fetch only supports http and https, not file:// urls. So how can I make the same feature of having the Chrome extension fetching/getting the local file?</p>
  <p>EDIT: Things I also tried:</p>
  <ol>
  <li><p>Making the XMLHttpRequest from injected iframe as suggested by answer.
  This gives error <code>net:ERR_UNKNOWN_URL_SCHEME</code> when making the request
  <a href="https://i.stack.imgur.com/5qXUb.png" rel="noreferrer"><img src="https://i.stack.imgur.com/5qXUb.png" alt="net:ERR_UNKNOWN_URL_SCHEME" /></a></p>
  </li>
  <li><p>Making the XMLHttpRequest from injected content script.
  This gives error <code>Access to XMLHttpRequest at 'file:///.../testfile1.docx.pdf' from origin 'null' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, chrome, chrome-extension, chrome-untrusted, https.</code>
  <a href="https://i.stack.imgur.com/TCzRP.png" rel="noreferrer"><img src="https://i.stack.imgur.com/TCzRP.png" alt="Access to XMLHttpRequest at 'file:///Users/clara.attermo/Downloads/testfile1.docx.pdf' from origin 'null' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, chrome, chrome-extension, chrome-untrusted, https." /></a></p>
  </li>
  </ol>
  <p><strong>From what I can understand from a lot of research access to <code>file://</code> is in general blocked and Chrome extension background scripts used to be an exception to this. Seems to me it was never allowed from content scripts or action popups.</strong></p>
  <p>My manifest.json for reference:</p>
  <pre class="lang-json prettyprint-override"><code>{
    &quot;manifest_version&quot;: 3,
    &quot;name&quot;: &quot;..&quot;,
    &quot;version&quot;: &quot;0.1&quot;,
    &quot;icons&quot;: {
      &quot;16&quot;: &quot;assets/icon-16x16.png&quot;,
      &quot;48&quot;: &quot;assets/icon-48x48.png&quot;,
      &quot;128&quot;: &quot;assets/icon-128x128.png&quot;
    },
    &quot;action&quot;: {
      &quot;default_title&quot;: &quot;..&quot;
    },
    &quot;background&quot;: {
      &quot;service_worker&quot;: &quot;background.js&quot;
    },
    &quot;permissions&quot;: [
      &quot;webRequest&quot;,
      &quot;activeTab&quot;,
      &quot;scripting&quot;,
      &quot;storage&quot;,
      &quot;unlimitedStorage&quot;,
      &quot;identity&quot;,
      &quot;pageCapture&quot;
    ],
    &quot;host_permissions&quot;: [
      &quot;&lt;all_urls&gt;&quot;
    ],
    &quot;web_accessible_resources&quot;: [{
      &quot;resources&quot;: [&quot;iframe.html&quot;],
      &quot;matches&quot;: [],
      &quot;extension_ids&quot;: []
    }]
  }
  </code></pre>
  <p>The content script is injected programmatically (using <code>webextension-polyfill</code> for promise support)</p>
  <pre class="lang-js prettyprint-override"><code>browser.action.onClicked.addListener(async (tab: Tab) =&gt; {
    await browser.scripting.executeScript({files: [ &quot;inject.js&quot; ], target: {tabId: tab.id}});
  });
  </code></pre>