enhancement
major
minor
major
minor
minor
#29935
Popup-Fenster (Skript-Rekorder, Dokumentation) lässt sich nach dem Schließen nicht sofort wieder öffnen (Edge/Chromium): Fehler "Cannot read properties of null (reading 'services')"
Wenn man in der Anwendung ein extra Fenster öffnet (z.B. den Scripting Recorder oder die Dokumentation), dieses Fenster dann irgendwann schließt und erneut öffnet, ohne vorher etwas anderes anzuklicken, lässt sich das Fenster das 2 Mal nicht mehr öffnen und es erscheint im Logger:
Client-side message: Uncaught JavaScript exception (exception: 'Uncaught TypeError: Cannot read properties of null (reading 'services')', component: 'rootLayout#masterFrame', source: 'http://localhost:8081/difa-aema/servlet/LayoutServlet/w5ffb0708bc7ec413-7e5e72c0eec0dbaa', layout: 'masterFrame.layout.xml', UserAgent[Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/155.0.0.0 Safari/537.36 Edg/155.0.0.0])
Ein F5 behebt das Problem, danach kann man das Fenster wieder öffnen.
Das Problem besteht nur im Microsoft Edge. Im Firefox taucht das Problem nicht auf.
Getestet mit TL 8.0.0 alpha11 und mit TL 7.10.2.
Kann im aktuellen TL7 Demo auf dem Testserver reproduziert werden.
Analyse
Nachgestellt im tl-demo (klassische Oberfläche) mit Chrome (gleiche Engine wie Edge): Entwickleroptionen → Skript-Rekorder, Fenster schließen, sofort erneut öffnen. Zwei Fehler im Client-Skript der klassischen Oberfläche (com.top_logic, script/tl/window.js und script/tl/simpleajax.js):
- Die Schließen-Meldung verliert das Rennen gegen den Öffnen-Befehl. Das Hauptfenster erfährt vom Schließen eines Zusatzfensters nur über einen Beobachter, der aus dem unload-Handler des Zusatzfensters gestartet wird und das Property closed des Fensters abfragt, bis es true ist (Lösung aus #22833). Erst dann wird onWindowClose an den Server geschickt. Kommt der Befehl zum erneuten Öffnen vorher beim Server an, ist das Fenster dort noch registriert; der Server antwortet mit winFocusWindow statt winOpenWindow, und der Client ignoriert das Fokussieren eines geschlossenen Fensters. Es öffnet sich nichts. Wird der unload-Handler vom Browser abgebrochen (Edge begrenzt dessen Laufzeit), bleibt die Sperre des Beobachters gesetzt und die Meldung kommt nie; dann hilft nur ein F5, bei dem das Hauptfenster die offenen Fenster neu abgleicht.
- Eine Antwort für das bereits geschlossene Fenster bringt die Antwortverarbeitung zum Absturz. Ein Request, den das Zusatzfenster beim Entladen noch abschickt, wird beantwortet, wenn das Fenster schon weg ist. Die requestsourcereference der Antwort zeigt auf das geschlossene Fenster; dessen document.defaultView ist in Chromium null (Firefox liefert weiter das Fenster), und BAL.getTopLevelWindowOf greift auf null.services zu. Die Exception lässt die Antwort zudem in der Liste der ausstehenden Antworten stehen, so dass sie ein zweites Mal verarbeitet wird.
Lösung
Nur Client-Skript, kein Server-Anteil:
- window.js: Ein Abgleich entfernt alle registrierten Zusatzfenster, deren closed-Flag gesetzt ist, und liefert ihre Namen. Die Beobachter für Laden und Entladen handeln nur noch, wenn die Registrierung noch ihren eigenen Eintrag enthält, damit ein verspäteter Beobachter kein unter gleichem Namen neu geöffnetes Fenster abmeldet. winFocusWindow meldet ein geschlossenes Fenster ab, statt es stillschweigend zu ignorieren.
- simpleajax.js: Jeder Request mit Sequenznummer trägt vor dem eigentlichen Befehl einen onWindowClose-Befehl für jedes beim Abgleich als geschlossen erkannte Fenster. Der Server führt die Befehle eines Requests der Reihe nach aus, so dass der Öffnen-Befehl das Fenster immer als geschlossen vorfindet und es neu öffnet, unabhängig davon, ob und wann unload feuert.
- simpleajax.js: Ein Request-Quellfenster, das geschlossen oder ohne defaultView ist, wird wie "keine Quelle" behandelt. Eine Antwort, deren Verarbeitung eine Exception wirft, wird verbraucht und über den Client-Fehlerkanal gemeldet, statt erneut verarbeitet zu werden.
Nicht Teil des Tickets: Ersatz des unload-Listeners durch pagehide (würde die bfcache-Fähigkeit aller Seiten ändern) und das Sperrprotokoll aus der IE-Zeit in window.js.
Test
Im tl-demo (klassische Oberfläche, Chrome/Edge): Entwickleroptionen → Skript-Rekorder öffnen, Fenster über den X-Knopf schließen, sofort erneut öffnen. Erwartung: Das Fenster öffnet sich erneut mit Inhalt, im Log erscheint keine Client-side message.