@@ -125,12 +125,15 @@ export default {
125125 return ;
126126 }
127127
128- // (Re)load the iframe by pointing it at the exporter. We push the project
129- // as soon as the iframe fires its load event (onFrameLoad) — that is the
130- // reliable signal that the exporter UI has initialised. The 'ready'
131- // postMessage handshake (onMessage) is a redundant fast path. A fallback
132- // timer only flags a real connectivity/embedding problem if *neither* the
133- // load event nor the handshake ever arrives.
128+ // (Re)load the iframe by pointing it at the exporter. We only push the
129+ // project once the exporter answers with the 'ready' postMessage handshake
130+ // (onMessage) — that is the *only* reliable signal that its scripts ran and
131+ // it can accept files. The iframe's load event (onFrameLoad) is NOT proof:
132+ // it also fires for a browser error page when the exporter server isn't
133+ // running (connection refused), and postMessage to that dead frame would
134+ // silently "succeed" and falsely report the project as handed over. A
135+ // fallback timer flags the connectivity/embedding problem if the handshake
136+ // never arrives.
134137 //
135138 // Bump reloadKey so the iframe reloads even when the URL is unchanged
136139 // (e.g. the user starts the exporter after opening this modal and clicks
@@ -140,28 +143,28 @@ export default {
140143
141144 this .clearHandshakeTimer ();
142145 this .handshakeTimer = setTimeout (() => {
143- if (! this .loaded && ! this . iframeReady ) {
146+ if (! this .iframeReady ) {
144147 this .errorKey = " exporter.errorNoHandshake" ;
145148 }
146149 }, 12000 );
147150 },
148151
149152 onFrameLoad() {
150153 this .loaded = true ;
151- this .errorKey = " " ;
152- this .clearHandshakeTimer ();
153154
154- // Nudge the embedded UI (so exporters that answer pings reply with
155- // 'ready'), then push the project. The load event guarantees the
156- // exporter's scripts have run and can accept files now.
155+ // The load event fires even for a browser error page (e.g. the exporter
156+ // server isn't running -> connection refused), so it is NOT proof that the
157+ // exporter is alive — we must not auto-send or report success here. Just
158+ // nudge the embedded UI with a ping; an exporter that is actually running
159+ // answers with 'ready' (onMessage), which is what triggers the send. If no
160+ // 'ready' arrives, the handshake timer reports the connection problem.
157161 const iframe = this .$refs .frame as HTMLIFrameElement | undefined ;
158162 if (iframe ?.contentWindow && this .activeOrigin ) {
159163 iframe .contentWindow .postMessage (
160164 { type: " liaexporter:ping" },
161165 this .activeOrigin
162166 );
163167 }
164- this .autoSend ();
165168 },
166169
167170 onMessage(event : MessageEvent ) {
@@ -290,7 +293,7 @@ export default {
290293 <button
291294 class =" btn btn-outline-secondary"
292295 @click =" resend"
293- :disabled =" (!loaded && ! iframeReady) || sending"
296+ :disabled =" ! iframeReady || sending"
294297 :title =" $t('exporter.resendTitle')"
295298 >
296299 <i class =" bi bi-arrow-repeat" ></i > {{ $t('exporter.resend') }}
0 commit comments