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:
- Read the request limits from
browser.storage.session. - Find a
patreon.comtab. - Make sure that the runner operates in that tab.
- Send a request to the runner.
- 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:
| Code | Condition | What the panel offers |
|---|---|---|
no-patreon-tab | No tab matches the pattern | Open Patreon: open a tab, wait for the runner, then sync |
patreon-tab-unreachable | A tab is open, but its runner does not answer | Reload 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.