Closed Bug 774398 Opened 12 years ago Closed 2 years ago

window.matchMedia('print') does not fire when the user prints

Categories

(Core :: CSS Parsing and Computation, defect)

13 Branch
x86
macOS
defect
Not set
normal

Tracking

()

RESOLVED FIXED
105 Branch
Tracking Status
firefox105 --- fixed

People

(Reporter: tj.vantoll, Assigned: emilio)

References

(Blocks 1 open bug)

Details

Attachments

(3 files)

User Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_4) AppleWebKit/536.11 (KHTML, like Gecko) Chrome/20.0.1132.57 Safari/536.11 Steps to reproduce: var mediaQueryList = window.matchMedia('print'); mediaQueryList.addListener(function(mql) { if (mql.matches) { console.log('before printing'); } else { console.log('after printing'); } }); Actual results: Nothing. Expected results: I would expect 'before printing' to be logged before the user prints and 'after printing' to be logged after the user prints, much like the onbeforeprint and onafterprint events. 'print' is a valid media-type so I would expect window.matchMedia to support it. This code does log 'before printing' and 'after printing' as expected in Chrome and Safari. Webkit actually chose not to implement the onbeforeprint and onafterprint events in favor of this API - http://code.google.com/p/chromium/issues/detail?id=105743. I go into more details about this on my blog - http://tjvantoll.com/2012/06/15/detecting-print-requests-with-javascript/. Thanks.
Component: Untriaged → DOM: Events
Product: Firefox → Core
The window you added the listener on is never switched to a print presentation, as you can see by the fact that it keeps rendering with the screen stylesheet. When you print, a new document is created, has a presentation in the print medium applied to it, and is then rasterized to the print output. It sounds like WebKit's behavior is just an implementation detail of how they implement printing that doesn't look to me like it follows the spec. But it's worth making the spec explicit here, one way or another. David?
Component: DOM: Events → Style System (CSS)
And in particular, the spec for the return value of matchMedia says: The matches must return true if the associated media query list matches the state of the rendered Document and false if it does not. but doesn't define which Document the "rendered Document" is. Certainly window.document never changes which media queries it matches while printing. Does WebKit also fire listeners due to the viewport size being different when printing and on screen? Or is it special-casing "print"?
Just ran across this bug while trying to understand why my media listener wasn't firing for printing in Firefox 24.0. Boris, in response to your question... This isn't firing during a print: var mqList1 = window.matchMedia("print"); mqList1.addListener(function (mql) { alert('print fired'); }); This is firing during window resize: var mqList2 = window.matchMedia("(min-width: 640px)"); mqList2.addListener(function (mql) { alert('max-width fired'); }); So it appears "print" is a special case.
> So it appears "print" is a special case. It's not. It's just that while printing we print a clone of the document that is in the window you called matchMedia on. That document, and its window, is never in the "print" medium. On the other hand, when you resize the window you're resizing the window you added the listener on, so of course the listener fires.
Oh, sorry, you were trying to answer my question. But I think you misunderstood it. If you do: var mqList2 = window.matchMedia("(min-width: 6in)"); mqList2.addListener(function (mql) { alert('max-width fired'); }); and your page is less than 6in wide, and then you print on a piece of paper that's 8in wide, does the alert come up in _WebKit_? I was asking whether WebKit special-cases the print case, not whether we do; I know we do not.
Stable 36.0.1985.125 m: Oddly enough, it does not fire if "your page is less than 6in wide, and then you print on a piece of paper that's 8in wide" But if the page IS greater than 6in. It fires twice, once before printing (widths are of the paper) and once when it switches back. Even if you up it to 9in, you get the same behavior even though the paper is narrower. 38.0.2115.0 canary: Again, it does not fire if "your page is less than 6in wide, and then you print on a piece of paper that's 8in wide" But if the page IS greater than 6in. It fires once when it switches back to "screen". Even if you up it to 9in, you get the same behavior even though the paper is narrower. The print matchmedia query never matches on canary. https://code.google.com/p/chromium/issues/detail?id=401179
Attached file PrintMediaTest.html (deleted) —
Boris's explanation is incorrect. The result of matchMedia("print") continues to be false (so the listener doesn't fire) even in the printed document, as this testcase demonstrates.
How does this testcase demonstrate it? The printed document never has script running in it.
Oh, maybe you're concluding that based on your beforeprint/afterprint events? Let me clearly describe how the printing sequence goes in Gecko: 1) Start with a document D1. 2) Fire beforeprint on D1's window. 3) Clone D1 to produce a new document D2. 4) Fire afterprint on D1's window 5) Asynchronously print D2 (creating a print presentation for it in the process.
I believe this ticket is asking for additions to that sequence (At least I am): 1) Start with a document D1. 2) Fire beforeprint on D1's window. +) Change media to print AND resize the viewport +) Fire any applicable media query listeners 3) Clone D1 to produce a new document D2. +) Restore media and viewport +) Fire any applicable media query listeners 4) Fire afterprint on D1's window 5) Asynchronously print D2 (creating a print presentation for it in the process. It may need to be done on D2, depending on how it changing paper works. Changing the paper size would also need to trigger media query listeners if needed. As it stands, Firefox has NO way to resize dynamic elements for printing (Canvas, SVG, etc) besides stretching.
>+) Change media to print AND resize the viewport You can't do that, because you have to keep rendering the document in the screen presentation during this time. This is easily user-observable if a media query listener does an alert() for example.
Well, for me this is an issue too and it's handled differently in different browsers. I believe WebKit is reusing the window in the background and triggering necessary CSS media changes as well as media listeners. Actually I believe this is a more accurate implementation because media queries are all about different presentation of the same content on different media. If you clone the document into a new window that does use the same CSS as the original browser window but does not contain the same Media Query Listeners this is clearly a bug in my view. How about this sequence? 1) Start with a document D1 on W1. 2) Fire beforeprint on D1's window W1. 3) Clone D1 to produce a new document D2. 4) Create new window W2 for printing 5) Attach D2 to W2 6) Clone Media Query Listeners of W1 to W2 7) Fire afterprint on D1's window 8) Asynchronously print D2 in W2 while firing all Media Query Listeners in W2 I can imagine though that this is not so simple to implement and if you're really using a new window maybe there is also a scoping issue and MQL's can't simply be cloned. Maybe, alternatively, FF should freeze Rendering in the window while printing and apply all necessary print media changes on the browser window in the background (the print dialogue is modal anyway) and then change back and unfreeze. I don't know whats the best solution here but definitely this should be different than it is today.
> 4) Create new window W2 for printing Fwiw, right now W2 is never created. > 8) Asynchronously print D2 in W2 while firing all Media Query Listeners in W2 W2 is an invisible window the user can't see, right? What do you expect us to do when you try to navigate it, or otherwise do something that requires user interaction (e.g. an alert() or prompt())? > Maybe, alternatively, FF should freeze Rendering in the window while printing I don't think that's viable. "printing" is an asynchronous operation that can unfortunately easily take tens of seconds on a large page. Right now that's OK because it's done incrementally, one printed page at a time, and doesn't block the user during this time. > the print dialogue is modal anyway "Printing" is a process that start right _after_ the user dismisses that modal dialog... This is why the beforeprint/afterprint events exist: to allow pages to hook into this process at well-defined points, instead of relying on side-effects of behavior that's not actually defined in any specification (i.e. the user-visible window transitioning media when printing happens)... May I ask what use case of yours is _not_ addressed by beforeprint/afterprint events?
At the time of execution of beforeprint, the page is not sized to that of the print media. This makes resizing graphics (SVG & Canvas) to the proper size basically impossible. Charts & Graphs that have dynamic width become stretched or squashed when printed.
I see, thank you. For SVG it seems like simply having them sized in a way that responds to the viewport size would be doable, no? But I agree there is a problem here for canvas that needs solving... Do you need the actual layout to be done in the print medium when you get notified? Or do you just need the viewport size to be the print viewport (page) size?
D3 charts have been the case where I need to have the ability to redraw the SVG. When you have a chart on a very high res display, if you scaled it and maintained the aspect ratio the printed version would be the smallest chart you've ever seen. Likewise, you have to change how many axis labels there are, etc. I would need the layout to have been completed before I get notified. My code updates the viewBox(svg) or width & height(Canvas) to match the parent element and then redraws the entire graphic synchronously. That way, updating the graphic shouldn't trigger a reflow of the page. I believe the Google Docs team has similar requirements https://code.google.com/p/chromium/issues/detail?id=423833 If possible, it is probably worth trying to get ahold of them and seeing what their needs are.
> I would need the layout to have been completed before I get notified OK. That's actually a pretty major showstopper. There are parts of print layout (e.g. handling of table headers/footers) that currently cannot run more than once (as in, they will crash if they do), so any script modifications to what's being printed must happen before layout happens. Obviously your particular script isn't planning on making any changes that affect layout, but we can't enforce that on scripts in general if we let them run...
I have the same requirement, I need to set a width of 21cm for printing (I can do this with a print.css) but I need to trigger a re-draw on a svg tag so the layout of the page is not zoomed out because the graph is not scaled properly… I actually tried on Windows too and it doesn't work their too. FF41
We have the same requirement for printing a grid with the ZK framework. The grid redraw code needs to be executed based on the print media dimensions. Otherwise, since most screens are very wide, the result on paper is too small to be readable. Just style-sheets are not enough in this case. Chrome offers a way to do it, IE works somehow, but in Firefox it's impossible to meet this requirement. Win7 FF42 (IE11, Chrome 53)

