Merge pull request #59141 from nextcloud/jtr/chore-EventSource-drop-legacy-IE-inline-fallback
Some checks failed
CodeQL Advanced / Analyze (actions) (push) Waiting to run
CodeQL Advanced / Analyze (javascript-typescript) (push) Waiting to run
Integration sqlite / changes (push) Waiting to run
Integration sqlite / integration-sqlite (master, 8.4, main, --tags ~@large files_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, capabilities_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, collaboration_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, comments_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, dav_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, federation_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, file_conversions) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, files_reminders) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, filesdrop_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, ldap_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, openldap_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, openldap_numerical_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, remoteapi_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, routing_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, setup_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, sharees_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, sharing_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, theming_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, videoverification_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite-summary (push) Blocked by required conditions
Psalm static code analysis / static-code-analysis (push) Has been cancelled
Psalm static code analysis / static-code-analysis-security (push) Has been cancelled
Psalm static code analysis / static-code-analysis-ocp (push) Has been cancelled
Psalm static code analysis / static-code-analysis-ncu (push) Has been cancelled
Psalm static code analysis / static-code-analysis-strict (push) Has been cancelled

chore(EventSource): drop no longer needed legacy fallback
This commit is contained in:
Ferdinand Thiessen 2026-03-21 22:47:14 +01:00 committed by GitHub
commit 9d8bab02fc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 49 additions and 149 deletions

View file

