Chrome Extension Development: Fetch non CORS URLs in v3
Tags: automation, chromeextension, chrome
Published: 2023-01-15 08:29:53

---
title: Chrome Extension Development: Fetch non CORS URLs in v3
tags: [automation, chromeextension, chrome]
canonical_url: https://keestalkstech.com/2023/01/chrome-extension-development-fetch-non-cors-urls-in-v3/
---

As browser security improves, many applications have implemented [CORS headers](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) to protect against unauthorized access. I'm working on a Chrome extension that needs to check if a certain URL is present on GitHub, before it opens the URL in a new tab. Let's explore what we can do.

## [The Manifest](#the-manifest)

Due to CORS headers, it is not possible to perform a fetch request for the `HEAD` method on a domain that is not included in the headers. However, by utilizing a worker script (in Chrome Extension version 3), it is possible to bypass this restriction. Let's configure the manifest:

```js
{
  "manifest_version": 3,
  "name": "My Extension Name",
  "description": "My Extension Description",
  "version": "1.0",
  "content_scripts": [
    {
      "matches": [ "https://*/*" ],
      "js": ["content.js"]
    }
  ],
  "background": {
    "service_worker": "background.js"
  },
  "permissions": ["activeTab"]
  "host_permissions": ["https://github.com/*"]
}
```

It's important to note that the `host_permissions` array in the manifest file needs to include the URL that the extension will inspect, in this case "github.com" must be added in the array. If you fail to do so, you won't ever be able to fetch the data!

## [The Background Script](#the-background-script)

Interaction between the content and background scripts can be achieved through [message passing](https://developer.chrome.com/docs/extensions/mv3/messaging/). In our `background.js` script we'll add an event listener.

```js
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
  if (request.checkIfUrlExists) {
    fetch(request.checkIfUrlExists, { method: "HEAD" })
      .then(response => sendResponse({ status: response.ok }))
      .catch(error => sendResponse({ status: false, error: JSON.stringify(error) }))
  }
  return true
})
```

## [The Content Script](#the-content-script)

With the background script set up, we can use the `chrome.runtime.sendMessage` API to send messages to it. To make it easier to use [async/await](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async%5Ffunction) in our extension, we'll wrap the call in a [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global%5FObjects/Promise):

```js
async function checkIfUrlExists(url) {
  const response = await new Promise(resolve => {
    chrome.runtime.sendMessage({ checkIfUrlExists: url }, resolve)
  })
  return response.status
}
```

## [Resolve URLs](#resolve-urls)

My extension needs to check if a certain URL is present on GitHub. Because I don't know the exact name of the file, I'll check multiple possibilities and return the first that has a successful response status:

```js
async function resolveUrl(name) {
  let repo = "https://github.com/keescbakker/my-workload-services"
  let commits = repo + "/commits/master/apps/applicationsets"

  let urls = [
    `${commits}/${name}.yml`,
    `${commits}/${name}.yaml`,
    `${commits}/${name}-service.yml`,
    `${commits}/${name}-service.yaml`,
    `${commits}/${name}-site.yml`,
    `${commits}/${name}-site.yaml`
  ]

  for (let url of urls) {
    if (await checkIfUrlExists(url)) {
      return url
    }
  }

  return null
}
```

Enjoy!