Comment

Comment by maxloh

No, it is possible with extensions.

Extensions can inject headers, such as Access-Control-Allow-Origin: *, to unblock cross-origin requests. In the Manifest V3 context, however, that might require patching window.fetch and window.XMLHttpRequest.

For example,

  // content.js
  window.fetch = async (...args) => {
    const request = args[0] instanceof Request ? args[0].url : args[0]
    const config = args[1] || {}

    return new Promise((resolve, reject) => {
      chrome.runtime.sendMessage({ action: "proxyFetch", request, config }, response => {
        if (response.error) {
          const err = new Error(response.error.message)
          err.name = response.error.name
          err.stack = response.error.stack
          if (response.error.cause) err.cause = response.error.cause
          reject(err)
        } else {
          const base64Data = response.dataUrl.split(",")[1]
          const bytes = Uint8Array.from(atob(base64Data), c => c.charCodeAt(0))
          const contentType = response.headers["content-type"] || "application/octet-stream"
          const blob = new Blob([bytes], { type: contentType })

          const status = response.status
          const statusText = response.statusText
          const headers = new Headers(response.headers)
          const body = status === 204 || status === 205 || status === 304 ? null : blob
          resolve(new Response(body, { status, statusText, headers }))
        }
      })
    })
  }

  // Background.js
  chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
    if (message.action === "proxyFetch") {
      fetch(message.request, message.config)
        .then(async res => {
          const headers = Object.fromEntries(res.headers.entries())
          const blob = await res.blob()
          const reader = new FileReader()
          reader.onloadend = () =>
            sendResponse({ status: res.status, statusText: res.statusText, headers, dataUrl: reader.result })
          reader.readAsDataURL(blob)
        })
        .catch(err => {
          const { name, message, code, stack } = err
          sendResponse({ error: { name, message, code, stack } })
        })

      // Keeps the message channel open for the async fetch
      return true
    }
  })
parent maxloh 3 months ago

Discussion

Live from the official Hacker News API

A modern reader for Hacker News · SvelteKit + Skeleton