Refactored and improved syncFetchPolicy fallback for file: and ftp: special cases.
This commit is contained in:
parent
058bf1b206
commit
3922f234f4
6
build.sh
6
build.sh
|
@ -69,7 +69,11 @@ if ! [ $(date -r "$LIB/tld.js" +'%Y%m%d') -ge $(date +'%Y%m%d') -a "$1" != "tld
|
||||||
git add src/lib/tld.js TLD && git commit -m'Updated TLDs.'
|
git add src/lib/tld.js TLD && git commit -m'Updated TLDs.'
|
||||||
fi
|
fi
|
||||||
|
|
||||||
./html5_events/html5_events.pl
|
if ./html5_events/html5_events.pl; then
|
||||||
|
# update full event list as an array in src/content/syncFetchPolicy.js
|
||||||
|
EVENTS=$(egrep '^on[a-z]+$' html5_events/html5_events_archive.txt | sed "s/^on//;s/.*/'&'/;H;1h;"'$!d;x;s/\n/, /g');
|
||||||
|
perl -pi -e 's/(\blet eventTypes\s*=\s*)\[.*?\]/$1['"$EVENTS"']/' src/content/syncFetchPolicy.js
|
||||||
|
fi
|
||||||
|
|
||||||
rm -rf "$BUILD" "$XPI"
|
rm -rf "$BUILD" "$XPI"
|
||||||
cp -pR "$SRC" "$BUILD"
|
cp -pR "$SRC" "$BUILD"
|
||||||
|
|
|
@ -103,12 +103,12 @@ sub patch
|
||||||
if ($must_replace) {
|
if ($must_replace) {
|
||||||
rename $dst, $src;
|
rename $dst, $src;
|
||||||
print "Patched.\n";
|
print "Patched.\n";
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
unlink $dst;
|
||||||
unlink $dst;
|
print "Nothing to do.\n";
|
||||||
print "Nothing to do.\n";
|
return 1;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
patch($SOURCE_FILE);
|
exit(patch($SOURCE_FILE));
|
||||||
|
|
|
@ -6,13 +6,6 @@ class DocumentCSP {
|
||||||
this.root = document.documentElement;
|
this.root = document.documentElement;
|
||||||
}
|
}
|
||||||
|
|
||||||
removeEventAttributes() {
|
|
||||||
console.debug("Removing event attributes"); // DEV_ONLY
|
|
||||||
let {root} = this;
|
|
||||||
this.rootAttrs = [...root.attributes].filter(a => a.name.toLowerCase().startsWith("on"));
|
|
||||||
for (let a of this.rootAttrs) root.removeAttributeNode(a);
|
|
||||||
}
|
|
||||||
|
|
||||||
apply(capabilities, embedding = CSP.isEmbedType(this.document.contentType)) {
|
apply(capabilities, embedding = CSP.isEmbedType(this.document.contentType)) {
|
||||||
let {document} = this;
|
let {document} = this;
|
||||||
if (!capabilities.has("script")) {
|
if (!capabilities.has("script")) {
|
||||||
|
@ -40,8 +33,6 @@ class DocumentCSP {
|
||||||
meta.setAttribute("http-equiv", header.name);
|
meta.setAttribute("http-equiv", header.name);
|
||||||
meta.setAttribute("content", header.value);
|
meta.setAttribute("content", header.value);
|
||||||
let root = document.documentElement;
|
let root = document.documentElement;
|
||||||
let rootAttrs = [...root.attributes].filter(a => a.name.toLowerCase().startsWith("on"));
|
|
||||||
for (let a of rootAttrs) root.removeAttributeNode(a);
|
|
||||||
|
|
||||||
let {head} = document;
|
let {head} = document;
|
||||||
let parent = head ||
|
let parent = head ||
|
||||||
|
@ -60,13 +51,4 @@ class DocumentCSP {
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
restoreEventAttributes() {
|
|
||||||
if (!this.rootAttrs) return;
|
|
||||||
console.debug("Restoring event attributes"); // DEV_ONLY
|
|
||||||
let {root, rootAttrs} = this;
|
|
||||||
for (let a of rootAttrs) {
|
|
||||||
root.setAttributeNodeNS(a);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
let listenersMap = new Map();
|
let listenersMap = new Map();
|
||||||
let backlog = new Set();
|
let backlog = new Set();
|
||||||
let documentCSP = new DocumentCSP(document);
|
|
||||||
|
|
||||||
let ns = {
|
let ns = {
|
||||||
debug: true, // DEV_ONLY
|
debug: true, // DEV_ONLY
|
||||||
|
@ -42,8 +41,9 @@
|
||||||
//, document.domain, document.baseURI, window.isSecureContext // DEV_ONLY
|
//, document.domain, document.baseURI, window.isSecureContext // DEV_ONLY
|
||||||
);
|
);
|
||||||
|
|
||||||
let requireDocumentCSP = /^(?:ftp|file):/.test(url);
|
if (this.syncFetchPolicy) { // ftp: or file: - no CSP headers yet
|
||||||
if (!requireDocumentCSP) {
|
this.syncFetchPolicy();
|
||||||
|
} else {
|
||||||
// CSP headers have been already provided by webRequest, we are not in a hurry...
|
// CSP headers have been already provided by webRequest, we are not in a hurry...
|
||||||
if (/^(javascript|about):/.test(url)) {
|
if (/^(javascript|about):/.test(url)) {
|
||||||
url = document.readyState === "loading"
|
url = document.readyState === "loading"
|
||||||
|
@ -68,118 +68,6 @@
|
||||||
asyncFetch();
|
asyncFetch();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Here we've got no CSP header yet (file: or ftp: URL), we need one
|
|
||||||
// injected in the DOM as soon as possible.
|
|
||||||
debug("No CSP yet for non-HTTP document load: fetching policy synchronously...");
|
|
||||||
documentCSP.removeEventAttributes();
|
|
||||||
|
|
||||||
let earlyScripts = [];
|
|
||||||
let dequeueEarlyScripts = (last = false) => {
|
|
||||||
if (!(ns.canScript && earlyScripts)) return;
|
|
||||||
if (earlyScripts.length === 0) {
|
|
||||||
earlyScripts = null;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
for (let s; s = earlyScripts.shift(); ) {
|
|
||||||
debug("Restoring", s);
|
|
||||||
s.firstChild._replaced = true;
|
|
||||||
s._original.replaceWith(s);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let syncFetch = callback => {
|
|
||||||
browser.runtime.sendSyncMessage(
|
|
||||||
{id: "fetchPolicy", url, contextUrl: url},
|
|
||||||
callback);
|
|
||||||
};
|
|
||||||
|
|
||||||
if (UA.isMozilla && document.readyState !== "complete") {
|
|
||||||
// Mozilla has already parsed the <head> element, we must take extra steps...
|
|
||||||
|
|
||||||
debug("Early parsing: preemptively suppressing events and script execution.");
|
|
||||||
|
|
||||||
{
|
|
||||||
let eventTypes = [];
|
|
||||||
for (let p in document.documentElement) if (p.startsWith("on")) eventTypes.push(p.substring(2));
|
|
||||||
let eventSuppressor = e => {
|
|
||||||
try {
|
|
||||||
debug("Event suppressor called for ", e.type, e.target, earlyScripts, e.target._earlyScript); // DEV_ONLY
|
|
||||||
if (!earlyScripts || document.readyState === "complete") {
|
|
||||||
debug("Stopping event suppression");
|
|
||||||
for (let et of eventTypes) document.removeEventListener(et, eventSuppressor, true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ns.canScript || e.target._earlyScript) {
|
|
||||||
e.stopImmediatePropagation();
|
|
||||||
debug(`Suppressing ${e.type} on `, e.target); // DEV_ONLY
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
error(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
debug("Starting event suppression");
|
|
||||||
for (let et of eventTypes) document.addEventListener(et, eventSuppressor, true);
|
|
||||||
|
|
||||||
ns.on("capabilities", () => {
|
|
||||||
if (!ns.canScript) {
|
|
||||||
try {
|
|
||||||
for (node of document.querySelectorAll("*")) {
|
|
||||||
let evAttrs = [...node.attributes].filter(a => a.name.toLowerCase().startsWith("on"));
|
|
||||||
for (let a of evAttrs) {
|
|
||||||
debug("Reparsing event attribute after CSP", a, node);
|
|
||||||
node.removeAttributeNode(a);
|
|
||||||
node.setAttributeNodeNS(a);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
error(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
addEventListener("beforescriptexecute", e => {
|
|
||||||
debug(e.type, e.target);
|
|
||||||
if (earlyScripts) {
|
|
||||||
let s = e.target;
|
|
||||||
if (s._replaced) {
|
|
||||||
debug("Replaced script found");
|
|
||||||
dequeueEarlyScripts(true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let replacement = document.createRange().createContextualFragment(s.outerHTML);
|
|
||||||
replacement._original = s;
|
|
||||||
s._earlyScript = true;
|
|
||||||
earlyScripts.push(replacement);
|
|
||||||
e.preventDefault();
|
|
||||||
dequeueEarlyScripts(true);
|
|
||||||
debug("Blocked early script");
|
|
||||||
}
|
|
||||||
}, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
let setup = policy => {
|
|
||||||
debug("Fetched %o, readyState %s", policy, document.readyState); // DEV_ONLY
|
|
||||||
this.setup(policy);
|
|
||||||
documentCSP.restoreEventAttributes();
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let attempts = 3; attempts-- > 0;) {
|
|
||||||
try {
|
|
||||||
syncFetch(setup);
|
|
||||||
break;
|
|
||||||
} catch (e) {
|
|
||||||
if (!Messages.isMissingEndpoint(e) || document.readyState === "complete") {
|
|
||||||
error(e);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
error("Background page not ready yet, retrying to fetch policy...")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
dequeueEarlyScripts();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
setup(policy) {
|
setup(policy) {
|
||||||
|
@ -196,7 +84,7 @@
|
||||||
} else {
|
} else {
|
||||||
let perms = policy.permissions;
|
let perms = policy.permissions;
|
||||||
this.capabilities = new Set(perms.capabilities);
|
this.capabilities = new Set(perms.capabilities);
|
||||||
documentCSP.apply(this.capabilities, this.embeddingDocument);
|
new DocumentCSP(document).apply(this.capabilities, this.embeddingDocument);
|
||||||
}
|
}
|
||||||
this.canScript = this.allows("script");
|
this.canScript = this.allows("script");
|
||||||
this.fire("capabilities");
|
this.fire("capabilities");
|
||||||
|
@ -209,9 +97,5 @@
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
if (this.ns) {
|
this.ns = this.ns ? Object.assign(this.ns, ns) : ns;
|
||||||
this.ns.merge(ns);
|
|
||||||
} else {
|
|
||||||
this.ns = ns;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -244,8 +244,7 @@
|
||||||
console.debug("sendSyncMessage suspending on ", records)
|
console.debug("sendSyncMessage suspending on ", records)
|
||||||
suspend();
|
suspend();
|
||||||
});
|
});
|
||||||
domSuspender.observe(document.documentElement, {childList: true});
|
domSuspender.observe(document, {childList: true, subtree: true});
|
||||||
|
|
||||||
let finalize = () => {
|
let finalize = () => {
|
||||||
console.debug("sendSyncMessage finalizing");
|
console.debug("sendSyncMessage finalizing");
|
||||||
domSuspender.disconnect();
|
domSuspender.disconnect();
|
||||||
|
|
|
@ -72,7 +72,15 @@
|
||||||
"css": [
|
"css": [
|
||||||
"/content/content.css"
|
"/content/content.css"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"run_at": "document_start",
|
||||||
|
"matches": ["file://*/*", "ftp://*/*"],
|
||||||
|
"js": [
|
||||||
|
"lib/SyncMessage.js",
|
||||||
|
"content/syncFetchPolicy.js"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"run_at": "document_start",
|
"run_at": "document_start",
|
||||||
"matches": ["<all_urls>"],
|
"matches": ["<all_urls>"],
|
||||||
|
@ -83,7 +91,6 @@
|
||||||
"lib/browser-polyfill.js",
|
"lib/browser-polyfill.js",
|
||||||
"lib/log.js",
|
"lib/log.js",
|
||||||
"lib/uuid.js",
|
"lib/uuid.js",
|
||||||
"lib/SyncMessage.js",
|
|
||||||
"lib/sha256.js",
|
"lib/sha256.js",
|
||||||
"lib/Messages.js",
|
"lib/Messages.js",
|
||||||
"lib/CSP.js",
|
"lib/CSP.js",
|
||||||
|
|
Loading…
Reference in New Issue