1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2  *
3  * This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #include "nsPrintingProxy.h"
8 
9 #include "mozilla/ClearOnShutdown.h"
10 #include "mozilla/dom/ContentChild.h"
11 #include "mozilla/dom/BrowserChild.h"
12 #include "mozilla/layout/RemotePrintJobChild.h"
13 #include "mozilla/SpinEventLoopUntil.h"
14 #include "mozilla/Unused.h"
15 #include "nsIDocShell.h"
16 #include "nsIPrintingPromptService.h"
17 #include "nsIPrintSession.h"
18 #include "nsPIDOMWindow.h"
19 #include "nsPrintSettingsService.h"
20 #include "nsServiceManagerUtils.h"
21 #include "PrintProgressDialogChild.h"
22 #include "PrintSettingsDialogChild.h"
23 
24 using namespace mozilla;
25 using namespace mozilla::dom;
26 using namespace mozilla::embedding;
27 using namespace mozilla::layout;
28 
29 static StaticRefPtr<nsPrintingProxy> sPrintingProxyInstance;
30 
31 NS_IMPL_ISUPPORTS(nsPrintingProxy, nsIPrintingPromptService)
32 
33 nsPrintingProxy::nsPrintingProxy() = default;
34 
35 nsPrintingProxy::~nsPrintingProxy() = default;
36 
37 /* static */
GetInstance()38 already_AddRefed<nsPrintingProxy> nsPrintingProxy::GetInstance() {
39   if (!sPrintingProxyInstance) {
40     sPrintingProxyInstance = new nsPrintingProxy();
41     if (!sPrintingProxyInstance) {
42       return nullptr;
43     }
44     nsresult rv = sPrintingProxyInstance->Init();
45     if (NS_FAILED(rv)) {
46       sPrintingProxyInstance = nullptr;
47       return nullptr;
48     }
49     ClearOnShutdown(&sPrintingProxyInstance);
50   }
51 
52   RefPtr<nsPrintingProxy> inst = sPrintingProxyInstance.get();
53   return inst.forget();
54 }
55 
Init()56 nsresult nsPrintingProxy::Init() {
57   mozilla::Unused << ContentChild::GetSingleton()->SendPPrintingConstructor(
58       this);
59   return NS_OK;
60 }
61 
62 NS_IMETHODIMP
ShowPrintDialog(mozIDOMWindowProxy * parent,nsIPrintSettings * printSettings)63 nsPrintingProxy::ShowPrintDialog(mozIDOMWindowProxy* parent,
64                                  nsIPrintSettings* printSettings) {
65   NS_ENSURE_ARG(printSettings);
66 
67   // If parent is null we are just being called to retrieve the print settings
68   // from the printer in the parent for print preview.
69   BrowserChild* pBrowser = nullptr;
70   if (parent) {
71     // Get the BrowserChild for this nsIDOMWindow, which we can then pass up to
72     // the parent.
73     nsCOMPtr<nsPIDOMWindowOuter> pwin = nsPIDOMWindowOuter::From(parent);
74     NS_ENSURE_STATE(pwin);
75     nsCOMPtr<nsIDocShell> docShell = pwin->GetDocShell();
76     NS_ENSURE_STATE(docShell);
77 
78     nsCOMPtr<nsIBrowserChild> tabchild = docShell->GetBrowserChild();
79     NS_ENSURE_STATE(tabchild);
80 
81     pBrowser = static_cast<BrowserChild*>(tabchild.get());
82   }
83 
84   // Next, serialize the nsIWebBrowserPrint and nsIPrintSettings we were given.
85   nsresult rv = NS_OK;
86   nsCOMPtr<nsIPrintSettingsService> printSettingsSvc =
87       do_GetService("@mozilla.org/gfx/printsettings-service;1", &rv);
88   NS_ENSURE_SUCCESS(rv, rv);
89 
90   PrintData inSettings;
91   rv = printSettingsSvc->SerializeToPrintData(printSettings, &inSettings);
92   NS_ENSURE_SUCCESS(rv, rv);
93 
94   nsCOMPtr<nsIPrintSession> session;
95   rv = printSettings->GetPrintSession(getter_AddRefs(session));
96   if (NS_SUCCEEDED(rv) && session) {
97     inSettings.remotePrintJobChild() = session->GetRemotePrintJob();
98   }
99 
100   // Now, the waiting game. The parent process should be showing
101   // the printing dialog soon. In the meantime, we need to spin a
102   // nested event loop while we wait for the results of the dialog
103   // to be returned to us.
104 
105   RefPtr<PrintSettingsDialogChild> dialog = new PrintSettingsDialogChild();
106   SendPPrintSettingsDialogConstructor(dialog);
107 
108   mozilla::Unused << SendShowPrintDialog(dialog, pBrowser, inSettings);
109 
110   SpinEventLoopUntil([&, dialog]() { return dialog->returned(); });
111 
112   rv = dialog->result();
113   NS_ENSURE_SUCCESS(rv, rv);
114 
115   rv = printSettingsSvc->DeserializeToPrintSettings(dialog->data(),
116                                                     printSettings);
117   return NS_OK;
118 }
119 
120 NS_IMETHODIMP
ShowPrintProgressDialog(mozIDOMWindowProxy * parent,nsIPrintSettings * printSettings,nsIObserver * openDialogObserver,bool isForPrinting,nsIWebProgressListener ** webProgressListener,nsIPrintProgressParams ** printProgressParams,bool * notifyOnOpen)121 nsPrintingProxy::ShowPrintProgressDialog(
122     mozIDOMWindowProxy* parent,
123     nsIPrintSettings* printSettings,  // ok to be null
124     nsIObserver* openDialogObserver,  // ok to be null
125     bool isForPrinting, nsIWebProgressListener** webProgressListener,
126     nsIPrintProgressParams** printProgressParams, bool* notifyOnOpen) {
127   NS_ENSURE_ARG(parent);
128   NS_ENSURE_ARG(webProgressListener);
129   NS_ENSURE_ARG(printProgressParams);
130   NS_ENSURE_ARG(notifyOnOpen);
131 
132   // Get the BrowserChild for this nsIDOMWindow, which we can then pass up to
133   // the parent.
134   nsCOMPtr<nsPIDOMWindowOuter> pwin = nsPIDOMWindowOuter::From(parent);
135   NS_ENSURE_STATE(pwin);
136   nsCOMPtr<nsIDocShell> docShell = pwin->GetDocShell();
137   NS_ENSURE_STATE(docShell);
138   nsCOMPtr<nsIBrowserChild> tabchild = docShell->GetBrowserChild();
139   BrowserChild* pBrowser = static_cast<BrowserChild*>(tabchild.get());
140 
141   RefPtr<PrintProgressDialogChild> dialogChild =
142       new PrintProgressDialogChild(openDialogObserver, printSettings);
143 
144   SendPPrintProgressDialogConstructor(dialogChild);
145 
146   // Get the RemotePrintJob if we have one available.
147   RefPtr<RemotePrintJobChild> remotePrintJob;
148   if (printSettings) {
149     nsCOMPtr<nsIPrintSession> printSession;
150     nsresult rv = printSettings->GetPrintSession(getter_AddRefs(printSession));
151     if (NS_SUCCEEDED(rv) && printSession) {
152       remotePrintJob = printSession->GetRemotePrintJob();
153     }
154   }
155 
156   // NOTE: We set notifyOnOpen to true unconditionally. If the parent process
157   // would get `false` for notifyOnOpen, then it will synthesize a notification
158   // which will be sent asynchronously down to the child.
159   *notifyOnOpen = true;
160   mozilla::Unused << SendShowProgress(pBrowser, dialogChild, remotePrintJob,
161                                       isForPrinting);
162 
163   // If we have a RemotePrintJob that will be being used as a more general
164   // forwarder for print progress listeners. Once we always have one we can
165   // remove the interface from PrintProgressDialogChild.
166   if (!remotePrintJob) {
167     NS_ADDREF(*webProgressListener = dialogChild);
168   }
169   NS_ADDREF(*printProgressParams = dialogChild);
170 
171   return NS_OK;
172 }
173 
174 NS_IMETHODIMP
ShowPageSetupDialog(mozIDOMWindowProxy * parent,nsIPrintSettings * printSettings)175 nsPrintingProxy::ShowPageSetupDialog(mozIDOMWindowProxy* parent,
176                                      nsIPrintSettings* printSettings) {
177   return NS_ERROR_NOT_IMPLEMENTED;
178 }
179 
SavePrintSettings(nsIPrintSettings * aPS,bool aUsePrinterNamePrefix,uint32_t aFlags)180 nsresult nsPrintingProxy::SavePrintSettings(nsIPrintSettings* aPS,
181                                             bool aUsePrinterNamePrefix,
182                                             uint32_t aFlags) {
183   nsresult rv;
184   nsCOMPtr<nsIPrintSettingsService> printSettingsSvc =
185       do_GetService("@mozilla.org/gfx/printsettings-service;1", &rv);
186   NS_ENSURE_SUCCESS(rv, rv);
187 
188   PrintData settings;
189   rv = printSettingsSvc->SerializeToPrintData(aPS, &settings);
190   NS_ENSURE_SUCCESS(rv, rv);
191 
192   Unused << SendSavePrintSettings(settings, aUsePrinterNamePrefix, aFlags, &rv);
193   return rv;
194 }
195 
AllocPPrintProgressDialogChild()196 PPrintProgressDialogChild* nsPrintingProxy::AllocPPrintProgressDialogChild() {
197   // The parent process will never initiate the PPrintProgressDialog
198   // protocol connection, so no need to provide an allocator here.
199   MOZ_ASSERT_UNREACHABLE(
200       "Allocator for PPrintProgressDialogChild should not "
201       "be called on nsPrintingProxy.");
202   return nullptr;
203 }
204 
DeallocPPrintProgressDialogChild(PPrintProgressDialogChild * aActor)205 bool nsPrintingProxy::DeallocPPrintProgressDialogChild(
206     PPrintProgressDialogChild* aActor) {
207   // The PrintProgressDialogChild implements refcounting, and
208   // will take itself out.
209   return true;
210 }
211 
AllocPPrintSettingsDialogChild()212 PPrintSettingsDialogChild* nsPrintingProxy::AllocPPrintSettingsDialogChild() {
213   // The parent process will never initiate the PPrintSettingsDialog
214   // protocol connection, so no need to provide an allocator here.
215   MOZ_ASSERT_UNREACHABLE(
216       "Allocator for PPrintSettingsDialogChild should not "
217       "be called on nsPrintingProxy.");
218   return nullptr;
219 }
220 
DeallocPPrintSettingsDialogChild(PPrintSettingsDialogChild * aActor)221 bool nsPrintingProxy::DeallocPPrintSettingsDialogChild(
222     PPrintSettingsDialogChild* aActor) {
223   // The PrintSettingsDialogChild implements refcounting, and
224   // will take itself out.
225   return true;
226 }
227 
228 already_AddRefed<PRemotePrintJobChild>
AllocPRemotePrintJobChild()229 nsPrintingProxy::AllocPRemotePrintJobChild() {
230   RefPtr<RemotePrintJobChild> remotePrintJob = new RemotePrintJobChild();
231   return remotePrintJob.forget();
232 }
233