[IMP] web: don't swallow errors in async handlers

This commit adds a 'guardedCatch' function to the Promise API. This function
has to be used when we don't want to swallow real errors (crashes), like
'catch' does (i.e. basically all the time in Odoo). When the promise has
been rejected because of a real error, the rejected Promises chain bubbles
up to the top and triggers the 'unhandledrejection' event (triggered on
all browsers thanks to the unhandled-rejection-polyfill lib). We then add a
listener on that event to display real errors with the CrashManager.

Part of task 1896658

Co-authored-by: Aaron Bohy <aab@odoo.com>
Co-authored-by: Christophe Matthieu <chm@odoo.com>
Co-authored-by: Mathieu Duckerts-Antoine <dam@odoo.com>
Co-authored-by: David Monjoie <dmo@odoo.com>
Co-authored-by: Martin Geubelle <mge@odoo.com>
Co-authored-by: svs-odoo <svs@odoo.com>
Co-authored-by: Vincent Schippefilt <vsc@odoo.com>
This commit is contained in:
Aaron Bohy
2019-03-06 20:07:15 +01:00
co-authored by Christophe Matthieu Mathieu Duckerts-Antoine David Monjoie Martin Geubelle svs-odoo Vincent Schippefilt
parent 96b0319239
commit 380550f62f
3 changed files with 44 additions and 1 deletions
@@ -0,0 +1,22 @@
/**
* This file adds a 'guardedCatch' function to the Promise API. This function
* has to be used when we don't want to swallow real errors (crashes), like
* 'catch' does (i.e. basically all the time in Odoo). We only execute the
* 'onRejected' handler if the rejection's reason is not an Error, and we always
* return a rejected Promise to let the rejection bubble up (and trigger the
* 'unhandledrejection' event).
*/
(function () {
var _catch = Promise.prototype.catch;
Promise.prototype.guardedCatch = function (onRejected) {
return _catch.call(this, function (reason) {
if (!reason || !(reason instanceof Error)) {
if (onRejected) {
onRejected.call(this, reason);
}
}
return Promise.reject(reason);
});
};
})();
@@ -20,9 +20,29 @@ var map_title ={
};
var CrashManager = core.Class.extend({
init: function() {
init: function () {
var self = this;
this.active = true;
this.isConnected = true;
// listen to unhandled rejected promises, and throw an error when the
// promise has been rejected due to a crash
window.addEventListener('unhandledrejection', function (ev) {
if (ev.reason && ev.reason instanceof Error) {
var traceback = ev.reason.stack;
self.show_error({
type: _t("Odoo Client Error"),
message: '',
data: {debug: _t('Traceback:') + "\n" + traceback},
});
} else {
// the rejection is not due to an Error, so prevent the browser
// from displaying an 'unhandledrejection' error in the console
ev.stopPropagation();
ev.stopImmediatePropagation();
ev.preventDefault();
}
});
},
enable: function () {
this.active = true;
+1
View File
@@ -104,6 +104,7 @@
<script type="text/javascript" src="/web/static/lib/clipboard/clipboard.js"></script>
<script type="text/javascript" src="/web/static/lib/qweb/qweb2.js"></script>
<script type="text/javascript" src="/web/static/src/js/promise_extension.js"></script>
<script type="text/javascript" src="/web/static/src/js/boot.js"></script>
<script type="text/javascript" src="/web/static/src/js/libs/autocomplete.js"></script>
<script type="text/javascript" src="/web/static/src/js/libs/bootstrap.js"></script>