• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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