Move error handler into separate file
This commit moves the global error handler into a separate file, so that it can catch module loading errors. This also adds support for properly displaying error messages with newlines in them (since the module loader may throw those)
This commit is contained in:
parent
e6a8eb15ca
commit
adfc9d3f54
|
@ -0,0 +1,61 @@
|
|||
// NB: this should *not* be included as a module until we have
|
||||
// native support in the browsers, so that our error handler
|
||||
// can catch script-loading errors.
|
||||
|
||||
|
||||
(function(){
|
||||
"use strict";
|
||||
|
||||
function convertNewlines(msg, parentElem) {
|
||||
const lines = msg.split("\n");
|
||||
lines.forEach(function (line) {
|
||||
parentElem.appendChild(document.createElement("br"));
|
||||
parentElem.appendChild(document.createTextNode(line));
|
||||
});
|
||||
parentElem.removeChild(parentElem.firstChild);
|
||||
return parentElem;
|
||||
}
|
||||
|
||||
// Fallback for all uncought errors
|
||||
function handleError (event, err) {
|
||||
try {
|
||||
const msg = document.getElementById('noVNC_fallback_errormsg');
|
||||
|
||||
// Only show the initial error
|
||||
if (msg.hasChildNodes()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var div = document.createElement("div");
|
||||
div.classList.add('noVNC_message');
|
||||
convertNewlines(event.message, div);
|
||||
msg.appendChild(div);
|
||||
|
||||
if (event.filename !== undefined && event.lineno !== undefined && event.colno !== undefined) {
|
||||
div = document.createElement("div");
|
||||
div.className = 'noVNC_location';
|
||||
const text = event.filename + ":" + event.lineno + ":" + event.colno;
|
||||
div.appendChild(document.createTextNode(text));
|
||||
msg.appendChild(div);
|
||||
}
|
||||
|
||||
if ((err !== undefined) &&
|
||||
(err.stack !== undefined)) {
|
||||
div = document.createElement("div");
|
||||
div.className = 'noVNC_stack';
|
||||
div.appendChild(document.createTextNode(err.stack));
|
||||
msg.appendChild(div);
|
||||
}
|
||||
|
||||
document.getElementById('noVNC_fallback_error')
|
||||
.classList.add("noVNC_open");
|
||||
} catch (exc) {
|
||||
document.write("noVNC encountered an error.");
|
||||
}
|
||||
// Don't return true since this would prevent the error
|
||||
// from being printed to the browser console.
|
||||
return false;
|
||||
}
|
||||
window.addEventListener('error', function (evt) { handleError(evt, evt.error); });
|
||||
window.addEventListener('unhandledrejection', function (evt) { handleError(evt.reason, evt.reason); });
|
||||
})();
|
|
@ -247,6 +247,11 @@ select:active {
|
|||
font-weight: normal;
|
||||
}
|
||||
|
||||
#noVNC_fallback_errormsg .noVNC_message {
|
||||
display: inline-block;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
#noVNC_fallback_error .noVNC_location {
|
||||
font-style: italic;
|
||||
font-size: 0.8em;
|
||||
|
|
43
app/ui.js
43
app/ui.js
|
@ -21,46 +21,6 @@ import RFB from "../core/rfb.js";
|
|||
import Display from "../core/display.js";
|
||||
import * as WebUtil from "./webutil.js";
|
||||
|
||||
// Fallback for all uncought errors
|
||||
window.addEventListener('error', function(event) {
|
||||
try {
|
||||
var msg, div, text;
|
||||
|
||||
msg = document.getElementById('noVNC_fallback_errormsg');
|
||||
|
||||
// Only show the initial error
|
||||
if (msg.hasChildNodes()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
div = document.createElement("div");
|
||||
div.appendChild(document.createTextNode(event.message));
|
||||
msg.appendChild(div);
|
||||
|
||||
div = document.createElement("div");
|
||||
div.className = 'noVNC_location';
|
||||
text = event.filename + ":" + event.lineno + ":" + event.colno;
|
||||
div.appendChild(document.createTextNode(text));
|
||||
msg.appendChild(div);
|
||||
|
||||
if ((event.error !== undefined) &&
|
||||
(event.error.stack !== undefined)) {
|
||||
div = document.createElement("div");
|
||||
div.className = 'noVNC_stack';
|
||||
div.appendChild(document.createTextNode(event.error.stack));
|
||||
msg.appendChild(div);
|
||||
}
|
||||
|
||||
document.getElementById('noVNC_fallback_error')
|
||||
.classList.add("noVNC_open");
|
||||
} catch (exc) {
|
||||
document.write("noVNC encountered an error.");
|
||||
}
|
||||
// Don't return true since this would prevent the error
|
||||
// from being printed to the browser console.
|
||||
return false;
|
||||
});
|
||||
|
||||
const UI = {
|
||||
|
||||
connected: false,
|
||||
|
@ -1751,11 +1711,10 @@ if (l10n.language !== "en" && l10n.dictionary === undefined) {
|
|||
// wait for translations to load before loading the UI
|
||||
UI.prime();
|
||||
}, function (err) {
|
||||
throw err;
|
||||
throw err;
|
||||
});
|
||||
} else {
|
||||
UI.prime();
|
||||
}
|
||||
|
||||
|
||||
export default UI;
|
||||
|
|
|
@ -21,6 +21,17 @@ var vendor_path = path.resolve(__dirname, '..', 'vendor');
|
|||
var out_dir_base = path.resolve(__dirname, '..', 'build');
|
||||
var lib_dir_base = path.resolve(__dirname, '..', 'lib');
|
||||
|
||||
const no_copy_files = new Set([
|
||||
// skip these -- they don't belong in the processed application
|
||||
path.join(vendor_path, 'sinon.js'),
|
||||
path.join(vendor_path, 'browser-es-module-loader'),
|
||||
]);
|
||||
|
||||
const no_transform_files = new Set([
|
||||
// don't transform this -- we want it imported as-is to properly catch loading errors
|
||||
path.join(app_path, 'error-handler.js'),
|
||||
]);
|
||||
|
||||
// walkDir *recursively* walks directories trees,
|
||||
// calling the callback for all normal files found.
|
||||
var walkDir = function (base_path, cb, filter) {
|
||||
|
@ -93,6 +104,8 @@ var make_lib_files = function (import_format, source_maps, with_app_dir) {
|
|||
const helper = helpers[import_format];
|
||||
|
||||
var handleDir = (js_only, in_path_base, filename) => {
|
||||
if (no_copy_files.has(filename)) return;
|
||||
|
||||
const out_path = path.join(out_path_base, path.relative(in_path_base, filename));
|
||||
if(path.extname(filename) !== '.js') {
|
||||
if (!js_only) {
|
||||
|
@ -103,10 +116,17 @@ var make_lib_files = function (import_format, source_maps, with_app_dir) {
|
|||
}
|
||||
|
||||
fse.ensureDir(path.dirname(out_path), () => {
|
||||
if (no_transform_files.has(filename)) {
|
||||
console.log(`Writing ${out_path}`);
|
||||
fse.copy(filename, out_path, (err) => { if (err) throw err; });
|
||||
return;
|
||||
}
|
||||
|
||||
const opts = babel_opts();
|
||||
if (helper && helpers.optionsOverride) {
|
||||
helper.optionsOverride(opts);
|
||||
}
|
||||
|
||||
babel.transformFile(filename, babel_opts(), (err, res) => {
|
||||
console.log(`Writing ${out_path}`);
|
||||
if (err) throw err;
|
||||
|
@ -124,11 +144,11 @@ var make_lib_files = function (import_format, source_maps, with_app_dir) {
|
|||
});
|
||||
};
|
||||
|
||||
walkDir(core_path, handleDir.bind(null, true, in_path || core_path));
|
||||
walkDir(vendor_path, handleDir.bind(null, true, in_path || main_path), (filepath, stats) => !((stats.isDirectory() && path.basename(filepath) === 'browser-es-module-loader') || path.basename(filepath) === 'sinon.js'));
|
||||
walkDir(core_path, handleDir.bind(null, true, in_path || core_path), (filename, stats) => !no_copy_files.has(filename));
|
||||
walkDir(vendor_path, handleDir.bind(null, true, in_path || main_path), (filename, stats) => !no_copy_files.has(filename));
|
||||
|
||||
if (with_app_dir) {
|
||||
walkDir(app_path, handleDir.bind(null, false, in_path || app_path));
|
||||
walkDir(app_path, handleDir.bind(null, false, in_path || app_path), (filename, stats) => !no_copy_files.has(filename));
|
||||
|
||||
const out_app_path = path.join(out_path_base, 'app.js');
|
||||
if (helper && helper.appWriter) {
|
||||
|
|
|
@ -1180,7 +1180,22 @@ if (typeof document != 'undefined' && document.getElementsByTagName) {
|
|||
if (script.type == 'module' && !script.loaded) {
|
||||
script.loaded = true;
|
||||
if (script.src) {
|
||||
loader.import(script.src);
|
||||
loader.import(script.src).catch(function(err) {
|
||||
// dispatch an error event so that we can display in errors in browsers
|
||||
// that don't yet support unhandledrejection
|
||||
try {
|
||||
var evt = new Event('error');
|
||||
} catch (_eventError) {
|
||||
var evt = document.createEvent('Event');
|
||||
evt.initEvent('error', true, true);
|
||||
}
|
||||
evt.message = err.message;
|
||||
evt.error = err;
|
||||
window.dispatchEvent(evt);
|
||||
|
||||
// throw so it still shows up in the console
|
||||
throw err;
|
||||
});
|
||||
}
|
||||
// anonymous modules supported via a custom naming scheme and registry
|
||||
else {
|
||||
|
@ -1191,7 +1206,22 @@ if (typeof document != 'undefined' && document.getElementsByTagName) {
|
|||
|
||||
var anonName = resolveIfNotPlain(uri, baseURI);
|
||||
anonSources[anonName] = script.innerHTML;
|
||||
loader.import(anonName);
|
||||
loader.import(anonName).catch(function(err) {
|
||||
// dispatch an error event so that we can display in errors in browsers
|
||||
// that don't yet support unhandledrejection
|
||||
try {
|
||||
var evt = new Event('error');
|
||||
} catch (_eventError) {
|
||||
var evt = document.createEvent('Event');
|
||||
evt.initEvent('error', true, true);
|
||||
}
|
||||
evt.message = err.message;
|
||||
evt.error = err;
|
||||
window.dispatchEvent(evt);
|
||||
|
||||
// throw so it still shows up in the console
|
||||
throw err;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,7 +20,22 @@ if (typeof document != 'undefined' && document.getElementsByTagName) {
|
|||
if (script.type == 'module' && !script.loaded) {
|
||||
script.loaded = true;
|
||||
if (script.src) {
|
||||
loader.import(script.src);
|
||||
loader.import(script.src).catch(function(err) {
|
||||
// dispatch an error event so that we can display in errors in browsers
|
||||
// that don't yet support unhandledrejection
|
||||
try {
|
||||
var evt = new Event('error');
|
||||
} catch (_eventError) {
|
||||
var evt = document.createEvent('Event');
|
||||
evt.initEvent('error', true, true);
|
||||
}
|
||||
evt.message = err.message;
|
||||
evt.error = err;
|
||||
window.dispatchEvent(evt);
|
||||
|
||||
// throw so it still shows up in the console
|
||||
throw err;
|
||||
});
|
||||
}
|
||||
// anonymous modules supported via a custom naming scheme and registry
|
||||
else {
|
||||
|
@ -31,7 +46,22 @@ if (typeof document != 'undefined' && document.getElementsByTagName) {
|
|||
|
||||
var anonName = resolveIfNotPlain(uri, baseURI);
|
||||
anonSources[anonName] = script.innerHTML;
|
||||
loader.import(anonName);
|
||||
loader.import(anonName).catch(function(err) {
|
||||
// dispatch an error event so that we can display in errors in browsers
|
||||
// that don't yet support unhandledrejection
|
||||
try {
|
||||
var evt = new Event('error');
|
||||
} catch (_eventError) {
|
||||
var evt = document.createEvent('Event');
|
||||
evt.initEvent('error', true, true);
|
||||
}
|
||||
evt.message = err.message;
|
||||
evt.error = err;
|
||||
window.dispatchEvent(evt);
|
||||
|
||||
// throw so it still shows up in the console
|
||||
throw err;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
3
vnc.html
3
vnc.html
|
@ -59,6 +59,9 @@
|
|||
src='http://getfirebug.com/releases/lite/1.2/firebug-lite-compressed.js'></script>
|
||||
-->
|
||||
|
||||
<!-- this is included as a normal file in order to catch script-loading errors as well -->
|
||||
<script type="text/javascript" src="app/error-handler.js"></script>
|
||||
|
||||
<!-- begin scripts -->
|
||||
<script src="vendor/browser-es-module-loader/dist/browser-es-module-loader.js"></script>
|
||||
<script type="module" src="app/ui.js"></script>
|
||||
|
|
Loading…
Reference in New Issue