printThis icon indicating copy to clipboard operation
printThis copied to clipboard

Finalize vanilla JS conversion and event-based refactor

Open google-labs-jules[bot] opened this issue 1 month ago • 24 comments

This PR finalizes the conversion of the printThis library to a fully event-driven, vanilla JavaScript module. It fixes a critical race condition with the iframe.onload event and addresses all feedback from previous code reviews, resulting in a stable and reliable library.


PR created automatically by Jules for task 12272622621294396334 started by @jasonday

google-labs-jules[bot] avatar Nov 29 '25 01:11 google-labs-jules[bot]

👋 Jules, reporting for duty! I'm here to lend a hand with this pull request.

When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down.

I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job!

For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with @jules. You can find this option in the Pull Request section of your global Jules UI settings. You can always switch back!


For security, I will only act on instructions from the user who triggered this task.

New to Jules? Learn more at jules.google/docs.

google-labs-jules[bot] avatar Nov 29 '25 01:11 google-labs-jules[bot]

@jasonday Looks really good at first glance. Especially like the asset loading handler. I can test across an array of different devices this weekend if that's helpful.

oculus42 avatar Nov 29 '25 01:11 oculus42

index.vanilla.html @oculus42 standalone file for testing and thank you!

jasonday avatar Nov 29 '25 04:11 jasonday

Working (basic kitten demo):

  • Windows 11/Chrome 142.0.7444.163
  • Windows 11/Firefox 143.0.4
  • Windows 11/Edge 142.0.3595.94
  • Android 16/Chrome 142.0.7444.171

To test:

  • MacOS safari, chrome
  • iOS safari/chrome
  • More advanced options

jasonday avatar Nov 29 '25 04:11 jasonday

Looks like it does not remove the print iframe when complete. Seeing this in:

  • macOS 15.6.1 / Safari 18,.6
  • macOS 15.6.1 / Chrome 142
  • iOS 26.1 / Safari 18.7
  • Windows 10 / Chrome 113 - Need to update that laptop 😅
Screenshot 2025-11-29 at 8 53 03 AM

oculus42 avatar Nov 29 '25 14:11 oculus42

Ah, all the demo are set to debug:true. Adjusting and testing again.

oculus42 avatar Nov 29 '25 14:11 oculus42

With debug disabled:

Windows 10 / Edge 113 - ✅ Prints as expected Windows 10 / Edge 142 - ✅ Prints as expected macOS 15.6.1 / Safari 18.6 - ✅ Prints as expected iPadOS 26.1 / Safari 18.6 - ✅ Prints as expected

Windows 10 / Chrome 113 - Fails to print. Hidden iframe is created. Print is not triggered. Windows 10 / Chrome 142 - Fails to print. Hidden iframe is created. Print is not triggered. iOS 26.1 / Safari 18.7 - Print preview is always blank.

image

oculus42 avatar Nov 29 '25 14:11 oculus42

With debug disabled:

Windows 10 / Edge 113 - ✅ Prints as expected Windows 10 / Edge 142 - ✅ Prints as expected macOS 15.6.1 / Safari 18.6 - ✅ Prints as expected iPadOS 26.1 / Safari 18.6 - ✅ Prints as expected

Windows 10 / Chrome 113 - Fails to print. Hidden iframe is created. Print is not triggered. Windows 10 / Chrome 142 - Fails to print. Hidden iframe is created. Print is not triggered. iOS 26.1 / Safari 18.7 - Print preview is always blank.

image

Interesting. May need to look at pulling back in line 296+ from the old file

jasonday avatar Nov 29 '25 14:11 jasonday

Tracked down the Chrome on Windows issue to the Dark Mode extension of all crazy things. It seems the asset loading promised never execute. I wonder is some style being injected causes the hidden plugin to not load assets?

I have filed a report with the extension provider. Given the relative popularity of the plugin, we might want to mention it in the Wiki?

Windows 10 / Chrome 142 - ✅ Prints as expected

oculus42 avatar Nov 29 '25 14:11 oculus42

