Derive capture scope from host_permissions, drop the target URL setting
The popup's target URL pattern was doing two jobs. As a fallback for locate requests it is now dead — every step carries its firm's pattern. As the thing deciding which tab gets scraped it was still load-bearing: without it, capture falls back to whichever tab is active, which means scraping a banking or mail tab and storing it in the dashboard's database. So the setting goes, but the scoping moves to the manifest rather than disappearing. host_permissions already lists exactly the hosts this extension is allowed to read; capture now queries those (minus localhost, which is the dashboard mirroring its own output back). The two cannot drift apart, and adding a firm — which means adding its host to the manifest anyway — scopes capture without a second place to remember. With more than one firm open, capture prefers the active tab over the first match, so it follows attention rather than tab order. That case could not arise while a single pattern matched one site. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
22db6eae8d
commit
65a1103cda
+23
-20
@@ -8,10 +8,6 @@
|
||||
const DEFAULTS = {
|
||||
apiBase: 'http://localhost:3000',
|
||||
pollSeconds: 3,
|
||||
// Chrome match pattern, e.g. "https://*.tradovate.com/*". Empty = whichever
|
||||
// tab is active in the last focused window — which would mean scraping your
|
||||
// banking or email tab if you switched to one. Default it to the broker.
|
||||
targetUrlPattern: 'https://*.tradeify.co/*',
|
||||
// Optional CSS selector — matching elements get their on-screen position
|
||||
// measured alongside the HTML.
|
||||
elementSelector: '',
|
||||
@@ -97,22 +93,27 @@ function pageCapture(selector) {
|
||||
|
||||
// ── Capture pipeline ────────────────────────────────────────────────────────
|
||||
|
||||
async function pickTab(cfg) {
|
||||
if (cfg.targetUrlPattern) {
|
||||
try {
|
||||
const tabs = await chrome.tabs.query({ url: cfg.targetUrlPattern });
|
||||
return tabs.find((t) => /^https?:/.test(t.url || ''));
|
||||
} catch {
|
||||
throw new Error(`Invalid target URL pattern: ${cfg.targetUrlPattern}`);
|
||||
}
|
||||
}
|
||||
/** The sites this extension is allowed to touch, taken from the manifest rather
|
||||
* than from a setting.
|
||||
*
|
||||
* These are the same hosts host_permissions grants, so they cannot drift out of
|
||||
* sync with what the extension can actually read, and adding a firm — which
|
||||
* means adding its host to the manifest anyway — scopes capture automatically.
|
||||
* localhost is filtered out: that is the dashboard, and capturing it would just
|
||||
* mirror our own output back. */
|
||||
function firmPatterns() {
|
||||
return chrome.runtime.getManifest().host_permissions
|
||||
.filter((p) => !/\/\/(localhost|127\.0\.0\.1)/.test(p));
|
||||
}
|
||||
|
||||
const [tab] = await chrome.tabs.query({ active: true, lastFocusedWindow: true });
|
||||
// chrome://, about:, the Web Store and PDF viewers can't be scripted.
|
||||
if (!tab || !/^https?:/.test(tab.url || '')) return undefined;
|
||||
// Skip the dashboard itself, otherwise it just captures its own output.
|
||||
if (tab.url.startsWith(cfg.apiBase)) return undefined;
|
||||
return tab;
|
||||
async function pickTab(cfg) {
|
||||
const tabs = (await chrome.tabs.query({ url: firmPatterns() }))
|
||||
.filter((t) => /^https?:/.test(t.url || ''));
|
||||
if (tabs.length === 0) return undefined;
|
||||
|
||||
// Prefer the one being looked at, so with two firms open the capture follows
|
||||
// attention rather than tab order.
|
||||
return tabs.find((t) => t.active) ?? tabs[0];
|
||||
}
|
||||
|
||||
async function captureAndSend(cfg) {
|
||||
@@ -245,7 +246,9 @@ function waitForTabLoad(tabId, timeoutMs = 15000) {
|
||||
}
|
||||
|
||||
async function resolveLocateTab(cfg, request) {
|
||||
const pattern = request.urlPattern || cfg.targetUrlPattern;
|
||||
// Every step carries its firm's pattern; the manifest hosts are the fallback
|
||||
// for a bare request (the CLI's locate without --url).
|
||||
const pattern = request.urlPattern || firmPatterns();
|
||||
if (pattern) {
|
||||
const tabs = await chrome.tabs.query({ url: pattern });
|
||||
const tab = tabs.find((t) => /^https?:/.test(t.url || ''));
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"manifest_version": 3,
|
||||
"name": "AutoFirmer Capture",
|
||||
"version": "0.5.0",
|
||||
"version": "0.6.0",
|
||||
"description": "Scrapes the HTML of the target tab and posts it to the AutoFirmer dashboard while the AutoBuyer is switched on.",
|
||||
"permissions": ["scripting", "tabs", "storage", "alarms"],
|
||||
"host_permissions": [
|
||||
|
||||
@@ -27,9 +27,6 @@
|
||||
<label for="pollSeconds">Poll interval (seconds)</label>
|
||||
<input id="pollSeconds" type="number" min="1" max="120" />
|
||||
|
||||
<label for="targetUrlPattern">Target URL pattern (blank = active tab)</label>
|
||||
<input id="targetUrlPattern" placeholder="https://*.tradeify.co/*" />
|
||||
|
||||
<label for="elementSelector">Element selector (optional)</label>
|
||||
<input id="elementSelector" placeholder="button.buy" />
|
||||
|
||||
|
||||
+1
-2
@@ -1,8 +1,7 @@
|
||||
const FIELDS = ['apiBase', 'pollSeconds', 'targetUrlPattern', 'elementSelector'];
|
||||
const FIELDS = ['apiBase', 'pollSeconds', 'elementSelector'];
|
||||
const DEFAULTS = {
|
||||
apiBase: 'http://localhost:3000',
|
||||
pollSeconds: 3,
|
||||
targetUrlPattern: 'https://*.tradeify.co/*',
|
||||
elementSelector: '',
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user