Skip to content

Commit 703d85f

Browse files
committed
"Soft reload" approach to fix file: and ftp: issues.
1 parent f9d4e08 commit 703d85f

File tree

3 files changed

+37
-56
lines changed

3 files changed

+37
-56
lines changed

src/content/syncFetchPolicy.js

Lines changed: 35 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,6 @@
88
// injected in the DOM as soon as possible.
99
debug("No CSP yet for non-HTTP document load: fetching policy synchronously...");
1010

11-
let earlyScripts = [];
12-
let dequeueEarlyScripts = (last = false) => {
13-
if (!(ns.canScript && earlyScripts)) return;
14-
if (earlyScripts.length === 0) {
15-
earlyScripts = null;
16-
return;
17-
}
18-
for (let s; earlyScripts && (s = earlyScripts.shift()); ) {
19-
debug("Restoring", s);
20-
s.firstChild._replaced = true;
21-
s._original.replaceWith(s);
22-
}
23-
}
24-
2511
let syncFetch = callback => {
2612
browser.runtime.sendSyncMessage(
2713
{id: "fetchPolicy", url, contextUrl: url},
@@ -31,24 +17,24 @@
3117
if (UA.isMozilla && document.readyState !== "complete") {
3218
// Mozilla has already parsed the <head> element, we must take extra steps...
3319

20+
let softReloading = true;
3421
debug("Early parsing: preemptively suppressing events and script execution.");
3522

3623
{
24+
3725
// List updated by build.sh from https://hg.mozilla.org/mozilla-central/raw-file/tip/xpcom/ds/StaticAtoms.py
3826
// whenever html5_events/html5_events.pl retrieves something new.
3927
let eventTypes = ['abort', 'mozaccesskeynotfound', 'activate', 'afterprint', 'afterscriptexecute', 'animationcancel', 'animationend', 'animationiteration', 'animationstart', 'audioprocess', 'auxclick', 'beforecopy', 'beforecut', 'beforeinput', 'beforepaste', 'beforeprint', 'beforescriptexecute', 'beforeunload', 'blocked', 'blur', 'bounce', 'boundschange', 'broadcast', 'bufferedamountlow', 'cached', 'cancel', 'change', 'chargingchange', 'chargingtimechange', 'checking', 'click', 'close', 'command', 'commandupdate', 'complete', 'compositionend', 'compositionstart', 'compositionupdate', 'connect', 'connectionavailable', 'contextmenu', 'copy', 'cut', 'dblclick', 'dischargingtimechange', 'downloading', 'data', 'drag', 'dragdrop', 'dragend', 'dragenter', 'dragexit', 'dragleave', 'dragover', 'dragstart', 'drain', 'drop', 'error', 'finish', 'focus', 'focusin', 'focusout', 'fullscreenchange', 'fullscreenerror', 'get', 'hashchange', 'input', 'inputsourceschange', 'install', 'invalid', 'keydown', 'keypress', 'keyup', 'languagechange', 'levelchange', 'load', 'loading', 'loadingdone', 'loadingerror', 'popstate', 'merchantvalidation', 'message', 'messageerror', 'midimessage', 'mousedown', 'mouseenter', 'mouseleave', 'mouselongtap', 'mousemove', 'mouseout', 'mouseover', 'mouseup', 'mozfullscreenchange', 'mozfullscreenerror', 'mozkeydownonplugin', 'mozkeyuponplugin', 'mozpointerlockchange', 'mozpointerlockerror', 'mute', 'notificationclick', 'notificationclose', 'noupdate', 'obsolete', 'online', 'offline', 'open', 'orientationchange', 'overflow', 'pagehide', 'pageshow', 'paste', 'payerdetailchange', 'paymentmethodchange', 'pointerlockchange', 'pointerlockerror', 'popuphidden', 'popuphiding', 'popuppositioned', 'popupshowing', 'popupshown', 'processorerror', 'push', 'pushsubscriptionchange', 'readystatechange', 'rejectionhandled', 'remove', 'requestprogress', 'resourcetimingbufferfull', 'responseprogress', 'reset', 'resize', 'scroll', 'select', 'selectionchange', 'selectend', 'selectstart', 'set', 'shippingaddresschange', 'shippingoptionchange', 'show', 'squeeze', 'squeezeend', 'squeezestart', 'statechange', 'storage', 'submit', 'success', 'typechange', 'terminate', 'text', 'toggle', 'tonechange', 'touchstart', 'touchend', 'touchmove', 'touchcancel', 'transitioncancel', 'transitionend', 'transitionrun', 'transitionstart', 'underflow', 'unhandledrejection', 'unload', 'unmute', 'updatefound', 'updateready', 'upgradeneeded', 'versionchange', 'visibilitychange', 'voiceschanged', 'vrdisplayactivate', 'vrdisplayconnect', 'vrdisplaydeactivate', 'vrdisplaydisconnect', 'vrdisplaypresentchange', 'webkitanimationend', 'webkitanimationiteration', 'webkitanimationstart', 'webkittransitionend', 'wheel', 'zoom', 'begin', 'end', 'repeat', 'pointerdown', 'pointermove', 'pointerup', 'pointercancel', 'pointerover', 'pointerout', 'pointerenter', 'pointerleave', 'gotpointercapture', 'lostpointercapture', 'devicemotion', 'deviceorientation', 'absolutedeviceorientation', 'deviceproximity', 'mozorientationchange', 'userproximity', 'devicelight', 'devicechange', 'mozvisualresize', 'mozvisualscroll', 'mozshowdropdown', 'scrollend', 'loadend', 'loadstart', 'progress', 'suspend', 'emptied', 'stalled', 'play', 'pause', 'loadedmetadata', 'loadeddata', 'waiting', 'playing', 'canplay', 'canplaythrough', 'seeking', 'seeked', 'timeout', 'timeupdate', 'ended', 'formdata', 'ratechange', 'durationchange', 'volumechange', 'addtrack', 'controllerchange', 'cuechange', 'enter', 'exit', 'encrypted', 'waitingforkey', 'keystatuseschange', 'removetrack', 'dataavailable', 'warning', 'start', 'stop', 'photo', 'gamepadbuttondown', 'gamepadbuttonup', 'gamepadaxismove', 'gamepadconnected', 'gamepaddisconnected', 'fetch', 'audiostart', 'audioend', 'soundstart', 'soundend', 'speechstart', 'speechend', 'result', 'nomatch', 'resume', 'mark', 'boundary', 'activated', 'deactivated', 'metadatachange', 'playbackstatechange', 'positionstatechange', 'supportedkeyschange', 'sourceopen', 'sourceended', 'sourceclosed', 'updatestart', 'update', 'updateend', 'addsourcebuffer', 'removesourcebuffer', 'appinstalled', 'activestatechanged', 'adapteradded', 'adapterremoved', 'alerting', 'antennaavailablechange', 'attributechanged', 'attributereadreq', 'attributewritereq', 'beforeevicted', 'busy', 'callschanged', 'cardstatechange', 'cfstatechange', 'characteristicchanged', 'clirmodechange', 'connected', 'connecting', 'connectionstatechanged', 'currentchannelchanged', 'currentsourcechanged', 'datachange', 'dataerror', 'deleted', 'deliveryerror', 'deliverysuccess', 'devicefound', 'devicepaired', 'deviceunpaired', 'dialing', 'disabled', 'disconnect', 'disconnected', 'disconnecting', 'displaypasskeyreq', 'draggesture', 'eitbroadcasted', 'emergencycbmodechange', 'enabled', 'enterpincodereq', 'evicted', 'failed', 'frequencychange', 'groupchange', 'headphoneschange', 'held', 'hfpstatuschanged', 'hidstatuschanged', 'holding', 'iccchange', 'iccdetected', 'iccinfochange', 'iccundetected', 'incoming', 'mapfolderlistingreq', 'mapgetmessagereq', 'mapmessageslistingreq', 'mapmessageupdatereq', 'mapsendmessagereq', 'mapsetmessagestatusreq', 'mousewheel', 'mozbrowserafterkeydown', 'mozbrowserafterkeyup', 'mozbrowserbeforekeydown', 'mozbrowserbeforekeyup', 'mozinterruptbegin', 'mozinterruptend', 'moznetworkdownload', 'moznetworkupload', 'moztimechange', 'newrdsgroup', 'obexpasswordreq', 'otastatuschange', 'overflowchanged', 'paint', 'pairingaborted', 'pairingconfirmationreq', 'pairingconsentreq', 'pendingchange', 'pichange', 'pschange', 'ptychange', 'pullphonebookreq', 'pullvcardentryreq', 'pullvcardlistingreq', 'radiostatechange', 'rdsdisabled', 'rdsenabled', 'readerror', 'readsuccess', 'ready', 'received', 'reloadpage', 'remoteheld', 'remoteresumed', 'requestmediaplaystatus', 'resuming', 'retrieving', 'rtchange', 'scanningstatechanged', 'scostatuschanged', 'sending', 'sent', 'speakerforcedchange', 'statuschanged', 'stkcommand', 'stksessionend', 'storageareachanged', 'ussdreceived', 'voicechange', 'websocket'];
4028
let eventSuppressor = e => {
4129
try {
42-
debug("Event suppressor called for ", e.type, e.target, earlyScripts, e.target._earlyScript); // DEV_ONLY
43-
if (!earlyScripts || document.readyState === "complete") {
44-
debug("Stopping event suppression");
45-
for (let et of eventTypes) document.removeEventListener(et, eventSuppressor, true);
46-
return;
47-
}
30+
debug("Event suppressor called for ", e.type, e.target); // DEV_ONLY
4831

49-
if (!ns.canScript || e.target._earlyScript) {
32+
if (softReloading) {
5033
e.stopPropagation();
5134
debug(`Suppressing ${e.type} on `, e.target); // DEV_ONLY
35+
} else {
36+
debug("Stopping event suppression");
37+
for (let et of eventTypes) document.removeEventListener(et, eventSuppressor, true);
5238
}
5339
} catch (e) {
5440
error(e);
@@ -58,7 +44,30 @@
5844
for (let et of eventTypes) document.addEventListener(et, eventSuppressor, true);
5945

6046
ns.on("capabilities", () => {
61-
if (!ns.canScript) {
47+
let {readyState} = document;
48+
debug("Readystate: %s, canScript: ", readyState, ns.canScript);
49+
if (ns.canScript) {
50+
let softReload = e => {
51+
try {
52+
debug("Soft reload", e);
53+
removeEventListener("DOMContentLoaded", softReload, true);
54+
let document = window.wrappedJSObject.document;
55+
let html = document.documentElement.outerHTML;
56+
document.open();
57+
softReloading = false;
58+
document.write(html);
59+
document.close();
60+
} catch(e) {
61+
error(e);
62+
}
63+
}
64+
if (readyState === "loading") {
65+
debug("Deferring softReload to DOMContentLoaded...");
66+
addEventListener("DOMContentLoaded", softReload, true);
67+
} else {
68+
softReload();
69+
}
70+
} else {
6271
try {
6372
for (let node of document.querySelectorAll("*")) {
6473
let evAttrs = [...node.attributes].filter(a => a.name.toLowerCase().startsWith("on"));
@@ -68,6 +77,7 @@
6877
node.setAttributeNodeNS(a);
6978
}
7079
}
80+
softReloading = false;
7181
} catch (e) {
7282
error(e);
7383
}
@@ -76,21 +86,11 @@
7686
}
7787

7888
addEventListener("beforescriptexecute", e => {
89+
if (!e.isTrusted) return;
7990
debug(e.type, e.target);
80-
if (earlyScripts) {
81-
let s = e.target;
82-
if (s._replaced) {
83-
debug("Replaced script found");
84-
dequeueEarlyScripts(true);
85-
return;
86-
}
87-
let replacement = document.createRange().createContextualFragment(s.outerHTML);
88-
replacement._original = s;
89-
s._earlyScript = true;
90-
earlyScripts.push(replacement);
91+
if (softReloading) {
9192
e.preventDefault();
92-
dequeueEarlyScripts(true);
93-
debug("Blocked early script");
93+
debug("Blocked early script", s);
9494
}
9595
}, true);
9696
}
@@ -112,5 +112,4 @@
112112
error("Background page not ready yet, retrying to fetch policy...")
113113
}
114114
}
115-
dequeueEarlyScripts();
116115
}

src/lib/SyncMessage.js

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -216,11 +216,6 @@
216216

217217
if (MOZILLA) {
218218

219-
// In order to cope with inconsistencies in XHR synchronicity,
220-
// allowing scripts to be executed (especially with synchronous loads
221-
// or when other extensions manipulate the DOM early) we additionally
222-
// suspend on beforescriptexecute events
223-
224219
let startTime = Date.now(); // DEV_ONLY
225220
let suspendURL = url + "&suspend=true";
226221
let suspended = 0;
@@ -240,22 +235,8 @@
240235
console.debug("sendSyncMessage resume #%s/%s - %sms", id, suspended, Date.now() - startTime); // DEV_ONLY
241236
};
242237

243-
let domSuspender = new MutationObserver(records => {
244-
console.debug("sendSyncMessage suspending on ", records)
245-
suspend();
246-
});
247-
domSuspender.observe(document, {childList: true, subtree: true});
248-
249-
let bodySuspender = e => {
250-
console.debug("Suspending on DOMContentLoaded");
251-
suspend();
252-
};
253-
254-
addEventListener("DOMContentLoaded", bodySuspender, true);
255238
let finalize = () => {
256239
console.debug("sendSyncMessage finalizing");
257-
domSuspender.disconnect();
258-
removeEventListener("DOMContentLoaded", bodySuspender, true);
259240
};
260241

261242
// on Firefox we first need to send an async message telling the

src/manifest.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,6 @@
7777
"run_at": "document_start",
7878
"matches": ["file://*/*", "ftp://*/*"],
7979
"js": [
80-
"lib/SyncMessage.js",
8180
"content/syncFetchPolicy.js"
8281
]
8382
},
@@ -89,6 +88,7 @@
8988
"js": [
9089
"lib/UA.js",
9190
"lib/browser-polyfill.js",
91+
"lib/SyncMessage.js",
9292
"lib/log.js",
9393
"lib/uuid.js",
9494
"lib/sha256.js",
@@ -106,6 +106,7 @@
106106
"content/sanitizePaste.js"
107107
]
108108
},
109+
109110
{
110111
"run_at": "document_start",
111112
"matches": ["ftp://*/*"],

0 commit comments

Comments
 (0)