Bug 1980225 - fix race condition with content analysis and printing to file a=RyanVM DONTBUILD

With content analysis active, if a DLP agent pops up a dialog but allows
the print to happen (for example, when responding REPORT_ONLY), this can
cause ::StartDocW() to fail for "printer"s that pop open a dialog for the
user to choose where to save the file. (i.e. Microsoft Print to PDF)

After trying some various hacks, the one that worked was calling
SetForegroundWindow() on a Firefox window before calling ::StartDocW().
It even works if the Firefox window that we call SetForegroundWindow()
isn't the one that the print is happening in. (which I verified by starting
a print and then quickly clicking on another window)

I put this behind a pref just in case this causes problems in the future,
but I doubt it will.

I also reverted the previous attempt to fix this by calling
::LockSetForegroundWindow(), which does not work.

Original Revision: https://phabricator.services.mozilla.com/D261572

Differential Revision: https://phabricator.services.mozilla.com/D262385
This commit is contained in:
Greg Stoll
2025-08-25 19:48:50 +00:00
committed by rvandermeulen@mozilla.com
parent ac82b5a387
commit 64d0b99892
2 changed files with 48 additions and 1 deletions

View File

@@ -7,7 +7,14 @@
#include "cairo-win32.h"
#include "mozilla/gfx/HelpersCairo.h"
#include "mozilla/StaticPrefs_browser.h"
#include "mozilla/WidgetUtils.h"
#include "nsCoord.h"
#include "nsIContentAnalysis.h"
#include "nsIWidget.h"
#include "nsIWindowMediator.h"
#include "nsPIDOMWindow.h"
#include "nsServiceManagerUtils.h"
#include "nsString.h"
namespace mozilla {
@@ -52,6 +59,8 @@ already_AddRefed<PrintTargetWindows> PrintTargetWindows::CreateOrNull(HDC aDC) {
return target.forget();
}
LazyLogModule gPrintingLog("printing");
nsresult PrintTargetWindows::BeginPrinting(const nsAString& aTitle,
const nsAString& aPrintToFileName,
int32_t aStartPage,
@@ -74,6 +83,36 @@ nsresult PrintTargetWindows::BeginPrinting(const nsAString& aTitle,
docinfo.lpszDatatype = nullptr;
docinfo.fwType = 0;
// If we just did content analysis on this print request, it may have popped
// up a dialog, and this can prevent StartDocW() from working properly if the
// printer wants to pop up a dialog window (to get a file name to save to, for
// example). Setting the foreground window to any browser window seems to work
// around this. See bug 1980225.
if (nsIContentAnalysis::MightBeActive() &&
StaticPrefs::browser_contentanalysis_print_set_foreground_window()) {
nsCOMPtr<nsIWindowMediator> winMediator =
do_GetService(NS_WINDOWMEDIATOR_CONTRACTID);
if (winMediator) {
nsCOMPtr<mozIDOMWindowProxy> domWindow;
nsresult rv =
winMediator->GetMostRecentBrowserWindow(getter_AddRefs(domWindow));
if (NS_SUCCEEDED(rv) && domWindow) {
nsPIDOMWindowOuter* win = nsPIDOMWindowOuter::From(domWindow);
if (win) {
nsCOMPtr<nsIWidget> widget =
widget::WidgetUtils::DOMWindowToWidget(win);
if (widget) {
HWND hwnd =
static_cast<HWND>(widget->GetNativeData(NS_NATIVE_WINDOW));
BOOL foregroundReturn = ::SetForegroundWindow(hwnd);
MOZ_LOG(gPrintingLog, mozilla::LogLevel::Debug,
("Called SetForegroundWindow(), which returned %d",
foregroundReturn));
}
}
}
}
}
// If the user selected Microsoft Print to PDF or XPS Document Printer, then
// the following StartDoc call will put up a dialog window to prompt the
// user to provide the name and location of the file to be saved. A zero or
@@ -84,7 +123,8 @@ nsresult PrintTargetWindows::BeginPrinting(const nsAString& aTitle,
// XXX We should perhaps introduce a new NS_ERROR_USER_CANCELLED errer.
int result = ::StartDocW(mDC, &docinfo);
if (result <= 0) {
if (::GetLastError() == ERROR_CANCELLED) {
DWORD lastError = ::GetLastError();
if (lastError == ERROR_CANCELLED) {
return NS_ERROR_ABORT;
}
return NS_ERROR_FAILURE;

View File

@@ -1232,6 +1232,13 @@
value: false
mirror: always
# On Windows, whether to bring the foreground window to the front when printing
# to avoid a DLP agent stealing focus and cancelling the print (bug 1980225)
- name: browser.contentanalysis.print_set_foreground_window
type: bool
value: true
mirror: always
# What content analysis should return if there is a problem communicating
# with the agent. (see DefaultResponse enum in ContentAnalysis.h)
# Make sure these stay in sync with the out-of-range check in Policies.sys.mjs.