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