1 // Copyright (c) 2014 The Chromium Embedded Framework Authors.
2 // Portions copyright (c) 2012 The Chromium Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5
6 #include "libcef/browser/printing/print_dialog_linux.h"
7
8 #include <string>
9 #include <vector>
10
11 #include "libcef/browser/extensions/browser_extensions_util.h"
12 #include "libcef/browser/print_settings_impl.h"
13 #include "libcef/browser/thread_util.h"
14 #include "libcef/common/app_manager.h"
15 #include "libcef/common/frame_util.h"
16
17 #include "base/bind.h"
18 #include "base/files/file_util.h"
19 #include "base/lazy_instance.h"
20 #include "base/logging.h"
21 #include "base/strings/utf_string_conversions.h"
22 #include "base/values.h"
23 #include "content/public/browser/global_routing_id.h"
24 #include "printing/metafile.h"
25 #include "printing/mojom/print.mojom-shared.h"
26 #include "printing/print_job_constants.h"
27 #include "printing/print_settings.h"
28
29 using content::BrowserThread;
30 using printing::PageRanges;
31 using printing::PrintSettings;
32
33 class CefPrintDialogCallbackImpl : public CefPrintDialogCallback {
34 public:
CefPrintDialogCallbackImpl(CefRefPtr<CefPrintDialogLinux> dialog)35 explicit CefPrintDialogCallbackImpl(CefRefPtr<CefPrintDialogLinux> dialog)
36 : dialog_(dialog) {}
37
38 CefPrintDialogCallbackImpl(const CefPrintDialogCallbackImpl&) = delete;
39 CefPrintDialogCallbackImpl& operator=(const CefPrintDialogCallbackImpl&) =
40 delete;
41
Continue(CefRefPtr<CefPrintSettings> settings)42 void Continue(CefRefPtr<CefPrintSettings> settings) override {
43 if (CEF_CURRENTLY_ON_UIT()) {
44 if (dialog_.get()) {
45 dialog_->OnPrintContinue(settings);
46 dialog_ = nullptr;
47 }
48 } else {
49 CEF_POST_TASK(CEF_UIT,
50 base::BindOnce(&CefPrintDialogCallbackImpl::Continue, this,
51 settings));
52 }
53 }
54
Cancel()55 void Cancel() override {
56 if (CEF_CURRENTLY_ON_UIT()) {
57 if (dialog_.get()) {
58 dialog_->OnPrintCancel();
59 dialog_ = nullptr;
60 }
61 } else {
62 CEF_POST_TASK(CEF_UIT,
63 base::BindOnce(&CefPrintDialogCallbackImpl::Cancel, this));
64 }
65 }
66
Disconnect()67 void Disconnect() { dialog_ = nullptr; }
68
69 private:
70 CefRefPtr<CefPrintDialogLinux> dialog_;
71
72 IMPLEMENT_REFCOUNTING(CefPrintDialogCallbackImpl);
73 };
74
75 class CefPrintJobCallbackImpl : public CefPrintJobCallback {
76 public:
CefPrintJobCallbackImpl(CefRefPtr<CefPrintDialogLinux> dialog)77 explicit CefPrintJobCallbackImpl(CefRefPtr<CefPrintDialogLinux> dialog)
78 : dialog_(dialog) {}
79
80 CefPrintJobCallbackImpl(const CefPrintJobCallbackImpl&) = delete;
81 CefPrintJobCallbackImpl& operator=(const CefPrintJobCallbackImpl&) = delete;
82
Continue()83 void Continue() override {
84 if (CEF_CURRENTLY_ON_UIT()) {
85 if (dialog_.get()) {
86 dialog_->OnJobCompleted();
87 dialog_ = nullptr;
88 }
89 } else {
90 CEF_POST_TASK(CEF_UIT,
91 base::BindOnce(&CefPrintJobCallbackImpl::Continue, this));
92 }
93 }
94
Disconnect()95 void Disconnect() { dialog_ = nullptr; }
96
97 private:
98 CefRefPtr<CefPrintDialogLinux> dialog_;
99
100 IMPLEMENT_REFCOUNTING(CefPrintJobCallbackImpl);
101 };
102
103 // static
CreatePrintDialog(PrintingContextLinux * context)104 printing::PrintDialogGtkInterface* CefPrintDialogLinux::CreatePrintDialog(
105 PrintingContextLinux* context) {
106 CEF_REQUIRE_UIT();
107 return new CefPrintDialogLinux(context);
108 }
109
110 // static
GetPdfPaperSize(printing::PrintingContextLinux * context)111 gfx::Size CefPrintDialogLinux::GetPdfPaperSize(
112 printing::PrintingContextLinux* context) {
113 CEF_REQUIRE_UIT();
114
115 gfx::Size size;
116
117 auto browser = extensions::GetOwnerBrowserForGlobalId(
118 frame_util::MakeGlobalId(context->render_process_id(),
119 context->render_frame_id()),
120 nullptr);
121 DCHECK(browser);
122 if (browser && browser->GetClient()) {
123 if (auto handler = browser->GetClient()->GetPrintHandler()) {
124 const printing::PrintSettings& settings = context->settings();
125 CefSize cef_size = handler->GetPdfPaperSize(
126 browser.get(), settings.device_units_per_inch());
127 size.SetSize(cef_size.width, cef_size.height);
128 }
129 }
130
131 if (size.IsEmpty()) {
132 LOG(ERROR) << "Empty size value returned in GetPdfPaperSize; "
133 "PDF printing will fail.";
134 }
135 return size;
136 }
137
138 // static
OnPrintStart(CefRefPtr<CefBrowserHostBase> browser)139 void CefPrintDialogLinux::OnPrintStart(CefRefPtr<CefBrowserHostBase> browser) {
140 CEF_REQUIRE_UIT();
141 DCHECK(browser);
142 if (browser && browser->GetClient()) {
143 if (auto handler = browser->GetClient()->GetPrintHandler()) {
144 handler->OnPrintStart(browser.get());
145 }
146 }
147 }
148
CefPrintDialogLinux(PrintingContextLinux * context)149 CefPrintDialogLinux::CefPrintDialogLinux(PrintingContextLinux* context)
150 : context_(context) {
151 DCHECK(context_);
152 browser_ = extensions::GetOwnerBrowserForGlobalId(
153 frame_util::MakeGlobalId(context_->render_process_id(),
154 context_->render_frame_id()),
155 nullptr);
156 DCHECK(browser_);
157 }
158
~CefPrintDialogLinux()159 CefPrintDialogLinux::~CefPrintDialogLinux() {
160 // It's not safe to dereference |context_| during the destruction of this
161 // object because the PrintJobWorker which owns |context_| may already have
162 // been deleted.
163 CEF_REQUIRE_UIT();
164 ReleaseHandler();
165 }
166
UseDefaultSettings()167 void CefPrintDialogLinux::UseDefaultSettings() {
168 UpdateSettings(std::make_unique<PrintSettings>(), true);
169 }
170
UpdateSettings(std::unique_ptr<PrintSettings> settings)171 void CefPrintDialogLinux::UpdateSettings(
172 std::unique_ptr<PrintSettings> settings) {
173 UpdateSettings(std::move(settings), false);
174 }
175
ShowDialog(gfx::NativeView parent_view,bool has_selection,PrintingContextLinux::PrintSettingsCallback callback)176 void CefPrintDialogLinux::ShowDialog(
177 gfx::NativeView parent_view,
178 bool has_selection,
179 PrintingContextLinux::PrintSettingsCallback callback) {
180 CEF_REQUIRE_UIT();
181
182 SetHandler();
183 if (!handler_.get()) {
184 std::move(callback).Run(printing::mojom::ResultCode::kCanceled);
185 return;
186 }
187
188 callback_ = std::move(callback);
189
190 CefRefPtr<CefPrintDialogCallbackImpl> callback_impl(
191 new CefPrintDialogCallbackImpl(this));
192
193 if (!handler_->OnPrintDialog(browser_.get(), has_selection,
194 callback_impl.get())) {
195 callback_impl->Disconnect();
196 OnPrintCancel();
197 }
198 }
199
PrintDocument(const printing::MetafilePlayer & metafile,const std::u16string & document_name)200 void CefPrintDialogLinux::PrintDocument(
201 const printing::MetafilePlayer& metafile,
202 const std::u16string& document_name) {
203 // This runs on the print worker thread, does not block the UI thread.
204 DCHECK(!CEF_CURRENTLY_ON_UIT());
205
206 // The document printing tasks can outlive the PrintingContext that created
207 // this dialog.
208 AddRef();
209
210 bool success = base::CreateTemporaryFile(&path_to_pdf_);
211
212 if (success) {
213 base::File file;
214 file.Initialize(path_to_pdf_,
215 base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
216 success = metafile.SaveTo(&file);
217 file.Close();
218 if (!success)
219 base::DeleteFile(path_to_pdf_);
220 }
221
222 if (!success) {
223 LOG(ERROR) << "Saving metafile failed";
224 // Matches AddRef() above.
225 Release();
226 return;
227 }
228
229 // No errors, continue printing.
230 CEF_POST_TASK(
231 CEF_UIT, base::BindOnce(&CefPrintDialogLinux::SendDocumentToPrinter, this,
232 document_name));
233 }
234
AddRefToDialog()235 void CefPrintDialogLinux::AddRefToDialog() {
236 AddRef();
237 }
238
ReleaseDialog()239 void CefPrintDialogLinux::ReleaseDialog() {
240 Release();
241 }
242
SetHandler()243 void CefPrintDialogLinux::SetHandler() {
244 if (handler_.get())
245 return;
246
247 if (browser_ && browser_->GetClient()) {
248 handler_ = browser_->GetClient()->GetPrintHandler();
249 }
250 }
251
ReleaseHandler()252 void CefPrintDialogLinux::ReleaseHandler() {
253 if (handler_.get()) {
254 handler_->OnPrintReset(browser_.get());
255 handler_ = nullptr;
256 }
257 }
258
UpdateSettings(std::unique_ptr<PrintSettings> settings,bool get_defaults)259 bool CefPrintDialogLinux::UpdateSettings(
260 std::unique_ptr<PrintSettings> settings,
261 bool get_defaults) {
262 CEF_REQUIRE_UIT();
263
264 SetHandler();
265 if (!handler_.get())
266 return false;
267
268 CefRefPtr<CefPrintSettingsImpl> settings_impl(
269 new CefPrintSettingsImpl(std::move(settings), false));
270 handler_->OnPrintSettings(browser_.get(), settings_impl.get(), get_defaults);
271
272 context_->InitWithSettings(settings_impl->TakeOwnership());
273 return true;
274 }
275
SendDocumentToPrinter(const std::u16string & document_name)276 void CefPrintDialogLinux::SendDocumentToPrinter(
277 const std::u16string& document_name) {
278 CEF_REQUIRE_UIT();
279
280 if (!handler_.get()) {
281 OnJobCompleted();
282 return;
283 }
284
285 CefRefPtr<CefPrintJobCallbackImpl> callback_impl(
286 new CefPrintJobCallbackImpl(this));
287
288 if (!handler_->OnPrintJob(browser_.get(), document_name, path_to_pdf_.value(),
289 callback_impl.get())) {
290 callback_impl->Disconnect();
291 OnJobCompleted();
292 }
293 }
294
OnPrintContinue(CefRefPtr<CefPrintSettings> settings)295 void CefPrintDialogLinux::OnPrintContinue(
296 CefRefPtr<CefPrintSettings> settings) {
297 CefPrintSettingsImpl* impl =
298 static_cast<CefPrintSettingsImpl*>(settings.get());
299 context_->InitWithSettings(impl->TakeOwnership());
300 std::move(callback_).Run(printing::mojom::ResultCode::kSuccess);
301 }
302
OnPrintCancel()303 void CefPrintDialogLinux::OnPrintCancel() {
304 std::move(callback_).Run(printing::mojom::ResultCode::kCanceled);
305 }
306
OnJobCompleted()307 void CefPrintDialogLinux::OnJobCompleted() {
308 CEF_POST_BACKGROUND_TASK(
309 base::BindOnce(base::GetDeleteFileCallback(), path_to_pdf_));
310
311 // Printing finished. Matches AddRef() in PrintDocument();
312 Release();
313 }
314