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.'
|
||||
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"
|
||||
cp -pR "$SRC" "$BUILD"
|
||||
|
|
|
@ -103,12 +103,12 @@ sub patch
|
|||
if ($must_replace) {
|
||||
rename $dst, $src;
|
||||
print "Patched.\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
unlink $dst;
|
||||
print "Nothing to do.\n";
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
patch($SOURCE_FILE);
|
||||
unlink $dst;
|
||||
print "Nothing to do.\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
exit(patch($SOURCE_FILE));
|
||||
|
|
|
@ -6,13 +6,6 @@ class DocumentCSP {
|
|||
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)) {
|
||||
let {document} = this;
|
||||
if (!capabilities.has("script")) {
|
||||
|
@ -40,8 +33,6 @@ class DocumentCSP {
|
|||
meta.setAttribute("http-equiv", header.name);
|
||||
meta.setAttribute("content", header.value);
|
||||
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 parent = head ||
|
||||
|
@ -60,13 +51,4 @@ class DocumentCSP {
|
|||
}
|
||||
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';
|
||||
let listenersMap = new Map();
|
||||
let backlog = new Set();
|
||||
let documentCSP = new DocumentCSP(document);
|
||||
|
||||
let ns = {
|
||||
debug: true, // DEV_ONLY
|
||||
|
@ -42,8 +41,9 @@
|
|||
//, document.domain, document.baseURI, window.isSecureContext // DEV_ONLY
|
||||
);
|
||||
|
||||
let requireDocumentCSP = /^(?:ftp|file):/.test(url);
|
||||
if (!requireDocumentCSP) {
|
||||
if (this.syncFetchPolicy) { // ftp: or file: - no CSP headers yet
|
||||
this.syncFetchPolicy();
|
||||
} else {
|
||||
// CSP headers have been already provided by webRequest, we are not in a hurry...
|
||||
if (/^(javascript|about):/.test(url)) {
|
||||
url = document.readyState === "loading"
|
||||
|
@ -68,118 +68,6 @@
|
|||
asyncFetch();
|
||||
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) {
|
||||
|
@ -196,7 +84,7 @@
|
|||
} else {
|
||||
let perms = policy.permissions;
|
||||
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.fire("capabilities");
|
||||
|
@ -209,9 +97,5 @@
|
|||
},
|
||||
};
|
||||
|
||||
if (this.ns) {
|
||||
this.ns.merge(ns);
|
||||
} else {
|
||||
this.ns = ns;
|
||||
}
|
||||
this.ns = this.ns ? Object.assign(this.ns, ns) : ns;
|
||||
}
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -244,8 +244,7 @@
|
|||
console.debug("sendSyncMessage suspending on ", records)
|
||||
suspend();
|
||||
});
|
||||
domSuspender.observe(document.documentElement, {childList: true});
|
||||
|
||||
domSuspender.observe(document, {childList: true, subtree: true});
|
||||
let finalize = () => {
|
||||
console.debug("sendSyncMessage finalizing");
|
||||
domSuspender.disconnect();
|
||||
|
|
|
@ -73,6 +73,14 @@
|
|||
"/content/content.css"
|
||||
]
|
||||
},
|
||||
{
|
||||
"run_at": "document_start",
|
||||
"matches": ["file://*/*", "ftp://*/*"],
|
||||
"js": [
|
||||
"lib/SyncMessage.js",
|
||||
"content/syncFetchPolicy.js"
|
||||
]
|
||||
},
|
||||
{
|
||||
"run_at": "document_start",
|
||||
"matches": ["<all_urls>"],
|
||||
|
@ -83,7 +91,6 @@
|
|||
"lib/browser-polyfill.js",
|
||||
"lib/log.js",
|
||||
"lib/uuid.js",
|
||||
"lib/SyncMessage.js",
|
||||
"lib/sha256.js",
|
||||
"lib/Messages.js",
|
||||
"lib/CSP.js",
|
||||
|
|
Loading…
Reference in New Issue