Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,10 @@ You should do this for **every** Chrome extension you use. Most extensions are c
Automatically closes inactive tabs after a configurable timeout (default: 5 minutes). Set excluded hosts to keep important tabs alive. View and re-open recently closed tabs.

### 🍪 Cookie Editor
Full cookie manager for the current site. View, edit, add, and delete cookies. Export cookies as JSON. Expand any cookie to see and modify all fields including domain, path, SameSite, secure, and httpOnly flags.
Full cookie manager for the current site. View, edit, add, and delete cookies. Export cookies as JSON or import from a JSON file (compatible with exports from this tool). Expand any cookie to see and modify all fields including domain, path, SameSite, secure, and httpOnly flags.

### 🔐 Custom Headers
Add, edit, and remove custom HTTP header rules for debugging and development. Set or override headers on outgoing requests or incoming responses. Scope rules to the current site or apply them globally. Rules are applied via Chrome's `declarativeNetRequest` API and stored locally in `chrome.storage.local`.

### 🔀 Redirect Tracer
See every redirect hop your browser took to reach the current page. Shows status codes (301, 302, 307, etc.) with a visual chain. Copy the full redirect chain to clipboard.
Expand Down
47 changes: 46 additions & 1 deletion background.js
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,51 @@ chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
}
});

// ═══════════════════════════════════
// Custom Headers (declarativeNetRequest)
// ═══════════════════════════════════
const ALL_RESOURCE_TYPES = [
"main_frame", "sub_frame", "stylesheet", "script", "image",
"font", "object", "xmlhttprequest", "ping", "media", "websocket", "other"
];

function isValidHeaderRule(r) {
if (!r.name || r.value === undefined) return false;
if (r.scope === "all") return true;
return !!(r.domain || "").replace(/^\./, "");
}

async function applyHeaderRules() {
const { headerRules = [] } = await chrome.storage.local.get("headerRules");
const dnrRules = headerRules
.filter(isValidHeaderRule)
.map((r, i) => {
const action = r.target === "response"
? { type: "modifyHeaders", responseHeaders: [{ header: r.name, operation: "set", value: r.value }] }
: { type: "modifyHeaders", requestHeaders: [{ header: r.name, operation: "set", value: r.value }] };
const condition = r.scope === "all"
? { resourceTypes: ALL_RESOURCE_TYPES }
: { requestDomains: [(r.domain || "").replace(/^\./, "")], resourceTypes: ALL_RESOURCE_TYPES };
return { id: i + 1, priority: 1, action, condition };
});

try {
const existing = await chrome.declarativeNetRequest.getDynamicRules();
await chrome.declarativeNetRequest.updateDynamicRules({
removeRuleIds: existing.map(r => r.id),
addRules: dnrRules,
});
} catch (err) {
console.error("[superlevels] Failed to apply header rules:", err);
}
}

applyHeaderRules();

chrome.storage.onChanged.addListener((changes) => {
if (changes.headerRules) applyHeaderRules();
});

