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