Repository navigation
Expand file tree
/
Copy pathcompose.html
More file actions
147 lines (147 loc) · 6 KB
/
Copy pathcompose.html
File metadata and controls
147 lines (147 loc) · 6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta
http-equiv="Content-Security-Policy"
content="default-src 'self'; script-src 'self' 'unsafe-inline' 'wasm-unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob: https:; font-src 'self' data:; connect-src 'self' https://api.forwardemail.net wss://api.forwardemail.net; worker-src 'self' blob:; frame-src 'self'; object-src 'none'; base-uri 'self'; form-action 'self'; media-src 'none'"
/>
<title>Compose — Forward Email</title>
</head>
<body>
<div id="compose-root"></div>
<div id="toasts-root"></div>
<script>
// Fatal-error diagnostic overlay — see index.html for full notes.
// Registered before /src/compose-main.ts so it catches module-init
// failures in the compose window's bundle (which has the same dep
// graph risks as the main window).
(function () {
var SHOWN = false;
// See index.html: CSP violations originating from browser
// extensions (they often inject eval()) are outside the app's
// control and must not trigger the fatal overlay.
function isExtensionViolation(e) {
if (!e) return false;
var re =
/^(moz-extension|chrome-extension|safari-web-extension|safari-extension|webkit-masked-url|extension):/i;
return re.test(String(e.sourceFile || '')) || re.test(String(e.blockedURI || ''));
}
// See index.html for both rules. A browser-sanitized "Script error."
// at 0:0 with no error object comes from an injected script, not the
// app; and once compose-main.ts has mounted, runtime errors belong to
// the in-app logger and toast, not a panel over the draft.
function isSanitizedForeignError(event) {
if (!event || event.error) return false;
var msg = String(event.message || '');
if (!/^Script error\.?$/i.test(msg)) return false;
return !event.filename && !(event.lineno > 0);
}
function appHasBooted() {
return window.__appBootstrapped === true;
}
function showFatal(detail) {
if (SHOWN) {
var prev = document.getElementById('fe-fatal-error');
if (prev) {
prev.textContent += '\n\n' + detail;
return;
}
}
SHOWN = true;
var d = document.createElement('div');
d.id = 'fe-fatal-error';
d.style.cssText = [
'position:fixed',
'inset:0',
'z-index:2147483647',
'background:#1a1a1a',
'color:#f5f5f5',
'font:12px/1.45 ui-monospace,Menlo,Consolas,monospace',
'padding:16px 18px',
'overflow:auto',
'white-space:pre-wrap',
'word-break:break-word',
].join(';');
d.textContent = detail;
(document.body || document.documentElement).appendChild(d);
}
function describe(label, err) {
var lines = [];
lines.push('Fatal (compose): ' + label);
lines.push('UA: ' + navigator.userAgent);
lines.push('URL: ' + location.href);
lines.push('Time: ' + new Date().toISOString());
lines.push('');
var name = (err && err.name) || 'Error';
var msg =
(err && (err.message || err.reason || (err.toString && err.toString()))) || String(err);
lines.push('Type: ' + name);
lines.push('Message: ' + msg);
if (err && err.fileName) lines.push('File: ' + err.fileName);
if (err && err.lineNumber != null) lines.push('Line: ' + err.lineNumber);
if (err && err.columnNumber != null) lines.push('Col: ' + err.columnNumber);
if (err && err.stack) {
lines.push('');
lines.push('Stack:');
lines.push(String(err.stack));
}
return lines.join('\n');
}
window.addEventListener(
'error',
function (event) {
var t = event.target;
if (t && t !== window && t.tagName) return;
if (isSanitizedForeignError(event)) {
try {
console.warn('[fe-fatal][compose] ignoring sanitized cross-origin script error');
} catch (_) {}
return;
}
var err = event.error || {
name: 'Error',
message: event.message || 'unknown error',
fileName: event.filename,
lineNumber: event.lineno,
columnNumber: event.colno,
};
try {
console.error('[fe-fatal][compose]', err, event);
} catch (_) {}
if (appHasBooted()) return;
showFatal(describe('error', err));
},
true,
);
window.addEventListener('unhandledrejection', function (event) {
var reason = event.reason || { message: 'unhandled rejection' };
try {
console.error('[fe-fatal][compose][unhandledrejection]', reason);
} catch (_) {}
if (appHasBooted()) return;
showFatal(describe('unhandledrejection', reason));
});
document.addEventListener('securitypolicyviolation', function (e) {
try {
console.error('[fe-fatal][compose][csp]', e);
} catch (_) {}
if (isExtensionViolation(e)) return;
if (appHasBooted()) return;
showFatal(
[
'CSP violation',
'directive: ' + e.violatedDirective,
'effective: ' + e.effectiveDirective,
'blockedURI: ' + e.blockedURI,
'sourceFile: ' + e.sourceFile + ':' + e.lineNumber + ':' + e.columnNumber,
'sample: ' + (e.sample || ''),
].join('\n'),
);
});
})();
</script>
<script type="module" src="/src/compose-main.ts"></script>
</body>
</html>