Skip to content

Commit 60f9041

Browse files
authored
Add cookies from document.cookies (#18)
* content script for cookies * change poll time, tidy
1 parent 9fb7232 commit 60f9041

File tree

3 files changed

+164
-5
lines changed

3 files changed

+164
-5
lines changed

OpacityCore/src/main/assets/extension/background.js

Lines changed: 56 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
const cookieStore = {};
2+
13
browser.webRequest.onHeadersReceived.addListener(
24
function (details) {
35
let url = details.url;
@@ -24,13 +26,13 @@ browser.webRequest.onHeadersReceived.addListener(
2426
// Parse cookies
2527
let cookieDict = {};
2628
cookies.split("\n").forEach((cookie) => {
27-
let parts = cookie.split(";").map(p => p.trim());
29+
let parts = cookie.split(";").map((p) => p.trim());
2830

2931
let [name, value] = parts[0].split("=");
3032

3133
cookieDict[name] = value;
3234

33-
parts.slice(1).forEach(attr => {
35+
parts.slice(1).forEach((attr) => {
3436
let [key, val] = attr.split("=");
3537
if (key.toLowerCase() === "domain") {
3638
/** RFC 6265
@@ -46,18 +48,67 @@ browser.webRequest.onHeadersReceived.addListener(
4648
});
4749
});
4850

51+
const domain = cookie_domain || request_domain;
52+
if (!cookieStore[domain]) {
53+
cookieStore[domain] = { cookies: {} };
54+
}
55+
56+
Object.keys(cookieDict).forEach((name) => {
57+
cookieStore[domain].cookies[name] = cookieDict[name];
58+
});
4959

5060
// Send cookies back to the app (GeckoView) via messaging
5161
browser.runtime.sendNativeMessage("gecko", {
5262
event: "cookies",
53-
cookies: cookieDict,
54-
domain: cookie_domain || request_domain,
63+
cookies: cookieStore[domain].cookies,
64+
domain: domain,
5565
});
5666
},
5767
{ urls: ["<all_urls>"] }, // Intercept all URLs
5868
["responseHeaders"]
5969
);
6070

71+
// Handle messages from content script for document.cookies
72+
browser.runtime.onMessage.addListener(function (message, sender, _) {
73+
if (message.type === "cookie_operation") {
74+
try {
75+
const domain = message.domain || new URL(sender.tab.url).hostname;
76+
77+
if (!domain) {
78+
browser.runtime.sendNativeMessage("gecko", {
79+
event: "cookies",
80+
cookies: message.cookies,
81+
domain: message.domain,
82+
});
83+
84+
return;
85+
}
86+
87+
if (!cookieStore[domain]) {
88+
cookieStore[domain] = { cookies: {} };
89+
}
90+
91+
Object.keys(message.cookies || {}).forEach((name) => {
92+
const value = message.cookies[name];
93+
94+
cookieStore[domain].cookies[name] = value;
95+
});
96+
97+
browser.runtime.sendNativeMessage("gecko", {
98+
event: "cookies",
99+
cookies: cookieStore[domain].cookies,
100+
domain: domain,
101+
});
102+
} catch (err) {
103+
browser.runtime.sendNativeMessage("gecko", {
104+
event: "cookies",
105+
cookies: message.cookies,
106+
domain: message.domain,
107+
});
108+
}
109+
}
110+
});
111+
61112
browser.webNavigation.onDOMContentLoaded.addListener(function (details) {
62113
if (details.frameId === 0) {
63114
// Ensure it's the top-level frame
@@ -87,4 +138,4 @@ browser.webNavigation.onDOMContentLoaded.addListener(function (details) {
87138
});
88139
});
89140
}
90-
});
141+
});
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
const COOKIE_POLL_INTERVAL_MS = 1000;
2+
let lastCookieState = document.cookie;
3+
4+
const originalCookie = Object.getOwnPropertyDescriptor(
5+
Document.prototype,
6+
"cookie"
7+
);
8+
9+
const originalSetter = originalCookie.set;
10+
11+
const sendCookieData = (cookieData) => {
12+
if (!browser || !browser.runtime || !browser.runtime.sendMessage) {
13+
return;
14+
}
15+
16+
browser.runtime.sendMessage({
17+
type: "cookie_operation",
18+
cookies: cookieData,
19+
domain: window.location.hostname || "",
20+
});
21+
};
22+
23+
const cookieSetter = (value) => {
24+
let result;
25+
26+
// call the original setter
27+
try {
28+
if (typeof originalSetter === "function") {
29+
result = originalSetter.call(this, value);
30+
}
31+
} catch (error) {}
32+
33+
if (!value) {
34+
return result;
35+
}
36+
37+
const mainCookiePart = value.split(";")[0];
38+
const cookieParts = mainCookiePart.split("=");
39+
40+
if (cookieParts.length >= 2) {
41+
const cookieName = cookieParts[0].trim();
42+
const cookieValue = cookieParts.slice(1).join("=").trim();
43+
44+
// send to background script
45+
if (cookieName) {
46+
const cookieData = { [cookieName]: cookieValue };
47+
sendCookieData(cookieData);
48+
}
49+
}
50+
51+
return result;
52+
};
53+
54+
// apply descriptor with the new setter
55+
Object.defineProperty(Document.prototype, "cookie", {
56+
configurable: originalCookie.configurable,
57+
enumerable: originalCookie.enumerable,
58+
get: originalCookie.get,
59+
set: cookieSetter,
60+
});
61+
62+
// poll changes to document.cookies
63+
const pollForCookies = () => {
64+
setTimeout(() => {
65+
try {
66+
const currentCookieState = document.cookie;
67+
if (currentCookieState === lastCookieState) {
68+
pollForCookies();
69+
return;
70+
}
71+
72+
const parsedCookies = (() => {
73+
const cookies = {};
74+
if (!currentCookieState) return cookies;
75+
76+
currentCookieState.split(";").forEach((cookie) => {
77+
const parts = cookie.trim().split("=");
78+
if (parts.length >= 2) {
79+
const name = parts[0].trim();
80+
const value = parts.slice(1).join("=").trim();
81+
if (name) {
82+
cookies[name] = value;
83+
}
84+
}
85+
});
86+
87+
return cookies;
88+
})();
89+
90+
lastCookieState = currentCookieState;
91+
92+
sendCookieData(parsedCookies);
93+
pollForCookies();
94+
} catch (error) {
95+
pollForCookies();
96+
}
97+
}, COOKIE_POLL_INTERVAL_MS);
98+
};
99+
100+
pollForCookies();

OpacityCore/src/main/assets/extension/manifest.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,14 @@
1414
"background.js"
1515
]
1616
},
17+
"content_scripts": [
18+
{
19+
"matches": ["<all_urls>"],
20+
"js": ["content.js"],
21+
"run_at": "document_start",
22+
"all_frames": true
23+
}
24+
],
1725
"permissions": [
1826
"webRequest",
1927
"activeTab",

0 commit comments

Comments
 (0)