File:  [mozdev] / chimera / NSBrowserView.mm
Revision 1.6: download - view: text, annotated - select for diffs - revision graph
Mon Feb 4 07:15:33 2002 UTC (17 years, 8 months ago) by hyatt
Branches: MAIN
CVS tags: HEAD
Got the memory leak! We are golden!

    1: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
    2: /* ***** BEGIN LICENSE BLOCK *****
    3:  * Version: NPL 1.1/GPL 2.0/LGPL 2.1
    4:  *
    5:  * The contents of this file are subject to the Netscape Public License
    6:  * Version 1.1 (the "License"); you may not use this file except in
    7:  * compliance with the License. You may obtain a copy of the License at
    8:  * http://www.mozilla.org/NPL/
    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:  *
   24:  * Alternatively, the contents of this file may be used under the terms of
   25:  * either the GNU General Public License Version 2 or later (the "GPL"), or 
   26:  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
   27:  * in which case the provisions of the GPL or the LGPL are applicable instead
   28:  * of those above. If you wish to allow use of your version of this file only
   29:  * under the terms of either the GPL or the LGPL, and not to allow others to
   30:  * use your version of this file under the terms of the NPL, indicate your
   31:  * decision by deleting the provisions above and replace them with the notice
   32:  * and other provisions required by the GPL or the LGPL. If you do not delete
   33:  * the provisions above, a recipient may use your version of this file under
   34:  * the terms of any one of the NPL, the GPL or the LGPL.
   35:  *
   36:  * ***** END LICENSE BLOCK ***** */
   37: 
   38: #import "NSBrowserView.h"
   39: #import "ProgressDlgController.h"
   40: #import "nsCocoaBrowserService.h"
   41: 
   42: // Embedding includes
   43: #include "nsCWebBrowser.h"
   44: #include "nsIInterfaceRequestor.h"
   45: #include "nsIWebBrowserChrome.h"
   46: #include "nsIEmbeddingSiteWindow.h"
   47: #include "nsIWebProgressListener.h"
   48: #include "nsIWebBrowser.h"
   49: #include "nsIWebNavigation.h"
   50: #include "nsIURI.h"
   51: #include "nsIDOMWindow.h"
   52: #include "nsWeakReference.h"
   53: 
   54: // XPCOM and String includes
   55: #include "nsCRT.h"
   56: #include "nsXPIDLString.h"
   57: #include "nsCOMPtr.h"
   58: 
   59: #include "nsIWebBrowserFocus.h"
   60: #include "nsIDOMHTMLDocument.h"
   61: #include "nsIDocument.h"
   62: #include "nsIURL.h"
   63: #include "nsIWebBrowserPersist.h"
   64: #include "nsIProperties.h"
   65: #include "nsIRequest.h"
   66: #include "nsIChannel.h"
   67: #include "nsIHttpChannel.h"
   68: #include "nsIPref.h"
   69: #include "nsIMIMEService.h"
   70: #include "nsIMIMEInfo.h"
   71: #include "nsISHistory.h"
   72: #include "nsIHistoryEntry.h"
   73: #include "nsISHEntry.h"
   74: 
   75: const char* persistContractID = "@mozilla.org/embedding/browser/nsWebBrowserPersist;1";
   76: const char* dirServiceContractID = "@mozilla.org/file/directory_service;1";
   77: 
   78: class nsCocoaBrowserListener : public nsSupportsWeakReference,
   79:                                public nsIInterfaceRequestor,
   80: 			       public nsIWebBrowserChrome,
   81: 			       public nsIEmbeddingSiteWindow,
   82:                                public nsIWebProgressListener
   83: {
   84: public:
   85:   nsCocoaBrowserListener(NSBrowserView* aView);
   86:   virtual ~nsCocoaBrowserListener();
   87: 
   88:   NS_DECL_ISUPPORTS
   89:   NS_DECL_NSIINTERFACEREQUESTOR
   90:   NS_DECL_NSIWEBBROWSERCHROME
   91:   NS_DECL_NSIEMBEDDINGSITEWINDOW
   92:   NS_DECL_NSIWEBPROGRESSLISTENER
   93: 
   94:   void AddListener(id <NSBrowserListener> aListener);
   95:   void RemoveListener(id <NSBrowserListener> aListener);
   96:   void SetContainer(id <NSBrowserContainer> aContainer);
   97: 
   98: private:
   99:   NSBrowserView* mView;     // WEAK - it owns us
  100:   NSMutableArray* mListeners;
  101:   id <NSBrowserContainer> mContainer;
  102:   PRBool mIsModal;
  103:   PRUint32 mChromeFlags;
  104: };
  105: 
  106: nsCocoaBrowserListener::nsCocoaBrowserListener(NSBrowserView* aView)
  107:   : mView(aView), mContainer(nsnull), mIsModal(PR_FALSE), mChromeFlags(0)
  108: {
  109:   NS_INIT_ISUPPORTS();
  110:   mListeners = [[NSMutableArray alloc] init];
  111: }
  112: 
  113: nsCocoaBrowserListener::~nsCocoaBrowserListener()
  114: {
  115:   [mListeners release];
  116:   mView = nsnull;
  117:   if (mContainer) {
  118:     [mContainer release];
  119:   }
  120: }
  121: 
  122: NS_IMPL_ISUPPORTS5(nsCocoaBrowserListener,
  123: 		   nsIInterfaceRequestor,
  124: 		   nsIWebBrowserChrome,
  125: 		   nsIEmbeddingSiteWindow,
  126: 		   nsIWebProgressListener,
  127:      		   nsISupportsWeakReference)
  128: 
  129: // Implementation of nsIInterfaceRequestor
  130: NS_IMETHODIMP 
  131: nsCocoaBrowserListener::GetInterface(const nsIID &aIID, void** aInstancePtr)
  132: {
  133:   if (aIID.Equals(NS_GET_IID(nsIDOMWindow))) {
  134:     nsCOMPtr<nsIWebBrowser> browser = dont_AddRef([mView getWebBrowser]);
  135:     if (browser)
  136:       return browser->GetContentDOMWindow((nsIDOMWindow **) aInstancePtr);
  137:   }
  138:   
  139:   return QueryInterface(aIID, aInstancePtr);
  140: }
  141: 
  142: // Implementation of nsIWebBrowserChrome
  143: /* void setStatus (in unsigned long statusType, in wstring status); */
  144: NS_IMETHODIMP 
  145: nsCocoaBrowserListener::SetStatus(PRUint32 statusType, const PRUnichar *status)
  146: {
  147:   if (!mContainer) {
  148:     return NS_ERROR_FAILURE;
  149:   }
  150: 
  151:   NSString* str = nsnull;
  152:   if (status && (*status != PRUnichar(0))) {
  153:     str = [NSString stringWithCharacters:status length:nsCRT::strlen(status)];
  154:   }
  155: 
  156:   [mContainer setStatus:str ofType:(NSStatusType)statusType];
  157: 
  158:   return NS_OK;
  159: }
  160: 
  161: /* attribute nsIWebBrowser webBrowser; */
  162: NS_IMETHODIMP 
  163: nsCocoaBrowserListener::GetWebBrowser(nsIWebBrowser * *aWebBrowser)
  164: {
  165:   NS_ENSURE_ARG_POINTER(aWebBrowser);
  166:   if (!mView) {
  167:     return NS_ERROR_FAILURE;
  168:   }
  169:   *aWebBrowser = [mView getWebBrowser];
  170: 
  171:   return NS_OK;
  172: }
  173: NS_IMETHODIMP 
  174: nsCocoaBrowserListener::SetWebBrowser(nsIWebBrowser * aWebBrowser)
  175: {
  176:   if (!mView) {
  177:     return NS_ERROR_FAILURE;
  178:   }
  179: 
  180:   [mView setWebBrowser:aWebBrowser];
  181: 
  182:   return NS_OK;
  183: }
  184: 
  185: /* attribute unsigned long chromeFlags; */
  186: NS_IMETHODIMP 
  187: nsCocoaBrowserListener::GetChromeFlags(PRUint32 *aChromeFlags)
  188: {
  189:   NS_ENSURE_ARG_POINTER(aChromeFlags);
  190:   *aChromeFlags = mChromeFlags;
  191:   return NS_OK;
  192: }
  193: NS_IMETHODIMP 
  194: nsCocoaBrowserListener::SetChromeFlags(PRUint32 aChromeFlags)
  195: {
  196:   // XXX Do nothing with them for now
  197:   mChromeFlags = aChromeFlags;
  198:   return NS_OK;
  199: }
  200: 
  201: /* void destroyBrowserWindow (); */
  202: NS_IMETHODIMP 
  203: nsCocoaBrowserListener::DestroyBrowserWindow()
  204: {
  205:   // XXX Could send this up to the container, but for now,
  206:   // we just destroy the enclosing window.
  207:   NSWindow* window = [mView window];
  208: 
  209:   if (window) {
  210:     [window close];
  211:   }
  212: 
  213:   return NS_OK;
  214: }
  215: 
  216: /* void sizeBrowserTo (in long aCX, in long aCY); */
  217: NS_IMETHODIMP 
  218: nsCocoaBrowserListener::SizeBrowserTo(PRInt32 aCX, PRInt32 aCY)
  219: {
  220:   if (mContainer) {
  221:     NSSize size;
  222:     
  223:     size.width = (float)aCX;
  224:     size.height = (float)aCY;
  225: 
  226:     [mContainer sizeBrowserTo:size];
  227:   }
  228:   
  229:   return NS_OK;
  230: }
  231: 
  232: /* void showAsModal (); */
  233: NS_IMETHODIMP 
  234: nsCocoaBrowserListener::ShowAsModal()
  235: {
  236:   if (!mView) {
  237:     return NS_ERROR_FAILURE;
  238:   }
  239: 
  240:   NSWindow* window = [mView window];
  241: 
  242:   if (!window) {
  243:     return NS_ERROR_FAILURE;
  244:   }
  245: 
  246:   mIsModal = PR_TRUE;
  247:   int result = [NSApp runModalForWindow:window];
  248:   mIsModal = PR_FALSE;
  249: 
  250:   return (nsresult)result;
  251: }
  252: 
  253: /* boolean isWindowModal (); */
  254: NS_IMETHODIMP 
  255: nsCocoaBrowserListener::IsWindowModal(PRBool *_retval)
  256: {
  257:   NS_ENSURE_ARG_POINTER(_retval);
  258: 
  259:   *_retval = mIsModal;
  260: 
  261:   return NS_OK;
  262: }
  263: 
  264: /* void exitModalEventLoop (in nsresult aStatus); */
  265: NS_IMETHODIMP 
  266: nsCocoaBrowserListener::ExitModalEventLoop(nsresult aStatus)
  267: {
  268:   [NSApp stopModalWithCode:(int)aStatus];
  269: 
  270:   return NS_OK;
  271: }
  272: 
  273: // Implementation of nsIEmbeddingSiteWindow
  274: /* void setDimensions (in unsigned long flags, in long x, in long y, in long cx, in long cy); */
  275: NS_IMETHODIMP 
  276: nsCocoaBrowserListener::SetDimensions(PRUint32 flags, 
  277: 				      PRInt32 x, 
  278: 				      PRInt32 y, 
  279: 				      PRInt32 cx, 
  280: 				      PRInt32 cy)
  281: {
  282:   if (!mView) {
  283:     return NS_ERROR_FAILURE;
  284:   }
  285: 
  286:   NSWindow* window = [mView window];
  287:   if (!window) {
  288:     return NS_ERROR_FAILURE;
  289:   }
  290: 
  291:   if (flags & nsIEmbeddingSiteWindow::DIM_FLAGS_POSITION) {
  292:     NSPoint origin;
  293:     origin.x = (float)x;
  294:     origin.y = (float)y;
  295:     [window setFrameOrigin:origin];
  296:   }
  297: 
  298:   if (flags & nsIEmbeddingSiteWindow::DIM_FLAGS_SIZE_OUTER) {
  299:     NSRect frame = [window frame];
  300:     frame.size.width = (float)cx;
  301:     frame.size.height = (float)cy;
  302:     [window setFrame:frame display:YES];
  303:   }
  304:   else if (flags & nsIEmbeddingSiteWindow::DIM_FLAGS_SIZE_INNER) {
  305:     NSSize size;
  306:     size.width = (float)cx;
  307:     size.height = (float)cy;
  308:     [window setContentSize:size];
  309:   }
  310: 
  311:   return NS_OK;
  312: }
  313: 
  314: /* void getDimensions (in unsigned long flags, out long x, out long y, out long cx, out long cy); */
  315: NS_IMETHODIMP 
  316: nsCocoaBrowserListener::GetDimensions(PRUint32 flags, 
  317: 				      PRInt32 *x, 
  318: 				      PRInt32 *y, 
  319: 				      PRInt32 *cx, 
  320: 				      PRInt32 *cy)
  321: {
  322:   NS_ENSURE_ARG_POINTER(x);
  323:   NS_ENSURE_ARG_POINTER(y);
  324:   NS_ENSURE_ARG_POINTER(cx);
  325:   NS_ENSURE_ARG_POINTER(cy);
  326: 
  327:   if (!mView) {
  328:     return NS_ERROR_FAILURE;
  329:   }
  330: 
  331:   NSWindow* window = [mView window];
  332:   if (!window) {
  333:     return NS_ERROR_FAILURE;
  334:   }
  335: 
  336:   NSRect frame = [window frame];
  337:   if (flags & nsIEmbeddingSiteWindow::DIM_FLAGS_POSITION) {
  338:     *x = (PRInt32)frame.origin.x;
  339:     *y = (PRInt32)frame.origin.y;
  340:   }
  341:   if (flags & nsIEmbeddingSiteWindow::DIM_FLAGS_SIZE_OUTER) {
  342:     *cx = (PRInt32)frame.size.width;
  343:     *cy = (PRInt32)frame.size.height;
  344:   }
  345:   else if (flags & nsIEmbeddingSiteWindow::DIM_FLAGS_SIZE_INNER) {
  346:     NSView* contentView = [window contentView];
  347:     NSRect contentFrame = [contentView frame];
  348:     *cx = (PRInt32)contentFrame.size.width;
  349:     *cy = (PRInt32)contentFrame.size.height;    
  350:   }
  351: 
  352:   return NS_OK;
  353: }
  354: 
  355: /* void setFocus (); */
  356: NS_IMETHODIMP 
  357: nsCocoaBrowserListener::SetFocus()
  358: {
  359:   if (!mView) {
  360:     return NS_ERROR_FAILURE;
  361:   }
  362: 
  363:   NSWindow* window = [mView window];
  364:   if (!window) {
  365:     return NS_ERROR_FAILURE;
  366:   }
  367: 
  368:   [window makeKeyAndOrderFront:window];
  369: 
  370:   return NS_OK;
  371: }
  372: 
  373: /* attribute boolean visibility; */
  374: NS_IMETHODIMP 
  375: nsCocoaBrowserListener::GetVisibility(PRBool *aVisibility)
  376: {
  377:   NS_ENSURE_ARG_POINTER(aVisibility);
  378: 
  379:   if (!mView) {
  380:     return NS_ERROR_FAILURE;
  381:   }
  382: 
  383:   NSWindow* window = [mView window];
  384:   if (!window) {
  385:     return NS_ERROR_FAILURE;
  386:   }
  387: 
  388:   *aVisibility = [window isMiniaturized];
  389: 
  390:   return NS_OK;
  391: }
  392: NS_IMETHODIMP 
  393: nsCocoaBrowserListener::SetVisibility(PRBool aVisibility)
  394: {
  395:   if (!mView) {
  396:     return NS_ERROR_FAILURE;
  397:   }
  398: 
  399:   NSWindow* window = [mView window];
  400:   if (!window) {
  401:     return NS_ERROR_FAILURE;
  402:   }
  403: 
  404:   if (aVisibility) {
  405:     [window deminiaturize:window];
  406:   }
  407:   else {
  408:     [window miniaturize:window];
  409:   }
  410: 
  411:   return NS_OK;
  412: }
  413: 
  414: /* attribute wstring title; */
  415: NS_IMETHODIMP 
  416: nsCocoaBrowserListener::GetTitle(PRUnichar * *aTitle)
  417: {
  418:   NS_ENSURE_ARG_POINTER(aTitle);
  419: 
  420:   if (!mContainer) {
  421:     return NS_ERROR_FAILURE;
  422:   }
  423: 
  424:   NSString* title = [mContainer title];
  425:   unsigned int length = [title length];
  426:   if (length) {
  427:     *aTitle = (PRUnichar*)nsMemory::Alloc((length+1)*sizeof(PRUnichar));
  428:     if (!*aTitle) {
  429:       return NS_ERROR_OUT_OF_MEMORY;
  430:     }
  431:     [title getCharacters:*aTitle];
  432:   }
  433:   else {
  434:     *aTitle = nsnull;
  435:   }
  436:   
  437:   return NS_OK;
  438: }
  439: NS_IMETHODIMP 
  440: nsCocoaBrowserListener::SetTitle(const PRUnichar * aTitle)
  441: {
  442:   NS_ENSURE_ARG(aTitle);
  443: 
  444:   if (!mContainer) {
  445:     return NS_ERROR_FAILURE;
  446:   }
  447: 
  448:   NSString* str = [NSString stringWithCharacters:aTitle length:nsCRT::strlen(aTitle)];
  449:   [mContainer setTitle:str];
  450: 
  451:   return NS_OK;
  452: }
  453: 
  454: /* [noscript] readonly attribute voidPtr siteWindow; */
  455: NS_IMETHODIMP 
  456: nsCocoaBrowserListener::GetSiteWindow(void * *aSiteWindow)
  457: {
  458:   NS_ENSURE_ARG_POINTER(aSiteWindow);
  459: 
  460:   if (!mView) {
  461:     return NS_ERROR_FAILURE;
  462:   }
  463: 
  464:   NSWindow* window = [mView window];
  465:   if (!window) {
  466:     return NS_ERROR_FAILURE;
  467:   }
  468: 
  469:   *aSiteWindow = (void*)window;
  470: 
  471:   return NS_OK;
  472: }
  473: 
  474: // Implementation of nsIWebProgressListener
  475: /* void onStateChange (in nsIWebProgress aWebProgress, in nsIRequest aRequest, in long aStateFlags, in unsigned long aStatus); */
  476: NS_IMETHODIMP 
  477: nsCocoaBrowserListener::OnStateChange(nsIWebProgress *aWebProgress, 
  478: 				      nsIRequest *aRequest, 
  479: 				      PRInt32 aStateFlags, 
  480: 				      PRUint32 aStatus)
  481: {
  482:   NSEnumerator* enumerator = [mListeners objectEnumerator];
  483:   id obj;
  484:   
  485:   if (aStateFlags & nsIWebProgressListener::STATE_IS_NETWORK) {
  486:     if (aStateFlags & nsIWebProgressListener::STATE_START) {
  487:       while ((obj = [enumerator nextObject])) {
  488: 	[obj onLoadingStarted];
  489:       }
  490:     }
  491:     else if (aStateFlags & nsIWebProgressListener::STATE_STOP) {
  492:       while ((obj = [enumerator nextObject])) {
  493: 	[obj onLoadingCompleted:(NS_SUCCEEDED(aStatus))];
  494:       }
  495:     }
  496:   }
  497: 
  498: 
  499:   return NS_OK;
  500: }
  501: 
  502: /* void onProgressChange (in nsIWebProgress aWebProgress, in nsIRequest aRequest, in long aCurSelfProgress, in long aMaxSelfProgress, in long aCurTotalProgress, in long aMaxTotalProgress); */
  503: NS_IMETHODIMP 
  504: nsCocoaBrowserListener::OnProgressChange(nsIWebProgress *aWebProgress, 
  505: 					 nsIRequest *aRequest, 
  506: 					 PRInt32 aCurSelfProgress, 
  507: 					 PRInt32 aMaxSelfProgress, 
  508: 					 PRInt32 aCurTotalProgress, 
  509: 					 PRInt32 aMaxTotalProgress)
  510: {
  511:   NSEnumerator* enumerator = [mListeners objectEnumerator];
  512:   id obj;
  513:  
  514:   while ((obj = [enumerator nextObject])) {
  515:     [obj onProgressChange:aCurTotalProgress outOf:aMaxTotalProgress];
  516:   }
  517:   
  518:   return NS_OK;
  519: }
  520: 
  521: /* void onLocationChange (in nsIWebProgress aWebProgress, in nsIRequest aRequest, in nsIURI location); */
  522: NS_IMETHODIMP 
  523: nsCocoaBrowserListener::OnLocationChange(nsIWebProgress *aWebProgress, 
  524: 					 nsIRequest *aRequest, 
  525: 					 nsIURI *location)
  526: {
  527:   nsXPIDLCString spec;
  528:   
  529:   location->GetSpec(getter_Copies(spec));
  530:   if (!spec) {
  531:     return NS_ERROR_FAILURE;
  532:   }
  533: 
  534:   const char* cstr = spec.get();
  535:   NSString* str = [NSString stringWithCString:cstr];
  536:   NSURL* url = [NSURL URLWithString:str];
  537: 
  538:   NSEnumerator* enumerator = [mListeners objectEnumerator];
  539:   id obj;
  540:  
  541:   while ((obj = [enumerator nextObject])) {
  542:     [obj onLocationChange:url];
  543:   }
  544: 
  545:   return NS_OK;
  546: }
  547: 
  548: /* void onStatusChange (in nsIWebProgress aWebProgress, in nsIRequest aRequest, in nsresult aStatus, in wstring aMessage); */
  549: NS_IMETHODIMP 
  550: nsCocoaBrowserListener::OnStatusChange(nsIWebProgress *aWebProgress, 
  551: 				       nsIRequest *aRequest, 
  552: 				       nsresult aStatus, 
  553: 				       const PRUnichar *aMessage)
  554: {
  555:   return NS_OK;
  556: }
  557: 
  558: /* void onSecurityChange (in nsIWebProgress aWebProgress, in nsIRequest aRequest, in long state); */
  559: NS_IMETHODIMP 
  560: nsCocoaBrowserListener::OnSecurityChange(nsIWebProgress *aWebProgress, 
  561: 					 nsIRequest *aRequest, 
  562: 					 PRInt32 state)
  563: {
  564:   return NS_OK;
  565: }
  566: 
  567: void 
  568: nsCocoaBrowserListener::AddListener(id <NSBrowserListener> aListener)
  569: {
  570:   [mListeners addObject:aListener];
  571: }
  572: 
  573: void 
  574: nsCocoaBrowserListener::RemoveListener(id <NSBrowserListener> aListener)
  575: {
  576:   [mListeners removeObject:aListener];
  577: }
  578: 
  579: void 
  580: nsCocoaBrowserListener::SetContainer(id <NSBrowserContainer> aContainer)
  581: {
  582:   [mContainer autorelease];
  583: 
  584:   mContainer = aContainer;
  585: 
  586:   [mContainer retain];
  587: }
  588: 
  589: // Implementation of a header sniffer class that is used when saving Web pages and images.
  590: class nsHeaderSniffer :  public nsIWebProgressListener
  591: {
  592: public:
  593:     nsHeaderSniffer(nsIWebBrowserPersist* aPersist, nsIFile* aFile, nsIURI* aURL,
  594:                     nsIDOMDocument* aDocument, nsIInputStream* aPostData, PRBool aBypassCache,
  595:                     NSView* aFilterView, NSPopUpButton* aFilterList)
  596:     {
  597:         NS_INIT_REFCNT();
  598:         mPersist = aPersist;
  599:         mTmpFile = aFile;
  600:         mURL = aURL;
  601:         mDocument = aDocument;
  602:         mPostData = aPostData;
  603:         mBypassCache = aBypassCache;
  604:         mFilterView = aFilterView;
  605:         mFilterList = aFilterList;
  606:     }
  607:                   
  608:     virtual ~nsHeaderSniffer() 
  609:     {
  610:     };
  611: 
  612:     NS_DECL_ISUPPORTS
  613:     NS_DECL_NSIWEBPROGRESSLISTENER
  614:   
  615: protected:
  616:     void PerformSave();
  617:     
  618: private:
  619:     nsIWebBrowserPersist* mPersist; // Weak. It owns us as a listener.
  620:     nsCOMPtr<nsIFile> mTmpFile;
  621:     nsCOMPtr<nsIURI> mURL;
  622:     nsCOMPtr<nsIDOMDocument> mDocument;
  623:     nsCOMPtr<nsIInputStream> mPostData;
  624:     PRBool mBypassCache;
  625:     nsCString mContentType;
  626:     nsCString mContentDisposition;
  627:     NSView* mFilterView;
  628:     NSPopUpButton* mFilterList;
  629: };
  630: 
  631: NS_IMPL_ISUPPORTS1(nsHeaderSniffer, nsIWebProgressListener)
  632: 
  633: // Implementation of nsIWebProgressListener
  634: /* void onStateChange (in nsIWebProgress aWebProgress, in nsIRequest aRequest, in long aStateFlags, in unsigned long aStatus); */
  635: NS_IMETHODIMP 
  636: nsHeaderSniffer::OnStateChange(nsIWebProgress *aWebProgress, 
  637: 				      nsIRequest *aRequest, 
  638: 				      PRInt32 aStateFlags, 
  639: 				      PRUint32 aStatus)
  640: {
  641:     if (aStateFlags & nsIWebProgressListener::STATE_START) {
  642:         nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
  643:         nsXPIDLCString contentType;
  644:         channel->GetContentType(getter_Copies(contentType));
  645:         mContentType = contentType;
  646:         
  647:         // Get the content-disposition if we're an HTTP channel.
  648:         nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
  649:         if (httpChannel) {
  650:             nsXPIDLCString disp;
  651:             httpChannel->GetResponseHeader("content-disposition", getter_Copies(disp));
  652:             mContentDisposition = disp;
  653:         }
  654:         
  655:         mPersist->CancelSave();
  656:         PRBool exists;
  657:         mTmpFile->Exists(&exists);
  658:         if (exists)
  659:             mTmpFile->Remove(PR_FALSE);
  660:         PerformSave();
  661:     }
  662:     return NS_OK;
  663: }
  664: 
  665: void nsHeaderSniffer::PerformSave()
  666: {
  667:     // Are we an HTML document? If so, we will want to append an accessory view to
  668:     // the save dialog to provide the user with the option of doing a complete
  669:     // save vs. a single file save.
  670:     PRBool isHTML = (mDocument && mContentType.Equals("text/html") ||
  671:                      mContentType.Equals("text/xml") ||
  672:                      mContentType.Equals("application/xhtml+xml"));
  673:     
  674:     // Next find out the directory that we should start in.
  675:     nsCOMPtr<nsIPrefService> prefs(do_GetService("@mozilla.org/preferences-service;1"));
  676:     if (!prefs)
  677:         return;
  678:     nsCOMPtr<nsIPrefBranch> dirBranch;
  679:     prefs->GetBranch("browser.download.", getter_AddRefs(dirBranch));
  680:     PRInt32 filterIndex = 0;
  681:     if (dirBranch) {
  682:         nsresult rv = dirBranch->GetIntPref("save_converter_index", &filterIndex);
  683:         if (NS_FAILED(rv))
  684:             filterIndex = 0;
  685:     }
  686:     if (mFilterList)
  687:         [mFilterList selectItemAtIndex: filterIndex];
  688:         
  689:     // We need to figure out what file name to use.
  690:     nsCAutoString defaultFileName;
  691:     
  692:     if (!mContentDisposition.IsEmpty()) {
  693:         // (1) Use the HTTP header suggestion.
  694:         PRInt32 index = mContentDisposition.Find("filename=");
  695:         if (index >= 0) {
  696:             // Take the substring following the prefix.
  697:             index += 9;
  698:             nsCAutoString filename;
  699:             mContentDisposition.Right(filename, mContentDisposition.Length() - index);
  700:             defaultFileName = filename;
  701:         }
  702:     }
  703:     
  704:     if (defaultFileName.IsEmpty()) {
  705:         nsCOMPtr<nsIURL> url(do_QueryInterface(mURL));
  706:         if (url) {
  707:             nsXPIDLCString fileName;
  708:             url->GetFileName(getter_Copies(fileName));
  709:             defaultFileName = fileName; // (2) For file URLs, use the file name.
  710:         }
  711:     }
  712:     
  713:     if (defaultFileName.IsEmpty() && mDocument && isHTML) {
  714:         nsCOMPtr<nsIDOMHTMLDocument> htmlDoc(do_QueryInterface(mDocument));
  715:         nsAutoString title;
  716:         if (htmlDoc)
  717:             htmlDoc->GetTitle(title); // (3) Use the title of the document.
  718:         defaultFileName.AssignWithConversion(title);
  719:     }
  720:     
  721:     if (defaultFileName.IsEmpty()) {
  722:         // (4) Use the caller provided name. XXXdwh
  723:     }
  724: 
  725:     if (defaultFileName.IsEmpty()) {
  726:         // (5) Use the host.
  727:         nsXPIDLCString host;
  728:         mURL->GetHost(getter_Copies(host));
  729:         defaultFileName = host;
  730:     }
  731:     
  732:     // One last case to handle about:blank and other fruity untitled pages.
  733:     if (defaultFileName.IsEmpty())
  734:         defaultFileName = "untitled";
  735:         
  736:     // Validate the file name to ensure legality.
  737:     for (PRUint32 i = 0; i < defaultFileName.Length(); i++)
  738:         if (defaultFileName[i] == ':' || defaultFileName[i] == '/')
  739:             defaultFileName.SetCharAt(i, ' ');
  740:             
  741:     // Make sure the appropriate extension is appended to the suggested file name.
  742:     nsCOMPtr<nsIURI> fileURI(do_CreateInstance("@mozilla.org/network/standard-url;1"));
  743:     nsCOMPtr<nsIURL> fileURL(do_QueryInterface(fileURI));
  744:     if (!fileURL)
  745:         return;
  746:     fileURL->SetFilePath(defaultFileName.get());
  747:     
  748:     nsXPIDLCString fileEx;
  749:     fileURL->GetFileExtension(getter_Copies(fileEx));
  750:     nsDependentCString fileExtension(fileEx.get());
  751:     
  752:     PRBool setExtension = PR_FALSE;
  753:     if (mContentType.Equals("text/html")) {
  754:         if (fileExtension.IsEmpty() || (!fileExtension.Equals("htm") && !fileExtension.Equals("html"))) {
  755:             defaultFileName += ".html";
  756:             setExtension = PR_TRUE;
  757:         }
  758:     }
  759:     
  760:     if (!setExtension && fileExtension.IsEmpty()) {
  761:         nsCOMPtr<nsIMIMEService> mimeService(do_GetService("@mozilla.org/mime;1"));
  762:         if (!mimeService)
  763:             return;
  764:         nsCOMPtr<nsIMIMEInfo> mimeInfo;
  765:         mimeService->GetFromMIMEType(mContentType.get(), getter_AddRefs(mimeInfo));
  766:         
  767:         PRUint32 extCount;
  768:         char** extList;
  769:         mimeInfo->GetFileExtensions(&extCount, &extList);
  770:         
  771:         if (extCount > 0) {
  772:             defaultFileName += ".";
  773:             defaultFileName += extList[0];
  774:         }
  775:     }
  776:     
  777:     // Now it's time to pose the save dialog.
  778:     NSSavePanel* savePanel = [NSSavePanel savePanel];
  779:     NSString* file = nil;
  780:     if (!defaultFileName.IsEmpty())
  781:         file = [[NSString alloc] initWithCString: defaultFileName.get()];
  782:         
  783:     if (isHTML)
  784:         [savePanel setAccessoryView: mFilterView];
  785:         
  786:     if ([savePanel runModalForDirectory: nil file: file] == NSFileHandlingPanelCancelButton)
  787:         return;
  788:        
  789:     // Release the file string.
  790:     [file release];
  791:     
  792:     // Update the filter index.
  793:     if (isHTML && mFilterList) {
  794:         filterIndex = [mFilterList indexOfSelectedItem];
  795:         dirBranch->SetIntPref("save_converter_index", filterIndex);
  796:     }
  797:     
  798:     // Convert the content type to text/plain if it was selected in the filter.
  799:     if (isHTML && filterIndex == 2)
  800:         mContentType = "text/plain";
  801:     
  802:     nsCOMPtr<nsISupports> sourceData;
  803:     if (isHTML && filterIndex != 1)
  804:         sourceData = do_QueryInterface(mDocument);
  805:     else
  806:         sourceData = do_QueryInterface(mURL);
  807:         
  808:     nsCOMPtr<nsIWebBrowserPersist> webPersist(do_CreateInstance(persistContractID));
  809:     ProgressDlgController* progressDialog = [[ProgressDlgController alloc] initWithWindowNibName: @"ProgressDialog"];
  810:     [progressDialog setWebPersist: webPersist 
  811:                     source: sourceData.get()
  812:                     destination: [savePanel filename]
  813:                     contentType: mContentType.get()
  814:                     postData: mPostData
  815:                     bypassCache: mBypassCache];
  816:                     
  817:     [progressDialog showWindow: progressDialog];
  818: }
  819: 
  820: /* void onProgressChange (in nsIWebProgress aWebProgress, in nsIRequest aRequest, in long aCurSelfProgress, in long aMaxSelfProgress, in long aCurTotalProgress, in long aMaxTotalProgress); */
  821: NS_IMETHODIMP 
  822: nsHeaderSniffer::OnProgressChange(nsIWebProgress *aWebProgress, 
  823: 					 nsIRequest *aRequest, 
  824: 					 PRInt32 aCurSelfProgress, 
  825: 					 PRInt32 aMaxSelfProgress, 
  826: 					 PRInt32 aCurTotalProgress, 
  827: 					 PRInt32 aMaxTotalProgress)
  828: {
  829:   return NS_OK;
  830: }
  831: 
  832: /* void onLocationChange (in nsIWebProgress aWebProgress, in nsIRequest aRequest, in nsIURI location); */
  833: NS_IMETHODIMP 
  834: nsHeaderSniffer::OnLocationChange(nsIWebProgress *aWebProgress, 
  835: 					 nsIRequest *aRequest, 
  836: 					 nsIURI *location)
  837: {
  838:   return NS_OK;
  839: }
  840: 
  841: /* void onStatusChange (in nsIWebProgress aWebProgress, in nsIRequest aRequest, in nsresult aStatus, in wstring aMessage); */
  842: NS_IMETHODIMP 
  843: nsHeaderSniffer::OnStatusChange(nsIWebProgress *aWebProgress, 
  844: 				       nsIRequest *aRequest, 
  845: 				       nsresult aStatus, 
  846: 				       const PRUnichar *aMessage)
  847: {
  848:   return NS_OK;
  849: }
  850: 
  851: /* void onSecurityChange (in nsIWebProgress aWebProgress, in nsIRequest aRequest, in long state); */
  852: NS_IMETHODIMP 
  853: nsHeaderSniffer::OnSecurityChange(nsIWebProgress *aWebProgress, 
  854: 					              nsIRequest *aRequest, 
  855:                                   PRInt32 state)
  856: {
  857:   return NS_OK;
  858: }
  859: 
  860: 
  861: @implementation NSBrowserView
  862: 
  863: - (id)initWithFrame:(NSRect)frame
  864: {
  865:   [super initWithFrame:frame];
  866: 
  867:   nsresult rv = nsCocoaBrowserService::InitEmbedding();
  868:   if (NS_FAILED(rv)) {
  869:     // XXX need to throw
  870:   }
  871:   
  872:   _listener = new nsCocoaBrowserListener(self);
  873:   NS_ADDREF(_listener);
  874: 
  875:   // Create the web browser instance
  876:   nsCOMPtr<nsIWebBrowser> browser = do_CreateInstance(NS_WEBBROWSER_CONTRACTID, &rv);
  877:   if (NS_FAILED(rv)) {
  878:     // XXX need to throw
  879:   }
  880: 
  881:   _webBrowser = browser;
  882:   NS_ADDREF(_webBrowser);
  883: 
  884:   // Set the container nsIWebBrowserChrome
  885:   _webBrowser->SetContainerWindow(NS_STATIC_CAST(nsIWebBrowserChrome *, 
  886: 						 _listener));
  887:   
  888:   // Register as a listener for web progress
  889:   nsCOMPtr<nsIWeakReference> weak = do_GetWeakReference(NS_STATIC_CAST(nsIWebProgressListener*, _listener));
  890:   _webBrowser->AddWebBrowserListener(weak, NS_GET_IID(nsIWebProgressListener));
  891: 
  892:   // Hook up the widget hierarchy with us as the parent
  893:   nsCOMPtr<nsIBaseWindow> baseWin = do_QueryInterface(_webBrowser);
  894:   baseWin->InitWindow((NSView*)self, nsnull, 0, 0, 
  895: 		      frame.size.width, frame.size.height);
  896:   baseWin->Create();
  897: 
  898:   return self;
  899: }
  900: 
  901: - (void)destroyWebBrowser
  902: {
  903:   nsCOMPtr<nsIBaseWindow> baseWin = do_QueryInterface(_webBrowser);
  904:   baseWin->Destroy();
  905: }
  906: 
  907: - (void)dealloc 
  908: {
  909:   NSLog(@"NSBrowserView got dealloced.");
  910:   
  911:   NS_RELEASE(_listener);
  912:   NS_IF_RELEASE(_webBrowser);
  913:   nsCocoaBrowserService::TermEmbedding();
  914: 
  915:   [super dealloc];
  916: }
  917: 
  918: - (void)setFrame:(NSRect)frameRect 
  919: {
  920:   [super setFrame:frameRect];
  921:   if (_webBrowser) {
  922:     nsCOMPtr<nsIBaseWindow> window = do_QueryInterface(_webBrowser);
  923:     window->SetSize((PRInt32)frameRect.size.width, 
  924: 		    (PRInt32)frameRect.size.height,
  925: 		    PR_TRUE);
  926:   }
  927: }
  928: 
  929: - (void)addListener:(id <NSBrowserListener>)listener
  930: {
  931:   _listener->AddListener(listener);
  932: }
  933: 
  934: - (void)removeListener:(id <NSBrowserListener>)listener
  935: {
  936:   _listener->RemoveListener(listener);
  937: }
  938: 
  939: - (void)setContainer:(id <NSBrowserContainer>)container
  940: {
  941:   _listener->SetContainer(container);
  942: }
  943: 
  944: - (nsIDOMWindow*)getContentWindow
  945: {
  946:   nsIDOMWindow* window;
  947: 
  948:   _webBrowser->GetContentDOMWindow(&window);
  949: 
  950:   return window;
  951: }
  952: 
  953: - (void)loadURI:(NSURL *)url flags:(unsigned int)flags
  954: {
  955:   nsCOMPtr<nsIWebNavigation> nav = do_QueryInterface(_webBrowser);
  956:   
  957:   NSString* spec = [url absoluteString];
  958:   int length = [spec length];
  959:   PRUnichar* specStr = nsMemory::Alloc((length+1) * sizeof(PRUnichar));
  960:   [spec getCharacters:specStr];
  961:   specStr[length] = PRUnichar(0);
  962:   
  963: 
  964:   PRUint32 navFlags = nsIWebNavigation::LOAD_FLAGS_NONE;
  965:   if (flags & NSLoadFlagsDontPutInHistory) {
  966:     navFlags |= nsIWebNavigation::LOAD_FLAGS_BYPASS_HISTORY;
  967:   }
  968:   if (flags & NSLoadFlagsReplaceHistoryEntry) {
  969:     navFlags |= nsIWebNavigation::LOAD_FLAGS_REPLACE_HISTORY;
  970:   }
  971:   if (flags & NSLoadFlagsBypassCacheAndProxy) {
  972:     navFlags |= nsIWebNavigation::LOAD_FLAGS_BYPASS_CACHE | 
  973:                 nsIWebNavigation::LOAD_FLAGS_BYPASS_PROXY;
  974:   }
  975: 
  976:   nsresult rv = nav->LoadURI(specStr, navFlags, nsnull, nsnull, nsnull);
  977:   if (NS_FAILED(rv)) {
  978:     // XXX need to throw
  979:   }
  980: 
  981:   nsMemory::Free(specStr);
  982: }
  983: 
  984: - (void)reload:(unsigned int)flags
  985: {
  986:   nsCOMPtr<nsIWebNavigation> nav = do_QueryInterface(_webBrowser);
  987: 
  988:   PRUint32 navFlags = nsIWebNavigation::LOAD_FLAGS_NONE;
  989:   if (flags & NSLoadFlagsBypassCacheAndProxy) {
  990:     navFlags |= nsIWebNavigation::LOAD_FLAGS_BYPASS_CACHE | 
  991:                 nsIWebNavigation::LOAD_FLAGS_BYPASS_PROXY;
  992:   }
  993: 
  994:   nsresult rv = nav->Reload(navFlags);
  995:   if (NS_FAILED(rv)) {
  996:     // XXX need to throw
  997:   }  
  998: }
  999: 
 1000: - (BOOL)canGoBack
 1001: {
 1002:   nsCOMPtr<nsIWebNavigation> nav = do_QueryInterface(_webBrowser);
 1003: 
 1004:   PRBool can;
 1005:   nav->GetCanGoBack(&can);
 1006: 
 1007:   return can ? YES : NO;
 1008: }
 1009: 
 1010: - (BOOL)canGoForward
 1011: {
 1012:   nsCOMPtr<nsIWebNavigation> nav = do_QueryInterface(_webBrowser);
 1013: 
 1014:   PRBool can;
 1015:   nav->GetCanGoForward(&can);
 1016: 
 1017:   return can ? YES : NO;
 1018: }
 1019: 
 1020: - (void)goBack
 1021: {
 1022:   nsCOMPtr<nsIWebNavigation> nav = do_QueryInterface(_webBrowser);
 1023: 
 1024:   nsresult rv = nav->GoBack();
 1025:   if (NS_FAILED(rv)) {
 1026:     // XXX need to throw
 1027:   }  
 1028: }
 1029: 
 1030: - (void)goForward
 1031: {
 1032:   nsCOMPtr<nsIWebNavigation> nav = do_QueryInterface(_webBrowser);
 1033: 
 1034:   nsresult rv = nav->GoForward();
 1035:   if (NS_FAILED(rv)) {
 1036:     // XXX need to throw
 1037:   }  
 1038: }
 1039: 
 1040: - (void)gotoIndex:(int)index
 1041: {
 1042:   nsCOMPtr<nsIWebNavigation> nav = do_QueryInterface(_webBrowser);
 1043: 
 1044:   nsresult rv = nav->GotoIndex(index);
 1045:   if (NS_FAILED(rv)) {
 1046:     // XXX need to throw
 1047:   }    
 1048: }
 1049: 
 1050: - (void)stop:(unsigned int)flags
 1051: {
 1052:   nsCOMPtr<nsIWebNavigation> nav = do_QueryInterface(_webBrowser);
 1053: 
 1054:   nsresult rv = nav->Stop(flags);
 1055:   if (NS_FAILED(rv)) {
 1056:     // XXX need to throw
 1057:   }    
 1058: }
 1059: 
 1060: - (NSURL*)getCurrentURI
 1061: {
 1062:   nsCOMPtr<nsIURI> uri;
 1063:   nsCOMPtr<nsIWebNavigation> nav = do_QueryInterface(_webBrowser);
 1064: 
 1065:   nav->GetCurrentURI(getter_AddRefs(uri));
 1066:   if (!uri) {
 1067:     return nsnull;
 1068:   }
 1069: 
 1070:   nsXPIDLCString spec;
 1071:   uri->GetSpec(getter_Copies(spec));
 1072:   
 1073:   const char* cstr = spec.get();
 1074:   NSString* str = [NSString stringWithCString:cstr];
 1075:   NSURL* url = [NSURL URLWithString:str];
 1076:   
 1077:   return url;
 1078: }
 1079: 
 1080: - (nsIWebBrowser*)getWebBrowser
 1081: {
 1082:   NS_IF_ADDREF(_webBrowser);
 1083:   return _webBrowser;
 1084: }
 1085: 
 1086: - (void)setWebBrowser:(nsIWebBrowser*)browser
 1087: {
 1088:   _webBrowser = browser;
 1089: 
 1090:   if (_webBrowser) {
 1091:     // Set the container nsIWebBrowserChrome
 1092:     _webBrowser->SetContainerWindow(NS_STATIC_CAST(nsIWebBrowserChrome *, 
 1093: 						   _listener));
 1094: 
 1095:     NSRect frame = [self frame];
 1096:  
 1097:     // Hook up the widget hierarchy with us as the parent
 1098:     nsCOMPtr<nsIBaseWindow> baseWin = do_QueryInterface(_webBrowser);
 1099:     baseWin->InitWindow((NSView*)self, nsnull, 0, 0, 
 1100: 			frame.size.width, frame.size.height);
 1101:     baseWin->Create();
 1102:   }
 1103: 
 1104: }
 1105: 
 1106: -(void) saveInternal: (nsIURI*)aURI
 1107:         withDocument: (nsIDOMDocument*)aDocument
 1108:         bypassCache: (BOOL)aBypassCache
 1109:         filterView: (NSView*)aFilterView
 1110:         filterList: (NSPopUpButton*)aFilterList
 1111: {
 1112:     // Create our web browser persist object.  This is the object that knows
 1113:     // how to actually perform the saving of the page (and of the images
 1114:     // on the page).
 1115:     nsCOMPtr<nsIWebBrowserPersist> webPersist(do_CreateInstance(persistContractID));
 1116:     if (!webPersist)
 1117:         return;
 1118:     
 1119:     // Make a temporary file object that we can save to.
 1120:     nsCOMPtr<nsIProperties> dirService(do_GetService(dirServiceContractID));
 1121:     if (!dirService)
 1122:         return;
 1123:     nsCOMPtr<nsIFile> tmpFile;
 1124:     dirService->Get("TmpD", NS_GET_IID(nsIFile), getter_AddRefs(tmpFile));
 1125:     static short unsigned int tmpRandom = 0;
 1126:     nsCAutoString tmpNo; tmpNo.AssignWithConversion(tmpRandom++);
 1127:     nsCAutoString saveFile("-sav");
 1128:     saveFile += tmpNo;
 1129:     saveFile += "tmp";
 1130:     tmpFile->Append(saveFile.get()); 
 1131:     
 1132:     // Get the post data if we're an HTML doc.
 1133:     nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(_webBrowser));
 1134:     nsCOMPtr<nsISHistory> sessionHistory;
 1135:     webNav->GetSessionHistory(getter_AddRefs(sessionHistory));
 1136:     nsCOMPtr<nsIHistoryEntry> entry;
 1137:     PRInt32 sindex;
 1138:     sessionHistory->GetIndex(&sindex);
 1139:     sessionHistory->GetEntryAtIndex(sindex, PR_FALSE, getter_AddRefs(entry));
 1140:     nsCOMPtr<nsISHEntry> shEntry(do_QueryInterface(entry));
 1141:     nsCOMPtr<nsIInputStream> postData;
 1142:     if (shEntry)
 1143:         shEntry->GetPostData(getter_AddRefs(postData));
 1144:     
 1145:     nsHeaderSniffer* sniffer = new nsHeaderSniffer(webPersist, tmpFile, aURI, 
 1146:                                                    aDocument, postData, aBypassCache,
 1147:                                                    aFilterView, aFilterList);
 1148:     if (!sniffer)
 1149:         return;
 1150:     webPersist->SetProgressListener(sniffer);
 1151:     webPersist->SaveURI(aURI, nsnull, tmpFile);
 1152: }
 1153: 
 1154: -(void)saveDocument: (NSView*)aFilterView filterList: (NSPopUpButton*)aFilterList
 1155: {
 1156:     nsCOMPtr<nsIWebBrowserFocus> wbf(do_QueryInterface(_webBrowser));
 1157:     nsCOMPtr<nsIDOMWindow> domWindow;
 1158:     wbf->GetFocusedWindow(getter_AddRefs(domWindow));
 1159:     if (!domWindow)
 1160:         _webBrowser->GetContentDOMWindow(getter_AddRefs(domWindow));
 1161:     if (!domWindow)
 1162:         return;
 1163:     
 1164:     nsCOMPtr<nsIDOMDocument> domDocument;
 1165:     domWindow->GetDocument(getter_AddRefs(domDocument));
 1166:     if (!domDocument)
 1167:         return;
 1168:     nsCOMPtr<nsIDocument> doc(do_QueryInterface(domDocument));
 1169:     if (!doc)
 1170:         return;
 1171:     nsCOMPtr<nsIURI> url;
 1172:     doc->GetDocumentURL(getter_AddRefs(url));
 1173:      
 1174:     [self saveInternal: url.get()
 1175:           withDocument: domDocument
 1176:           bypassCache: NO
 1177:           filterView: aFilterView
 1178:           filterList: aFilterList];
 1179: }
 1180: 
 1181: @end
 1182: 

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>