|
452 | 452 | } |
453 | 453 |
|
454 | 454 | /** |
455 | | - * Handle conversion using external converter service. |
456 | | - * Used in WordPress Playground where we can't register Service Workers. |
| 455 | + * External converter popup reference (for Playground mode). |
| 456 | + * We use a popup instead of iframe because iframes inside Playground |
| 457 | + * cannot have Cross-Origin Isolation enabled. |
| 458 | + */ |
| 459 | + let externalConverterPopup = null; |
| 460 | + |
| 461 | + /** |
| 462 | + * Handle conversion using external converter service via popup window. |
| 463 | + * Used in WordPress Playground where: |
| 464 | + * - Internal popups are blocked |
| 465 | + * - Iframes cannot have Cross-Origin Isolation |
| 466 | + * - But external popups (to different domains) may work |
457 | 467 | * |
458 | 468 | * @param {jQuery} $btn The button element. |
459 | 469 | * @param {string} action Action type (preview, download). |
|
462 | 472 | */ |
463 | 473 | async function handleExternalConverterConversion($btn, action, targetFormat, sourceFormat) { |
464 | 474 | try { |
465 | | - // Initialize external converter if needed |
466 | | - initExternalConverter(); |
467 | | - |
468 | 475 | // Step 1: Generate source document via AJAX |
469 | 476 | updateModal( |
470 | 477 | strings.generating || 'Generating document...', |
|
501 | 508 | } |
502 | 509 | const docBuffer = await docResponse.arrayBuffer(); |
503 | 510 |
|
504 | | - // Step 3: Wait for external converter to be ready |
| 511 | + // Step 3: Open external converter in a popup window |
| 512 | + // This is different from internal popups - external domains may work in Playground |
505 | 513 | updateModal( |
506 | | - strings.loadingWasm || 'Loading LibreOffice...', |
507 | | - 'Waiting for converter to initialize...' |
| 514 | + strings.loadingWasm || 'Opening converter...', |
| 515 | + 'A popup window will open. Please allow popups if blocked.' |
508 | 516 | ); |
509 | 517 |
|
510 | | - // Poll for ready status if not ready yet |
511 | | - if (!externalConverterReady && externalConverterIframe) { |
512 | | - const expectedOrigin = new URL(config.externalConverterUrl).origin; |
| 518 | + // Open the external converter |
| 519 | + const popupWidth = 600; |
| 520 | + const popupHeight = 400; |
| 521 | + const left = (window.screen.width - popupWidth) / 2; |
| 522 | + const top = (window.screen.height - popupHeight) / 2; |
513 | 523 |
|
514 | | - // Send ping and wait |
515 | | - for (let i = 0; i < 60; i++) { // Wait up to 60 seconds |
516 | | - externalConverterIframe.contentWindow.postMessage({ |
517 | | - type: 'ping', |
518 | | - requestId: 'poll_' + Date.now() |
519 | | - }, expectedOrigin); |
| 524 | + externalConverterPopup = window.open( |
| 525 | + config.externalConverterUrl, |
| 526 | + 'documentate_external_converter', |
| 527 | + `width=${popupWidth},height=${popupHeight},left=${left},top=${top},menubar=no,toolbar=no,location=no,status=yes,resizable=yes` |
| 528 | + ); |
520 | 529 |
|
521 | | - await new Promise(resolve => setTimeout(resolve, 1000)); |
| 530 | + if (!externalConverterPopup) { |
| 531 | + throw new Error('Popup blocked. Please allow popups for this site and try again.'); |
| 532 | + } |
522 | 533 |
|
523 | | - if (externalConverterReady) { |
524 | | - break; |
| 534 | + // Step 4: Wait for converter to be ready |
| 535 | + updateModal( |
| 536 | + strings.loadingWasm || 'Loading LibreOffice...', |
| 537 | + 'Downloading WASM (~50MB). This may take a while the first time.' |
| 538 | + ); |
| 539 | + |
| 540 | + const expectedOrigin = new URL(config.externalConverterUrl).origin; |
| 541 | + |
| 542 | + // Wait for ready message from popup |
| 543 | + const waitForReady = new Promise((resolve, reject) => { |
| 544 | + const timeout = setTimeout(() => { |
| 545 | + reject(new Error('Converter initialization timeout (120s)')); |
| 546 | + }, 120000); |
| 547 | + |
| 548 | + const readyHandler = (event) => { |
| 549 | + if (event.origin !== expectedOrigin) return; |
| 550 | + |
| 551 | + if (event.data.type === 'ready') { |
| 552 | + clearTimeout(timeout); |
| 553 | + window.removeEventListener('message', readyHandler); |
| 554 | + resolve(); |
| 555 | + } else if (event.data.type === 'pong' && event.data.ready) { |
| 556 | + clearTimeout(timeout); |
| 557 | + window.removeEventListener('message', readyHandler); |
| 558 | + resolve(); |
525 | 559 | } |
526 | | - } |
| 560 | + }; |
| 561 | + |
| 562 | + window.addEventListener('message', readyHandler); |
| 563 | + |
| 564 | + // Poll for ready status |
| 565 | + const pollInterval = setInterval(() => { |
| 566 | + if (externalConverterPopup && !externalConverterPopup.closed) { |
| 567 | + try { |
| 568 | + externalConverterPopup.postMessage({ |
| 569 | + type: 'ping', |
| 570 | + requestId: 'poll_' + Date.now() |
| 571 | + }, expectedOrigin); |
| 572 | + } catch (e) { |
| 573 | + // Popup might not be ready yet |
| 574 | + } |
| 575 | + } else { |
| 576 | + clearInterval(pollInterval); |
| 577 | + clearTimeout(timeout); |
| 578 | + window.removeEventListener('message', readyHandler); |
| 579 | + reject(new Error('Converter window was closed')); |
| 580 | + } |
| 581 | + }, 1000); |
| 582 | + |
| 583 | + // Clear interval when done |
| 584 | + const origResolve = resolve; |
| 585 | + resolve = () => { |
| 586 | + clearInterval(pollInterval); |
| 587 | + origResolve(); |
| 588 | + }; |
| 589 | + }); |
527 | 590 |
|
528 | | - if (!externalConverterReady) { |
529 | | - throw new Error('External converter timeout'); |
530 | | - } |
531 | | - } |
| 591 | + await waitForReady; |
532 | 592 |
|
533 | | - // Step 4: Send document to external converter |
| 593 | + // Step 5: Send document to converter |
534 | 594 | updateModal( |
535 | 595 | strings.convertingBrowser || 'Converting...', |
536 | 596 | 'Processing with LibreOffice WASM...' |
537 | 597 | ); |
538 | 598 |
|
539 | | - const expectedOrigin = new URL(config.externalConverterUrl).origin; |
540 | | - externalConverterIframe.contentWindow.postMessage({ |
| 599 | + // Set up result handler |
| 600 | + const waitForResult = new Promise((resolve, reject) => { |
| 601 | + const timeout = setTimeout(() => { |
| 602 | + reject(new Error('Conversion timeout (120s)')); |
| 603 | + }, 120000); |
| 604 | + |
| 605 | + const resultHandler = (event) => { |
| 606 | + if (event.origin !== expectedOrigin) return; |
| 607 | + |
| 608 | + if (event.data.type === 'result') { |
| 609 | + clearTimeout(timeout); |
| 610 | + window.removeEventListener('message', resultHandler); |
| 611 | + resolve(event.data); |
| 612 | + } else if (event.data.type === 'error') { |
| 613 | + clearTimeout(timeout); |
| 614 | + window.removeEventListener('message', resultHandler); |
| 615 | + reject(new Error(event.data.error || 'Conversion failed')); |
| 616 | + } |
| 617 | + }; |
| 618 | + |
| 619 | + window.addEventListener('message', resultHandler); |
| 620 | + }); |
| 621 | + |
| 622 | + // Send the document |
| 623 | + externalConverterPopup.postMessage({ |
541 | 624 | type: 'convert', |
542 | 625 | buffer: docBuffer, |
543 | 626 | format: targetFormat, |
544 | 627 | requestId: Date.now().toString() |
545 | 628 | }, expectedOrigin); |
546 | 629 |
|
547 | | - // Result will be handled by the message listener |
| 630 | + // Wait for result |
| 631 | + const result = await waitForResult; |
| 632 | + |
| 633 | + // Step 6: Handle result |
| 634 | + if (result.blob) { |
| 635 | + handleExternalConversionResult(result.blob, action, targetFormat); |
| 636 | + } else { |
| 637 | + throw new Error('No result blob received'); |
| 638 | + } |
| 639 | + |
| 640 | + // Close popup after success |
| 641 | + if (externalConverterPopup && !externalConverterPopup.closed) { |
| 642 | + externalConverterPopup.close(); |
| 643 | + } |
548 | 644 |
|
549 | 645 | } catch (error) { |
550 | 646 | console.error('Documentate external conversion error:', error); |
551 | 647 | showError(error.message || strings.errorGeneric || 'Conversion error.'); |
552 | 648 | pendingConversion = null; |
| 649 | + |
| 650 | + // Close popup on error |
| 651 | + if (externalConverterPopup && !externalConverterPopup.closed) { |
| 652 | + externalConverterPopup.close(); |
| 653 | + } |
553 | 654 | } |
554 | 655 | } |
555 | 656 |
|
|
0 commit comments