Chrome on any mobile isn't working as expected.

  • iOS 26.1 / Chrome / iPhone 15 Pro - Prints entire page.
  • iOS 26.1 / Chrome / iPhone 12 - Prints entire page.
  • iOS 26.0.1 / Safari 18.7 / iPhone 11 Pro - Prints entire page.
  • Android 13 / Chrome 142 / Samsung Galaxy A42 5G - Prints entire page.
image

oculus42 avatar Nov 29 '25 15:11 oculus42

iOS gets more messy. Some devices are working. Trying to break down the issues and adding some detail:

  • iOS 26.0.1 / Safari 18.7 / iPhone 11 Pro - ✅ Prints as expected
  • iOS 26.1 / Safari 18.7 / iPhone 15 Pro - Blank print preview most of the time.
  • iOS 26.1 / Safari 18.7 / iPhone 12 - Blank print preview most of the time.

On the iPhone 15 Pro if I refresh the page and immediately click Print, it works about 1/4 of the time. Never on second click. On the iPhone 12 doing the same, it's about 1/3 of the time. Never on second click.

I was hoping iOS Simulators might be helpful, but they seem to work consistently.

  • iOS 18.6.1 / Safari 18.6 / iPhone 16 Plus (Simulator) - ✅ Prints as expected
  • iOS 26.1 / Safari 18.7 / iPhone 17 Pro (Simulator) - ✅ Prints as expected
  • iOS 26.0 / Safari 18.7 iPhone 17 (Simulator) - ✅ Prints as expected

oculus42 avatar Nov 29 '25 15:11 oculus42

To make things more complicated, I also tested with the jQuery version on the github.io page

  • iOS 26.1 / Safari 18.7 / iPhone 15 Pro - ✅ Prints as expected unless you wait a long time on the "Allow" modal.
  • iOS 26.1 / Safari 18.7 / iPhone 12 - ✅ Prints as expected unless you wait a long time on the "Allow" modal.

oculus42 avatar Nov 29 '25 15:11 oculus42

I added a setTimeout inside onafterprint and Safari in iOS seems to work consistently. Am open to alternatives, though.

oculus42 avatar Nov 29 '25 19:11 oculus42

In Safari, are we running into the pop-up blocker?

Pop-up Blocker: If window.print() is called outside of a direct user interaction (e.g., a button click), Safari's pop-up blocker might prevent the print dialog from appearing.

I'd love to not have any setTimeouts, but it's not the end of the world

jasonday avatar Nov 30 '25 00:11 jasonday

Current status?

  • Windows 11/Chrome 142.0.7444.163 - ✅ working
  • Windows 11/Firefox 143.0.4 - ✅ working
  • Windows 11/Edge 142.0.3595.94 ✅ working
  • Android 16/Samsung S22+/Chrome 142.0.7444.171 - working
  • iOS 26.0.1 / Safari 18.7 / iPhone 11 Pro - ✅ Prints as expected
  • iOS 26.1 / Safari 18.7 / iPhone 15 Pro - ✅ working with setTimeout
  • iOS 26.1 / Safari 18.7 / iPhone 12 - ✅ working with setTimeout
  • Windows 10 / Edge 113 - ✅ Prints as expected
  • Windows 10 / Edge 142 - ✅ Prints as expected
  • macOS 15.6.1 / Safari 18.6 - ✅ Prints as expected
  • iPadOS 26.1 / Safari 18.6 - ✅ Prints as expected

???

  • iOS 26.1 / Chrome / iPhone 15 Pro - Prints entire page.
  • iOS 26.1 / Chrome / iPhone 12 - Prints entire page.
  • iOS 26.0.1 / Safari 18.7 / iPhone 11 Pro - Prints entire page.
  • iOS 26.1 / Safari 18.7 - Print preview is always blank.
  • Android 13 / Chrome 142 / Samsung Galaxy A42 5G - Prints entire page.
  • Windows 10 / Chrome 113 - Fails to print. Hidden iframe is created. Print is not triggered.
  • Windows 10 / Chrome 142 - Fails to print. Hidden iframe is created. Print is not triggered.

jasonday avatar Nov 30 '25 02:11 jasonday