// Set defaults on install
chrome.runtime.onInstalled.addListener(() => {
chrome.storage.local.get(["enabled", "timeoutMin", "exclusions"], (data) => {
Expand All @@ -222,4 +267,4 @@ chrome.runtime.onInstalled.addListener(() => {
chrome.storage.local.set(defaults);
}
});
});
});
2 changes: 1 addition & 1 deletion manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"name": "🚀 SuperLevels",
"version": "2.0",
"description": "The super Chrome extension by @levelsio — dark mode, GDPR cookie dismisser, YouTube unhook, tab cleaner, cookie editor, redirect tracer, live CSS editor, X/Twitter dim mode, JS toggle, JSON formatter, music recognizer, picture-in-picture, Google Maps links, and view image.",
"permissions": ["tabs", "storage", "alarms", "cookies", "activeTab", "webRequest", "webNavigation", "tabCapture", "contentSettings", "scripting"],
"permissions": ["tabs", "storage", "alarms", "cookies", "activeTab", "webRequest", "webNavigation", "tabCapture", "contentSettings", "scripting", "declarativeNetRequest"],
"host_permissions": ["<all_urls>"],
"background": {
"service_worker": "background.js"
Expand Down
78 changes: 78 additions & 0 deletions popup.html
Original file line number Diff line number Diff line change
Expand Up @@ -700,6 +700,24 @@
.toolbar button:hover { color: #fff; background: rgba(255,255,255,0.04); }
.toolbar button.danger:hover { color: #e94560; }

/* ── Headers page ── */
.header-scope {
font-size: 10px; padding: 2px 7px; border-radius: 4px;
font-weight: 600; margin-left: auto; white-space: nowrap; flex-shrink: 0;
}
.header-scope.site { background: #1e3a5f; color: #4fa3e0; }
.header-scope.all { background: #3a1e5f; color: #b07ae0; }
.cookie-field select {
width: 100%; background: #0f0f23; border: 1px solid #2a2a4a;
color: #ccc; padding: 6px 8px; border-radius: 6px; font-size: 13px;
}
.import-toast {
position: fixed; bottom: 60px; left: 50%; transform: translateX(-50%);
background: #1a1a2e; border: 1px solid #2a2a4a; color: #aaa;
padding: 6px 14px; border-radius: 6px; font-size: 12px;
z-index: 200; pointer-events: none;
}

/* ── Add cookie modal ── */
.modal-overlay {
display: none; position: fixed; inset: 0;
Expand Down Expand Up @@ -744,6 +762,7 @@
<button data-page="music">🎵 Music</button>
<button data-page="pip">🖼 PiP</button>
<button data-page="jsonformat">{} JSON</button>
<button data-page="headers">&#128272; Headers</button>
</div>

<!-- ═══ Tab Cleaner Page ═══ -->
Expand Down Expand Up @@ -793,11 +812,16 @@ <h2>Excluded Hosts</h2>
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><polyline points="23 4 23 10 17 10"/><path d="M20.49 15a9 9 0 11-2.12-9.36L23 10"/></svg>
<span>Refresh</span>
</button>
<button id="btnImport" title="Import Cookies from JSON">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>
<span>Import</span>
</button>
<button id="btnExport" title="Export Cookies">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="12" y1="18" x2="12" y2="12"/><polyline points="9 15 12 12 15 15"/></svg>
<span>Export</span>
</button>
</div>
<input type="file" id="importFileInput" accept=".json" style="display:none">
</div>

<!-- ═══ Redirect Tracer Page ═══ -->
Expand Down Expand Up @@ -1100,6 +1124,27 @@ <h2>Excluded Hosts</h2>
</div>
</div>

<!-- ═══ Headers Page ═══ -->
<div class="page" id="page-headers">
<div class="page-body" id="headersBody">
<div class="cookie-header">
<div class="domain" id="headersDomain">Loading...</div>
<div class="count" id="headersCount">0</div>
</div>
<div id="headersList"></div>
</div>
<div class="toolbar">
<button id="btnAddHeader" title="Add Header Rule">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>
<span>Add</span>
</button>
<button id="btnClearHeaders" class="danger" title="Clear All Header Rules">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><polyline points="3 6 5 6 21 6"/><path d="M19 6v14a2 2 0 01-2 2H7a2 2 0 01-2-2V6m3 0V4a2 2 0 012-2h4a2 2 0 012 2v2"/></svg>
<span>Clear All</span>
</button>
</div>
</div>

<!-- Add Cookie Modal -->
<div class="modal-overlay" id="addModal">
<div class="modal">
Expand Down Expand Up @@ -1127,6 +1172,39 @@ <h3>🍪 Add Cookie</h3>
</div>
</div>

<!-- Add Header Modal -->
<div class="modal-overlay" id="addHeaderModal">
<div class="modal">
<h3>&#128272; Add Header Rule</h3>
<div class="cookie-field">
<label>Header Name</label>
<input type="text" id="newHeaderName" placeholder="e.g. Authorization">
</div>
<div class="cookie-field">
<label>Value</label>
<textarea id="newHeaderValue" placeholder="e.g. Bearer my-token"></textarea>
</div>
<div class="cookie-field">
<label>Scope</label>
<select id="newHeaderScope">
<option value="site">Current site only</option>
<option value="all">All sites</option>
</select>
</div>
<div class="cookie-field">
<label>Apply to</label>
<select id="newHeaderTarget">
<option value="request">Request headers (outgoing)</option>
<option value="response">Response headers (incoming)</option>
</select>
</div>
<div class="modal-btns">
<button class="btn-cancel" id="headerModalCancel">Cancel</button>
<button class="btn-save" id="headerModalSave">Add Rule</button>
</div>
</div>
</div>

<script src="popup.js"></script>
</body>
</html>
Loading