I am using FF65 in GNU/Linux Ubuntu 18.10 having the same problem, the base case written 7 years ago still is valid:

var mediaQueryList = window.matchMedia('print');
mediaQueryList.addListener(function(mql) {
if (mql.matches) {
console.log('before printing');
} else {
console.log('after printing');
}
});

In Google Chrome is working perfectly.

I must hide some css class elements in printing mode and unhide or show after leave the printing mode.

I am freelancer and it is difficult to explain to final clients that they could ONLY print with Google Chrome and FF, IE and others are still in ice age era for just print a document and not working in 2019.

Please mozilla devs, me and other users would like more priority or progress in this annoying bug. Thanks

(In reply to Ángel Guzmán Maeso from comment #20)

I must hide some css class elements in printing mode and unhide or show after leave the printing mode.

Why doesn't media queries work in this case? (or in the ones above really...)

@media print {
  .my-element, .my-other-element { display: none }
}

Though maybe I'm missing something?

In general, I don't think firing the media change reflects how Gecko does printing at all, as Boris has explained above.

It doesn't work for things like Canvas or SVG where simply resizing them with CSS just stretches them (or worse clears them in the case of canvas)

Also hitting this -- same scenario: I need to resize charts dynamically (using HighCharts) to match the page size. In Chrome I can do this perfectly using window.matchMedia('print'), but this doesn't work in FF. As other posters have indicated, window.onbeforeprint also doesn't work because at this point the document isn't yet sized to the page.

(In reply to Boris Zbarsky [:bzbarsky] from comment #1)

When you print, a new document is created, has a presentation in the print
medium applied to it, and is then rasterized to the print output.

This isn't true. The document is still the same. It has all the data that the user entered and all of the visual modifications from before opening the print dialogue. Maybe it was so 9 years ago, when somebody first discovered that this isn't working. The problem is that it also still has elements at the same width as the window is. I'm designing a complex UI that needs to react to window size changes by code, and it also needs to do that before printing to make the layout fit on the printed page. But it happily remains at whatever width the window was before.

Firefox only supports the "beforeprint" and "afterprint" events but "beforeprint" is fired before the "print" media query is active, so it's useless to update the page's layout. (This is the same behaviour as in Chrome.)

Chrome can tell the page when the "print" media query is active so it can update its layout to the real available page width. That works perfectly. Firefox just can't print such pages. Fortunately, Firefox is just my desktop primary browser and the printing to a downloadable PDF in my case is done through Chrome on the server. But Firefox users shouldn't print my page directly because that fails like it's 1995.

This isn't true. The document is still the same.

Just to be clear, what I wrote is how printing is actually implemented. The existing document is cloned and the clone is printed. See https://searchfox.org/mozilla-central/rev/083983b7f09b00cdfe4f70396e39ea4f8a1735e1/dom/base/nsGlobalWindowOuter.cpp#5366 in the window.print() implementation, for example; the user printing has a similar effect.

Note that printing is an asynchronous process that can take quite a while, and the document can be mutated by script while this process is happening. Those mutations will not show up in the print output, because the document state was snapshotted during the clone.

Hello there, hitting this issue as well. I need to listen for the media query change to then trigger a "resize" event. This will force JS libraries such as React Grid Layout to recompute their size. Use case is similar as ones quoted above. It works perfect in Chrome but not in Firefox.

Also as stated by Yves Goergen above, "beforeprint" won't work because the media query is not yet applied when it fires: if I trigger a "resize" event, it will not correctly resize, because the "print" media query is not yet applied to the page. So I specifically need to listen to the "print" media query.

This might not be useful enough to land tho, given the various use cases
here which seem to rely on having a different CSS viewport size.

(FWIW I posted a testcase with a viewport-size media query over in bug 1775142 comment 8, too.)

Attachment #9283273 - Attachment description: WIP: Bug 774398 - Potential hack to make print event listeners fire. → WIP: Bug 774398 - Potential hack to make print and viewport media query listeners fire.
Assignee: nobody → emilio
Attachment #9283273 - Attachment description: WIP: Bug 774398 - Potential hack to make print and viewport media query listeners fire. → Bug 774398 - Hack to make print and viewport media query listeners fire when printing. r=jfkthame,#layout
Status: UNCONFIRMED → ASSIGNED
Ever confirmed: true
Pushed by ealvarez@mozilla.com: https://hg.mozilla.org/integration/autoland/rev/164b776f5b1f Hack to make print and viewport media query listeners fire when printing. r=geckoview-reviewers,owlish,dholbert
Status: ASSIGNED → RESOLVED
Closed: 2 years ago
Resolution: --- → FIXED
Target Milestone: --- → 105 Branch
Regressions: 1798218
Regressions: 1800897
Regressions: 1814400
Blocks: 1815643
Regressions: 1830163
Regressions: 1830307
Duplicate of this bug: 1815643
No longer blocks: 1815643
No longer blocks: 1775142
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Creator:
Created:
Updated:
Size: