MicrosoftEdge / WebView2Feedback

Feedback and discussions about Microsoft Edge WebView2
https://aka.ms/webview2
444 stars 53 forks source link

Controlling the print dialog/window #42

Open verelpode opened 5 years ago

verelpode commented 5 years ago

@liminzhu wrote:

For the WebView2 we're building though, you can use window.print. WebView opens the same printing prompt and prints what the browser does. I was messing with this,

webviewWindow->Navigate(L"https://www.twitter.com/");
EventRegistrationToken token;
webviewWindow->add_NavigationCompleted(Callback<IWebView2NavigationCompletedEventHandler>(
[](IWebView2WebView* wv, IWebView2NavigationCompletedEventArgs* args) -> HRESULT {
webviewWindow->ExecuteScript(L"window.print();", nullptr);
return S_OK;
}).Get(), &token);

That's great. We'll be among the people using that feature. However, considering that the above technique uses ExecuteScript, will printing always still work if EcmaScript is disabled? I'm referring to WebView2 having a setting equivalent to Windows.UI.Xaml.Controls.WebViewSettings.IsJavaScriptEnabled, that is put_IsScriptEnabled.

For this reason, I suggest adding a simple "OpenPrintDialog" method to the API, even if it is implemented by doing exactly the same as the ExecuteScript snippet above. The advantages of "OpenPrintDialog" are:

  1. Allows you to change the internal implementation in a future version of WebView2, without changing the public API. Such a change might become necessary for some reason and then you'll be glad that the change can be performed without breaking compatibility with the public API.
  2. Can succeed regardless of whether EcmaScript is disabled.
  3. Protects the public API against possible future changes in the behavior of EcmaScript.
  4. Although the ExecuteScript technique works, it is obscure and will leave people wondering/asking: How to print?

Can you make a "ClosePrintDialog" method in WebView2 that closes any print dialog/window that may be open? (Does nothing if no print dialog is currently open.) Related: If a WebView2 XAML element has it's Visibility property set to Collapsed, or if the WebView2 element is removed/detached from the element tree, what happens to the print dialog? If it remains visible because it's in a separate floating window, then the "ClosePrintDialog" method would be especially useful to solve problems here.

Can you make a "IsPrintDialogOpen" method that returns a boolean indicating whether the/any print dialog/window is open/displayed/visible?

Can you make an event that is triggered when the print dialog/window is closed? Ideally the event arguments would include a boolean that specifies whether or not the dialog was closed via the Cancel button/key/action. An extra bonus feature would be the inclusion of a background print job identifier in the arguments of this event.

When the user closes the print dialog by clicking the "Print"/"OK" button, is it possible to intercept the print job settings and change them and/or cancel the print job? Reasons include:

Virtual printers that print to files

When using a virtual printer named "Print to PDF" or "Save as PDF", when it prompts for the file name and location, and when it runs in a UWP app, have the security rules already been tested successfully? Meaning, will it enforce the same security rules as Windows.Storage.Pickers.FileSavePicker even when running via a separate-process mode?

In our kiosk system, we must prevent kiosk users seeing the local file system. Therefore, would it be possible to make the following boolean settings in WebView2?

Examples of the virtual printers that exist in Windows are:

Alternative designs include:

Summary

AB#27694507

liminzhu commented 5 years ago

Thanks for the detailed request! We are actually making the change to allow ExecuteScript even when JS is disabled in IWebView2Settings since we trust the host app knows what it is doing. So print would still work. There are also some events, namely onbeforeprint and onafterprint, which help you detect the timing. Obviously a print method directly from WebView can still be on the table if necessary.

I'm not aware of any way to intercept a print job for the cases you mentioned, so have to think through this a bit more. We will also need to work with the UWP WebView2 folks to understand the security implications of virtual printers (cc @adambarlow).

verelpode commented 5 years ago

We are actually making the change to allow ExecuteScript even when JS is disabled in IWebView2Settings since we trust the host app knows what it is doing.

I think that is a good decision.

Obviously a print method directly from WebView can still be on the table if necessary.

I think a WebView2.OpenPrintDialog method may still be necessary or desirable because JavaScript window.print(); might only be supported or guaranteed with HTML webpages (correct?). I think there is no guarantee or API promise or standard that says that JS window.print(); will/should be supported at times when WebView2 is directly displaying non-HTML content such as PDF, SVG, PNG, etc.

Re SVG and PNG: Obviously JS window.print(); is supported when the SVG or PNG image is displayed inside a HTML webpage, but what about the situation where the image is displayed directly without any HTML at all? For example, a URI that is a direct link to an SVG image:

webView2Instance->Navigate(L"http://www.example.com/xxxxxxx/test-image.svg");

Likewise for PDF: The JS window.print(); is defined in the HTML standard, but there doesn't exist any standard that says that WebView2's built-in PDF viewer should support window.print(); or any other JS functions. Therefore I think a WebView2.OpenPrintDialog method would be a good solution. What do you think?

Re priorities: Although I'd appreciate having the OpenPrintDialog method, I think a few other issues are much higher priority, especially the need for a GetCookies method as previously discussed.

verelpode commented 4 years ago

Correct me if I'm wrong, but doesn't issue #7 need to be reopened (or otherwise addressed here in issue #42) because the current implementation of window.print(); won't work at all in Windows Phone 10? Actually it won't work at all in any UWP app running in any device (desktop PC, laptop, tablet, or smartphone) -- correct? It's completely broken across all UWP devices. It also won't work with WPF apps and WinForms apps.

Currently window.print(); displays Chromium's print dialog that is implemented in the old Win16 system that was updated and renamed to Win32, but Win32 and Win16 dialogs cannot be displayed in a smartphone running Windows Phone 10 (or any other UWP devices).

If I'm not mistaken, in order to fix this, the entire Win32 print dialog must be ripped out of WebView2, and deleted, and replaced with an event that notifies the WinUI side of WebView2 to display the standard WinUI/UWP printing GUI.

I hope my message isn't written in a confusing manner. To clarify, I'm talking about the following print dialog. Currently WebView2 displays this Chromium Win32 print dialog:

print

The above screenshot shows the current print dialog but that is Chromium's old/obsolete Win32 code that doesn't work (won't ever work) in Windows Phone 10 or any other UWP devices, as far as I know. It seems like the best plan would be to delete the current print dialog source code in WebView2 and instead use an event to notify the upcoming WinUI side of WebView2 to display the standard WinUI/UWP printing GUI. This could be incorporated into a larger plan to delete all of the old Win32 GUI code in WebView2 because all of that is obsolete and doesn't work in any UWP desktop computers, laptops, tablets, or smartphones when running WebView2 in modern UWP apps.

This issue isn't limited to UWP. It's also applicable to WebView2 running in WPF apps and in WinForms apps. Chromium's old Win32 print dialog (and all other Win32 GUI displayed by Chromium/WebView2) doesn't work with:

david-risney commented 4 years ago

We don't have support for UWP, WPF, or WinForms yet. For each of these when we do if we have an issue with print we can open issues for them then. Thanks

nikki9696 commented 3 years ago

Hi, "For each of these when we do if we have an issue with print we can open issues for them then"

Is there an open issue for this for the winforms version yet? I came here because we're swapping our webbrowser to this new control, and this javascript print opens the print dialog in the tiny area we use for display instead of as a modal dialog (system print dialog), as we're used to and prefer. Also, we use ShowPrintPreviewDialog and I see no way to get to that? Also, we use a web browser to load a file frequently, and you cannot add scripts to a file...(e.g. pdf)

Thanks!

asklar commented 3 years ago

the platform has requests around converting html or other web content into pdf, which would benefit from this feature being implemented. See https://github.com/microsoft/react-native-windows/issues/7723

ajtruckle commented 2 years ago

I have two fundamental issues with the Print Dialog concept in WebView2:

  1. If you try to print when your control is only half the dialog, the print dialog is squished into that control. With CHtmlView it had its own pop up window that could be maximised. I simply can’t use this control in my software as the Print dialog is squished and it gets worse as you resize the window containing the control down.

  2. In my current app that uses CHtmlView I have two browsers. One is for the editor. The other is hidden. When the user prints we prepare a special version of the document for loading into the hidden browser. We then bring up a print dialog for the hidden browser. This way our editor can show a editor version of the document. I can’t work out how to have a second hidden browser and do the same thing.

So these two reasons mean I can’t use the control. I raised these issues over 2 years ago and nothing has changed to my knowledge?

ajtruckle commented 2 years ago

this javascript print opens the print dialog in the tiny area we use for display instead of as a modal dialog (system print dialog), as we're used to and prefer.

Exactly my observation. A crippling limitation. It is as if the assumption is the control is full screen and if it were, why not just use a browser? Since the control is advertised for using in apps it needs to address this problem. I agree that the browser should display a system print window will option to maximise like it used to. Or make current one support this.

andyste1 commented 2 years ago

+1 for the issue of the daft print settings dialog that gets rendered within the WebView2 component, rather than appearing as a modal dialog. Our team has had a requirement this week for displaying HTML pages in one of our WPF desktop apps, so this issue with WebView2 is disappointing to say the least.

Unfortunately the WPF WebBrowser component is no better - still no Print() method (although bizarrely the Winforms version does). Printing via js is possible but WebBrowser doesn't have a print settings dialog. (I also looked at CefSharp, but wasn't happy with 600Mb+ of redistributable assemblies!).

