Skip to content

Commit

Permalink
fix: enable deviceName validation on Linux (#34901)
Browse files Browse the repository at this point in the history
  • Loading branch information
trop[bot] committed Jul 14, 2022
1 parent 8313ea4 commit 76975df
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 39 deletions.
58 changes: 29 additions & 29 deletions shell/browser/api/electron_api_web_contents.cc
Expand Up @@ -419,21 +419,34 @@ bool IsDeviceNameValid(const std::u16string& device_name) {
bool printer_exists = new_printer != nullptr;
PMRelease(new_printer);
return printer_exists;
#elif BUILDFLAG(IS_WIN)
printing::ScopedPrinterHandle printer;
return printer.OpenPrinterWithName(base::as_wcstr(device_name));
#else
return true;
scoped_refptr<printing::PrintBackend> print_backend =
printing::PrintBackend::CreateInstance(
g_browser_process->GetApplicationLocale());
return print_backend->IsValidPrinter(base::UTF16ToUTF8(device_name));
#endif
}

std::pair<std::string, std::u16string> GetDefaultPrinterAsync() {
// This function returns a validated device name.
// If the user passed one to webContents.print(), we check that it's valid and
// return it or fail if the network doesn't recognize it. If the user didn't
// pass a device name, we first try to return the system default printer. If one
// isn't set, then pull all the printers and use the first one or fail if none
// exist.
std::pair<std::string, std::u16string> GetDeviceNameToUse(
const std::u16string& device_name) {
#if BUILDFLAG(IS_WIN)
// Blocking is needed here because Windows printer drivers are oftentimes
// not thread-safe and have to be accessed on the UI thread.
base::ThreadRestrictions::ScopedAllowIO allow_io;
#endif

if (!device_name.empty()) {
if (!IsDeviceNameValid(device_name))
return std::make_pair("Invalid deviceName provided", std::u16string());
return std::make_pair(std::string(), device_name);
}

scoped_refptr<printing::PrintBackend> print_backend =
printing::PrintBackend::CreateInstance(
g_browser_process->GetApplicationLocale());
Expand All @@ -446,14 +459,16 @@ std::pair<std::string, std::u16string> GetDefaultPrinterAsync() {
if (code != printing::mojom::ResultCode::kSuccess)
LOG(ERROR) << "Failed to get default printer name";

// Check for existing printers and pick the first one should it exist.
if (printer_name.empty()) {
printing::PrinterList printers;
if (print_backend->EnumeratePrinters(&printers) !=
printing::mojom::ResultCode::kSuccess)
return std::make_pair("Failed to enumerate printers", std::u16string());
if (!printers.empty())
printer_name = printers.front().printer_name;
if (printers.empty())
return std::make_pair("No printers available on the network",
std::u16string());

printer_name = printers.front().printer_name;
}

return std::make_pair(std::string(), base::UTF8ToUTF16(printer_name));
Expand Down Expand Up @@ -2581,12 +2596,11 @@ bool WebContents::IsCurrentlyAudible() {
}

#if BUILDFLAG(ENABLE_PRINTING)
void WebContents::OnGetDefaultPrinter(
void WebContents::OnGetDeviceNameToUse(
base::Value::Dict print_settings,
printing::CompletionCallback print_callback,
std::u16string device_name,
bool silent,
// <error, default_printer>
// <error, device_name>
std::pair<std::string, std::u16string> info) {
// The content::WebContents might be already deleted at this point, and the
// PrintViewManagerElectron class does not do null check.
Expand All @@ -2603,16 +2617,7 @@ void WebContents::OnGetDefaultPrinter(
}

// If the user has passed a deviceName use it, otherwise use default printer.
std::u16string printer_name = device_name.empty() ? info.second : device_name;

// If there are no valid printers available on the network, we bail.
if (printer_name.empty() || !IsDeviceNameValid(printer_name)) {
if (print_callback)
std::move(print_callback).Run(false, "no valid printers available");
return;
}

print_settings.Set(printing::kSettingDeviceName, printer_name);
print_settings.Set(printing::kSettingDeviceName, info.second);

auto* print_view_manager =
PrintViewManagerElectron::FromWebContents(web_contents());
Expand Down Expand Up @@ -2702,11 +2707,6 @@ void WebContents::Print(gin::Arguments* args) {
// Printer device name as opened by the OS.
std::u16string device_name;
options.Get("deviceName", &device_name);
if (!device_name.empty() && !IsDeviceNameValid(device_name)) {
gin_helper::ErrorThrower(args->isolate())
.ThrowError("webContents.print(): Invalid deviceName provided.");
return;
}

int scale_factor = 100;
options.Get("scaleFactor", &scale_factor);
Expand Down Expand Up @@ -2796,10 +2796,10 @@ void WebContents::Print(gin::Arguments* args) {
}

print_task_runner_->PostTaskAndReplyWithResult(
FROM_HERE, base::BindOnce(&GetDefaultPrinterAsync),
base::BindOnce(&WebContents::OnGetDefaultPrinter,
FROM_HERE, base::BindOnce(&GetDeviceNameToUse, device_name),
base::BindOnce(&WebContents::OnGetDeviceNameToUse,
weak_factory_.GetWeakPtr(), std::move(settings),
std::move(callback), device_name, silent));
std::move(callback), silent));
}

v8::Local<v8::Promise> WebContents::PrintToPDF(base::DictionaryValue settings) {
Expand Down
11 changes: 5 additions & 6 deletions shell/browser/api/electron_api_web_contents.h
Expand Up @@ -217,12 +217,11 @@ class WebContents : public ExclusiveAccessContext,
void HandleNewRenderFrame(content::RenderFrameHost* render_frame_host);

#if BUILDFLAG(ENABLE_PRINTING)
void OnGetDefaultPrinter(base::Value::Dict print_settings,
printing::CompletionCallback print_callback,
std::u16string device_name,
bool silent,
// <error, default_printer_name>
std::pair<std::string, std::u16string> info);
void OnGetDeviceNameToUse(base::Value::Dict print_settings,
printing::CompletionCallback print_callback,
bool silent,
// <error, device_name>
std::pair<std::string, std::u16string> info);
void Print(gin::Arguments* args);
// Print current page as PDF.
v8::Local<v8::Promise> PrintToPDF(base::DictionaryValue settings);
Expand Down
10 changes: 6 additions & 4 deletions spec-main/api-web-contents-spec.ts
Expand Up @@ -179,10 +179,12 @@ describe('webContents module', () => {
}).to.throw('webContents.print(): Invalid optional callback provided.');
});

ifit(process.platform !== 'linux')('throws when an invalid deviceName is passed', () => {
expect(() => {
w.webContents.print({ deviceName: 'i-am-a-nonexistent-printer' }, () => {});
}).to.throw('webContents.print(): Invalid deviceName provided.');
it('fails when an invalid deviceName is passed', (done) => {
w.webContents.print({ deviceName: 'i-am-a-nonexistent-printer' }, (success, reason) => {
expect(success).to.equal(false);
expect(reason).to.match(/Invalid deviceName provided/);
done();
});
});

it('throws when an invalid pageSize is passed', () => {
Expand Down

0 comments on commit 76975df

Please sign in to comment.