@ -20,42 +20,22 @@ function OCEventSource(src, data) {
let joinChar
this.typelessListeners = []
this.closed = false
this.listeners = {}
if (data) {
for (name in data) {
dataStr += name + '=' + encodeURIComponent(data[name]) + '&'
}
}
dataStr += 'requesttoken=' + encodeURIComponent(getRequestToken())
if (!this.useFallBack && typeof EventSource !== 'undefined') {
joinChar = '&'
if (src.indexOf('?') === -1) {
joinChar = '?'
}
this.source = new EventSource(src + joinChar + dataStr)
this.source.onmessage = function(e) {
for (let i = 0; i < this.typelessListeners.length; i++) {
this.typelessListeners[i](JSON.parse(e.data))
}
}.bind(this)
} else {
const iframeId = 'oc_eventsource_iframe_' + OCEventSource.iframeCount
OCEventSource.fallBackSources[OCEventSource.iframeCount] = this
const iframe = document.createElement('iframe')
iframe.id = iframeId
iframe.style.display = 'none'
joinChar = '&'
if (src.indexOf('?') === -1) {
joinChar = '?'
}
iframe.src = src + joinChar + 'fallback=true&fallback_id=' + OCEventSource.iframeCount + '&' + dataStr
this.iframe = iframe
document.body.appendChild(this.iframe)
this.useFallBack = true
OCEventSource.iframeCount++
joinChar = '&'
if (src.indexOf('?') === -1) {
joinChar = '?'
}
this.source = new EventSource(src + joinChar + dataStr)
this.source.onmessage = function(e) {
for (let i = 0; i < this.typelessListeners.length; i++) {
this.typelessListeners[i](JSON.parse(e.data))
}
}.bind(this)
// add close listener
this.listen('__internal__', function(data) {
if (data === 'close') {
@ -63,45 +43,8 @@ function OCEventSource(src, data) {
}
}.bind(this))
}
OCEventSource.fallBackSources = []
OCEventSource.iframeCount = 0// number of fallback iframes
OCEventSource.fallBackCallBack = function(id, type, data) {
OCEventSource.fallBackSources[id].fallBackCallBack(type, data)
}
OCEventSource.prototype = {
typelessListeners: [],
iframe: null,
listeners: {}, // only for fallback
useFallBack: false,
/**
* Fallback callback for browsers that don't have the
* native EventSource object.
*
* Calls the registered listeners.
*
* @private
* @param {string} type event type
* @param {object} data received data
*/
fallBackCallBack: function(type, data) {
let i
// ignore messages that might appear after closing
if (this.closed) {
return
}
if (type) {
if (typeof this.listeners.done !== 'undefined') {
for (i = 0; i < this.listeners[type].length; i++) {
this.listeners[type][i](data)
}
}
} else {
for (i = 0; i < this.typelessListeners.length; i++) {
this.typelessListeners[i](data)
}
}
},
lastLength: 0, // for fallback
/**
* Listen to a given type of events.
*
@ -111,20 +54,13 @@ OCEventSource.prototype = {
listen: function(type, callback) {
if (callback && callback.call) {
if (type) {
if (this.useFallBack) {
if (!this.listeners[type]) {
this.listeners[type] = []
this.source.addEventListener(type, function(e) {
if (typeof e.data !== 'undefined') {
callback(JSON.parse(e.data))
} else {
callback('')
}
this.listeners[type].push(callback)
} else {
this.source.addEventListener(type, function(e) {
if (typeof e.data !== 'undefined') {
callback(JSON.parse(e.data))
} else {
callback('')
}
}, false)
}
}, false)
} else {
this.typelessListeners.push(callback)
}
@ -135,9 +71,7 @@ OCEventSource.prototype = {
*/
close: function() {
this.closed = true
if (typeof this.source !== 'undefined') {
this.source.close()
}
this.source.close()
},
}

4
dist/9396-9396.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

4
dist/core-login.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

4
dist/core-main.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

4
dist/core-update.js vendored
View file

@ -1,2 +1,2 @@
(()=>{"use strict";var e,r,t,o={42716(e,r,t){var o=t(21777),a=t(81222),n=t(85471);t.nc=(0,o.aV)();const i=(0,n.$V)(()=>Promise.all([t.e(4208),t.e(9396)]).then(t.bind(t,31098))),l=(0,n.$V)(()=>Promise.all([t.e(4208),t.e(428)]).then(t.bind(t,428))),c=(0,a.C)("core","updaterView");new n.Ay({name:"NextcloudUpdater",render:e=>e("adminCli"===c?l:i)}).$mount("#core-updater")}},a={};function n(e){var r=a[e];if(void 0!==r)return r.exports;var t=a[e]={id:e,loaded:!1,exports:{}};return o[e].call(t.exports,t,t.exports,n),t.loaded=!0,t.exports}n.m=o,e=[],n.O=(r,t,o,a)=>{if(!t){var i=1/0;for(u=0;u<e.length;u++){for(var[t,o,a]=e[u],l=!0,c=0;c<t.length;c++)(!1&a||i>=a)&&Object.keys(n.O).every(e=>n.O[e](t[c]))?t.splice(c--,1):(l=!1,a<i&&(i=a));if(l){e.splice(u--,1);var d=o();void 0!==d&&(r=d)}}return r}a=a||0;for(var u=e.length;u>0&&e[u-1][2]>a;u--)e[u]=e[u-1];e[u]=[t,o,a]},n.n=e=>{var r=e&&e.__esModule?()=>e.default:()=>e;return n.d(r,{a:r}),r},n.d=(e,r)=>{for(var t in r)n.o(r,t)&&!n.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:r[t]})},n.f={},n.e=e=>Promise.all(Object.keys(n.f).reduce((r,t)=>(n.f[t](e,r),r),[])),n.u=e=>e+"-"+e+".js?v="+{428:"2532bb7008a9517fee02",5862:"580b9c2e231a9169a12f",6798:"1a6cf42d93801a926a3d",7471:"b4ac70873a3ab192efd0",9396:"6022b551225ee81ae5ad"}[e],n.o=(e,r)=>Object.prototype.hasOwnProperty.call(e,r),r={},t="nextcloud-ui-legacy:",n.l=(e,o,a,i)=>{if(r[e])r[e].push(o);else{var l,c;if(void 0!==a)for(var d=document.getElementsByTagName("script"),u=0;u<d.length;u++){var s=d[u];if(s.getAttribute("src")==e||s.getAttribute("data-webpack")==t+a){l=s;break}}l||(c=!0,(l=document.createElement("script")).charset="utf-8",n.nc&&l.setAttribute("nonce",n.nc),l.setAttribute("data-webpack",t+a),l.src=e),r[e]=[o];var p=(t,o)=>{l.onerror=l.onload=null,clearTimeout(f);var a=r[e];if(delete r[e],l.parentNode&&l.parentNode.removeChild(l),a&&a.forEach(e=>e(o)),t)return t(o)},f=setTimeout(p.bind(null,void 0,{type:"timeout",target:l}),12e4);l.onerror=p.bind(null,l.onerror),l.onload=p.bind(null,l.onload),c&&document.head.appendChild(l)}},n.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.nmd=e=>(e.paths=[],e.children||(e.children=[]),e),n.j=6344,(()=>{var e;globalThis.importScripts&&(e=globalThis.location+"");var r=globalThis.document;if(!e&&r&&(r.currentScript&&"SCRIPT"===r.currentScript.tagName.toUpperCase()&&(e=r.currentScript.src),!e)){var t=r.getElementsByTagName("script");if(t.length)for(var o=t.length-1;o>-1&&(!e||!/^http(s?):/.test(e));)e=t[o--].src}if(!e)throw new Error("Automatic publicPath is not supported in this browser");e=e.replace(/^blob:/,"").replace(/#.*$/,"").replace(/\?.*$/,"").replace(/\/[^\/]+$/,"/"),n.p=e})(),(()=>{n.b="undefined"!=typeof document&&document.baseURI||self.location.href;var e={6344:0};n.f.j=(r,t)=>{var o=n.o(e,r)?e[r]:void 0;if(0!==o)if(o)t.push(o[2]);else{var a=new Promise((t,a)=>o=e[r]=[t,a]);t.push(o[2]=a);var i=n.p+n.u(r),l=new Error;n.l(i,t=>{if(n.o(e,r)&&(0!==(o=e[r])&&(e[r]=void 0),o)){var a=t&&("load"===t.type?"missing":t.type),i=t&&t.target&&t.target.src;l.message="Loading chunk "+r+" failed.\n("+a+": "+i+")",l.name="ChunkLoadError",l.type=a,l.request=i,o[1](l)}},"chunk-"+r,r)}},n.O.j=r=>0===e[r];var r=(r,t)=>{var o,a,[i,l,c]=t,d=0;if(i.some(r=>0!==e[r])){for(o in l)n.o(l,o)&&(n.m[o]=l[o]);if(c)var u=c(n)}for(r&&r(t);d<i.length;d++)a=i[d],n.o(e,a)&&e[a]&&e[a][0](),e[a]=0;return n.O(u)},t=globalThis.webpackChunknextcloud_ui_legacy=globalThis.webpackChunknextcloud_ui_legacy||[];t.forEach(r.bind(null,0)),t.push=r.bind(null,t.push.bind(t))})(),n.nc=void 0;var i=n.O(void 0,[4208],()=>n(42716));i=n.O(i)})();
//# sourceMappingURL=core-update.js.map?v=f7c00486abeea321e9dc
(()=>{"use strict";var e,r,t,o={42716(e,r,t){var o=t(21777),a=t(81222),n=t(85471);t.nc=(0,o.aV)();const i=(0,n.$V)(()=>Promise.all([t.e(4208),t.e(9396)]).then(t.bind(t,31098))),l=(0,n.$V)(()=>Promise.all([t.e(4208),t.e(428)]).then(t.bind(t,428))),c=(0,a.C)("core","updaterView");new n.Ay({name:"NextcloudUpdater",render:e=>e("adminCli"===c?l:i)}).$mount("#core-updater")}},a={};function n(e){var r=a[e];if(void 0!==r)return r.exports;var t=a[e]={id:e,loaded:!1,exports:{}};return o[e].call(t.exports,t,t.exports,n),t.loaded=!0,t.exports}n.m=o,e=[],n.O=(r,t,o,a)=>{if(!t){var i=1/0;for(u=0;u<e.length;u++){for(var[t,o,a]=e[u],l=!0,c=0;c<t.length;c++)(!1&a||i>=a)&&Object.keys(n.O).every(e=>n.O[e](t[c]))?t.splice(c--,1):(l=!1,a<i&&(i=a));if(l){e.splice(u--,1);var d=o();void 0!==d&&(r=d)}}return r}a=a||0;for(var u=e.length;u>0&&e[u-1][2]>a;u--)e[u]=e[u-1];e[u]=[t,o,a]},n.n=e=>{var r=e&&e.__esModule?()=>e.default:()=>e;return n.d(r,{a:r}),r},n.d=(e,r)=>{for(var t in r)n.o(r,t)&&!n.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:r[t]})},n.f={},n.e=e=>Promise.all(Object.keys(n.f).reduce((r,t)=>(n.f[t](e,r),r),[])),n.u=e=>e+"-"+e+".js?v="+{428:"2532bb7008a9517fee02",5862:"580b9c2e231a9169a12f",6798:"1a6cf42d93801a926a3d",7471:"b4ac70873a3ab192efd0",9396:"3878274a9f370ac61247"}[e],n.o=(e,r)=>Object.prototype.hasOwnProperty.call(e,r),r={},t="nextcloud-ui-legacy:",n.l=(e,o,a,i)=>{if(r[e])r[e].push(o);else{var l,c;if(void 0!==a)for(var d=document.getElementsByTagName("script"),u=0;u<d.length;u++){var s=d[u];if(s.getAttribute("src")==e||s.getAttribute("data-webpack")==t+a){l=s;break}}l||(c=!0,(l=document.createElement("script")).charset="utf-8",n.nc&&l.setAttribute("nonce",n.nc),l.setAttribute("data-webpack",t+a),l.src=e),r[e]=[o];var p=(t,o)=>{l.onerror=l.onload=null,clearTimeout(f);var a=r[e];if(delete r[e],l.parentNode&&l.parentNode.removeChild(l),a&&a.forEach(e=>e(o)),t)return t(o)},f=setTimeout(p.bind(null,void 0,{type:"timeout",target:l}),12e4);l.onerror=p.bind(null,l.onerror),l.onload=p.bind(null,l.onload),c&&document.head.appendChild(l)}},n.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.nmd=e=>(e.paths=[],e.children||(e.children=[]),e),n.j=6344,(()=>{var e;globalThis.importScripts&&(e=globalThis.location+"");var r=globalThis.document;if(!e&&r&&(r.currentScript&&"SCRIPT"===r.currentScript.tagName.toUpperCase()&&(e=r.currentScript.src),!e)){var t=r.getElementsByTagName("script");if(t.length)for(var o=t.length-1;o>-1&&(!e||!/^http(s?):/.test(e));)e=t[o--].src}if(!e)throw new Error("Automatic publicPath is not supported in this browser");e=e.replace(/^blob:/,"").replace(/#.*$/,"").replace(/\?.*$/,"").replace(/\/[^\/]+$/,"/"),n.p=e})(),(()=>{n.b="undefined"!=typeof document&&document.baseURI||self.location.href;var e={6344:0};n.f.j=(r,t)=>{var o=n.o(e,r)?e[r]:void 0;if(0!==o)if(o)t.push(o[2]);else{var a=new Promise((t,a)=>o=e[r]=[t,a]);t.push(o[2]=a);var i=n.p+n.u(r),l=new Error;n.l(i,t=>{if(n.o(e,r)&&(0!==(o=e[r])&&(e[r]=void 0),o)){var a=t&&("load"===t.type?"missing":t.type),i=t&&t.target&&t.target.src;l.message="Loading chunk "+r+" failed.\n("+a+": "+i+")",l.name="ChunkLoadError",l.type=a,l.request=i,o[1](l)}},"chunk-"+r,r)}},n.O.j=r=>0===e[r];var r=(r,t)=>{var o,a,[i,l,c]=t,d=0;if(i.some(r=>0!==e[r])){for(o in l)n.o(l,o)&&(n.m[o]=l[o]);if(c)var u=c(n)}for(r&&r(t);d<i.length;d++)a=i[d],n.o(e,a)&&e[a]&&e[a][0](),e[a]=0;return n.O(u)},t=globalThis.webpackChunknextcloud_ui_legacy=globalThis.webpackChunknextcloud_ui_legacy||[];t.forEach(r.bind(null,0)),t.push=r.bind(null,t.push.bind(t))})(),n.nc=void 0;var i=n.O(void 0,[4208],()=>n(42716));i=n.O(i)})();
//# sourceMappingURL=core-update.js.map?v=72a6e6b45d2c6ad2ba27

File diff suppressed because one or more lines are too long

View file

@ -2,7 +2,7 @@
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2020-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2020-2026 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
* SPDX-License-Identifier: AGPL-3.0-only
*/
@ -12,8 +12,6 @@ use OCP\IEventSource;
use OCP\IRequest;
class EventSource implements IEventSource {
private bool $fallback = false;
private int $fallBackId = 0;
private bool $started = false;
public function __construct(
@ -31,25 +29,7 @@ class EventSource implements IEventSource {
\OC_Util::obEnd();
header('Cache-Control: no-cache');
header('X-Accel-Buffering: no');
$this->fallback = isset($_GET['fallback']) && $_GET['fallback'] == 'true';
if ($this->fallback) {
$this->fallBackId = (int)$_GET['fallback_id'];
/**
* FIXME: The default content-security-policy of ownCloud forbids inline
* JavaScript for security reasons. IE starting on Windows 10 will
* however also obey the CSP which will break the event source fallback.
*
* As a workaround thus we set a custom policy which allows the execution
* of inline JavaScript.
*
* @link https://github.com/owncloud/core/issues/14286
*/
header("Content-Security-Policy: default-src 'none'; script-src 'unsafe-inline'");
header('Content-Type: text/html');
echo str_repeat('<span></span>' . PHP_EOL, 10); //dummy data to keep IE happy
} else {
header('Content-Type: text/event-stream');
}
header('Content-Type: text/event-stream');
if (!$this->request->passesStrictCookieCheck()) {
header('Location: ' . \OC::$WEBROOT);
exit();
@ -63,16 +43,10 @@ class EventSource implements IEventSource {
}
/**
* send a message to the client
*
* @param string $type
* @param mixed $data
*
* @throws \BadMethodCallException
* if only one parameter is given, a typeless message will be send with that parameter as data
* @suppress PhanDeprecatedFunction
*/
public function send($type, $data = null) {
public function send(string $type, mixed $data = null): void {
if ($data && !preg_match('/^[A-Za-z0-9_]+$/', $type)) {
throw new \BadMethodCallException('Type needs to be alphanumeric (' . $type . ')');
}
@ -81,24 +55,15 @@ class EventSource implements IEventSource {
$data = $type;
$type = null;
}
if ($this->fallback) {
$response = '<script type="text/javascript">window.parent.OC.EventSource.fallBackCallBack('
. $this->fallBackId . ',"' . ($type ?? '') . '",' . json_encode($data, JSON_HEX_TAG) . ')</script>' . PHP_EOL;
echo $response;
} else {
if ($type) {
echo 'event: ' . $type . PHP_EOL;
}
echo 'data: ' . json_encode($data, JSON_HEX_TAG) . PHP_EOL;
if ($type) {
echo 'event: ' . $type . PHP_EOL;
}
echo 'data: ' . json_encode($data, JSON_HEX_TAG) . PHP_EOL;
echo PHP_EOL;
flush();
}
/**
* close the connection of the event source
*/
public function close() {
public function close(): void {
$this->send('__internal__', 'close'); //server side closing can be an issue, let the client do it
}
}

View file

@ -1,5 +1,6 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
@ -8,31 +9,31 @@
namespace OCP;
/**
* wrapper for server side events (http://en.wikipedia.org/wiki/Server-sent_events)
* includes a fallback for older browsers and IE
* Wrapper for Server-Sent Events (SSE).
*
* use server side events with caution, to many open requests can hang the server
* Use SSE with caution: too many concurrent open requests can overload or stall the server.
*
* The event source will initialize the connection to the client when the first data is sent
* The connection is opened lazily when the first event is sent.
*
* @see https://developer.mozilla.org/docs/Web/API/Server-sent_events
* @since 8.0.0
*/
interface IEventSource {
/**
* send a message to the client
* Sends an event to the client.
*
* @param string $type One of success, notice, error, failure and done. Used in core/js/update.js
* @param mixed $data
* @return void
* @param string $type Event type/name.
* @param mixed $data Event payload.
*
* if only one parameter is given, a typeless message will be send with that parameter as data
* If only one argument is provided, it is sent as a typeless payload (legacy behavior).
* @since 8.0.0
*/
public function send($type, $data = null);
public function send(string $type, mixed $data = null): void;
/**
* close the connection of the event source
* @return void
* Closes the SSE connection.
*
* @since 8.0.0
*/
public function close();
public function close(): void;
}