Commit 107cccc1 authored by Nikita Podguzov's avatar Nikita Podguzov Committed by Commit Bot

Printing API: Add print job request dialog

This is shown to the users if a non-whitelisted extension calls
chrome.printing.submitJob().
If user clicks "Accept" button, the print job is sent to the printer.
If they click "Deny" button, the dialog is closed and nothing happens,
extension receives REJECTED status.

Bug: 996785
Change-Id: Idf9cbc5af4ae740252ac32d2a64f6928c61eb955
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2007277
Commit-Queue: Nikita Podguzov <nikitapodguzov@chromium.org>
Reviewed-by: default avatarPeter Boström <pbos@chromium.org>
Reviewed-by: default avatarDevlin <rdevlin.cronin@chromium.org>
Reviewed-by: default avatarSean Kau <skau@chromium.org>
Cr-Commit-Position: refs/heads/master@{#738225}
parent 9071d74e
...@@ -4654,6 +4654,20 @@ Keep your key file in a safe place. You will need it to create new versions of y ...@@ -4654,6 +4654,20 @@ Keep your key file in a safe place. You will need it to create new versions of y
''' It also controls what page is shown when you click the Home button or search from the Omnibox. ''' ''' It also controls what page is shown when you click the Home button or search from the Omnibox. '''
</message> </message>
<!-- Strings for the print job confirmation dialog triggered by chrome.printing -->
<message name="IDS_EXTENSIONS_PRINTING_API_PRINT_REQUEST_BUBBLE_TITLE" desc="Title of a bubble with print job confirmation dialog triggered by chrome.printing API">
Print request
</message>
<message name="IDS_EXTENSIONS_PRINTING_API_PRINT_REQUEST_BUBBLE_HEADING" desc="Heading of a bubble with print job confirmation dialog triggered by chrome.printing API">
"<ph name="EXTENSION_NAME">$1<ex>Managed Printing</ex></ph>" wants to print <ph name="FILE_NAME">$2<ex>sample.pdf</ex></ph> with <ph name="PRINTER_NAME">$3<ex>Lexmark CS417dn</ex></ph>.
</message>
<message name="IDS_EXTENSIONS_PRINTING_API_PRINT_REQUEST_ALLOW" desc="Text for the allow button on the print job confirmation dialog">
Allow
</message>
<message name="IDS_EXTENSIONS_PRINTING_API_PRINT_REQUEST_DENY" desc="Text for the deny button on the print job confirmation dialog">
Deny
</message>
<message name="IDS_EXTENSIONS_NTP_CONTROLLED_FIRST_LINE" desc="Text displayed as the first line in the NTP bubble when an extension has changed the new tab page."> <message name="IDS_EXTENSIONS_NTP_CONTROLLED_FIRST_LINE" desc="Text displayed as the first line in the NTP bubble when an extension has changed the new tab page.">
An extension has changed what page is shown when you open a new tab. An extension has changed what page is shown when you open a new tab.
</message> </message>
......
...@@ -8,21 +8,30 @@ ...@@ -8,21 +8,30 @@
#include <utility> #include <utility>
#include "base/bind.h" #include "base/bind.h"
#include "base/memory/read_only_shared_memory_region.h"
#include "base/strings/utf_string_conversions.h" #include "base/strings/utf_string_conversions.h"
#include "base/threading/sequenced_task_runner_handle.h" #include "base/threading/sequenced_task_runner_handle.h"
#include "base/values.h"
#include "chrome/browser/chromeos/extensions/printing/print_job_controller.h" #include "chrome/browser/chromeos/extensions/printing/print_job_controller.h"
#include "chrome/browser/chromeos/extensions/printing/printer_capabilities_provider.h" #include "chrome/browser/chromeos/extensions/printing/printer_capabilities_provider.h"
#include "chrome/browser/chromeos/extensions/printing/printing_api_utils.h" #include "chrome/browser/chromeos/extensions/printing/printing_api_utils.h"
#include "chrome/browser/chromeos/printing/cups_printers_manager.h" #include "chrome/browser/chromeos/printing/cups_printers_manager.h"
#include "chrome/browser/printing/printing_service.h" #include "chrome/browser/printing/printing_service.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser_dialogs.h"
#include "chrome/browser/ui/native_window_tracker.h"
#include "chrome/common/pref_names.h"
#include "chrome/services/printing/public/mojom/pdf_flattener.mojom.h" #include "chrome/services/printing/public/mojom/pdf_flattener.mojom.h"
#include "chromeos/printing/printer_configuration.h" #include "chromeos/printing/printer_configuration.h"
#include "components/prefs/pref_service.h"
#include "content/public/browser/browser_context.h" #include "content/public/browser/browser_context.h"
#include "extensions/browser/blob_reader.h" #include "extensions/browser/blob_reader.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/browser/image_loader.h"
#include "extensions/common/extension.h"
#include "printing/backend/print_backend.h" #include "printing/backend/print_backend.h"
#include "printing/metafile_skia.h" #include "printing/metafile_skia.h"
#include "printing/print_settings.h" #include "printing/print_settings.h"
#include "ui/gfx/image/image.h"
namespace extensions { namespace extensions {
...@@ -41,27 +50,47 @@ constexpr char kUnsupportedTicket[] = ...@@ -41,27 +50,47 @@ constexpr char kUnsupportedTicket[] =
"Ticket is unsupported on the given printer"; "Ticket is unsupported on the given printer";
constexpr char kInvalidData[] = "Invalid document"; constexpr char kInvalidData[] = "Invalid document";
constexpr int kIconSize = 64;
// Returns true if |extension_id| is in the whitelist.
bool IsUserConfirmationRequired(content::BrowserContext* browser_context,
const std::string& extension_id) {
const base::ListValue* list =
Profile::FromBrowserContext(browser_context)
->GetPrefs()
->GetList(prefs::kPrintingAPIExtensionsWhitelist);
base::Value value(extension_id);
return list->Find(value) == list->end();
}
} // namespace } // namespace
PrintJobSubmitter::PrintJobSubmitter( PrintJobSubmitter::PrintJobSubmitter(
gfx::NativeWindow native_window,
content::BrowserContext* browser_context, content::BrowserContext* browser_context,
chromeos::CupsPrintersManager* printers_manager, chromeos::CupsPrintersManager* printers_manager,
PrinterCapabilitiesProvider* printer_capabilities_provider, PrinterCapabilitiesProvider* printer_capabilities_provider,
PrintJobController* print_job_controller, PrintJobController* print_job_controller,
mojo::Remote<printing::mojom::PdfFlattener>* pdf_flattener, mojo::Remote<printing::mojom::PdfFlattener>* pdf_flattener,
const std::string& extension_id, scoped_refptr<const extensions::Extension> extension,
api::printing::SubmitJobRequest request) api::printing::SubmitJobRequest request)
: browser_context_(browser_context), : native_window_(native_window),
browser_context_(browser_context),
printers_manager_(printers_manager), printers_manager_(printers_manager),
printer_capabilities_provider_(printer_capabilities_provider), printer_capabilities_provider_(printer_capabilities_provider),
print_job_controller_(print_job_controller), print_job_controller_(print_job_controller),
pdf_flattener_(pdf_flattener), pdf_flattener_(pdf_flattener),
extension_id_(extension_id), extension_(extension),
request_(std::move(request)) {} request_(std::move(request)) {
DCHECK(extension);
if (native_window)
native_window_tracker_ = NativeWindowTracker::Create(native_window);
}
PrintJobSubmitter::~PrintJobSubmitter() = default; PrintJobSubmitter::~PrintJobSubmitter() = default;
void PrintJobSubmitter::Start(SubmitJobCallback callback) { void PrintJobSubmitter::Start(SubmitJobCallback callback) {
DCHECK(!callback_);
callback_ = std::move(callback); callback_ = std::move(callback);
if (!CheckContentType()) { if (!CheckContentType()) {
FireErrorCallback(kUnsupportedContentType); FireErrorCallback(kUnsupportedContentType);
...@@ -95,6 +124,7 @@ void PrintJobSubmitter::CheckPrinter() { ...@@ -95,6 +124,7 @@ void PrintJobSubmitter::CheckPrinter() {
FireErrorCallback(kInvalidPrinterId); FireErrorCallback(kInvalidPrinterId);
return; return;
} }
printer_name_ = base::UTF8ToUTF16(printer->display_name());
printer_capabilities_provider_->GetPrinterCapabilities( printer_capabilities_provider_->GetPrinterCapabilities(
request_.job.printer_id, request_.job.printer_id,
base::BindOnce(&PrintJobSubmitter::CheckCapabilitiesCompatibility, base::BindOnce(&PrintJobSubmitter::CheckCapabilitiesCompatibility,
...@@ -150,30 +180,83 @@ void PrintJobSubmitter::OnDocumentDataRead(std::unique_ptr<std::string> data, ...@@ -150,30 +180,83 @@ void PrintJobSubmitter::OnDocumentDataRead(std::unique_ptr<std::string> data,
weak_ptr_factory_.GetWeakPtr())); weak_ptr_factory_.GetWeakPtr()));
} }
void PrintJobSubmitter::OnPdfFlattenerDisconnected() {
FireErrorCallback(kInvalidData);
}
void PrintJobSubmitter::OnPdfFlattened( void PrintJobSubmitter::OnPdfFlattened(
base::ReadOnlySharedMemoryRegion flattened_pdf) { base::ReadOnlySharedMemoryRegion flattened_pdf) {
const auto mapping = flattened_pdf.Map(); auto mapping = flattened_pdf.Map();
if (!mapping.IsValid()) { if (!mapping.IsValid()) {
FireErrorCallback(kInvalidData); FireErrorCallback(kInvalidData);
return; return;
} }
flattened_pdf_mapping_ = std::move(mapping);
// Directly submit the job if the extension is whitelisted.
if (!IsUserConfirmationRequired(browser_context_, extension_->id())) {
StartPrintJob();
return;
}
extensions::ImageLoader::Get(browser_context_)
->LoadImageAtEveryScaleFactorAsync(
extension_.get(), gfx::Size(kIconSize, kIconSize),
base::BindOnce(&PrintJobSubmitter::ShowPrintJobConfirmationDialog,
weak_ptr_factory_.GetWeakPtr()));
}
void PrintJobSubmitter::ShowPrintJobConfirmationDialog(
const gfx::Image& extension_icon) {
// If the browser window was closed during API request handling, change
// |native_window_| appropriately.
if (native_window_tracker_ && native_window_tracker_->WasNativeWindowClosed())
native_window_ = gfx::kNullNativeWindow;
chrome::ShowPrintJobConfirmationDialog(
native_window_, extension_->id(), base::UTF8ToUTF16(extension_->name()),
extension_icon.AsImageSkia(), settings_->title(), printer_name_,
base::BindOnce(&PrintJobSubmitter::OnPrintJobConfirmationDialogClosed,
weak_ptr_factory_.GetWeakPtr()));
}
void PrintJobSubmitter::OnPrintJobConfirmationDialogClosed(bool accepted) {
// If the user hasn't accepted a print job or the extension is
// unloaded/disabled by the time the dialog is closed, reject the request.
if (!accepted || !ExtensionRegistry::Get(browser_context_)
->enabled_extensions()
.Contains(extension_->id())) {
OnPrintJobRejected();
return;
}
StartPrintJob();
}
void PrintJobSubmitter::StartPrintJob() {
auto metafile = std::make_unique<printing::MetafileSkia>(); auto metafile = std::make_unique<printing::MetafileSkia>();
CHECK(metafile->InitFromData(mapping.memory(), mapping.size())); CHECK(metafile->InitFromData(flattened_pdf_mapping_.memory(),
flattened_pdf_mapping_.size()));
DCHECK(extension_);
DCHECK(settings_);
print_job_controller_->StartPrintJob( print_job_controller_->StartPrintJob(
extension_id_, std::move(metafile), std::move(settings_), extension_->id(), std::move(metafile), std::move(settings_),
base::BindOnce(&PrintJobSubmitter::OnPrintJobSubmitted, base::BindOnce(&PrintJobSubmitter::OnPrintJobSubmitted,
weak_ptr_factory_.GetWeakPtr())); weak_ptr_factory_.GetWeakPtr()));
} }
void PrintJobSubmitter::OnPdfFlattenerDisconnected() { void PrintJobSubmitter::OnPrintJobRejected() {
FireErrorCallback(kInvalidData); DCHECK(callback_);
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback_),
api::printing::SUBMIT_JOB_STATUS_USER_REJECTED,
nullptr, base::nullopt));
} }
void PrintJobSubmitter::OnPrintJobSubmitted( void PrintJobSubmitter::OnPrintJobSubmitted(
std::unique_ptr<std::string> job_id) { std::unique_ptr<std::string> job_id) {
DCHECK(job_id); DCHECK(job_id);
DCHECK(callback_);
base::SequencedTaskRunnerHandle::Get()->PostTask( base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE, FROM_HERE,
base::BindOnce(std::move(callback_), api::printing::SUBMIT_JOB_STATUS_OK, base::BindOnce(std::move(callback_), api::printing::SUBMIT_JOB_STATUS_OK,
...@@ -181,6 +264,7 @@ void PrintJobSubmitter::OnPrintJobSubmitted( ...@@ -181,6 +264,7 @@ void PrintJobSubmitter::OnPrintJobSubmitted(
} }
void PrintJobSubmitter::FireErrorCallback(const std::string& error) { void PrintJobSubmitter::FireErrorCallback(const std::string& error) {
DCHECK(callback_);
base::SequencedTaskRunnerHandle::Get()->PostTask( base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE, FROM_HERE,
base::BindOnce(std::move(callback_), base::nullopt, nullptr, error)); base::BindOnce(std::move(callback_), base::nullopt, nullptr, error));
......
...@@ -9,10 +9,12 @@ ...@@ -9,10 +9,12 @@
#include <string> #include <string>
#include "base/callback.h" #include "base/callback.h"
#include "base/memory/read_only_shared_memory_region.h"
#include "base/memory/weak_ptr.h" #include "base/memory/weak_ptr.h"
#include "base/optional.h" #include "base/optional.h"
#include "chrome/common/extensions/api/printing.h" #include "chrome/common/extensions/api/printing.h"
#include "mojo/public/cpp/bindings/remote.h" #include "mojo/public/cpp/bindings/remote.h"
#include "ui/gfx/native_widget_types.h"
namespace base { namespace base {
class ReadOnlySharedMemoryRegion; class ReadOnlySharedMemoryRegion;
...@@ -26,6 +28,10 @@ namespace content { ...@@ -26,6 +28,10 @@ namespace content {
class BrowserContext; class BrowserContext;
} // namespace content } // namespace content
namespace gfx {
class Image;
}
namespace printing { namespace printing {
namespace mojom { namespace mojom {
class PdfFlattener; class PdfFlattener;
...@@ -34,8 +40,11 @@ struct PrinterSemanticCapsAndDefaults; ...@@ -34,8 +40,11 @@ struct PrinterSemanticCapsAndDefaults;
class PrintSettings; class PrintSettings;
} // namespace printing } // namespace printing
class NativeWindowTracker;
namespace extensions { namespace extensions {
class Extension;
class PrinterCapabilitiesProvider; class PrinterCapabilitiesProvider;
class PrintJobController; class PrintJobController;
...@@ -52,15 +61,17 @@ class PrintJobSubmitter { ...@@ -52,15 +61,17 @@ class PrintJobSubmitter {
std::unique_ptr<std::string> job_id, std::unique_ptr<std::string> job_id,
base::Optional<std::string> error)>; base::Optional<std::string> error)>;
PrintJobSubmitter(content::BrowserContext* browser_context, PrintJobSubmitter(gfx::NativeWindow native_window,
content::BrowserContext* browser_context,
chromeos::CupsPrintersManager* printers_manager, chromeos::CupsPrintersManager* printers_manager,
PrinterCapabilitiesProvider* printer_capabilities_provider, PrinterCapabilitiesProvider* printer_capabilities_provider,
PrintJobController* print_job_controller, PrintJobController* print_job_controller,
mojo::Remote<printing::mojom::PdfFlattener>* pdf_flattener, mojo::Remote<printing::mojom::PdfFlattener>* pdf_flattener,
const std::string& extension_id, scoped_refptr<const extensions::Extension> extension,
api::printing::SubmitJobRequest request); api::printing::SubmitJobRequest request);
~PrintJobSubmitter(); ~PrintJobSubmitter();
// Only one call to Start() should happen at a time.
// |callback| is called asynchronously with the success or failure of the // |callback| is called asynchronously with the success or failure of the
// process. // process.
void Start(SubmitJobCallback callback); void Start(SubmitJobCallback callback);
...@@ -80,24 +91,42 @@ class PrintJobSubmitter { ...@@ -80,24 +91,42 @@ class PrintJobSubmitter {
void OnDocumentDataRead(std::unique_ptr<std::string> data, void OnDocumentDataRead(std::unique_ptr<std::string> data,
int64_t total_blob_length); int64_t total_blob_length);
void OnPdfFlattenerDisconnected();
void OnPdfFlattened(base::ReadOnlySharedMemoryRegion flattened_pdf); void OnPdfFlattened(base::ReadOnlySharedMemoryRegion flattened_pdf);
void OnPdfFlattenerDisconnected(); void ShowPrintJobConfirmationDialog(const gfx::Image& extension_icon);
void OnPrintJobConfirmationDialogClosed(bool accepted);
void StartPrintJob();
void OnPrintJobRejected();
void OnPrintJobSubmitted(std::unique_ptr<std::string> job_id); void OnPrintJobSubmitted(std::unique_ptr<std::string> job_id);
void FireErrorCallback(const std::string& error); void FireErrorCallback(const std::string& error);
// These objects are owned by PrintingAPIHandler. gfx::NativeWindow native_window_;
content::BrowserContext* const browser_context_; content::BrowserContext* const browser_context_;
// Tracks whether |native_window_| got destroyed.
std::unique_ptr<NativeWindowTracker> native_window_tracker_;
// These objects are owned by PrintingAPIHandler.
chromeos::CupsPrintersManager* const printers_manager_; chromeos::CupsPrintersManager* const printers_manager_;
PrinterCapabilitiesProvider* const printer_capabilities_provider_; PrinterCapabilitiesProvider* const printer_capabilities_provider_;
PrintJobController* const print_job_controller_; PrintJobController* const print_job_controller_;
mojo::Remote<printing::mojom::PdfFlattener>* const pdf_flattener_; mojo::Remote<printing::mojom::PdfFlattener>* const pdf_flattener_;
const std::string extension_id_; // TODO(crbug.com/996785): Consider tracking extension being unloaded instead
// of storing scoped_refptr.
scoped_refptr<const extensions::Extension> extension_;
api::printing::SubmitJobRequest request_; api::printing::SubmitJobRequest request_;
std::unique_ptr<printing::PrintSettings> settings_; std::unique_ptr<printing::PrintSettings> settings_;
base::string16 printer_name_;
base::ReadOnlySharedMemoryMapping flattened_pdf_mapping_;
// This is cleared after the request is handled (successfully or not).
SubmitJobCallback callback_; SubmitJobCallback callback_;
base::WeakPtrFactory<PrintJobSubmitter> weak_ptr_factory_{this}; base::WeakPtrFactory<PrintJobSubmitter> weak_ptr_factory_{this};
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include "base/bind.h" #include "base/bind.h"
#include "base/values.h" #include "base/values.h"
#include "chrome/browser/chromeos/extensions/printing/printing_api_handler.h" #include "chrome/browser/chromeos/extensions/printing/printing_api_handler.h"
#include "chrome/browser/extensions/chrome_extension_function_details.h"
#include "extensions/browser/quota_service.h" #include "extensions/browser/quota_service.h"
namespace extensions { namespace extensions {
...@@ -30,7 +31,8 @@ ExtensionFunction::ResponseAction PrintingSubmitJobFunction::Run() { ...@@ -30,7 +31,8 @@ ExtensionFunction::ResponseAction PrintingSubmitJobFunction::Run() {
api::printing::SubmitJob::Params::Create(*args_)); api::printing::SubmitJob::Params::Create(*args_));
EXTENSION_FUNCTION_VALIDATE(params.get()); EXTENSION_FUNCTION_VALIDATE(params.get());
PrintingAPIHandler::Get(browser_context()) PrintingAPIHandler::Get(browser_context())
->SubmitJob(extension_id(), std::move(params), ->SubmitJob(ChromeExtensionFunctionDetails(this).GetNativeWindowForUI(),
extension_, std::move(params),
base::BindOnce( base::BindOnce(
&PrintingSubmitJobFunction::OnPrintJobSubmitted, this)); &PrintingSubmitJobFunction::OnPrintJobSubmitted, this));
......
...@@ -114,13 +114,14 @@ void PrintingAPIHandler::RegisterProfilePrefs(PrefRegistrySimple* registry) { ...@@ -114,13 +114,14 @@ void PrintingAPIHandler::RegisterProfilePrefs(PrefRegistrySimple* registry) {
} }
void PrintingAPIHandler::SubmitJob( void PrintingAPIHandler::SubmitJob(
const std::string& extension_id, gfx::NativeWindow native_window,
scoped_refptr<const extensions::Extension> extension,
std::unique_ptr<api::printing::SubmitJob::Params> params, std::unique_ptr<api::printing::SubmitJob::Params> params,
PrintJobSubmitter::SubmitJobCallback callback) { PrintJobSubmitter::SubmitJobCallback callback) {
auto print_job_submitter = std::make_unique<PrintJobSubmitter>( auto print_job_submitter = std::make_unique<PrintJobSubmitter>(
browser_context_, printers_manager_, &printer_capabilities_provider_, native_window, browser_context_, printers_manager_,
&print_job_controller_, &pdf_flattener_, extension_id, &printer_capabilities_provider_, &print_job_controller_, &pdf_flattener_,
std::move(params->request)); std::move(extension), std::move(params->request));
PrintJobSubmitter* print_job_submitter_ptr = print_job_submitter.get(); PrintJobSubmitter* print_job_submitter_ptr = print_job_submitter.get();
print_job_submitter_ptr->Start(base::BindOnce( print_job_submitter_ptr->Start(base::BindOnce(
&PrintingAPIHandler::OnPrintJobSubmitted, weak_ptr_factory_.GetWeakPtr(), &PrintingAPIHandler::OnPrintJobSubmitted, weak_ptr_factory_.GetWeakPtr(),
......
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
#include "extensions/browser/browser_context_keyed_api_factory.h" #include "extensions/browser/browser_context_keyed_api_factory.h"
#include "extensions/browser/event_router_factory.h" #include "extensions/browser/event_router_factory.h"
#include "mojo/public/cpp/bindings/remote.h" #include "mojo/public/cpp/bindings/remote.h"
#include "ui/gfx/native_widget_types.h"
class PrefRegistrySimple; class PrefRegistrySimple;
...@@ -83,7 +84,13 @@ class PrintingAPIHandler : public BrowserContextKeyedAPI, ...@@ -83,7 +84,13 @@ class PrintingAPIHandler : public BrowserContextKeyedAPI,
// Register the printing API preference with the |registry|. // Register the printing API preference with the |registry|.
static void RegisterProfilePrefs(PrefRegistrySimple* registry); static void RegisterProfilePrefs(PrefRegistrySimple* registry);
void SubmitJob(const std::string& extension_id, // Submits the job to printing pipeline.
// If |extension| is not present among PrintingAPIExtensionsWhitelist
// extensions, special print job request dialog is shown to the user to ask
// for their confirmation.
// |native_window| is needed to show this dialog.
void SubmitJob(gfx::NativeWindow native_window,
scoped_refptr<const extensions::Extension> extension,
std::unique_ptr<api::printing::SubmitJob::Params> params, std::unique_ptr<api::printing::SubmitJob::Params> params,
PrintJobSubmitter::SubmitJobCallback callback); PrintJobSubmitter::SubmitJobCallback callback);
......
...@@ -100,5 +100,10 @@ gfx::NativeWindow ChromeExtensionFunctionDetails::GetNativeWindowForUI() { ...@@ -100,5 +100,10 @@ gfx::NativeWindow ChromeExtensionFunctionDetails::GetNativeWindowForUI() {
// As a last resort, find a browser. // As a last resort, find a browser.
Browser* browser = chrome::FindBrowserWithProfile(GetProfile()); Browser* browser = chrome::FindBrowserWithProfile(GetProfile());
// If there are no browser windows open, no window is available.
// This could happen e.g. if extension launches a long process or simple
// sleep() in the background script, during which browser is closed.
if (!browser)
return nullptr;
return browser->window()->GetNativeWindow(); return browser->window()->GetNativeWindow();
} }
...@@ -1661,6 +1661,8 @@ jumbo_static_library("ui") { ...@@ -1661,6 +1661,8 @@ jumbo_static_library("ui") {
"views/apps/chrome_native_app_window_views_aura_ash.cc", "views/apps/chrome_native_app_window_views_aura_ash.cc",
"views/apps/chrome_native_app_window_views_aura_ash.h", "views/apps/chrome_native_app_window_views_aura_ash.h",
"views/chrome_views_delegate_chromeos.cc", "views/chrome_views_delegate_chromeos.cc",
"views/extensions/print_job_confirmation_dialog_view.cc",
"views/extensions/print_job_confirmation_dialog_view.h",
"views/extensions/request_file_system_dialog_view.cc", "views/extensions/request_file_system_dialog_view.cc",
"views/extensions/request_file_system_dialog_view.h", "views/extensions/request_file_system_dialog_view.h",
"views/frame/browser_frame_ash.cc", "views/frame/browser_frame_ash.cc",
......
...@@ -126,6 +126,23 @@ void ShowPWAInstallBubble(content::WebContents* web_contents, ...@@ -126,6 +126,23 @@ void ShowPWAInstallBubble(content::WebContents* web_contents,
// user interaction. // user interaction.
void SetAutoAcceptPWAInstallConfirmationForTesting(bool auto_accept); void SetAutoAcceptPWAInstallConfirmationForTesting(bool auto_accept);
#if defined(OS_CHROMEOS)
// Shows the print job confirmation dialog bubble anchored to the toolbar icon
// for the extension.
// If there's no toolbar icon, shows a modal dialog using
// CreateBrowserModalDialogViews(). Note that this dialog is shown up even if we
// have no |parent| window.
void ShowPrintJobConfirmationDialog(gfx::NativeWindow parent,
const std::string& extension_id,
const base::string16& extension_name,
const gfx::ImageSkia& extension_icon,
const base::string16& print_job_title,
const base::string16& printer_name,
base::OnceCallback<void(bool)> callback);
#endif // OS_CHROMEOS
#if defined(OS_MACOSX) #if defined(OS_MACOSX)
// Bridging methods that show/hide the toolkit-views based Task Manager on Mac. // Bridging methods that show/hide the toolkit-views based Task Manager on Mac.
...@@ -251,6 +268,7 @@ enum class DialogIdentifier { ...@@ -251,6 +268,7 @@ enum class DialogIdentifier {
QR_CODE_GENERATOR = 99, QR_CODE_GENERATOR = 99,
CROSTINI_FORCE_CLOSE = 100, CROSTINI_FORCE_CLOSE = 100,
APP_UNINSTALL = 101, APP_UNINSTALL = 101,
PRINT_JOB_CONFIRMATION = 102,
// Add values above this line with a corresponding label in // Add values above this line with a corresponding label in
// tools/metrics/histograms/enums.xml // tools/metrics/histograms/enums.xml
MAX_VALUE MAX_VALUE
......
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ui/views/extensions/print_job_confirmation_dialog_view.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/ui/ui_features.h"
#include "chrome/browser/ui/views/chrome_layout_provider.h"
#include "chrome/browser/ui/views/chrome_typography.h"
#include "chrome/browser/ui/views/extensions/extensions_toolbar_container.h"
#include "chrome/browser/ui/views/frame/browser_view.h"
#include "chrome/browser/ui/views/frame/toolbar_button_provider.h"
#include "chrome/browser/ui/views/toolbar/browser_actions_container.h"
#include "chrome/browser/ui/views/toolbar/toolbar_action_view.h"
#include "chrome/grit/generated_resources.h"
#include "components/constrained_window/constrained_window_views.h"
#include "components/strings/grit/components_strings.h"
#include "content/public/browser/browser_context.h"
#include "extensions/common/constants.h"
#include "extensions/common/extension.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/display/screen.h"
#include "ui/gfx/geometry/insets.h"
#include "ui/gfx/image/image_skia_operations.h"
#include "ui/views/controls/label.h"
#include "ui/views/layout/box_layout.h"
#include "ui/views/widget/widget.h"
// static
void PrintJobConfirmationDialogView::Show(
gfx::NativeWindow parent,
const std::string& extension_id,
const base::string16& extension_name,
const gfx::ImageSkia& extension_icon,
const base::string16& print_job_title,
const base::string16& printer_name,
base::OnceCallback<void(bool)> callback) {
// TODO (crbug.com/996785): Extract common code with
// ExtensionUninstallDialogViews::Show() to separate methods: first method to
// get an anchor view and the second one to show a BubbleDialogDelegateView.
// We may want to show dialog even if there is no appropriate browser view,
// i.e. |parent| is null or kNullNativeWindow. In that case we use
// constrained_window::CreateBrowserModalDialogViews() (see below).
BrowserView* const browser_view =
parent ? BrowserView::GetBrowserViewForNativeWindow(parent) : nullptr;
ToolbarActionView* anchor_view = nullptr;
ExtensionsToolbarContainer* const container =
browser_view ? browser_view->toolbar_button_provider()
->GetExtensionsToolbarContainer()
: nullptr;
if (container) {
anchor_view = container->GetViewForId(extension_id);
} else if (browser_view &&
!base::FeatureList::IsEnabled(features::kExtensionsToolbarMenu)) {
BrowserActionsContainer* const browser_actions_container =
browser_view->toolbar_button_provider()->GetBrowserActionsContainer();
ToolbarActionView* const reference_view =
browser_actions_container
? browser_actions_container->GetViewForId(extension_id)
: nullptr;
if (reference_view && reference_view->GetVisible())
anchor_view = reference_view;
}
auto* print_job_confirmation_dialog_view = new PrintJobConfirmationDialogView(
anchor_view, extension_name, extension_icon, print_job_title,
printer_name, std::move(callback));
if (anchor_view) {
views::Widget* const widget = views::BubbleDialogDelegateView::CreateBubble(
print_job_confirmation_dialog_view);
if (container) {
container->ShowWidgetForExtension(widget, extension_id);
} else {
DCHECK(!base::FeatureList::IsEnabled(features::kExtensionsToolbarMenu));
widget->Show();
}
} else {
constrained_window::CreateBrowserModalDialogViews(
print_job_confirmation_dialog_view, parent)
->Show();
}
}
PrintJobConfirmationDialogView::PrintJobConfirmationDialogView(
ToolbarActionView* anchor_view,
const base::string16& extension_name,
const gfx::ImageSkia& extension_icon,
const base::string16& print_job_title,
const base::string16& printer_name,
base::OnceCallback<void(bool)> callback)
: BubbleDialogDelegateView(anchor_view,
anchor_view ? views::BubbleBorder::TOP_RIGHT
: views::BubbleBorder::NONE),
extension_name_(extension_name),
extension_icon_(gfx::ImageSkiaOperations::CreateResizedImage(
extension_icon,
skia::ImageOperations::ResizeMethod::RESIZE_GOOD,
gfx::Size(extension_misc::EXTENSION_ICON_SMALL,
extension_misc::EXTENSION_ICON_SMALL))),
callback_(std::move(callback)),
dialog_is_bubble_(anchor_view != nullptr) {
DialogDelegate::set_button_label(
ui::DIALOG_BUTTON_OK,
l10n_util::GetStringUTF16(
IDS_EXTENSIONS_PRINTING_API_PRINT_REQUEST_ALLOW));
DialogDelegate::set_button_label(
ui::DIALOG_BUTTON_CANCEL,
l10n_util::GetStringUTF16(
IDS_EXTENSIONS_PRINTING_API_PRINT_REQUEST_DENY));
ChromeLayoutProvider* provider = ChromeLayoutProvider::Get();
SetLayoutManager(std::make_unique<views::BoxLayout>(
views::BoxLayout::Orientation::kVertical, gfx::Insets(),
provider->GetDistanceMetric(views::DISTANCE_RELATED_CONTROL_VERTICAL)));
// Add margins for the icon plus the icon-title padding so that the dialog
// contents align with the title text.
set_margins(
margins() +
gfx::Insets(0, margins().left() + extension_misc::EXTENSION_ICON_SMALL, 0,
0));
auto heading = std::make_unique<views::Label>(
l10n_util::GetStringFUTF16(
IDS_EXTENSIONS_PRINTING_API_PRINT_REQUEST_BUBBLE_HEADING,
extension_name, print_job_title, printer_name),
CONTEXT_BODY_TEXT_LARGE, views::style::STYLE_SECONDARY);
heading->SetMultiLine(true);
heading->SetHorizontalAlignment(gfx::ALIGN_LEFT);
heading->SetAllowCharacterBreak(true);
AddChildView(std::move(heading));
chrome::RecordDialogCreation(
chrome::DialogIdentifier::PRINT_JOB_CONFIRMATION);
}
PrintJobConfirmationDialogView::~PrintJobConfirmationDialogView() = default;
bool PrintJobConfirmationDialogView::Accept() {
OnDialogClosed(true);
return true;
}
bool PrintJobConfirmationDialogView::Cancel() {
OnDialogClosed(false);
return true;
}
gfx::Size PrintJobConfirmationDialogView::CalculatePreferredSize() const {
const int width =
ChromeLayoutProvider::Get()->GetDistanceMetric(
dialog_is_bubble_ ? DISTANCE_BUBBLE_PREFERRED_WIDTH
: DISTANCE_MODAL_DIALOG_PREFERRED_WIDTH) -
margins().width();
return gfx::Size(width, GetHeightForWidth(width));
}
ui::ModalType PrintJobConfirmationDialogView::GetModalType() const {
return ui::MODAL_TYPE_WINDOW;
}
base::string16 PrintJobConfirmationDialogView::GetWindowTitle() const {
return l10n_util::GetStringFUTF16(
IDS_EXTENSIONS_PRINTING_API_PRINT_REQUEST_BUBBLE_TITLE, extension_name_);
}
gfx::ImageSkia PrintJobConfirmationDialogView::GetWindowIcon() {
return extension_icon_;
}
bool PrintJobConfirmationDialogView::ShouldShowWindowIcon() const {
return true;
}
bool PrintJobConfirmationDialogView::ShouldShowCloseButton() const {
return false;
}
void PrintJobConfirmationDialogView::OnDialogClosed(bool accepted) {
DCHECK(callback_);
std::move(callback_).Run(accepted);
}
namespace chrome {
void ShowPrintJobConfirmationDialog(gfx::NativeWindow parent,
const std::string& extension_id,
const base::string16& extension_name,
const gfx::ImageSkia& extension_icon,
const base::string16& print_job_title,
const base::string16& printer_name,
base::OnceCallback<void(bool)> callback) {
PrintJobConfirmationDialogView::Show(parent, extension_id, extension_name,
extension_icon, print_job_title,
printer_name, std::move(callback));
}
} // namespace chrome
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_UI_VIEWS_EXTENSIONS_PRINT_JOB_CONFIRMATION_DIALOG_VIEW_H_
#define CHROME_BROWSER_UI_VIEWS_EXTENSIONS_PRINT_JOB_CONFIRMATION_DIALOG_VIEW_H_
#include <memory>
#include "base/callback_forward.h"
#include "base/strings/string16.h"
#include "ui/base/ui_base_types.h"
#include "ui/gfx/image/image_skia.h"
#include "ui/gfx/native_widget_types.h"
#include "ui/views/bubble/bubble_dialog_delegate_view.h"
namespace gfx {
class Image;
} // namespace gfx
class ToolbarActionView;
// The dialog's view, owned by the views framework.
class PrintJobConfirmationDialogView : public views::BubbleDialogDelegateView {
public:
static void Show(gfx::NativeWindow parent,
const std::string& extension_id,
const base::string16& extension_name,
const gfx::ImageSkia& extension_icon,
const base::string16& print_job_title,
const base::string16& printer_name,
base::OnceCallback<void(bool)> callback);
PrintJobConfirmationDialogView(ToolbarActionView* anchor_view,
const base::string16& extension_name,
const gfx::ImageSkia& extension_icon,
const base::string16& print_job_title,
const base::string16& printer_name,
base::OnceCallback<void(bool)> callback);
~PrintJobConfirmationDialogView() override;
PrintJobConfirmationDialogView(const PrintJobConfirmationDialogView&) =
delete;
PrintJobConfirmationDialogView& operator=(
const PrintJobConfirmationDialogView&) = delete;
private:
// views::DialogDelegateView:
bool Accept() override;
bool Cancel() override;
gfx::Size CalculatePreferredSize() const override;
// views::WidgetDelegate:
ui::ModalType GetModalType() const override;
base::string16 GetWindowTitle() const override;
gfx::ImageSkia GetWindowIcon() override;
bool ShouldShowWindowIcon() const override;
bool ShouldShowCloseButton() const override;
// Called when the dialog is closing.
void OnDialogClosed(bool accepted);
// The name of the extension we are showing the dialog for.
const base::string16 extension_name_;
// Image with extension's icon.
const gfx::ImageSkia extension_icon_;
// Callback to call after the dialog is accepted or rejected.
base::OnceCallback<void(bool)> callback_;
// TODO(pbos): Find a more direct way of determining if there's a bubble than
// checking |anchor_view|.
const bool dialog_is_bubble_;
};
#endif // CHROME_BROWSER_UI_VIEWS_EXTENSIONS_PRINT_JOB_CONFIRMATION_DIALOG_VIEW_H_
...@@ -23,7 +23,10 @@ namespace printing { ...@@ -23,7 +23,10 @@ namespace printing {
// The status of $(ref:submitJob) request. // The status of $(ref:submitJob) request.
enum SubmitJobStatus { enum SubmitJobStatus {
// Sent print job request is accepted. // Sent print job request is accepted.
OK OK,
// Sent print job request is rejected by the user.
USER_REJECTED
}; };
// Response for $(ref:submitJob) request. // Response for $(ref:submitJob) request.
......
...@@ -14887,6 +14887,7 @@ to ensure that the crash string is shown properly on the user-facing crash UI. ...@@ -14887,6 +14887,7 @@ to ensure that the crash string is shown properly on the user-facing crash UI.
<int value="99" label="QR Code Generator"/> <int value="99" label="QR Code Generator"/>
<int value="100" label="Crostini Force Close"/> <int value="100" label="Crostini Force Close"/>
<int value="101" label="App Uninstall"/> <int value="101" label="App Uninstall"/>
<int value="102" label="Print Job Confirmation"/>
</enum> </enum>
<enum name="DialogOriginRelationship"> <enum name="DialogOriginRelationship">
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment