Skip to content

Service worker

entrypoints/background.ts routes messages. It is the only component that writes the cache (ADR-003, ADR-004). It starts when it receives a message. Then it does these steps in sequence:

  1. Read the request limits from browser.storage.session.
  2. Find a patreon.com tab.
  3. Make sure that the runner operates in that tab.
  4. Send a request to the runner.
  5. Write the reply to the cache.

The worker never calls Patreon itself. It keeps no state between messages.

How the worker finds the runner. findRunnerTab() finds the tabs that match *://*.patreon.com/* and prefers the active tab. It sends a test message to each tab. A tab that the user opened before the installation has no runner. For such a tab, the worker adds the runner with scripting.executeScript.

findRunnerTab() reports two conditions, because they need two different remedies:

CodeConditionWhat the panel offers
no-patreon-tabNo tab matches the patternOpen Patreon: open a tab, wait for the runner, then sync
patreon-tab-unreachableA tab is open, but its runner does not answerReload the tab (gopher/reload-patreon): reload that tab and select it

Before this separation one code covered both. A user whose tab needed a reload read “open a tab” for a tab that was already open. The button selected the tab that did not operate.

There is no offscreen document. Chrome makes it necessary to declare a reason for an offscreen document, and the reason must agree with the use. No reason in the documentation applies to “fetch with credentials” (see notes about the APIs). Also, each new tab is a tab that the user can see (ADR-005). Thus, if no patreon.com tab is open, the worker replies with no-patreon-tab, and the panel offers to open one. gopher/runner-state gives the same answer without a request to Patreon, and the panel asks it first (see options and lifecycle). The result is a limit: to get new data, a patreon.com tab must be open. To read the cache, no tab is necessary.

The worker examines each message before it does the work. isOwnSender() compares sender.id with browser.runtime.id. asPanelRequest() and asRunnerRequest() in lib/messages.ts examine the type field and each other field. The worker ignores a message that fails an examination and sends no reply. Each listener returns true when the reply comes later. Note that only the panel starts a message to the worker. The worker reaches the runner with tabs.sendMessage, and the runner answers. Thus no message arrives from the patreon.com origin.