summaryrefslogtreecommitdiff
path: root/src/content/content.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/content/content.js')
-rw-r--r--src/content/content.js218
1 files changed, 30 insertions, 188 deletions
diff --git a/src/content/content.js b/src/content/content.js
index a5d996d..c7fc045 100644
--- a/src/content/content.js
+++ b/src/content/content.js
@@ -1,112 +1,12 @@
'use strict';
+// debug = () => {}; // REL_ONLY
- // debug = () => {}; // REL_ONLY
-{
- let listenersMap = new Map();
- let backlog = new Set();
- var ns = {
- on(eventName, listener) {
- let listeners = listenersMap.get(eventName);
- if (!listeners) listenersMap.set(eventName, listeners = new Set());
- listeners.add(listener);
- if (backlog.has(eventName)) this.fire(eventName, listener);
- },
- detach(eventName, listener) {
- let listeners = listenersMap.get(eventName);
- if (listeners) listeners.delete(listener);
- },
- fire(eventName, listener = null) {
- if (listener) {
- listener({type:eventName, source: this});
- return;
- }
- let listeners = listenersMap.get(eventName);
- if (listeners) {
- for (let l of listeners) {
- this.fire(eventName, l);
- }
- }
- backlog.add(eventName);
- },
- setup(DEFAULT, MARKER) {
- this.perms.DEFAULT = DEFAULT;
- if(!this.perms.CURRENT) this.perms.CURRENT = DEFAULT;
-
- // ugly hack: since now we use registerContentScript instead of the
- // filterRequest dynamic script injection hack, we use top.name
- // to store per-tab information. We don't want web content to
- // mess with it, though, so we wrap it around auto-hiding accessors
- this.perms.MARKER = MARKER;
- let eraseTabInfoRx = new RegExp(`[^]*${MARKER},?`);
- if (eraseTabInfoRx.test(top.name)) {
- let _name = top.name;
- let tabInfoRx = new RegExp(`^${MARKER}\\[([^]*?)\\]${MARKER},`);
- if (top === window) { // wrap to hide
- Reflect.defineProperty(top.wrappedJSObject, "name", {
- get: exportFunction(() => top.name.replace(eraseTabInfoRx, ""), top.wrappedJSObject),
- set: exportFunction(value => {
- let preamble = top.name.match(tabInfoRx);
- top.name = `${preamble && preamble[0] || ""}${value}`;
- return value;
- }, top.wrappedJSObject)
- });
- }
- let tabInfoMatch = _name.match(tabInfoRx);
- if (tabInfoMatch) try {
- this.perms.tabInfo = JSON.parse(tabInfoMatch[1]);
- } catch (e) {
- error(e);
- }
- }
-
- if (!this.perms.DEFAULT || this.perms.tabInfo.unrestricted) {
- this.allows = () => true;
- this.capabilities = Object.assign(
- new Set(["script"]), { has() { return true; } });
- } else {
- let perms = this.perms.CURRENT || this.perms.DEFAULT;
- this.capabilities = new Set(perms.capabilities);
- new DocumentCSP(document).apply(this.capabilities);
- }
- ns.fire("perms");
- },
- perms: { DEFAULT: null, CURRENT: null, tabInfo: {}, MARKER: "" },
-
- allows(cap) {
- return this.capabilities && this.capabilities.has(cap);
- },
-
- getWindowName() {
- return top !== window || !this.perms.MARKER ? window.name
- : window.name.split(this.perms.MARKER + ",").pop();
- }
- }
-}
-
-var canScript = true, shouldScript = false;
-
-let now = () => performance.now() + performance.timeOrigin;
+var _ = browser.i18n.getMessage;
function createHTMLElement(name) {
return document.createElementNS("http://www.w3.org/1999/xhtml", name);
}
-function probe() {
- try {
- debug("Probing execution...");
- let s = document.createElement("script");
- s.textContent = ";";
- document.documentElement.appendChild(s);
- s.remove();
- } catch(e) {
- debug(e);
- }
-}
-
-var _ = browser.i18n.getMessage;
-
-var embeddingDocument = false;
-
var seen = {
_map: new Map(),
_list: null,
@@ -128,9 +28,8 @@ Messages.addHandler({
seen.record(event);
}
if (ownFrame) {
- init();
if (!allowed && PlaceHolder.canReplace(policyType)) {
- request.embeddingDocument = embeddingDocument;
+ request.embeddingDocument = ns.embeddingDocument;
PlaceHolder.create(policyType, request);
}
}
@@ -142,87 +41,38 @@ Messages.addHandler({
}
});
-if (document.readyState !== "complete") {
- let pageshown = e => {
- removeEventListener("pageshow", pageshown);
- init();
- };
- addEventListener("pageshow", pageshown);
-} else {
- init(true);
-}
-let notifyPage = async () => {
+
+debug(`Loading NoScript in document %s, scripting=%s, readyState %s`,
+ document.URL, ns.canScript, document.readyState);
+
+var notifyPage = async () => {
debug("Page %s shown, %s", document.URL, document.readyState);
if (document.readyState === "complete") {
try {
- await Messages.send("pageshow", {seen: seen.list, canScript});
+ if (!("canScript" in ns)) {
+ let childPolicy = await Messages.send("fetchChildPolicy", {url: document.URL, contextUrl: top.location.href});
+ ns.config.CURRENT = childPolicy.CURRENT;
+ ns.setup(childPolicy.DEFAULT, childPolicy.MARKER);
+ return;
+ }
+
+ await Messages.send("pageshow", {seen: seen.list, canScript: ns.canScript});
return true;
} catch (e) {
debug(e);
+ if (/Receiving end does not exist/.test(e.message)) {
+ window.setTimeout(notifyPage, 2000);
+ }
}
}
return false;
}
-var queryingStatus = false;
-
-function reload(noCache = false) {
- init = () => {};
- location.reload(noCache);
-}
+notifyPage();
-async function init(oldPage = false) {
- if (queryingStatus) return;
- if (!document.URL.startsWith("http")) {
- return;
- }
- queryingStatus = true;
+window.addEventListener("pageshow", notifyPage);
- debug(`init() called in document %s, contentType %s readyState %s, frameElement %o`,
- document.URL, document.contentType, document.readyState, window.frameElement && frameElement.data);
-
- try {
- ({canScript, shouldScript} = await Messages.send("queryDocStatus", {url: document.URL}));
- debug(`document %s, canScript=%s, shouldScript=%s, readyState %s`, document.URL, canScript, shouldScript, document.readyState);
- if (canScript) {
- if (oldPage) {
- probe();
- return;
- }
- if (!shouldScript &&
- (document.readyState !== "complete" ||
- now() - performance.timing.domContentLoadedEventStart < 5000)) {
- // Something wrong: scripts can run, permissions say they shouldn't.
- // Was webRequest bypassed by caching/session restore/service workers?
- window.stop();
- let noCache = !!navigator.serviceWorker.controller;
- if (noCache) {
- for (let r of await navigator.serviceWorker.getRegistrations()) {
- await r.unregister();
- }
- }
- debug("Reloading %s (%s)", document.URL, noCache ? "no cache" : "cached");
- reload(noCache);
- return;
- }
- }
- init = () => {};
- } catch (e) {
- debug("Error querying docStatus", e);
- if (!oldPage &&
- /Receiving end does not exist/.test(e.message)) {
- // probably startup and bg page not ready yet, hence no CSP: reload!
- debug("Reloading", document.URL);
- reload();
- } else {
- setTimeout(() => init(oldPage), 100);
- }
- return;
- } finally {
- queryingStatus = false;
- }
-
- if (!canScript) onScriptDisabled();
+ns.on("capabilities", () => {
seen.record({
request: {
key: "noscript-probe",
@@ -230,21 +80,13 @@ async function init(oldPage = false) {
documentUrl: document.URL,
type: window === window.top ? "main_frame" : "script",
},
- allowed: canScript
- }
- );
-
- debug(`Loading NoScript in document %s, scripting=%s, readyState %s`,
- document.URL, canScript, document.readyState);
-
- if (/application|video|audio/.test(document.contentType)) {
- debug("Embedding document detected");
- embeddingDocument = true;
- window.addEventListener("pageshow", e => {
- debug("Active content still in document %s: %o", document.url, document.querySelectorAll("embed,object,video,audio"));
- }, true);
- // document.write("<plaintext>");
+ allowed: ns.canScript
+ });
+
+ if (!ns.canScript) {
+ if (document.readyState !== "loading") onScriptDisabled();
+ window.addEventListener("DOMContentLoaded", onScriptDisabled);
}
+
notifyPage();
- addEventListener("pageshow", notifyPage);
-}
+});