Bug 470455 - test_database_sync_embed_visits.js leaks, r=sdwilsh
[wine-gecko.git] / widget / src / cocoa / nsPrintSettingsX.mm
blob1f1d9a976ecc2437a7884ac6e6e85f51a6d4a76f
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4  *
5  * The contents of this file are subject to the Mozilla Public License Version
6  * 1.1 (the "License"); you may not use this file except in compliance with
7  * the License. You may obtain a copy of the License at
8  * http://www.mozilla.org/MPL/
9  *
10  * Software distributed under the License is distributed on an "AS IS" basis,
11  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12  * for the specific language governing rights and limitations under the
13  * License.
14  *
15  * The Original Code is mozilla.org code.
16  *
17  * The Initial Developer of the Original Code is
18  * Netscape Communications Corporation.
19  * Portions created by the Initial Developer are Copyright (C) 1998
20  * the Initial Developer. All Rights Reserved.
21  *
22  * Contributor(s):
23  *   Conrad Carlen <ccarlen@netscape.com>
24  *
25  * Alternatively, the contents of this file may be used under the terms of
26  * either of the GNU General Public License Version 2 or later (the "GPL"),
27  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28  * in which case the provisions of the GPL or the LGPL are applicable instead
29  * of those above. If you wish to allow use of your version of this file only
30  * under the terms of either the GPL or the LGPL, and not to allow others to
31  * use your version of this file under the terms of the MPL, indicate your
32  * decision by deleting the provisions above and replace them with the notice
33  * and other provisions required by the GPL or the LGPL. If you do not delete
34  * the provisions above, a recipient may use your version of this file under
35  * the terms of any one of the MPL, the GPL or the LGPL.
36  *
37  * ***** END LICENSE BLOCK ***** */
39 #include "nsPrintSettingsX.h"
40 #include "nsObjCExceptions.h"
41 #include "nsIPrintSessionX.h"
43 #include "nsIPrefService.h"
44 #include "nsIPrefBranch.h"
45 #include "nsServiceManagerUtils.h"
47 #include "plbase64.h"
48 #include "prmem.h"
49 #include "prnetdb.h"
52 // This struct should be represented identically on all architectures, and
53 // there shouldn't be any padding before the data field.
54 struct FrozenHandle {
55   PRUint32 size;
56   char data[0];
60 #define PRINTING_PREF_BRANCH            "print."
61 #define MAC_OS_X_PAGE_SETUP_PREFNAME    "macosx.pagesetup-2"
64 // Utility class stack-based handle ownership
65 class StHandleOwner
67 public:
68   StHandleOwner(Handle inHandle)
69     : mHandle(inHandle)
70   {
71   }
73   ~StHandleOwner()
74   {
75     NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
77     if (mHandle)
78       ::DisposeHandle(mHandle);
80     NS_OBJC_END_TRY_ABORT_BLOCK;
81   }
83   Handle GetHandle() { return mHandle; }
85   void   ClearHandle(Boolean disposeIt = false)
86   {
87     NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
89     if (disposeIt)
90       ::DisposeHandle(mHandle);
92     mHandle = nsnull;
94     NS_OBJC_END_TRY_ABORT_BLOCK;
95   }
97 protected:
98   Handle mHandle;
102 //      Utility class for saving, locking, and restoring handle state.
103 //  Ok with null handle.
104 class StHandleLocker
106 public:
107   StHandleLocker(Handle theHandle)
108     :   mHandle(theHandle)
109   {
110     NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
112     if (mHandle) {
113       mOldHandleState = ::HGetState(mHandle);
114       ::HLock(mHandle);
115     }
117     NS_OBJC_END_TRY_ABORT_BLOCK;
118   }
120   ~StHandleLocker()
121   {
122     NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
124     if (mHandle)
125       ::HSetState(mHandle, mOldHandleState);
127     NS_OBJC_END_TRY_ABORT_BLOCK;
128   }
130 protected:
131   Handle mHandle;
132   SInt8 mOldHandleState;
136 NS_IMPL_ISUPPORTS_INHERITED1(nsPrintSettingsX, nsPrintSettings, nsIPrintSettingsX)
138 nsPrintSettingsX::nsPrintSettingsX() :
139   mPageFormat(kPMNoPageFormat),
140   mPrintSettings(kPMNoPrintSettings)
145 nsPrintSettingsX::nsPrintSettingsX(const nsPrintSettingsX& src) :
146   mPageFormat(kPMNoPageFormat),
147   mPrintSettings(kPMNoPrintSettings)
149   *this = src;
153 nsPrintSettingsX::~nsPrintSettingsX()
155   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
157   if (mPageFormat != kPMNoPageFormat) {
158     ::PMRelease(mPageFormat);
159     mPageFormat = kPMNoPageFormat;
160   }
161   if (mPrintSettings != kPMNoPrintSettings) {
162     ::PMRelease(mPrintSettings);
163     mPrintSettings = kPMNoPrintSettings;
164   }
166   NS_OBJC_END_TRY_ABORT_BLOCK;
170 nsPrintSettingsX& nsPrintSettingsX::operator=(const nsPrintSettingsX& rhs)
172   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
174   if (this == &rhs) {
175     return *this;
176   }
177   
178   nsPrintSettings::operator=(rhs);
180   OSStatus status;
181    
182   if (mPageFormat != kPMNoPageFormat) {
183     ::PMRelease(mPageFormat);
184     mPageFormat = kPMNoPageFormat;
185   }
186   if (rhs.mPageFormat != kPMNoPageFormat) {
187     PMPageFormat pageFormat;
188     status = ::PMCreatePageFormat(&pageFormat);
189     if (status == noErr) {
190       status = ::PMCopyPageFormat(rhs.mPageFormat, pageFormat);
191       if (status == noErr) {
192         mPageFormat = pageFormat;
193         // NOTE: No need to re-initialize mUnwriteableMargin here (even
194         // though mPageFormat is changing). It'll be copied correctly by
195         // nsPrintSettings::operator=.
196       } else {
197         ::PMRelease(pageFormat);
198       }
199     }
200   }
201   
202   if (mPrintSettings != kPMNoPrintSettings) {
203     ::PMRelease(mPrintSettings);
204     mPrintSettings = kPMNoPrintSettings;
205   }
206   if (rhs.mPrintSettings != kPMNoPrintSettings) {
207     PMPrintSettings    printSettings;
208     status = ::PMCreatePrintSettings(&printSettings);
209     if (status == noErr) {
210       status = ::PMCopyPrintSettings(rhs.mPrintSettings, printSettings);
211       if (status == noErr)
212         mPrintSettings = printSettings;
213       else
214         ::PMRelease(printSettings);
215     }
216   }
218   return *this;
220   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(*this);
224 nsresult nsPrintSettingsX::Init()
226   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
228   OSStatus status;
230   PMPrintSession printSession = NULL;
231   status = ::PMCreateSession(&printSession);
232   
233   if (status == noErr) {
234     // First, create a default page format
235     status = CreateDefaultPageFormat(printSession, mPageFormat);
236     InitUnwriteableMargin();
238     // Then, if no error, create the default print settings
239     if (status == noErr) {
240       status = CreateDefaultPrintSettings(printSession, mPrintSettings);
241     }
242     OSStatus tempStatus = ::PMRelease(printSession);
243     if (status == noErr)
244       status = tempStatus;
245   }
246   return (status == noErr) ? NS_OK : NS_ERROR_FAILURE;
248   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
252 // Should be called whenever mPageFormat changes.
253 NS_IMETHODIMP nsPrintSettingsX::InitUnwriteableMargin()
255   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
257   if (mPageFormat == kPMNoPageFormat)
258     return NS_OK;
260   PMPaper paper;
261   PMPaperMargins paperMargin;
262   ::PMGetPageFormatPaper(mPageFormat, &paper);
263   ::PMPaperGetMargins(paper, &paperMargin);
264   mUnwriteableMargin.top    = NS_POINTS_TO_TWIPS(paperMargin.top);
265   mUnwriteableMargin.left   = NS_POINTS_TO_TWIPS(paperMargin.left);
266   mUnwriteableMargin.bottom = NS_POINTS_TO_TWIPS(paperMargin.bottom);
267   mUnwriteableMargin.right  = NS_POINTS_TO_TWIPS(paperMargin.right);
269   return NS_OK;
271   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;  
275 NS_IMETHODIMP nsPrintSettingsX::GetNativePrintSession(PMPrintSession *aNativePrintSession)
277    NS_ENSURE_ARG_POINTER(aNativePrintSession);
278    *aNativePrintSession = nsnull;
279    
280    nsCOMPtr<nsIPrintSession> printSession;
281    GetPrintSession(getter_AddRefs(printSession));
282    if (!printSession)
283     return NS_ERROR_FAILURE;
284    nsCOMPtr<nsIPrintSessionX> printSessionX(do_QueryInterface(printSession));
285    if (!printSession)
286     return NS_ERROR_FAILURE;
288    return printSessionX->GetNativeSession(aNativePrintSession);
292 NS_IMETHODIMP nsPrintSettingsX::GetPMPageFormat(PMPageFormat *aPMPageFormat)
294   NS_ENSURE_ARG_POINTER(aPMPageFormat);
295   *aPMPageFormat = kPMNoPageFormat;
296   NS_ENSURE_STATE(mPageFormat != kPMNoPageFormat);
297   
298   *aPMPageFormat = mPageFormat;
299   OSStatus status = noErr;
300   
301   return (status == noErr) ? NS_OK : NS_ERROR_FAILURE;
305 NS_IMETHODIMP nsPrintSettingsX::SetPMPageFormat(PMPageFormat aPMPageFormat)
307   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
309   NS_ENSURE_ARG(aPMPageFormat);
310   
311   OSStatus status = ::PMRetain(aPMPageFormat);
312   if (status == noErr) {
313     if (mPageFormat)
314       status = ::PMRelease(mPageFormat);
315     mPageFormat = aPMPageFormat;
316     InitUnwriteableMargin();
317   }        
318   return (status == noErr) ? NS_OK : NS_ERROR_FAILURE;
320   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
324 NS_IMETHODIMP nsPrintSettingsX::GetPMPrintSettings(PMPrintSettings *aPMPrintSettings)
326   NS_ENSURE_ARG_POINTER(aPMPrintSettings);
327   *aPMPrintSettings = kPMNoPrintSettings;
328   NS_ENSURE_STATE(mPrintSettings != kPMNoPrintSettings);
329   
330   *aPMPrintSettings = mPrintSettings;
332   return NS_OK;
336 NS_IMETHODIMP nsPrintSettingsX::SetPMPrintSettings(PMPrintSettings aPMPrintSettings)
338   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
340   NS_ENSURE_ARG(aPMPrintSettings);
341   
342   OSStatus status = ::PMRetain(aPMPrintSettings);
343   if (status == noErr) {
344     if (mPrintSettings)
345       status = ::PMRelease(mPrintSettings);
346     mPrintSettings = aPMPrintSettings;
347   }        
348   return (status == noErr) ? NS_OK : NS_ERROR_FAILURE;
350   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
354 NS_IMETHODIMP nsPrintSettingsX::ReadPageFormatFromPrefs()
356   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
358   nsresult rv;
359   nsCOMPtr<nsIPrefService> prefService(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
360   if (NS_FAILED(rv))
361     return rv;
362   nsCOMPtr<nsIPrefBranch> prefBranch;
363   rv = prefService->GetBranch(PRINTING_PREF_BRANCH, getter_AddRefs(prefBranch));
364   if (NS_FAILED(rv))
365     return rv;
366       
367   nsXPIDLCString  encodedData;
368   rv = prefBranch->GetCharPref(MAC_OS_X_PAGE_SETUP_PREFNAME, getter_Copies(encodedData));
369   if (NS_FAILED(rv))
370     return rv;
372   // decode the base64
373   PRInt32 encodedDataLen = encodedData.Length();
374   FrozenHandle* frozenHandle =
375    (FrozenHandle*)::PL_Base64Decode(encodedData.get(), encodedDataLen, nsnull);
376   if (!frozenHandle)
377     return NS_ERROR_FAILURE;
379   PRUint32 handleSize = PR_ntohl(frozenHandle->size);
381   // Ensure that the length reported in the frozen handle agrees with the
382   // amount of decoded data.  At most 3 bytes of data map to 4 bytes when
383   // base64-encoded.
384   PRUint32 maximumDataSize = (encodedDataLen * 3) / 4 - sizeof(FrozenHandle);
385   PRUint32 minimumDataSize = maximumDataSize - 2;
386   if (handleSize > maximumDataSize || handleSize < minimumDataSize) {
387     free(frozenHandle);
388     return NS_ERROR_FAILURE;
389   }
391   Handle    decodedDataHandle = nsnull;
392   OSErr err = ::PtrToHand(frozenHandle->data, &decodedDataHandle, handleSize);
393   free(frozenHandle);
394   if (err != noErr)
395     return NS_ERROR_OUT_OF_MEMORY;
397   StHandleOwner   handleOwner(decodedDataHandle);  
399   OSStatus      status;
400   PMPageFormat  newPageFormat = kPMNoPageFormat;
401   
402   status = ::PMCreatePageFormat(&newPageFormat);
403   if (status == noErr) { 
404     status = ::PMUnflattenPageFormat(decodedDataHandle, &newPageFormat);
405     if (status == noErr) {
406       if (mPageFormat)
407         status = ::PMRelease(mPageFormat);
408       mPageFormat = newPageFormat; // PMCreatePageFormat returned it with a refcnt of 1
409       InitUnwriteableMargin();
410     }
411   }
412   return (status == noErr) ? NS_OK : NS_ERROR_FAILURE;
414   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
418 NS_IMETHODIMP nsPrintSettingsX::WritePageFormatToPrefs()
420   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
422   if (mPageFormat == kPMNoPageFormat)
423     return NS_ERROR_NOT_INITIALIZED;
424     
425   nsresult rv;
426   nsCOMPtr<nsIPrefService> prefService(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
427   if (NS_FAILED(rv))
428     return rv;
429   nsCOMPtr<nsIPrefBranch> prefBranch;
430   rv = prefService->GetBranch(PRINTING_PREF_BRANCH, getter_AddRefs(prefBranch));
431   if (NS_FAILED(rv))
432     return rv;
434   Handle    pageFormatHandle = nsnull;
435   OSStatus  err = ::PMFlattenPageFormat(mPageFormat, &pageFormatHandle);
436   if (err != noErr)
437     return NS_ERROR_FAILURE;
438     
439   StHandleOwner   handleOwner(pageFormatHandle);
440   StHandleLocker  handleLocker(pageFormatHandle);
442   // Save the handle in a struct that identifies the data length and
443   // the data itself, and wrap it all up in base64.  The length must be
444   // included because PL_DecodeBase64 doesn't return the size of the
445   // decoded data, and the handle will need to be reconstructed later with
446   // the correct size.
447   PRUint32 dataSize = ::GetHandleSize(pageFormatHandle);
448   PRUint32 frozenDataSize = sizeof(FrozenHandle) + dataSize;
449   FrozenHandle* frozenHandle = (FrozenHandle*)malloc(frozenDataSize);
450   if (!frozenHandle)
451     return NS_ERROR_OUT_OF_MEMORY;
453   frozenHandle->size = PR_htonl(dataSize);
454   memcpy(&frozenHandle->data, *pageFormatHandle, dataSize);
456   nsXPIDLCString  encodedData;
457   encodedData.Adopt(::PL_Base64Encode((char*)frozenHandle, frozenDataSize,
458                     nsnull));
459   free(frozenHandle);
460   if (!encodedData.get())
461     return NS_ERROR_OUT_OF_MEMORY;
463   return prefBranch->SetCharPref(MAC_OS_X_PAGE_SETUP_PREFNAME, encodedData);
465   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
469 nsresult nsPrintSettingsX::_Clone(nsIPrintSettings **_retval)
471   NS_ENSURE_ARG_POINTER(_retval);
472   *_retval = nsnull;
473   
474   nsPrintSettingsX *newSettings = new nsPrintSettingsX(*this);
475   if (!newSettings)
476     return NS_ERROR_FAILURE;
477   *_retval = newSettings;
478   NS_ADDREF(*_retval);
479   return NS_OK;
483 NS_IMETHODIMP nsPrintSettingsX::_Assign(nsIPrintSettings *aPS)
485   nsPrintSettingsX *printSettingsX = static_cast<nsPrintSettingsX*>(aPS);
486   if (!printSettingsX)
487     return NS_ERROR_UNEXPECTED;
488   *this = *printSettingsX;
489   return NS_OK;
493 OSStatus nsPrintSettingsX::CreateDefaultPageFormat(PMPrintSession aSession, PMPageFormat& outFormat)
495   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
497   OSStatus status;
498   PMPageFormat pageFormat;
499   
500   outFormat = kPMNoPageFormat;
501   status = ::PMCreatePageFormat(&pageFormat);
502   if (status == noErr && pageFormat != kPMNoPageFormat) {
503     status = ::PMSessionDefaultPageFormat(aSession, pageFormat);
504     if (status == noErr) {
505       outFormat = pageFormat;
506       return NS_OK;
507     }
508   }
509   return status;
511   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(noErr);
515 OSStatus nsPrintSettingsX::CreateDefaultPrintSettings(PMPrintSession aSession, PMPrintSettings& outSettings)
517   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
519   OSStatus status;
520   PMPrintSettings printSettings;
521   
522   outSettings = kPMNoPrintSettings;
523   status = ::PMCreatePrintSettings(&printSettings);
524   if (status == noErr && printSettings != kPMNoPrintSettings) {
525     status = ::PMSessionDefaultPrintSettings(aSession, printSettings);
526     if (status == noErr) {
527       outSettings = printSettings;
528       return noErr;
529     }
530   }
531   return status;
533   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(noErr);