BasketGolfer commented 2 years ago

+1 There is no way that I can see to programmatically set the available options and print to a physical printer (other than using say UI automation to script the dialog when it appears) - is there a workaround?

david-risney commented 2 years ago

We're currently working on a print API (no ETA yet). Until then the only two work arounds are using window.print() from JavaScript or using CoreWebView2.PrintToPdf to create a PDF file and then printing the PDF yourself.

monica-ch commented 1 year ago

Hi all, we've completed our design for the https://github.com/MicrosoftEdge/WebView2Feedback/pull/2604/files! Please review the pull request and add any feedback you have about this API. We appreciate your input and support!

monica-ch commented 1 year ago

Hi all, we have shipped Print APIs as experimental in the 1.0.1414 pre-release package: https://learn.microsoft.com/en-us/microsoft-edge/webview2/release-notes?tabs=dotnetcsharp#experimental-features. Please let us know if you have any feedback!

MikeCheel commented 1 year ago

Hi all, we have shipped Print APIs as experimental in the 1.0.1414 pre-release package: https://learn.microsoft.com/en-us/microsoft-edge/webview2/release-notes?tabs=dotnetcsharp#experimental-features. Please let us know if you have any feedback!

When I print a pdf file to Microsoft PDF using PrintAsync() I get a blank page and that's it. How does one print a pdf file?

monica-ch commented 1 year ago

@MikeCheel This is a known issue with PrintAsync which is printing a blank page for PDFs, we are tracking the bug here #3007

MikeCheel commented 1 year ago

Thank you for your quick reply

santey12 commented 1 year ago

Hi all, we have shipped Print APIs as experimental in the 1.0.1414 pre-release package: https://learn.microsoft.com/en-us/microsoft-edge/webview2/release-notes?tabs=dotnetcsharp#experimental-features. Please let us know if you have any feedback!

When I print a DOCX file to Microsoft PDF using PrintAsync() I get a blank page and that's it. How does one print a DOCX file?

monica-ch commented 1 year ago

@santey12 I assume browser/system print dialog isn't supported to print a DOCX file hence PrintAsync prints a blank page. One can use Print option provided in the docx file to print to any printer.

ajtruckle commented 1 year ago

Can you make a IsPrintDialogOpen method that returns a boolean indicating whether the/any print dialog/window is open/displayed/visible?

This is useful. We can then prevent our app from closing by looking to see if this api call returns true or not.

monica-ch commented 1 year ago

@ajtruckle You can also use JS beforePrint afterPrint to determine whether print is started/completed or cancelled and control closing the app.

ajtruckle commented 1 year ago

@monica-ch I like the idea, as you suggested something similar for another ticket https://github.com/MicrosoftEdge/WebView2Feedback/issues/3266.

But I struggle with it you see. I have two classes:

CWebBrowser has a function for registering all the event handlers. I tried adding:

void CWebBrowser::RegisterEventHandlers()
{
        // ...

    CHECK_FAILURE(m_pImpl->m_webView->add_WebMessageReceived(
        Microsoft::WRL::Callback<ICoreWebView2WebMessageReceivedEventHandler>(
            [this](
                ICoreWebView2* sender,
                ICoreWebView2WebMessageReceivedEventArgs* args)
            {
                wil::unique_cotaskmem_string messageRaw;
                CHECK_FAILURE(args->TryGetWebMessageAsString(&messageRaw));

                if (messageRaw.get() == L"PrintDone")
                {
                    ::MessageBeep(MB_OK);
                }
                return S_OK;
            })
        .Get(),
                nullptr));

    /// Register `afterprint` event
    m_pImpl->m_webView->ExecuteScript(
        LR"~(
             window.addEventListener('afterprint', (event) => {
                  window.chrome.webview.postMessage('PrintDone');
             });
        )~",
        Callback<ICoreWebView2ExecuteScriptCompletedHandler>(
            [](HRESULT error, PCWSTR result) -> HRESULT { return S_OK; })
        .Get());
}

It seems to do nothing. If I display the PrintUI and hit cancel, nothing happens.

ajtruckle commented 1 year ago

@monica-ch Researching tge internet about those two events implies they only fire as indicated. Print started. Print ended. So doesn’t account for cancel window.

I think a simply IsPrintUIVisible is much easier 😀

monica-ch commented 1 year ago

@ajtruckle Same code works from our sample app.

It gets invoked when Cancel is hit from Print dialog. See https://www.w3schools.com/jsref/tryit.asp?filename=tryjsref_onafterprint.

I believe Print ended here when print dialog is closed (with successful print or cancelled)

ajtruckle commented 1 year ago

@monica-ch Alright, so why is my attempt failing? 🤔