• 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 
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