From 65294a3e199896cf4e2c37ee267d88bbdbab834b Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Mon, 2 Mar 2020 10:43:19 +0100 Subject: [PATCH] fix error on window closing in Chrome 80+ caused by Chrome 80+ no longer allowing to send a synchronious ajax request from beforeunload handler, using sendBeacon (async request with keepalive=true) instead --- api/js/etemplate/etemplate2.js | 6 +++--- api/js/jsapi/egw_global.d.ts | 11 ++++++----- api/js/jsapi/egw_json.js | 18 ++++++++++++++---- calendar/js/app.js | 2 +- 4 files changed, 24 insertions(+), 13 deletions(-) diff --git a/api/js/etemplate/etemplate2.js b/api/js/etemplate/etemplate2.js index 85f60ba5fd..0c19c273c2 100644 --- a/api/js/etemplate/etemplate2.js +++ b/api/js/etemplate/etemplate2.js @@ -285,9 +285,9 @@ etemplate2.prototype.bind_unload = function() { this.destroy_session = jQuery.proxy(function(ev) { - var request = egw.json("EGroupware\\Api\\Etemplate::ajax_destroy_session", - [this.etemplate_exec_id], null, null, false); - request.sendRequest(); + // need to use async === "keepalive" to run via beforeunload + egw.json("EGroupware\\Api\\Etemplate::ajax_destroy_session", + [this.etemplate_exec_id], null, null, "keepalive").sendRequest(); }, this); if (!window.onbeforeunload) diff --git a/api/js/jsapi/egw_global.d.ts b/api/js/jsapi/egw_global.d.ts index f67c817a2c..3f5501aa97 100644 --- a/api/js/jsapi/egw_global.d.ts +++ b/api/js/jsapi/egw_global.d.ts @@ -673,13 +673,14 @@ declare class JsonRequest { /** * Sends the assembled request to the server - * @param {boolean} [async=false] Overrides async provided in constructor to give an easy way to make simple async requests + * @param {boolean|"keepalive"} _async true: asynchronious request, false: synchronious request, + * "keepalive": async. request with keepalive===true / sendBeacon, to be used in beforeunload event * @param {string} method ='POST' allow to eg. use a (cachable) 'GET' request instead of POST * @param {function} error option error callback(_xmlhttp, _err) used instead our default this.error * * @return {jqXHR} jQuery jqXHR request object */ - sendRequest(async? : boolean, method? : "POST"|"GET", error? : Function) + sendRequest(async? : boolean|"keepalive", method? : "POST"|"GET", error? : Function) /** * Open websocket to push server (and keeps it open) * @@ -721,14 +722,14 @@ declare interface IegwWndLocal extends IegwGlobal * which handles the actual request. If the menuaction is a full featured * url, this one will be used instead. * @param _parameters which should be passed to the menuaction function. - * @param _async specifies whether the request should be asynchronous or - * not. + * @param {boolean|"keepalive"} _async true: asynchronious request, false: synchronious request, + * "keepalive": async. request with keepalive===true / sendBeacon, to be used in beforeunload event * @param _callback specifies the callback function which should be * called, once the request has been sucessfully executed. * @param _context is the context which will be used for the callback function * @param _sender is a parameter being passed to the _callback function */ - json(_menuaction : string, _parameters? : any[], _callback? : Function, _context? : object, _async? : boolean, _sender?) : JsonRequest; + json(_menuaction : string, _parameters? : any[], _callback? : Function, _context? : object, _async? : boolean|"keepalive", _sender?) : JsonRequest; /** * Registers a new handler plugin. * diff --git a/api/js/jsapi/egw_json.js b/api/js/jsapi/egw_json.js index c7866c6d98..4e1b843f26 100644 --- a/api/js/jsapi/egw_json.js +++ b/api/js/jsapi/egw_json.js @@ -7,7 +7,6 @@ * @link http://www.egroupware.org * @author Andreas Stöckel (as AT stylite.de) * @author Ralf Becker - * @version $Id$ */ /*egw:uses @@ -53,7 +52,8 @@ egw.extend('json', egw.MODULE_WND_LOCAL, function(_app, _wnd) * @param {array} _parameters * @param {function} _callback * @param {object} _context - * @param {boolean} _async + * @param {boolean|"keepalive"} _async true: asynchronious request, false: synchronious request, + * "keepalive": async. request with keepalive===true / sendBeacon, to be used in boforeunload event * @param {object} _sender * @param {egw} _egw */ @@ -154,11 +154,12 @@ egw.extend('json', egw.MODULE_WND_LOCAL, function(_app, _wnd) /** * Sends the assembled request to the server - * @param {boolean} [async=false] Overrides async provided in constructor to give an easy way to make simple async requests + * @param {boolean|"keepalive"} _async Overrides async provided in constructor: true: asynchronious request, + * false: synchronious request, "keepalive": async. request with keepalive===true / sendBeacon, to be used in beforeunload event * @param {string} method ='POST' allow to eg. use a (cachable) 'GET' request instead of POST * @param {function} error option error callback(_xmlhttp, _err) used instead our default this.error * - * @return {jqXHR} jQuery jqXHR request object + * @return {jqXHR|boolean} jQuery jqXHR request object or for async==="keepalive" boolean is returned */ json_request.prototype.sendRequest = function(async, method, error) { @@ -176,6 +177,15 @@ egw.extend('json', egw.MODULE_WND_LOCAL, function(_app, _wnd) } }); + // send with keepalive===true or sendBeacon to be used in beforeunload event + if (this.async === "keepalive" && typeof navigator.sendBeacon !== "undefined") + { + const data = new FormData(); + data.append('json_data', request_obj); + //(window.opener||window).console.log("navigator.sendBeacon", this.url, request_obj, data.getAll('json_data')); + return navigator.sendBeacon(this.url, data); + } + // Send the request via AJAX using the jquery ajax function // we need to use jQuery of window of egw object, as otherwise the one from main window is used! // (causing eg. apply from server with app.$app.method to run in main window instead of popup) diff --git a/calendar/js/app.js b/calendar/js/app.js index 8a7b554ecd..ef144ac858 100644 --- a/calendar/js/app.js +++ b/calendar/js/app.js @@ -243,7 +243,7 @@ app.classes.calendar = (function(){ "use strict"; return AppJS.extend( { window.onbeforeunload = function () { this.egw.json('calendar.calendar_uiforms.ajax_unlock', - [content.data.id, content.data.lock_token],null,true,null,null).sendRequest(true); + [content.data.id, content.data.lock_token],null,true,"keepalive",null).sendRequest(); }; } }