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 = {
|
const DEFAULTS = {
|
||||||
apiBase: 'http://localhost:3000',
|
apiBase: 'http://localhost:3000',
|
||||||
pollSeconds: 3,
|
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
|
// Optional CSS selector — matching elements get their on-screen position
|
||||||
// measured alongside the HTML.
|
// measured alongside the HTML.
|
||||||
elementSelector: '',
|
elementSelector: '',
|
||||||
@@ -97,22 +93,27 @@ function pageCapture(selector) {
|
|||||||
|
|
||||||
// ── Capture pipeline ────────────────────────────────────────────────────────
|
// ── Capture pipeline ────────────────────────────────────────────────────────
|
||||||
|
|
||||||
async function pickTab(cfg) {
|
/** The sites this extension is allowed to touch, taken from the manifest rather
|
||||||
if (cfg.targetUrlPattern) {
|
* than from a setting.
|
||||||
try {
|
*
|
||||||
const tabs = await chrome.tabs.query({ url: cfg.targetUrlPattern });
|
* These are the same hosts host_permissions grants, so they cannot drift out of
|
||||||
return tabs.find((t) => /^https?:/.test(t.url || ''));
|
* sync with what the extension can actually read, and adding a firm — which
|
||||||
} catch {
|
* means adding its host to the manifest anyway — scopes capture automatically.
|
||||||
throw new Error(`Invalid target URL pattern: ${cfg.targetUrlPattern}`);
|
* 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 });
|
async function pickTab(cfg) {
|
||||||
// chrome://, about:, the Web Store and PDF viewers can't be scripted.
|
const tabs = (await chrome.tabs.query({ url: firmPatterns() }))
|
||||||
if (!tab || !/^https?:/.test(tab.url || '')) return undefined;
|
.filter((t) => /^https?:/.test(t.url || ''));
|
||||||
// Skip the dashboard itself, otherwise it just captures its own output.
|
if (tabs.length === 0) return undefined;
|
||||||
if (tab.url.startsWith(cfg.apiBase)) return undefined;
|
|
||||||
return tab;
|
// 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) {
|
async function captureAndSend(cfg) {
|
||||||
@@ -245,7 +246,9 @@ function waitForTabLoad(tabId, timeoutMs = 15000) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function resolveLocateTab(cfg, request) {
|
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) {
|
if (pattern) {
|
||||||
const tabs = await chrome.tabs.query({ url: pattern });
|
const tabs = await chrome.tabs.query({ url: pattern });
|
||||||
const tab = tabs.find((t) => /^https?:/.test(t.url || ''));
|
const tab = tabs.find((t) => /^https?:/.test(t.url || ''));
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"manifest_version": 3,
|
"manifest_version": 3,
|
||||||
"name": "AutoFirmer Capture",
|
"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.",
|
"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"],
|
"permissions": ["scripting", "tabs", "storage", "alarms"],
|
||||||
"host_permissions": [
|
"host_permissions": [
|
||||||
|
|||||||
@@ -27,9 +27,6 @@
|
|||||||
<label for="pollSeconds">Poll interval (seconds)</label>
|
<label for="pollSeconds">Poll interval (seconds)</label>
|
||||||
<input id="pollSeconds" type="number" min="1" max="120" />
|
<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>
|
<label for="elementSelector">Element selector (optional)</label>
|
||||||
<input id="elementSelector" placeholder="button.buy" />
|
<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 = {
|
const DEFAULTS = {
|
||||||
apiBase: 'http://localhost:3000',
|
apiBase: 'http://localhost:3000',
|
||||||
pollSeconds: 3,
|
pollSeconds: 3,
|
||||||
targetUrlPattern: 'https://*.tradeify.co/*',
|
|
||||||
elementSelector: '',
|
elementSelector: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user