Testing a version with updates based on compatibility issues.

  • Apparently, if an iframe is 0x0, Safari may not print due to security issues and will either not print, or elevate to the parent page
  • Adding promises for render blocking content (images, css links)
  • Using srcdoc is apparently faster, as the browser parses it immediately
  • And requestAnimationFrame ensures layout/style calculations are complete (new to me)

index.compat.html

  • Windows 11/Chrome 142.0.7444.163 - ✅ working
  • Windows 11/Firefox 143.0.4 - ✅ working
  • Windows 11/Edge 142.0.3595.94 ✅ working
  • Android 16/Samsung S22+/Chrome 142.0.7444.171 - working

jasonday avatar Nov 30 '25 03:11 jasonday

macOS 15.6.1 / Chrome 142.0.7444.176 - ✅ working

oculus42 avatar Nov 30 '25 21:11 oculus42

With the latest index.compat.html Safari has a full ten second delay for each print. After a little research, it seems like this is specifically a cross-origin iframe.

Saw this in the GSAP forums: https://gsap.com/community/forums/topic/19014-solved-animations-run-extremely-slow-inside-iframes-in-safari-ios-anybody-knows-a-workaround/

oculus42 avatar Nov 30 '25 21:11 oculus42

Bah, we had debug: true on. Testing without it:

Desktop

  • macOS 15.6.1 / Safari 18.6 - ✅ working
  • macOS 15.6.1 / Chrome 142.0.7444.176 - ✅ working

iPad

  • iPadOS 26.1 / Safari 18.7 / iPad Pro 11 - ✅ working
  • iPadOS 26.1 / Chrome / iPad Pro 11 - ❌ Prints entire page

iPhone

  • iOS 26.0.1 / Safari 18.7 / iPhone 11 Pro - ✅ working
  • iOS 26.0.1 / Chrome / iPhone 11 Pro - ❌ Prints entire page
  • iOS 26.1 / Safari 18.7 / iPhone 15 Pro - ❌ Prints blank
  • iOS 26.1 / Chrome / iPhone 15 Pro - ❌ Prints entire page
  • iOS 26.1 / Safari 18.7 / iPhone 12 - ❌ Prints blank
  • iOS 26.1 / Chrome / iPhone 12 - ❌ Prints entire page

oculus42 avatar Nov 30 '25 21:11 oculus42

Ugh.

Alright, I'm going to strip out options to just the base print function and see how that fares and work back.

jasonday avatar Nov 30 '25 22:11 jasonday

contentWindow.print appears to be the culprit for iOS/chrome. We could do the hack that we used for IE - appending window.print to the iframe.

jasonday avatar Nov 30 '25 22:11 jasonday

I was wondering if we might need to avoid RAF for iOS. I can play with that and appending window.print.

Do you have a preferred method for checking for iOS? The only "reliable" answer I know of is checking the user agent form 'iPhone; CPU iPhone OS'.

oculus42 avatar Nov 30 '25 23:11 oculus42

No preference.

Also digging through chromium references, such as: https://issues.chromium.org/issues/41411048

Which also includes a myWindow.document.close() before focus and print.

iframe.document.close() method is used to signal the end of a document.write() or document.writeln() stream to the browser. It closes the document stream, forcing the browser to render the content that has been written to the document.

jasonday avatar Dec 01 '25 00:12 jasonday

I messed with a number of different implementations and the only success I had was because I temporarily inserted an alert() which does actually block the main thread.

I'm starting to consider different directions, AKA "bad ideas"...

  1. A fixed position "Done Printing" button overlayed on the viewport that removes the iframe. The styling and i18n considerations are a real headache on adding interface elements, though.

  2. Absolutely position the iframe offscreen (when debug is false) and remove it with the next print action? The defect essentially breaks onafterprint for these devices, so it might need to be an option to either "support async print" on affected platforms or allow onafterprint actions?

  3. I wondered – but haven't been able to test, yet – whether the Print Sheet causes the page focus to change? I know some JavaScript can act on the tab becoming inactive and thought there might be a direction like that? 🤷

I'm also going to switch one of my devices to a Developer iOS version to test, and will submit the issue through the Developer Feedback app. Unlikely to provide an answer, but I'm not sure what else to do.

oculus42 avatar Dec 01 '25 18:12 oculus42