Back to index

lightning-sunbird  0.9+nobinonly
nsNativeAppSupportOS2.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
00002  * ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is Mozilla Communicator client code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Bill Law       law@netscape.com
00024  *   IBM Corp.
00025  *   Rich Walsh     dragtext@e-vertise.com
00026  *
00027  * Alternatively, the contents of this file may be used under the terms of
00028  * either of the GNU General Public License Version 2 or later (the "GPL"),
00029  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00030  * in which case the provisions of the GPL or the LGPL are applicable instead
00031  * of those above. If you wish to allow use of your version of this file only
00032  * under the terms of either the GPL or the LGPL, and not to allow others to
00033  * use your version of this file under the terms of the MPL, indicate your
00034  * decision by deleting the provisions above and replace them with the notice
00035  * and other provisions required by the GPL or the LGPL. If you do not delete
00036  * the provisions above, a recipient may use your version of this file under
00037  * the terms of any one of the MPL, the GPL or the LGPL.
00038  *
00039  * ***** END LICENSE BLOCK ***** */
00040 
00041 #define INCL_PM
00042 #define INCL_GPI
00043 #define INCL_DOS
00044 #define INCL_DOSERRORS
00045 #include <os2.h>
00046 
00047 #include "nsStringSupport.h"
00048 
00049 #include "nsNativeAppSupportBase.h"
00050 #include "nsNativeAppSupportOS2.h"
00051 #include "nsICmdLineService.h"
00052 #include "nsCOMPtr.h"
00053 #include "nsIComponentManager.h"
00054 #include "nsComponentManagerUtils.h"
00055 #include "nsIServiceManager.h"
00056 #include "nsServiceManagerUtils.h"
00057 #include "nsICmdLineHandler.h"
00058 #include "nsIDOMWindow.h"
00059 #include "nsXPCOM.h"
00060 #include "nsISupportsPrimitives.h"
00061 #include "nsISupportsArray.h"
00062 #include "nsIWindowWatcher.h"
00063 #include "nsIDOMWindowInternal.h"
00064 #include "nsIDOMChromeWindow.h"
00065 #include "nsIBrowserDOMWindow.h"
00066 #include "nsIScriptGlobalObject.h"
00067 #include "nsIDocShell.h"         
00068 #include "nsIBaseWindow.h"       
00069 #include "nsIWidget.h"
00070 #include "nsIAppShellService.h"
00071 #include "nsIAppStartup.h"
00072 #include "nsIProfileInternal.h"
00073 #include "nsIXULWindow.h"
00074 #include "nsIInterfaceRequestor.h"
00075 #include "nsIInterfaceRequestorUtils.h"
00076 #include "nsIPrefBranch.h"
00077 #include "nsIPrefService.h"
00078 #include "nsIPromptService.h"
00079 #include "nsNetCID.h"
00080 #include "nsIIOService.h"
00081 #include "nsIURI.h"
00082 #include "nsIObserverService.h"
00083 #include "nsXPFEComponentsCID.h"
00084 
00085 // These are needed to load a URL in a browser window.
00086 #include "nsIDOMLocation.h"
00087 #include "nsIJSContextStack.h"
00088 #include "nsIWindowMediator.h"
00089 
00090 #include "nsPaletteOS2.h"
00091 
00092 #include <sys/stat.h>
00093 #include <stdlib.h>
00094 #include <ctype.h>
00095 
00096 #include "prprf.h"
00097 
00098 // getting from nsAppRunner.  Use to help track down arguments.
00099 extern char **__argv;
00100 extern int   *__pargc;
00101 
00102 /* trying to keep this like Window's COPYDATASTRUCT, but a compiler error is
00103  * forcing me to add chBuff so that we can append the data to the end of the
00104  * structure
00105  */
00106 typedef struct _COPYDATASTRUCT
00107 {
00108    ULONG   dwData;
00109    ULONG   cbData;
00110    PVOID   lpData;
00111    CHAR    chBuff;
00112 }COPYDATASTRUCT, *PCOPYDATASTRUCT;
00113 #define WM_COPYDATA             (WM_USER + 60)
00114 
00115 char szCommandLine[2*CCHMAXPATH];
00116 
00117 static HWND hwndForDOMWindow( nsISupports * );
00118 
00119 static
00120 nsresult
00121 GetMostRecentWindow(const PRUnichar* aType, nsIDOMWindowInternal** aWindow) {
00122     nsresult rv;
00123     nsCOMPtr<nsIWindowMediator> med( do_GetService( NS_WINDOWMEDIATOR_CONTRACTID, &rv ) );
00124     if ( NS_FAILED( rv ) )
00125         return rv;
00126 
00127     if ( med )
00128         return med->GetMostRecentWindow( aType, aWindow );
00129 
00130     return NS_ERROR_FAILURE;
00131 }
00132 
00133 static
00134 void
00135 activateWindow( nsIDOMWindowInternal *win ) {
00136     // Try to get native window handle.
00137     HWND hwnd = hwndForDOMWindow( win );
00138     if ( hwnd ) {
00139 
00140         /* if we are looking at a client window, then we really probably want
00141          * the frame so that we can manipulate THAT
00142          */
00143         LONG id = WinQueryWindowUShort( hwnd, QWS_ID );
00144         if( id == FID_CLIENT )
00145         {
00146            hwnd = WinQueryWindow( hwnd, QW_PARENT );
00147         }
00148 
00149         // Restore the window in case it is minimized.
00150         // Use the OS call, if possible.
00151         WinSetWindowPos( hwnd, 0L, 0L, 0L, 0L, 0L, 
00152                          SWP_SHOW | SWP_RESTORE | SWP_ACTIVATE );
00153     } else {
00154         // Use internal method.
00155         win->Focus();
00156     }
00157 }
00158 
00159 
00160 
00161 // Define this macro to 1 to get DDE debugging output.
00162 #define MOZ_DEBUG_DDE 0
00163 
00164 #ifdef DEBUG_law
00165 #undef MOZ_DEBUG_DDE
00166 #define MOZ_DEBUG_DDE 1
00167 #endif
00168 
00169 class nsSplashScreenOS2 : public nsISplashScreen {
00170 public:
00171     nsSplashScreenOS2();
00172     ~nsSplashScreenOS2();
00173 
00174     NS_IMETHOD Show();
00175     NS_IMETHOD Hide();
00176 
00177     // nsISupports methods
00178     NS_IMETHOD_(nsrefcnt) AddRef() {
00179         mRefCnt++;
00180         return mRefCnt;
00181     }
00182     NS_IMETHOD_(nsrefcnt) Release() {
00183         --mRefCnt;
00184         if ( !mRefCnt ) {
00185             delete this;
00186             return 0;
00187         }
00188         return mRefCnt;
00189     }
00190     NS_IMETHOD QueryInterface( const nsIID &iid, void**p ) {
00191         nsresult rv = NS_OK;
00192         if ( p ) {
00193             *p = 0;
00194             if ( iid.Equals( NS_GET_IID( nsISplashScreen ) ) ) {
00195                 nsISplashScreen *result = this;
00196                 *p = result;
00197                 NS_ADDREF( result );
00198             } else if ( iid.Equals( NS_GET_IID( nsISupports ) ) ) {
00199                 nsISupports *result = NS_STATIC_CAST( nsISupports*, this );
00200                 *p = result;
00201                 NS_ADDREF( result );
00202             } else {
00203                 rv = NS_NOINTERFACE;
00204             }
00205         } else {
00206             rv = NS_ERROR_NULL_POINTER;
00207         }
00208         return rv;
00209     }
00210 
00211     void SetDialog( HWND dlg );
00212     void LoadBitmap();
00213     static nsSplashScreenOS2* GetPointer( HWND dlg );
00214 
00215     HWND mDlg;
00216     HBITMAP mBitmap;
00217     nsrefcnt mRefCnt;
00218     HDC hdcMemory;
00219     HPS hpsMemory;
00220     LONG mBitmapCX;
00221     LONG mBitmapCY;
00222 }; // class nsSplashScreenOS2
00223 
00224 MRESULT EXPENTRY DialogProc( HWND dlg, ULONG msg, MPARAM mp1, MPARAM mp2 );
00225 void ThreadProc (void *splashScreen);
00226 
00227 // Simple Win32 mutex wrapper.
00228 struct Mutex {
00229     Mutex( const char *name )
00230         : mName( name ),
00231           mHandle( 0 ),
00232           mState( 0xFFFF ) {
00233         /* OS/2 named semaphores must begin with "\\SEM32\\" to be valid */
00234         mName.Insert("\\SEM32\\", 0);
00235         APIRET rc = DosCreateMutexSem(mName.get(), &mHandle, 0, FALSE);
00236         if (rc != NO_ERROR) {
00237 #if MOZ_DEBUG_DDE
00238             printf( "CreateMutex error = 0x%08X\n", (int)rc );
00239 #endif
00240         }
00241     }
00242     ~Mutex() {
00243         if ( mHandle ) {
00244             // Make sure we release it if we own it.
00245             Unlock();
00246 
00247 
00248             APIRET rc = DosCloseMutexSem(mHandle);
00249             if (rc != NO_ERROR) {
00250 #if MOZ_DEBUG_DDE
00251                 printf( "CloseHandle error = 0x%08X\n", (int)rc );
00252 #endif
00253             }
00254         }
00255     }
00256     BOOL Lock( DWORD timeout ) {
00257         if ( mHandle ) {
00258 #if MOZ_DEBUG_DDE
00259             printf( "Waiting (%d msec) for DDE mutex...\n", (int)timeout );
00260 #endif
00261             mState = DosRequestMutexSem( mHandle, timeout );
00262 #if MOZ_DEBUG_DDE
00263             printf( "...wait complete, result = 0x%08X\n", (int)mState );
00264 #endif
00265             return (mState == NO_ERROR);
00266         } else {
00267             return FALSE;
00268         }
00269     }
00270     void Unlock() {
00271         if ( mHandle && mState == NO_ERROR ) {
00272 #if MOZ_DEBUG_DDE
00273             printf( "Releasing DDE mutex\n" );
00274 #endif
00275             DosReleaseMutexSem( mHandle );
00276             mState = 0xFFFF;
00277         }
00278     }
00279 private:
00280     nsCString mName;
00281     HMTX      mHandle;
00282     DWORD     mState;
00283 };
00284 
00285 /* DDE Notes
00286  *
00287  * This section describes the Win32 DDE service implementation for
00288  * SeaMonkey.  DDE is used on Win32 platforms to communicate between
00289  * separate instances of seamonkey.exe (or other Mozilla-based
00290  * executables), or, between the Win32 desktop shell and SeaMonkey.
00291  *
00292  * The first instance of SeaMonkey will become the "server" and
00293  * subsequent executables (and the shell) will use DDE to send
00294  * requests to that process.  The requests are DDE "execute" requests
00295  * that pass the command line arguments.
00296  *
00297  * SeaMonkey registers the DDE application "Mozilla" and currently
00298  * supports only the "WWW_OpenURL" topic.  This should be reasonably
00299  * compatible with applications that interfaced with Netscape
00300  * Communicator (and its predecessors?).  Note that even that topic
00301  * may not be supported in a compatible fashion as the command-line
00302  * options for SeaMonkey are different than for Communiator.
00303  *
00304  * It is imperative that at most one instance of SeaMonkey execute in
00305  * "server mode" at any one time.  The "native app support" in SeaMonkey
00306  * on Win32 ensures that only the server process performs XPCOM
00307  * initialization (that is not required for subsequent client processes
00308  * to communicate with the server process).
00309  *
00310  * To guarantee that only one server starts up, a Win32 "mutex" is used
00311  * to ensure only one process executes the server-detection code.  That
00312  * code consists of initializing DDE and doing a DdeConnect to SeaMonkey's
00313  * application/topic.  If that connection succeeds, then a server process
00314  * must be running already.
00315  * 
00316  * Otherwise, no server has started.  In that case, the current process
00317  * calls DdeNameService to register that application/topic.  Only at that
00318  * point does the mutex get released.
00319  *
00320  * There are a couple of subtleties that one should be aware of:
00321  * 
00322  * 1. It is imperative that DdeInitialize be called only after the mutex
00323  *    lock has been obtained.  The reason is that at shutdown, DDE
00324  *    notifications go out to all initialized DDE processes.  Thus, if
00325  *    the mutex is owned by a terminating intance of SeaMonkey, then
00326  *    calling DdeInitialize and then WaitForSingleObject will cause the
00327  *    DdeUninitialize from the terminating process to "hang" until the
00328  *    process waiting for the mutex times out (and can then service the
00329  *    notification that the DDE server is terminating).  So, don't mess
00330  *    with the sequence of things in the startup/shutdown logic.
00331  *
00332  * 2. All mutex requests are made with a reasonably long timeout value and
00333  *    are designed to "fail safe" (i.e., a timeout is treated as failure).
00334  * 
00335  * 3. An attempt has been made to minimize the degree to which the main
00336  *    SeaMonkey application logic needs to be aware of the DDE mechanisms
00337  *    implemented herein.  As a result, this module surfaces a very
00338  *    large-grained interface, consisting of simple start/stop methods.
00339  *    As a consequence, details of certain scenarios can be "lost."
00340  *    Particularly, incoming DDE requests can arrive after this module
00341  *    initiates the DDE server, but before SeaMonkey is initialized to the
00342  *    point where those requests can be serviced (e.g., open a browser
00343  *    window to a particular URL).  Since the client process sends the
00344  *    request early on, it may not be prepared to respond to that error.
00345  *    Thus, such situations may fail silently.  The design goal is that
00346  *    they fail harmlessly.  Refinements on this point will be made as
00347  *    details emerge (and time permits).
00348  */
00349 
00350 /* Update 2001 March
00351  *
00352  * A significant DDE bug in Windows is causing SeaMonkey to get wedged at
00353  * startup.  This is detailed in Bugzilla bug 53952
00354  * (http://bugzilla.mozilla.org/show_bug.cgi?id=53952).
00355  *
00356  * To resolve this, we are using a new strategy:
00357  *   o Use a "message window" to detect that SeaMonkey is already running and
00358  *     to pass requests from a second instance back to the first;
00359  *   o Run only as a "DDE server" (not as DDE client); this avoids the
00360  *     problematic call to DDEConnect().
00361  *
00362  * We still use the mutex semaphore to protect the code that detects
00363  * whether SeaMonkey is already running.
00364  */
00365 
00366 class nsNativeAppSupportOS2 : public nsNativeAppSupportBase
00367 {
00368 public:
00369     // Overrides of base implementation.
00370     NS_IMETHOD Start( PRBool *aResult );
00371     NS_IMETHOD Stop( PRBool *aResult );
00372     NS_IMETHOD Quit();
00373     NS_IMETHOD StartServerMode();
00374     NS_IMETHOD OnLastWindowClosing();
00375     NS_IMETHOD SetIsServerMode( PRBool isServerMode );
00376     NS_IMETHOD EnsureProfile(nsICmdLineService* args);
00377 
00378     // The "old" Start method (renamed).
00379     NS_IMETHOD StartDDE();
00380 
00381     // Utility function to handle a Win32-specific command line
00382     // option: "-console", which dynamically creates a Windows
00383     // console.
00384     // On OS/2, we use the return from CheckConsole to determine
00385     // whether or not to use the OS/2 specific turbo mode
00386     PRBool CheckConsole();
00387 
00388 private:
00389     static HDDEDATA APIENTRY HandleDDENotification( ULONG    idInst,
00390                                                     USHORT   uType,
00391                                                     USHORT   uFmt,
00392                                                     HCONV    hconv,
00393                                                     HSZ      hsz1,
00394                                                     HSZ      hsz2,
00395                                                     HDDEDATA hdata,
00396                                                     ULONG    dwData1,
00397                                                     ULONG    dwData2 );
00398     static nsresult HandleRequest( LPBYTE request, PRBool newWindow, nsIDOMWindow **aResult );
00399     static void ParseDDEArg( HSZ args, int index, nsCString& string);
00400     static void ParseDDEArg( const char* args, int index, nsCString& aString);
00401     static void ActivateLastWindow();
00402     static HDDEDATA CreateDDEData( DWORD value );
00403     static HDDEDATA CreateDDEData( LPBYTE value, DWORD len );
00404     static PRBool   InitTopicStrings();
00405     static int      FindTopic( HSZ topic );
00406     static nsresult GetCmdLineArgs( LPBYTE request, nsICmdLineService **aResult );
00407     static nsresult OpenWindow( const char *urlstr, const char *args, nsIDOMWindow **aResult );
00408     static nsresult OpenBrowserWindow( const char *args, PRBool newWindow, nsIDOMWindow **aResult );
00409     static nsresult ReParent( nsISupports *window, HWND newParent );
00410     static nsresult GetStartupURL(nsICmdLineService *args, nsCString& taskURL);
00411 
00412 
00413     static int   mConversations;
00414     enum {
00415         topicOpenURL,
00416         topicActivate,
00417         topicCancelProgress,
00418         topicVersion,
00419         topicRegisterViewer,
00420         topicUnRegisterViewer,
00421         topicGetWindowInfo,
00422         // Note: Insert new values above this line!!!!!
00423         topicCount // Count of the number of real topics
00424     };
00425 
00426     static HSZ   mApplication, mTopics[ topicCount ];
00427     static DWORD mInstance;
00428     static char *mAppName;
00429     static PRBool mInitialWindowActive;
00430     static PRBool mForceProfileStartup;
00431     static char mMutexName[];
00432     static PRBool mUseDDE;
00433     friend struct MessageWindow;
00434 }; // nsNativeAppSupportOS2
00435 
00436 nsSplashScreenOS2::nsSplashScreenOS2()
00437     : mDlg( 0 ), mBitmap( 0 ), mRefCnt( 0 ),
00438       hdcMemory( 0 ), hpsMemory( 0 ), mBitmapCX(0), mBitmapCY(0) {
00439 }
00440 
00441 nsSplashScreenOS2::~nsSplashScreenOS2() {
00442 #if MOZ_DEBUG_DDE
00443     printf( "splash screen dtor called\n" );
00444 #endif
00445     // Make sure dialog is gone.
00446     Hide();
00447 }
00448 
00449 NS_IMETHODIMP
00450 nsSplashScreenOS2::Show() {
00451     //Spawn new thread to display real splash screen.
00452     _beginthread( ThreadProc, NULL, 16384, (void *)this );
00453     return NS_OK;
00454 }
00455 
00456 NS_IMETHODIMP
00457 nsSplashScreenOS2::Hide() {
00458     if ( mDlg ) {
00459         // Dismiss the dialog.
00460         WinPostMsg(mDlg, WM_CLOSE, 0, 0);
00461         mDlg = 0;
00462         GpiSetBitmap(hpsMemory, NULLHANDLE);
00463         if (mBitmap) {
00464             GpiDeleteBitmap(mBitmap);
00465             mBitmap = 0;
00466         }
00467         if (hdcMemory) {
00468             DevCloseDC(hdcMemory);
00469             hdcMemory = 0;
00470         }
00471         if (hpsMemory) {
00472            GpiDestroyPS(hpsMemory);
00473            hpsMemory = 0;
00474         }
00475     }
00476     return NS_OK;
00477 }
00478 
00479 HBITMAP LoadAndSetBitmapFromFile(HPS hps, PSZ pszFilename)
00480 {
00481    FILE *fp = fopen(pszFilename, "rb");
00482    if (fp == NULL) {
00483       return NULLHANDLE;
00484    }
00485    fseek(fp, 0, SEEK_END );
00486    ULONG cbFile = ftell(fp);
00487    if (cbFile ==0) {
00488       fclose(fp);
00489       return NULLHANDLE;
00490    }
00491    fseek(fp, 0, SEEK_SET );
00492    PBYTE pBitmapData = (PBYTE)malloc(cbFile);
00493    fread((PVOID)pBitmapData, cbFile, 1, fp);
00494    fclose(fp);
00495 
00496    PBITMAPFILEHEADER2 pbfh2 = (PBITMAPFILEHEADER2)pBitmapData;
00497    PBITMAPINFOHEADER2 pbmp2 = NULL;
00498 
00499    switch (pbfh2->usType)
00500    {
00501       case BFT_BITMAPARRAY:
00502          /*
00503           *   If this is a Bitmap-Array, adjust pointer to the normal
00504           *   file header.  We'll just use the first bitmap in the
00505           *   array and ignore other device forms.
00506           */
00507          pbfh2 = &(((PBITMAPARRAYFILEHEADER2) pBitmapData)->bfh2);
00508          pbmp2 = &pbfh2->bmp2;
00509          break;
00510       case BFT_BMAP:
00511          pbmp2 = &pbfh2->bmp2;
00512          break;
00513       case BFT_ICON:
00514       case BFT_POINTER:
00515       case BFT_COLORICON:
00516       case BFT_COLORPOINTER:
00517       default:
00518          break;
00519    }
00520     
00521    if (pbmp2 == NULL) {
00522       free(pBitmapData);
00523       return NULLHANDLE;
00524    }
00525 
00526    LONG lScans;
00527    if (pbmp2->cbFix == sizeof(BITMAPINFOHEADER))
00528       lScans = (LONG) ((PBITMAPINFOHEADER)pbmp2)->cy;
00529    else
00530       lScans = pbmp2->cy;
00531 
00532    HBITMAP hbmp = GpiCreateBitmap(hps, pbmp2, 0L, NULL, NULL);
00533    if (!hbmp) {
00534       free(pBitmapData);
00535       return NULLHANDLE;
00536    }
00537 
00538    if (GpiSetBitmap(hps, hbmp) == HBM_ERROR) {
00539       GpiDeleteBitmap(hbmp);
00540       free(pBitmapData);
00541       return NULLHANDLE;
00542    }
00543 
00544    LONG lScansSet = GpiSetBitmapBits(hps, 0L, lScans, pBitmapData + pbfh2->offBits,
00545                                      (PBITMAPINFO2) pbmp2);
00546    free(pBitmapData);
00547 
00548    if (lScansSet != lScans) {
00549       GpiSetBitmap(hps, NULLHANDLE);
00550       GpiDeleteBitmap(hbmp);
00551       return NULLHANDLE;
00552    }
00553 
00554    return hbmp;
00555 }
00556 
00557 void
00558 nsSplashScreenOS2::LoadBitmap() {
00559     hdcMemory = DevOpenDC((HAB)0, OD_MEMORY, "*", 0L, NULL, 0L);
00560     SIZEL sizel = {0, 0};
00561     hpsMemory = GpiCreatePS((HAB)0, hdcMemory, &sizel,
00562                             PU_PELS | GPIF_DEFAULT | GPIT_MICRO | GPIA_ASSOC );
00563 
00564     // Check for '<program-name>.bmp" in same directory as executable.
00565     PPIB ppib;
00566     PTIB ptib;
00567     char fileName[CCHMAXPATH];
00568     DosGetInfoBlocks( &ptib, &ppib);
00569     DosQueryModuleName( ppib->pib_hmte, CCHMAXPATH, fileName);
00570     int fileNameLen = strlen(fileName);
00571     if (fileNameLen >=3) {
00572         fileName[ fileNameLen - 3 ] = 0;
00573         strcat( fileName, "bmp" );
00574         // Try to load bitmap from that file.
00575         mBitmap = LoadAndSetBitmapFromFile(hpsMemory, fileName);
00576     }
00577     if (!mBitmap) {
00578         mBitmap = GpiLoadBitmap(hpsMemory, NULL, IDB_SPLASH, 0L, 0L);
00579         GpiSetBitmap(hpsMemory, mBitmap);
00580     }
00581     BITMAPINFOHEADER bitmap;
00582     bitmap.cbFix = sizeof(BITMAPINFOHEADER);
00583     GpiQueryBitmapParameters(mBitmap, &bitmap);
00584     mBitmapCX = bitmap.cx;
00585     mBitmapCY = bitmap.cy;
00586 }
00587 
00588 MRESULT EXPENTRY DialogProc( HWND dlg, ULONG msg, MPARAM mp1, MPARAM mp2 ) {
00589     if ( msg == WM_INITDLG ) {
00590         // Store dialog handle.
00591         nsSplashScreenOS2 *splashScreen = (nsSplashScreenOS2*)mp2;
00592         if ( mp2 ) {
00593             splashScreen->SetDialog( dlg );
00594 
00595             // Try to load customized bitmap.
00596             splashScreen->LoadBitmap();
00597         }
00598 
00599         /* Size and center the splash screen correctly. The flags in the 
00600          * dialog template do not do the right thing if the user's 
00601          * machine is using large fonts.
00602          */ 
00603         HBITMAP hbitmap = splashScreen->mBitmap;
00604         if ( hbitmap ) {
00605             WinSetWindowPos( dlg,
00606                              HWND_TOP,
00607                              WinQuerySysValue( HWND_DESKTOP, SV_CXSCREEN )/2 - splashScreen->mBitmapCX/2,
00608                              WinQuerySysValue( HWND_DESKTOP, SV_CYSCREEN )/2 - splashScreen->mBitmapCY/2,
00609                              splashScreen->mBitmapCX,
00610                              splashScreen->mBitmapCY,
00611                              SWP_ACTIVATE | SWP_MOVE | SWP_SIZE );
00612             WinShowWindow( dlg, TRUE );
00613         }
00614         return (MRESULT)FALSE;
00615     }
00616     else if ( msg == WM_PAINT ) {
00617         nsSplashScreenOS2 *splashScreen = (nsSplashScreenOS2*)WinQueryWindowPtr( dlg, QWL_USER );
00618         HPS hps = WinBeginPaint (dlg, NULLHANDLE, NULL);
00619 #if 0
00620         nsPaletteOS2::SelectGlobalPalette(hps, dlg);
00621 #endif
00622         GpiErase (hps);
00623         POINTL aptl[8] = {{0, 0}, {splashScreen->mBitmapCX, splashScreen->mBitmapCY},
00624                           {0, 0}, {0, 0},
00625                           {0, 0}, {0, 0},
00626                           {0, 0}, {0, 0}};
00627 
00628         GpiBitBlt( hps, splashScreen->hpsMemory, 3L, aptl, ROP_SRCCOPY, 0L );
00629         WinEndPaint( hps );
00630         return (MRESULT)TRUE;
00631     }
00632 #if 0
00633     else if ( msg == WM_REALIZEPALETTE ) {
00634         HPS hps = WinGetPS(dlg);
00635         nsPaletteOS2::SelectGlobalPalette(hps, dlg);
00636         WinReleasePS(hps);
00637         WinInvalidateRect( dlg, 0, TRUE);
00638         return (MRESULT)TRUE;
00639     }
00640 #endif
00641     return WinDefDlgProc (dlg, msg, mp1, mp2);
00642 }
00643 
00644 void nsSplashScreenOS2::SetDialog( HWND dlg ) {
00645     // Save dialog handle.
00646     mDlg = dlg;
00647     // Store this pointer in the dialog.
00648     WinSetWindowPtr( mDlg, QWL_USER, this );
00649 }
00650 
00651 nsSplashScreenOS2 *nsSplashScreenOS2::GetPointer( HWND dlg ) {
00652     // Get result from dialog user data.
00653     PVOID data = WinQueryWindowPtr( dlg, QWL_USER );
00654     return (nsSplashScreenOS2*)data;
00655 }
00656 
00657 void ThreadProc(void *splashScreen) {
00658     HAB hab = WinInitialize( 0 );
00659     HMQ hmq = WinCreateMsgQueue( hab, 0 );
00660     WinDlgBox( HWND_DESKTOP, HWND_DESKTOP, (PFNWP)DialogProc, NULLHANDLE, IDD_SPLASH, (MPARAM)splashScreen );
00661     WinDestroyMsgQueue( hmq );
00662     WinTerminate( hab );
00663 //    _endthread();
00664 }
00665 
00666 #define TURBOD "mozturbo.exe"
00667 
00668 PRBool gAbortServer = PR_FALSE;
00669 
00670 PRBool
00671 nsNativeAppSupportOS2::CheckConsole() {
00672     CHAR pszAppPath[CCHMAXPATH];
00673     PPIB ppib;
00674     PTIB ptib;
00675     DosGetInfoBlocks(&ptib, &ppib);
00676     DosQueryModuleName(ppib->pib_hmte, CCHMAXPATH, pszAppPath);
00677     *strrchr(pszAppPath, '\\') = '\0'; // XXX DBCS misery
00678 
00679     for ( int i = 1; i < *__pargc; i++ ) {
00680         if ( strcmp( "-turbo", __argv[i] )  == 0 ||
00681              strcmp( "/turbo", __argv[i] )  == 0 ||
00682              strcmp( "-server", __argv[i] ) == 0 ||
00683              strcmp( "/server", __argv[i] ) == 0 ) {         
00684 
00685             struct stat st;
00686             CHAR pszTurboPath[CCHMAXPATH];
00687 
00688             strcpy(pszTurboPath, pszAppPath);
00689             strcat(pszTurboPath, "\\");
00690             strcat(pszTurboPath, TURBOD);
00691             int statrv = stat(pszTurboPath, &st);
00692 
00693             /* If we can't find the turbo EXE, use the builtin turbo */
00694             if (statrv == 0) {
00695               RESULTCODES rcodes;
00696               CHAR pszArgString[CCHMAXPATH];
00697 
00698               strcpy(pszArgString, pszTurboPath);
00699               strcat(pszArgString, " -l -p ");
00700               strcat(pszArgString, pszAppPath);
00701               pszArgString[strlen(pszTurboPath)] = '\0';
00702        
00703               DosExecPgm(NULL,0,EXEC_BACKGROUND,
00704                          pszArgString,
00705                          0, &rcodes,
00706                          pszTurboPath);
00707               return PR_FALSE; /* Don't start the app */
00708             } else {
00709               // Start in server mode (and suppress splash screen).   
00710               mServerMode = PR_TRUE;
00711               mShouldShowUI = PR_FALSE;
00712               __argv[i] = "-nosplash"; // Bit of a hack, but it works!
00713               // Ignore other args.
00714             }
00715         }
00716     }
00717 
00718     for ( int j = 1; j < *__pargc; j++ ) {
00719         if (strcmp("-killAll", __argv[j]) == 0 || strcmp("/killAll", __argv[j]) == 0 ||
00720             strcmp("-kill", __argv[j]) == 0 || strcmp("/kill", __argv[j]) == 0) {
00721 
00722             struct stat st;
00723             CHAR pszTurboPath[CCHMAXPATH];
00724 
00725             strcpy(pszTurboPath, pszAppPath);
00726             strcat(pszTurboPath, "\\");
00727             strcat(pszTurboPath, TURBOD);
00728             int statrv = stat(pszTurboPath, &st);
00729 
00730             /* If we can't find the turbo EXE, use the builtin turbo */
00731             if (statrv == 0) {
00732               RESULTCODES rcodes;
00733               CHAR pszArgString[CCHMAXPATH];
00734 
00735               strcpy(pszArgString, pszTurboPath);
00736               strcat(pszArgString, " -u");
00737               pszArgString[strlen(pszTurboPath)] = '\0';
00738              
00739               DosExecPgm(NULL,0,EXEC_BACKGROUND,
00740                          pszArgString,
00741                          0, &rcodes,
00742                          pszTurboPath);
00743               return PR_FALSE; /* Don't start the app */
00744             } else {
00745               gAbortServer = PR_TRUE;
00746             }
00747             break;
00748         }
00749     }
00750 
00751     return PR_TRUE; /* Start the app */
00752 }
00753 
00754 
00755 // Create and return an instance of class nsNativeAppSupportOS2.
00756 nsresult
00757 NS_CreateNativeAppSupport( nsINativeAppSupport **aResult ) {
00758     if ( aResult ) {
00759         nsNativeAppSupportOS2 *pNative = new nsNativeAppSupportOS2;
00760         if ( pNative ) {                                           
00761             // Check for dynamic console creation request.         
00762             // If CheckConsole returns PR_FALSE, we should
00763             // start the turbo daemon and close the app.
00764             if (pNative->CheckConsole() == PR_FALSE) {
00765                delete pNative;
00766                return NS_ERROR_FAILURE;
00767             }
00768             *aResult = pNative;                                    
00769             NS_ADDREF( *aResult );                                 
00770         } else {
00771             return NS_ERROR_OUT_OF_MEMORY;
00772         }
00773     } else {
00774         return NS_ERROR_NULL_POINTER;
00775     }
00776 
00777     return NS_OK;
00778 }
00779 
00780 // Create instance of OS/2 splash screen object.
00781 nsresult
00782 NS_CreateSplashScreen( nsISplashScreen **aResult ) {
00783     /* In order to handle -splash on the command line, */
00784     /* we use a variable to handle splash. Defaults to true. */
00785     /* We set it to false if you have turned off the logo */
00786     /* in OS/2, but then back to true if you specify -splash. */
00787     BOOL doSplashScreen = TRUE;
00788     if ( aResult ) {
00789         *aResult = 0;
00790         CHAR pBuffer[3];
00791         PrfQueryProfileString( HINI_USERPROFILE, "PM_ControlPanel", "LogoDisplayTime", "1", pBuffer, 3);
00792         if (pBuffer[0] == '0') {
00793           doSplashScreen = FALSE;
00794         } /* endif */
00795         for ( int i = 1; i < *__pargc; i++ ) {
00796             if ( strcmp( "-quiet", __argv[i] ) == 0
00797                  ||
00798                  strcmp( "/quiet", __argv[i] ) == 0 ) {
00799                  doSplashScreen = FALSE;
00800             }
00801             if ( strcmp( "-splash", __argv[i] ) == 0
00802                  ||
00803                  strcmp( "/splash", __argv[i] ) == 0 ) {
00804                  doSplashScreen = TRUE;
00805             }
00806         }
00807         if (!doSplashScreen) {
00808           return NS_OK;
00809         }
00810         *aResult = new nsSplashScreenOS2;
00811         if ( *aResult ) {
00812             NS_ADDREF( *aResult );
00813         } else {
00814             return NS_ERROR_OUT_OF_MEMORY;
00815         }
00816     } else {
00817         return NS_ERROR_NULL_POINTER;
00818     }
00819 
00820     return NS_OK;
00821 }
00822 
00823 // Constants
00824 #define MOZ_STARTUP_MUTEX_NAME "StartupMutex"
00825 #define MOZ_DDE_START_TIMEOUT 30000
00826 #define MOZ_DDE_STOP_TIMEOUT  15000
00827 #define MOZ_DDE_EXEC_TIMEOUT  15000
00828 
00829 // The array entries must match the enum ordering!
00830 const char * const topicNames[] = { "WWW_OpenURL",
00831                                     "WWW_Activate",
00832                                     "WWW_CancelProgress",
00833                                     "WWW_Version",
00834                                     "WWW_RegisterViewer",
00835                                     "WWW_UnRegisterViewer",
00836                                     "WWW_GetWindowInfo" };
00837 
00838 // Static member definitions.
00839 int   nsNativeAppSupportOS2::mConversations = 0;
00840 HSZ   nsNativeAppSupportOS2::mApplication   = 0;
00841 HSZ   nsNativeAppSupportOS2::mTopics[nsNativeAppSupportOS2::topicCount] = { 0 };
00842 DWORD nsNativeAppSupportOS2::mInstance      = 0;
00843 PRBool nsNativeAppSupportOS2::mInitialWindowActive = PR_FALSE;
00844 PRBool nsNativeAppSupportOS2::mForceProfileStartup = PR_FALSE;
00845 
00846 
00847 // Added this as pmddeml has no api like this
00848 int DdeCmpStringHandles( HSZ hsz1, HSZ hsz2 )
00849 {
00850   char chhsz1[CCHMAXPATH], chhsz2[CCHMAXPATH];
00851   int rc = -1;
00852 
00853   /* I am assuming that the strings will never be more that CCHMAXPATH in
00854    * length.  Safe bet (for now).  To find true length, call WinDdeQueryString
00855    * and pass in (hsz, NULL, 0L, 0) and it will return iLength.  Passing 0
00856    * for codepage will use the codepage of the current thread.
00857    */
00858   WinDdeQueryString( hsz1, chhsz1, sizeof( chhsz1 ), 0 );
00859   WinDdeQueryString( hsz2, chhsz2, sizeof( chhsz2 ),0 );
00860 
00861   rc = stricmp( chhsz1, chhsz2 );
00862 
00863   return(rc);
00864 }
00865 
00866 
00867 char *GetCommandLine()
00868 {
00869    /* This function is meant to be like the Window's GetCommandLine() function.
00870     * The value returned by pPIB->pib_pchcmd is of the format:
00871     * <executable>\0<command line parameters>.  So the first string is the
00872     * .exe and the second string is what followed on the command line.  Dorky,
00873     * but I guess it'll have to do.
00874     */
00875    PTIB pTIB = NULL;
00876    PPIB pPIB = NULL;
00877    APIRET rc = NO_ERROR;
00878    char *pchParam = NULL;
00879 
00880    rc = DosGetInfoBlocks( &pTIB, &pPIB );
00881    if( rc == NO_ERROR )
00882    {
00883       INT iLen = 0;
00884       char *pchTemp = NULL;
00885       pchParam = pPIB->pib_pchcmd;
00886       strcpy( szCommandLine, pchParam );
00887       iLen = strlen( pchParam );
00888 
00889       /* szCommandLine[iLen] is '\0', so see what's next.  Probably be another
00890        * '\0', a space or start of the arguments
00891        */
00892       pchTemp = &(pchParam[iLen+1]);
00893 
00894       /* At this point, szCommandLine holds just the program name.  Now we
00895        * go for the parameters.  If our next value is a space, ignore it
00896        * and go for the next character
00897        */
00898       if( *pchTemp )
00899       {
00900          szCommandLine[iLen] = ' ';
00901          iLen++;
00902          if( *pchTemp == ' ' )
00903          {
00904             pchTemp++;
00905          }
00906          strcpy( &(szCommandLine[iLen]), pchTemp );
00907       }
00908 
00909    }
00910 
00911    return( szCommandLine );
00912 }
00913 
00914 typedef struct _DDEMLFN
00915 {
00916    PFN   *fn;
00917    ULONG ord; 
00918 } DDEMLFN, *PDDEMLFN;
00919 
00920 DDEMLFN ddemlfnTable[] = 
00921 {
00922    { (PFN *)&WinDdeAbandonTransaction   ,100 },
00923    { (PFN *)&WinDdeAccessData           ,101 },
00924    { (PFN *)&WinDdeAddData              ,102 },
00925    { (PFN *)&WinDdeSubmitTransaction    ,103 },
00926    { (PFN *)&WinDdeCompareStringHandles ,104 },
00927    { (PFN *)&WinDdeConnect              ,105 },
00928    { (PFN *)&WinDdeConnectList          ,106 },
00929    { (PFN *)&WinDdeCreateDataHandle     ,107 },
00930    { (PFN *)&WinDdeCreateStringHandle   ,108 },
00931    { (PFN *)&WinDdeDisconnect           ,109 },
00932    { (PFN *)&WinDdeDisconnectList       ,110 },
00933    { (PFN *)&WinDdeEnableCallback       ,111 },
00934    { (PFN *)&WinDdeFreeDataHandle       ,112 },
00935    { (PFN *)&WinDdeFreeStringHandle     ,113 },
00936    { (PFN *)&WinDdeGetData              ,114 },
00937    { (PFN *)&WinDdeInitialize           ,116 },
00938    { (PFN *)&WinDdeKeepStringHandle     ,117 },
00939    { (PFN *)&WinDdeNameService          ,118 },
00940    { (PFN *)&WinDdePostAdvise           ,119 },
00941    { (PFN *)&WinDdeQueryConvInfo        ,120 },
00942    { (PFN *)&WinDdeQueryNextServer      ,121 },
00943    { (PFN *)&WinDdeQueryString          ,122 },
00944    { (PFN *)&WinDdeReconnect            ,123 },
00945    { (PFN *)&WinDdeSetUserHandle        ,124 },
00946    { (PFN *)&WinDdeUninitialize         ,126 },
00947    { (PFN *)NULL                           ,  0 }
00948 };
00949 
00950 /* SetupOS2ddeml makes sure that we can get pointers to all of the DDEML 
00951  * functions in demlfnTable using entrypoints.  If we can't find one of them
00952  * or can't load pmddeml.dll, then returns FALSE
00953  */
00954 BOOL SetupOS2ddeml()
00955 {
00956     BOOL bRC = FALSE;
00957     HMODULE hmodDDEML = NULLHANDLE;
00958     APIRET rc = NO_ERROR;
00959 
00960     rc = DosLoadModule( NULL, 0, "PMDDEML", &hmodDDEML );
00961     if( rc == NO_ERROR )
00962     {
00963        int i;
00964        /* all of this had better work.  Get ready to be a success! */
00965        bRC = TRUE;
00966        for( i=0; ddemlfnTable[i].ord != 0; i++ )
00967        {
00968           rc = DosQueryProcAddr( hmodDDEML, ddemlfnTable[i].ord, NULL,
00969                                  ddemlfnTable[i].fn );
00970           if( rc != NO_ERROR )
00971           {
00972              /* we're horked.  Return rc = horked */
00973              bRC = FALSE;
00974              break;
00975           }
00976        }
00977     } /* load PMDDEML */
00978 
00979     return( bRC );
00980 }
00981 
00982 char nsNativeAppSupportOS2::mMutexName[ 128 ] = { 0 };
00983 
00984 
00985 // Message window encapsulation.
00986 struct MessageWindow {
00987     // ctor/dtor are simplistic
00988     MessageWindow() {
00989         // Try to find window.
00990         HATOMTBL  hatomtbl;
00991         HENUM     henum;
00992         HWND      hwndNext;
00993         char      classname[CCHMAXPATH];
00994 
00995         mHandle = NULLHANDLE;
00996 
00997         hatomtbl = WinQuerySystemAtomTable();
00998         mMsgWindowAtom = WinFindAtom(hatomtbl, className());
00999         if (mMsgWindowAtom == 0)
01000         {
01001           // If there is not atom in the system table for this class name, then
01002           // we can assume that the app is not currently running.
01003           mMsgWindowAtom = WinAddAtom(hatomtbl, className());
01004         } else {
01005           // Found an existing atom for this class name.  Cycle through existing
01006           // windows and see if one with our window id and class name already 
01007           // exists
01008           henum = WinBeginEnumWindows(HWND_OBJECT);
01009           while ((hwndNext = WinGetNextWindow(henum)) != NULLHANDLE)
01010           {
01011             if (WinQueryWindowUShort(hwndNext, QWS_ID) == (USHORT)mMsgWindowAtom)
01012             {
01013               WinQueryClassName(hwndNext, CCHMAXPATH, classname);
01014               if (strcmp(classname, className()) == 0)
01015               {
01016                 mHandle = hwndNext;
01017                 break;
01018               }
01019             }
01020           }
01021         }
01022     }
01023 
01024     HWND getHWND() {
01025         return mHandle;
01026     }
01027 
01028     // Class name: appName + "MessageWindow"
01029     static const char *className() {
01030         static char classNameBuffer[128];
01031         static char *mClassName = 0;
01032         if ( !mClassName ) { 
01033             sprintf( classNameBuffer,
01034                          "%s%s",
01035                          nsNativeAppSupportOS2::mAppName,
01036                          "MessageWindow" );
01037             mClassName = classNameBuffer;
01038         }
01039         return mClassName;
01040     }
01041 
01042     // Create: Register class and create window.
01043     NS_IMETHOD Create() {
01044 
01045         // Register the window class.
01046         NS_ENSURE_TRUE( WinRegisterClass( 0, className(), 
01047                                           (PFNWP)&MessageWindow::WindowProc, 
01048                                           0L, 0 ), 
01049                         NS_ERROR_FAILURE );
01050 
01051         /* Giving a size but offscreen so that won't be seen, even if all
01052          * goes wrong.  Giving a size so that can be seen and understood by
01053          * tools
01054          */
01055         const char * pszClassName = className();
01056         mHandle = WinCreateWindow( HWND_OBJECT,
01057                                    pszClassName,
01058                                    NULL,        // name
01059                                    WS_DISABLED, // style
01060                                    0,0,     // x, y
01061                                    0,0,       // cx,cy
01062                                    HWND_DESKTOP,// owner
01063                                    HWND_BOTTOM,  // hwndbehind
01064                                    (USHORT)mMsgWindowAtom, // id
01065                                    NULL,        // pCtlData
01066                                    NULL );      // pres params
01067 
01068 #if MOZ_DEBUG_DDE
01069         printf( "Message window = 0x%08X\n", (int)mHandle );
01070 #endif
01071 
01072         return NS_OK;
01073     }
01074 
01075     // Destory:  Get rid of window and reset mHandle.
01076     NS_IMETHOD Destroy() {
01077         nsresult retval = NS_OK;
01078 
01079         if ( mHandle ) {
01080            HATOMTBL hatomtbl = WinQuerySystemAtomTable();
01081            WinDeleteAtom(hatomtbl, mMsgWindowAtom);
01082            
01083             // DestroyWindow can only destroy windows created from
01084             //  the same thread.
01085             BOOL desRes = WinDestroyWindow( mHandle );
01086             if ( FALSE != desRes ) {
01087                 mHandle = NULL;
01088             }
01089             else {
01090                 retval = NS_ERROR_FAILURE;
01091             }
01092         }
01093 
01094         return retval;
01095     }
01096 
01097     // SendRequest: Pass string via WM_COPYDATA to message window.
01098     NS_IMETHOD SendRequest( const char *cmd ) {
01099         /* Nothing like WM_COPYDATA in OS/2, where the OS allows pointers to be
01100          * passed to a different process and automatically accessible by that
01101          * process.  So we have to create shared mem on our side and then the
01102          * process that gets the WM_COPYDATA message has to do a 
01103          * DosGetSharedMem on this pointer to be able to access the data
01104          */
01105 
01106         COPYDATASTRUCT *pcds;
01107         APIRET rc = NO_ERROR;
01108         PVOID pvData = NULL;
01109         ULONG ulSize = sizeof(COPYDATASTRUCT)+strlen(cmd)+1;
01110         rc = DosAllocSharedMem( &pvData, NULL, ulSize,
01111                                 (PAG_COMMIT|PAG_READ|PAG_WRITE|OBJ_GETTABLE) );
01112 
01113         if( rc != NO_ERROR )
01114         {
01115            /* don't even try doing anything else.  Windows doesn't worry about
01116             * errors so I guess that we shouldn't either
01117             */
01118            return NS_OK;
01119         }
01120 
01121         memset( pvData, '\0', ulSize );
01122         pcds = (COPYDATASTRUCT *)(pvData);
01123         pcds->dwData = 0;
01124         pcds->cbData = strlen(cmd)+1;
01125         /* put the data in the buffer space immediately after the 
01126          * COPYDATASTRUCT
01127          */
01128         pcds->lpData = &(pcds->chBuff);
01129         if( cmd )
01130         {
01131            strcpy( (char *)pcds->lpData, cmd );
01132         }
01133         WinSendMsg( mHandle, WM_COPYDATA, 0, (MPARAM)pcds );
01134         DosFreeMem( pvData );
01135 
01136         return NS_OK;
01137     }
01138 
01139     // Window proc.
01140     static MRESULT EXPENTRY WindowProc( HWND msgWindow, ULONG msg, MPARAM wp, 
01141                                         MPARAM lp )
01142     {
01143         MRESULT rc = (MRESULT)TRUE;
01144 
01145         if ( msg == WM_COPYDATA ) {
01146             // This is an incoming request.
01147             COPYDATASTRUCT *cds = (COPYDATASTRUCT*)lp;
01148             DosGetSharedMem( (PVOID)cds, PAG_READ|PAG_WRITE );
01149 #if MOZ_DEBUG_DDE
01150             printf( "Incoming request: %s\n", (const char*)cds->lpData );
01151 #endif
01152             // Get current window and return its window handle.
01153             nsCOMPtr<nsIDOMWindow> win;
01154             (void)nsNativeAppSupportOS2::HandleRequest( (LPBYTE)cds->lpData, PR_FALSE, getter_AddRefs( win ) );
01155             return win ? (MRESULT)hwndForDOMWindow( win ) : 0;
01156  }
01157 
01158     /* We have to return a FALSE from WM_CREATE or this window will never
01159      * get off of the ground
01160      */
01161     else if ( msg == WM_CREATE ) {
01162         rc = (MRESULT)FALSE;
01163     }
01164 
01165     return rc;
01166 }
01167 
01168 private:
01169     HWND     mHandle;
01170     USHORT   mMsgWindowAtom;
01171 }; // struct MessageWindow
01172 
01173 static char nameBuffer[128] = { 0 };
01174 char *nsNativeAppSupportOS2::mAppName = nameBuffer;
01175 PRBool nsNativeAppSupportOS2::mUseDDE = PR_FALSE;
01176 
01177 /* Start: Tries to find the "message window" to determine if it
01178  *        exists.  If so, then SeaMonkey is already running.  In that
01179  *        case, we use the handle to the "message" window and send
01180  *        a request corresponding to this process's command line
01181  *        options.
01182  *        
01183  *        If not, then this is the first instance of SeaMonkey.  In
01184  *        that case, we create and set up the message window.
01185  *
01186  *        The checking for existance of the message window must
01187  *        be protected by use of a mutex semaphore.
01188  */
01189 NS_IMETHODIMP
01190 nsNativeAppSupportOS2::Start( PRBool *aResult ) {
01191     NS_ENSURE_ARG( aResult );
01192     NS_ENSURE_TRUE( mInstance == 0, NS_ERROR_NOT_INITIALIZED );
01193 
01194     nsresult rv = NS_ERROR_FAILURE;
01195     *aResult = PR_FALSE;
01196 
01197     // see if DDE should be enabled AND remove OS/2-specific
01198     // options the app's commandline handler won't recognize
01199     // (-console was handled earlier by StartOS2App())
01200     for (int i = 1; i < *__pargc; i++ ) {
01201         if (stricmp("-dde", __argv[i]) == 0 ||
01202             stricmp("/dde", __argv[i]) == 0)
01203             mUseDDE = PR_TRUE;
01204         else
01205             if (stricmp("-console", __argv[i]) != 0 &&
01206                 stricmp("/console", __argv[i]) != 0)
01207                 continue;
01208 
01209         for (int j = i; j < *__pargc; j++)
01210             __argv[j] = __argv[j+1];
01211 
01212         (*__pargc)--;
01213         i--;
01214     }
01215 
01216     // if this is a standalone instance, turn off DDE regardless of the
01217     // commandline, then skip out before we look for another instance
01218     if (getenv("MOZ_NO_REMOTE")) {
01219         mUseDDE = PR_FALSE;
01220         *aResult = PR_TRUE;
01221         return NS_OK;
01222     }
01223 
01224     // Grab mutex first.
01225     int retval;
01226     UINT id = ID_DDE_APPLICATION_NAME;
01227     retval = WinLoadString( NULLHANDLE, NULLHANDLE, id, sizeof(nameBuffer), nameBuffer );
01228     if ( retval == 0 ) {
01229         // No app name; just keep running.
01230         *aResult = PR_TRUE;
01231         return NS_OK;
01232     }
01233 
01234     // Build mutex name from app name.
01235     PR_snprintf( mMutexName, sizeof mMutexName, "%s%s", nameBuffer, MOZ_STARTUP_MUTEX_NAME );
01236     Mutex startupLock = Mutex( mMutexName );
01237 
01238     NS_ENSURE_TRUE( startupLock.Lock( MOZ_DDE_START_TIMEOUT ), NS_ERROR_FAILURE );
01239 
01240     /* We need to have a message queue to do the MessageWindow stuff (for both
01241      * Create() and SendRequest()).  If we don't have one, make it now.
01242      * If we are going to end up going away right after ::Start() returns,
01243      * then make sure to clean up the message queue.
01244      */
01245     MQINFO mqinfo;
01246     HAB hab = NULLHANDLE;
01247     HMQ hmqCurrent = WinQueryQueueInfo( HMQ_CURRENT, &mqinfo, 
01248                                         sizeof( MQINFO ) );
01249     if( !hmqCurrent )
01250     {
01251         hab = WinInitialize( 0 );
01252         hmqCurrent = WinCreateMsgQueue( hab, 0 );
01253     }
01254 
01255     // Search for existing message window.
01256     MessageWindow msgWindow;
01257     if ( msgWindow.getHWND() ) {
01258         // We are a client process.  Pass request to message window.
01259         char *cmd = GetCommandLine();
01260         rv = msgWindow.SendRequest( cmd );
01261     } else {
01262         // We will be server.
01263         if (!gAbortServer) {
01264             rv = msgWindow.Create();
01265             if ( NS_SUCCEEDED( rv ) ) {
01266                 if (mUseDDE) {
01267                     // Start up DDE server.
01268                     this->StartDDE();
01269                 }
01270                 // Tell caller to spin message loop.
01271                 *aResult = PR_TRUE;
01272             }
01273         }
01274     }
01275 
01276     startupLock.Unlock();
01277 
01278     if( *aResult == PR_FALSE )
01279     {
01280         /* This process isn't going to continue much longer.  Make sure that we
01281          * clean up the message queue
01282          */
01283         if (hmqCurrent)
01284            WinDestroyMsgQueue(hmqCurrent);
01285         if (hab)
01286            WinTerminate(hab);
01287     }
01288 
01289     return rv;
01290 }
01291 
01292 PRBool
01293 nsNativeAppSupportOS2::InitTopicStrings() {
01294     for ( int i = 0; i < topicCount; i++ ) {
01295         if ( !( mTopics[ i ] = WinDdeCreateStringHandle( (PSZ)topicNames[ i ], CP_WINANSI ) ) ) {
01296             return PR_FALSE;
01297         }
01298     }
01299     return PR_TRUE;
01300 }
01301 
01302 int
01303 nsNativeAppSupportOS2::FindTopic( HSZ topic ) {
01304     for ( int i = 0; i < topicCount; i++ ) {
01305         if ( DdeCmpStringHandles( topic, mTopics[i] ) == 0 ) {
01306             return i;
01307         }
01308     }
01309     return -1;
01310 }
01311 
01312 // Start DDE server.
01313 //
01314 // This used to be the Start() method when we were using DDE as the
01315 // primary IPC mechanism between secondary SeaMonkey processes and the
01316 // initial "server" process.
01317 //
01318 // Now, it simply initializes the DDE server.  The caller must check
01319 // that this process is to be the server, and, must acquire the DDE
01320 // startup mutex semaphore prior to calling this routine.  See ::Start(),
01321 // above.
01322 NS_IMETHODIMP
01323 nsNativeAppSupportOS2::StartDDE() {
01324     NS_ENSURE_TRUE( mInstance == 0, NS_ERROR_NOT_INITIALIZED );
01325 
01326     /* Get entrypoints for PMDDEML */
01327     BOOL bDDEML = SetupOS2ddeml();
01328 
01329     /* If we couldn't initialize DDEML, set mUSEDDE to PR_FALSE */
01330     /* so we don't do anything else DDE related */
01331     if (!bDDEML) {
01332        mUseDDE = PR_FALSE;
01333        return NS_OK;
01334     }
01335 
01336     // Initialize DDE.
01337     NS_ENSURE_TRUE( DDEERR_NO_ERROR == WinDdeInitialize( &mInstance,
01338                                                          nsNativeAppSupportOS2::HandleDDENotification,
01339                                                          APPCLASS_STANDARD,
01340                                                          0 ),
01341                     NS_ERROR_FAILURE );
01342 
01343     // Allocate DDE strings.
01344     NS_ENSURE_TRUE( ( mApplication = WinDdeCreateStringHandle( mAppName, CP_WINANSI ) ) && InitTopicStrings(),
01345                     NS_ERROR_FAILURE );
01346 
01347     // Next step is to register a DDE service.
01348     NS_ENSURE_TRUE( WinDdeNameService( mInstance, mApplication, 0, DNS_REGISTER ), NS_ERROR_FAILURE );
01349 
01350 #if MOZ_DEBUG_DDE
01351     printf( "DDE server started\n" );
01352 #endif
01353 
01354     return NS_OK;
01355 }
01356 
01357 // If no DDE conversations are pending, terminate DDE.
01358 NS_IMETHODIMP
01359 nsNativeAppSupportOS2::Stop( PRBool *aResult ) {
01360     NS_ENSURE_ARG( aResult );
01361     NS_ENSURE_TRUE( mInstance, NS_ERROR_NOT_INITIALIZED );
01362 
01363     nsresult rv = NS_OK;
01364     *aResult = PR_TRUE;
01365 
01366     if (!mUseDDE) {
01367        return rv;
01368     }
01369 
01370     Mutex ddeLock( mMutexName );
01371 
01372     if ( ddeLock.Lock( MOZ_DDE_STOP_TIMEOUT ) ) {
01373         if ( mConversations == 0 ) {
01374             this->Quit();
01375         } else {
01376             *aResult = PR_FALSE;
01377         }
01378 
01379         ddeLock.Unlock();
01380     }
01381     else {
01382         // No DDE application name specified, but that's OK.  Just
01383         // forge ahead.
01384         *aResult = PR_TRUE;
01385     }
01386 
01387     return rv;
01388 }
01389 
01390 // Terminate DDE regardless.
01391 NS_IMETHODIMP
01392 nsNativeAppSupportOS2::Quit() {
01393     // If another process wants to look for the message window, they need
01394     // to wait to hold the lock, in which case they will not find the
01395     // window as we will destroy ours under our lock.
01396     // When the mutex goes off the stack, it is unlocked via destructor.
01397     Mutex mutexLock(mMutexName);
01398     NS_ENSURE_TRUE(mutexLock.Lock(MOZ_DDE_START_TIMEOUT), NS_ERROR_FAILURE );
01399 
01400     // If we've got a message window to receive IPC or new window requests,
01401     // get rid of it as we are shutting down.
01402     // Note:  Destroy calls DestroyWindow, which will only work on a window
01403     //  created by the same thread.
01404     MessageWindow mw;
01405     mw.Destroy();
01406 
01407     if ( mInstance ) {
01408         // Unregister application name.
01409         WinDdeNameService( mInstance, mApplication, 0, DNS_UNREGISTER );
01410         // Clean up strings.
01411         if ( mApplication ) {
01412             WinDdeFreeStringHandle( mApplication );
01413             mApplication = 0;
01414         }
01415         for ( int i = 0; i < topicCount; i++ ) {
01416             if ( mTopics[i] ) {
01417                 WinDdeFreeStringHandle( mTopics[i] );
01418                 mTopics[i] = 0;
01419             }
01420         }
01421         WinDdeUninitialize( mInstance );
01422         mInstance = 0;
01423     }
01424 
01425     return NS_OK;
01426 }
01427 
01428 PRBool NS_CanRun()
01429 {
01430       return PR_FALSE; /*  We do this so that we can bail if we are using custom turbo mode */
01431 }
01432 
01433 #if MOZ_DEBUG_DDE
01434 // Macro to generate case statement for a given XTYP value.
01435 #define XTYP_CASE(t) \
01436     case t: result = #t; break
01437 
01438 static nsCString uTypeDesc( UINT uType ) {
01439     nsCString result;
01440     switch ( uType ) {
01441     XTYP_CASE(XTYP_ADVSTART);
01442     XTYP_CASE(XTYP_CONNECT);
01443     XTYP_CASE(XTYP_ADVREQ);
01444     XTYP_CASE(XTYP_REQUEST);
01445     XTYP_CASE(XTYP_WILDCONNECT);
01446     XTYP_CASE(XTYP_ADVDATA);
01447     XTYP_CASE(XTYP_EXECUTE);
01448     XTYP_CASE(XTYP_POKE);
01449     XTYP_CASE(XTYP_ADVSTOP);
01450     XTYP_CASE(XTYP_CONNECT_CONFIRM);
01451     XTYP_CASE(XTYP_DISCONNECT);
01452     XTYP_CASE(XTYP_ERROR);
01453     XTYP_CASE(XTYP_MONITOR);
01454     XTYP_CASE(XTYP_REGISTER);
01455     XTYP_CASE(XTYP_XACT_COMPLETE);
01456     XTYP_CASE(XTYP_UNREGISTER);
01457     default: result = "XTYP_?????";
01458     }
01459     return result;
01460 }
01461 
01462 static nsCString hszValue( DWORD instance, HSZ hsz ) {
01463     // Extract string from HSZ.
01464     nsCString result("[");
01465     DWORD len = WinDdeQueryString( hsz, NULL, NULL, CP_WINANSI );
01466     if ( len ) {
01467         char buffer[ 256 ];
01468         WinDdeQueryString( hsz, buffer, sizeof buffer, CP_WINANSI );
01469         result += buffer;
01470     }
01471     result += "]";
01472     return result;
01473 }
01474 #endif
01475 
01476 
01477 // Utility function to escape double-quotes within a string.
01478 static void escapeQuotes( nsString &aString ) {
01479     PRInt32 offset = -1;
01480     while( 1 ) {
01481        // Find next '"'.
01482        offset = FindCharInString( aString, '"', ++offset );
01483        if ( offset == kNotFound ) {
01484            // No more quotes, exit.
01485            break;
01486        } else {
01487            // Insert back-slash ahead of the '"'.
01488            aString.Insert( PRUnichar('\\'), offset );
01489            // Increment offset because we just inserted a slash
01490            offset++;
01491        }
01492     }
01493     return;
01494 }
01495 
01496 HDDEDATA APIENTRY
01497 nsNativeAppSupportOS2::HandleDDENotification( ULONG idInst,     // DDEML instance
01498                                               USHORT uType,     // transaction type
01499                                               USHORT uFmt,      // clipboard data format
01500                                               HCONV hconv,      // handle to the conversation
01501                                               HSZ hsz1,         // handle to a string
01502                                               HSZ hsz2,         // handle to a string
01503                                               HDDEDATA hdata,   // handle to a global memory object
01504                                               ULONG dwData1,    // transaction-specific data
01505                                               ULONG dwData2 ) { // transaction-specific data
01506 
01507 #if MOZ_DEBUG_DDE
01508     printf( "DDE: uType  =%s\n",      uTypeDesc( uType ).get() );
01509     printf( "     uFmt   =%u\n",      (unsigned)uFmt );
01510     printf( "     hconv  =%08x\n",    (int)hconv );
01511     printf( "     hsz1   =%08x:%s\n", (int)hsz1, hszValue( mInstance, hsz1 ).get() );
01512     printf( "     hsz2   =%08x:%s\n", (int)hsz2, hszValue( mInstance, hsz2 ).get() );
01513     printf( "     hdata  =%08x\n",    (int)hdata );
01514     printf( "     dwData1=%08x\n",    (int)dwData1 );
01515     printf( "     dwData2=%08x\n",    (int)dwData2 );
01516 #endif
01517 
01518     HDDEDATA result = 0;
01519     if ( uType & XCLASS_BOOL ) {
01520         switch ( uType ) {
01521             case XTYP_CONNECT:
01522                 // Make sure its for our service/topic.
01523                 if ( FindTopic( hsz1 ) != -1 ) {
01524                     // We support this connection.
01525                     result = (HDDEDATA)1;
01526                 }
01527                 break;
01528             case XTYP_CONNECT_CONFIRM:
01529                 // We don't care about the conversation handle, at this point.
01530                 result = (HDDEDATA)1;
01531                 break;
01532         }
01533     } else if ( uType & XCLASS_DATA ) {
01534         if ( uType == XTYP_REQUEST ) {
01535             switch ( FindTopic( hsz1 ) ) {
01536                 case topicOpenURL: {
01537                     // Open a given URL...
01538 
01539                     // Default is to open in current window.
01540                     PRBool new_window = PR_FALSE;
01541 
01542                     // Get the URL from the first argument in the command.
01543                     nsCAutoString url;
01544                     ParseDDEArg(hsz2, 0, url);
01545                     // Read the 3rd argument in the command to determine if a
01546                     // new window is to be used.
01547                     nsCAutoString windowID;
01548                     ParseDDEArg(hsz2, 2, windowID);
01549                     // "0" means to open the URL in a new window.
01550                     if ( windowID.Equals( "0" ) ) {
01551                         new_window = PR_TRUE;
01552                     }
01553 
01554                     // Make it look like command line args.
01555                     url.Insert( NS_STRINGIFY(MOZ_APP_NAME) " -url ", 0 );
01556 #if MOZ_DEBUG_DDE
01557                     printf( "Handling dde XTYP_REQUEST request: [%s]...\n", url.get() );
01558 #endif
01559                     // Now handle it.
01560                     nsCOMPtr<nsIDOMWindow> win;
01561                     HandleRequest( LPBYTE( url.get() ), new_window, getter_AddRefs( win ) );
01562                     // Return pseudo window ID.
01563                     result = CreateDDEData( 1 );
01564                     break;
01565                 }
01566                 case topicGetWindowInfo: {
01567                     // This topic has to get the current URL, get the current
01568                     // page title and then format the output into the DDE
01569                     // return string.  The return value is "URL","Page Title",
01570                     // "Window ID" however the window ID is not used for this
01571                     // command, therefore it is returned as a null string
01572 
01573                     // This isn't really a loop.  We just use "break"
01574                     // statements to bypass the remaining steps when
01575                     // something goes wrong.
01576                     do {
01577                         // Get most recently used Nav window.
01578                         nsCOMPtr<nsIDOMWindowInternal> navWin;
01579                         GetMostRecentWindow( NS_LITERAL_STRING( "navigator:browser" ).get(),
01580                                              getter_AddRefs( navWin ) );
01581                         if ( !navWin ) {
01582                             // There is not a window open
01583                             break;
01584                         }
01585                         // Get content window.
01586                         nsCOMPtr<nsIDOMWindow> content;
01587                         navWin->GetContent( getter_AddRefs( content ) );
01588                         if ( !content ) {
01589                             break;
01590                         }
01591                         // Convert that to internal interface.
01592                         nsCOMPtr<nsIDOMWindowInternal> internalContent( do_QueryInterface( content ) );
01593                         if ( !internalContent ) {
01594                             break;
01595                         }
01596                         // Get location.
01597                         nsCOMPtr<nsIDOMLocation> location;
01598                         internalContent->GetLocation( getter_AddRefs( location ) );
01599                         if ( !location ) {
01600                             break;
01601                         }
01602                         // Get href for URL.
01603                         nsAutoString url;
01604                         if ( NS_FAILED( location->GetHref( url ) ) ) {
01605                             break;
01606                         }
01607                         // Escape any double-quotes.
01608                         escapeQuotes( url );
01609 
01610                         // Now for the title; first, get the "window" script global object.
01611                         nsCOMPtr<nsIScriptGlobalObject> scrGlobalObj( do_QueryInterface( internalContent ) );
01612                         if ( !scrGlobalObj ) {
01613                             break;
01614                         }
01615                         // Then from its doc shell get the base window...
01616                         nsCOMPtr<nsIBaseWindow> baseWindow =
01617                           do_QueryInterface( scrGlobalObj->GetDocShell() );
01618                         if ( !baseWindow ) {
01619                             break;
01620                         }
01621                         // And from the base window we can get the title.
01622                         nsXPIDLString title;
01623                         if(!baseWindow) {
01624                             break;
01625                         }
01626                         baseWindow->GetTitle(getter_Copies(title));
01627                         // Escape any double-quotes in the title.
01628                         escapeQuotes( title );
01629 
01630                         // Use a string buffer for the output data, first
01631                         // save a quote.
01632                         nsCAutoString   outpt( NS_LITERAL_CSTRING("\"") );
01633                         // Now copy the URL converting the Unicode string
01634                         // to a single-byte ASCII string
01635                         outpt.Append( NS_LossyConvertUCS2toASCII( url ) );
01636                         // Add the "," used to separate the URL and the page
01637                         // title
01638                         outpt.Append( NS_LITERAL_CSTRING("\",\"") );
01639                         // Now copy the current page title to the return string
01640                         outpt.Append( NS_LossyConvertUCS2toASCII( title ));
01641                         // Fill out the return string with the remainin ",""
01642                         outpt.Append( NS_LITERAL_CSTRING( "\",\"\"" ));
01643 
01644                         // Create a DDE handle to a char string for the data
01645                         // being returned, this copies and creates a "shared"
01646                         // copy of the DDE response until the calling APP
01647                         // reads it and says it can be freed.
01648                         result = CreateDDEData( (LPBYTE)(const char*)outpt.get(),
01649                                                 outpt.Length() + 1 );
01650 #if MOZ_DEBUG_DDE
01651                         printf( "WWW_GetWindowInfo->%s\n", outpt.get() );
01652 #endif
01653                     } while ( PR_FALSE );
01654                     break;
01655                 }
01656                 case topicActivate: {
01657                     // Activate a Nav window...
01658                     nsCAutoString windowID;
01659                     ParseDDEArg(hsz2, 0, windowID);
01660                     // 4294967295 is decimal for 0xFFFFFFFF which is also a
01661                     //   correct value to do that Activate last window stuff
01662                     if ( windowID.Equals( "-1" ) ||
01663                          windowID.Equals( "4294967295" ) ) {
01664                         // We only support activating the most recent window (or a new one).
01665                         ActivateLastWindow();
01666                         // Return pseudo window ID.
01667                         result = CreateDDEData( 1 );
01668                     }
01669                     break;
01670                 }
01671                 case topicVersion: {
01672                     // Return version.  We're restarting at 1.0!
01673                     DWORD version = 1 << 16; // "1.0"
01674                     result = CreateDDEData( version );
01675                     break;
01676                 }
01677                 case topicRegisterViewer: {
01678                     // Register new viewer (not implemented).
01679                     result = CreateDDEData( PR_FALSE );
01680                     break;
01681                 }
01682                 case topicUnRegisterViewer: {
01683                     // Unregister new viewer (not implemented).
01684                     result = CreateDDEData( PR_FALSE );
01685                     break;
01686                 }
01687                 default:
01688                     break;
01689             }
01690         } else if ( uType & XTYP_POKE ) {
01691             switch ( FindTopic( hsz1 ) ) {
01692                 case topicCancelProgress: {
01693                     // "Handle" progress cancel (actually, pretty much ignored).
01694                     result = (HDDEDATA)DDE_FACK;
01695                     break;
01696                 }
01697                 default:
01698                     break;
01699             }
01700         }
01701     } else if ( uType & XCLASS_FLAGS ) {
01702         if ( uType == XTYP_EXECUTE ) {
01703             // Prove that we received the request.
01704             DWORD bytes;
01705             LPBYTE request = (LPBYTE)WinDdeAccessData( hdata, &bytes );
01706 #if MOZ_DEBUG_DDE
01707             printf( "Handling dde request: [%s]...\n", (char*)request );
01708 #endif
01709             // Default is to open in current window.
01710             PRBool new_window = PR_FALSE;
01711 
01712             nsCAutoString url;
01713             ParseDDEArg((const char*) request, 0, url);
01714 
01715             // Read the 3rd argument in the command to determine if a
01716             // new window is to be used.
01717             nsCAutoString windowID;
01718             ParseDDEArg((const char*) request, 2, windowID);
01719 
01720             // "0" means to open the URL in a new window.
01721             if ( windowID.Equals( "0" ) ) {
01722                 new_window = PR_TRUE;
01723             }
01724 
01725             // Make it look like command line args.
01726             url.Insert( NS_STRINGIFY(MOZ_APP_NAME) " -url ", 0 );
01727 #if MOZ_DEBUG_DDE
01728             printf( "Handling dde XTYP_REQUEST request: [%s]...\n", url.get() );
01729 #endif
01730             // Now handle it.
01731             nsCOMPtr<nsIDOMWindow> win;
01732             HandleRequest( LPBYTE( url.get() ), new_window, getter_AddRefs( win ) );
01733 
01734             // Release the data.
01735 //            DdeUnaccessData( hdata );
01736             result = (HDDEDATA)DDE_FACK;
01737         } else {
01738             result = (HDDEDATA)DDE_NOTPROCESSED;
01739         }
01740     } else if ( uType & XCLASS_NOTIFICATION ) {
01741     }
01742 #if MOZ_DEBUG_DDE
01743     printf( "DDE result=%d (0x%08X)\n", (int)result, (int)result );
01744 #endif
01745     return result;
01746 }
01747 
01748 // Utility function to advance to end of quoted string.
01749 // p+offset must point to the comma preceding the arg on entry.
01750 // On return, p+result points to the closing '"' (or end of the string
01751 // if the closing '"' is missing) if the arg is quoted.  If the arg
01752 // is not quoted, then p+result will point to the first character
01753 // of the arg.
01754 static PRInt32 advanceToEndOfQuotedArg( const char *p, PRInt32 offset, PRInt32 len ) {
01755     // Check whether the current arg is quoted.
01756     if ( p[++offset] == '"' ) {
01757         // Advance past the closing quote.
01758         while ( offset < len && p[++offset] != '"' ) {
01759             // If the current character is a backslash, then the
01760             // next character can't be a *real* '"', so skip it.
01761             if ( p[offset] == '\\' ) {
01762                 offset++;
01763             }
01764         }
01765     }
01766     return offset;
01767 }
01768 
01769 void nsNativeAppSupportOS2::ParseDDEArg( const char* args, int index, nsCString& aString) {
01770     if ( args ) {
01771         int argLen = strlen(args);
01772         nsDependentCString temp(args, argLen);
01773 
01774         // offset points to the comma preceding the desired arg.
01775         PRInt32 offset = -1;
01776         // Skip commas till we get to the arg we want.
01777         while( index-- ) {
01778             // If this arg is quoted, then go to closing quote.
01779             offset = advanceToEndOfQuotedArg( args, offset, argLen);
01780             // Find next comma.
01781             offset = FindCharInString( temp, ',', offset );
01782             if ( offset == kNotFound ) {
01783                 // No more commas, give up.
01784                 aString = args;
01785                 return;
01786             }
01787         }
01788         // The desired argument starts just past the preceding comma,
01789         // which offset points to, and extends until the following
01790         // comma (or the end of the string).
01791         //
01792         // Since the argument might be enclosed in quotes, we need to
01793         // deal with that before searching for the terminating comma.
01794         // We advance offset so it ends up pointing to the start of
01795         // the argument we want.
01796         PRInt32 end = advanceToEndOfQuotedArg( args, offset++, argLen );
01797         // Find next comma (or end of string).
01798         end = FindCharInString( temp, ',', end );
01799         if ( end == kNotFound ) {
01800             // Arg is the rest of the string.
01801             end = argLen;
01802         }
01803         // Extract result.
01804         aString.Assign( args + offset, end - offset );
01805     }
01806     return;
01807 }
01808 
01809 // Utility to parse out argument from a DDE item string.
01810 void nsNativeAppSupportOS2::ParseDDEArg( HSZ args, int index, nsCString& aString) {
01811     DWORD argLen = WinDdeQueryString( args, NULL, NULL, CP_WINANSI );
01812     // there wasn't any string, so return empty string
01813     if ( !argLen ) return;
01814     // Ensure result's buffer is sufficiently big.
01815     char *temp = (char *) malloc(argLen + 1);
01816     if ( !temp ) return;
01817     // Now get the string contents.
01818     WinDdeQueryString( args, temp, argLen + 1, CP_WINANSI );
01819     // Parse out the given arg.
01820     ParseDDEArg(temp, index, aString);
01821     free(temp);
01822 }
01823 
01824 void nsNativeAppSupportOS2::ActivateLastWindow() {
01825     nsCOMPtr<nsIDOMWindowInternal> navWin;
01826     GetMostRecentWindow( NS_LITERAL_STRING("navigator:browser").get(), getter_AddRefs( navWin ) );
01827     if ( navWin ) {
01828         // Activate that window.
01829         activateWindow( navWin );
01830     } else {
01831         // Need to create a Navigator window, then.
01832         nsCOMPtr<nsIDOMWindow> newWin;
01833         OpenBrowserWindow( "about:blank", PR_TRUE, getter_AddRefs( newWin ) );
01834     }
01835 }
01836 
01837 HDDEDATA nsNativeAppSupportOS2::CreateDDEData( DWORD value ) {
01838     return CreateDDEData( (LPBYTE)&value, sizeof value );
01839 }
01840 
01841 HDDEDATA nsNativeAppSupportOS2::CreateDDEData( LPBYTE value, DWORD len ) {
01842     HDDEDATA result = WinDdeCreateDataHandle( value,
01843                                               len,
01844                                               0,
01845                                               mApplication,
01846                                               CF_TEXT,
01847                                               0 );
01848     return result;
01849 }
01850 
01851 // Handle external request.  The first argument is the command line
01852 // received from the external client.  We convert that string to an
01853 // nsICmdLineService object via GetCmdLineArgs.  Then, we look for certain
01854 // well-known arguments.  This replicates code elsewhere, to some extent,
01855 // unfortunately (if you can fix that, please do).  The second argument may
01856 // be set to PR_TRUE to override the user's external link preference.
01857 nsresult
01858 nsNativeAppSupportOS2::HandleRequest( LPBYTE request, PRBool newWindow, nsIDOMWindow **aResult ) {
01859 
01860     // if initial hidden window is still being displayed, we need to ignore requests
01861     // because such requests might not function properly.  See bug 147223 for details
01862 
01863     if (mInitialWindowActive) {
01864       return NS_ERROR_FAILURE;
01865     }
01866 
01867     // Parse command line.
01868 
01869     nsCOMPtr<nsICmdLineService> args;
01870     nsresult rv;
01871 
01872     rv = GetCmdLineArgs( request, getter_AddRefs( args ) );
01873     if (NS_FAILED(rv)) return rv;
01874 
01875     nsCOMPtr<nsIAppStartup> appStartup (do_GetService(NS_APPSTARTUP_CONTRACTID, &rv));
01876     if (NS_FAILED(rv)) return rv;
01877 
01878     nsCOMPtr<nsINativeAppSupport> nativeApp;
01879     rv = appStartup->GetNativeAppSupport(getter_AddRefs( nativeApp ));
01880     if (NS_FAILED(rv)) return rv;
01881 
01882     // first see if there is a url
01883     nsXPIDLCString arg;
01884     rv = args->GetURLToLoad(getter_Copies(arg));
01885     if (NS_FAILED(rv)) return rv;
01886 
01887     if (!arg.IsEmpty() ) {
01888       // Launch browser.
01889 #if MOZ_DEBUG_DDE
01890       printf( "Launching browser on url [%s]...\n", arg.get() );
01891 #endif
01892       rv = nativeApp->EnsureProfile(args);
01893       if (NS_SUCCEEDED(rv))
01894         rv = OpenBrowserWindow( arg.get(), newWindow, aResult );
01895       return rv;
01896     }
01897 
01898 
01899     // ok, let's try the -chrome argument
01900     rv = args->GetCmdLineValue("-chrome", getter_Copies(arg));
01901     if (NS_SUCCEEDED(rv) && (const char*)arg ) {
01902       // Launch chrome.
01903 #if MOZ_DEBUG_DDE
01904       printf( "Launching chrome url [%s]...\n", (const char*)arg );
01905 #endif
01906       rv = nativeApp->EnsureProfile(args);
01907       if (NS_SUCCEEDED(rv))
01908         rv = OpenWindow( arg.get(), "", aResult );
01909       return rv;
01910     }
01911 
01912     // try for the "-profilemanager" argument, in which case we want the
01913     // profile manager to appear, but only if there are no windows open
01914 
01915     rv = args->GetCmdLineValue( "-profilemanager", getter_Copies(arg));
01916     if ( NS_SUCCEEDED(rv) && (const char*)arg ) { // -profilemanager on command line
01917       nsCOMPtr<nsIDOMWindowInternal> window;
01918       GetMostRecentWindow(0, getter_AddRefs(window));
01919       if (!window) { // there are no open windows
01920         mForceProfileStartup = PR_TRUE;
01921       }
01922     }
01923 
01924     // try for the "-kill" argument, to shut down the server
01925     rv = args->GetCmdLineValue( "-kill", getter_Copies(arg));
01926     if ( NS_SUCCEEDED(rv) && (const char*)arg ) {
01927       // Turn off server mode.
01928       nsCOMPtr<nsIAppStartup> appStartup
01929         (do_GetService(NS_APPSTARTUP_CONTRACTID, &rv));
01930       if (NS_FAILED(rv)) return rv;
01931 
01932       nsCOMPtr<nsINativeAppSupport> native;
01933       rv = appStartup->GetNativeAppSupport( getter_AddRefs( native ));
01934       if (NS_SUCCEEDED(rv)) {
01935         native->SetIsServerMode( PR_FALSE );
01936 
01937         // close app if there are no more top-level windows.
01938         rv = appStartup->Quit(nsIAppStartup::eConsiderQuit);
01939       }
01940 
01941       return rv;
01942     }
01943 
01944     // Try standard startup's command-line handling logic from nsAppRunner.cpp...
01945 
01946     // Need profile before opening windows.
01947     rv = nativeApp->EnsureProfile(args);
01948     if (NS_FAILED(rv)) return rv;
01949 
01950     // This will tell us whether the command line processing opened a window.
01951     PRBool windowOpened = PR_FALSE;
01952 
01953     // Process command line options.
01954     rv = DoCommandLines( args, &windowOpened );
01955 
01956     // If a window was opened, then we're done.
01957     // Note that we keep on trying in the unlikely event of an error.
01958     if (rv == NS_ERROR_NOT_AVAILABLE || rv == NS_ERROR_ABORT || windowOpened) {
01959       return NS_OK;
01960     }
01961 
01962     // ok, no idea what the param is.
01963 #if MOZ_DEBUG_DDE
01964     printf( "Unknown request [%s]\n", (char*) request );
01965 #endif
01966     // if all else fails, open a browser window
01967     const char * const contractID =
01968       "@mozilla.org/commandlinehandler/general-startup;1?type=browser";
01969     nsCOMPtr<nsICmdLineHandler> handler = do_GetService(contractID, &rv);
01970     if (NS_FAILED(rv)) return rv;
01971 
01972     nsXPIDLString defaultArgs;
01973     rv = handler->GetDefaultArgs(getter_Copies(defaultArgs));
01974     if (NS_FAILED(rv) || defaultArgs.IsEmpty()) return rv;
01975 
01976     NS_ConvertUTF16toUTF8 url( defaultArgs );
01977     return OpenBrowserWindow(url.get(), newWindow, aResult);
01978 }
01979 
01980 // Parse command line args according to MS spec
01981 // (see "Parsing C++ Command-Line Arguments" at
01982 // http://msdn.microsoft.com/library/devprods/vs6/visualc/vclang/_pluslang_parsing_c.2b2b_.command.2d.line_arguments.htm).
01983 nsresult
01984 nsNativeAppSupportOS2::GetCmdLineArgs( LPBYTE request, nsICmdLineService **aResult ) {
01985     nsresult rv = NS_OK;
01986 
01987     int justCounting = 1;
01988     char **argv = 0;
01989     // Flags, etc.
01990     int between = 1, quoted = 0, bSlashCount = 0;
01991     int argc = 0;
01992     char *p = (char*)request;
01993     nsCAutoString arg;
01994     // We loop if we've not finished the second pass through.
01995     while ( 1 ) {
01996         if ( between ) {
01997             // We are traversing whitespace between args.
01998             // Check for start of next arg.
01999             if (  *p != 0 && !isspace( *p ) ) {
02000                 // Start of another arg.
02001                 between = 0;
02002                 arg = "";
02003                 switch ( *p ) {
02004                     case '\\':
02005                         // Count the backslash.
02006                         bSlashCount = 1;
02007                         break;
02008                     case '"':
02009                         // Remember we're inside quotes.
02010                         quoted = 1;
02011                         break;
02012                     default:
02013                         // Add character to arg.
02014                         arg += *p;
02015                         break;
02016                 }
02017             } else {
02018                 // Another space between args, ignore it.
02019             }
02020         } else {
02021             // We are processing the contents of an argument.
02022             // Check for whitespace or end.
02023             if ( *p == 0 || ( !quoted && isspace( *p ) ) ) {
02024                 // Process pending backslashes (interpret them 
02025                 // literally since they're not followed by a ").
02026                 while( bSlashCount ) {
02027                     arg += '\\';
02028                     bSlashCount--;
02029                 }
02030                 // End current arg.
02031                 if ( !justCounting ) {
02032                     argv[argc] = new char[ arg.Length() + 1 ];
02033                     strcpy( argv[argc], arg.get() ); 
02034                 }
02035                 argc++;
02036                 // We're now between args.
02037                 between = 1;
02038             } else {
02039                 // Still inside argument, process the character.
02040                 switch ( *p ) {
02041                     case '"':
02042                         // First, digest preceding backslashes (if any).
02043                         while ( bSlashCount > 1 ) {
02044                             // Put one backsplash in arg for each pair.
02045                             arg += '\\';
02046                             bSlashCount -= 2;
02047                         }
02048                         if ( bSlashCount ) {
02049                             // Quote is literal.
02050                             arg += '"';
02051                             bSlashCount = 0;
02052                         } else {
02053                             // Quote starts or ends a quoted section.
02054                             if ( quoted ) {
02055                                 // Check for special case of consecutive double
02056                                 // quotes inside a quoted section.
02057                                 if ( *(p+1) == '"' ) {
02058                                     // This implies a literal double-quote.  Fake that
02059                                     // out by causing next double-quote to look as
02060                                     // if it was preceded by a backslash.
02061                                     bSlashCount = 1;
02062                                 } else {
02063                                     quoted = 0;
02064                                 }
02065                             } else {
02066                                 quoted = 1;
02067                             }
02068                         }
02069                         break;
02070                     case '\\':
02071                         // Add to count.
02072                         bSlashCount++;
02073                         break;
02074                     default:
02075                         // Accept any preceding backslashes literally.
02076                         while ( bSlashCount ) {
02077                             arg += '\\';
02078                             bSlashCount--;
02079                         }
02080                         // Just add next char to the current arg.
02081                         arg += *p;
02082                         break;
02083                 }
02084             }
02085         }
02086         // Check for end of input.
02087         if ( *p ) {
02088             // Go to next character.
02089             p++;
02090         } else {
02091             // If on first pass, go on to second.
02092             if ( justCounting ) {
02093                 // Allocate argv array.
02094                 argv = new char*[ argc ];
02095     
02096                 // Start second pass, initialize again
02097                 justCounting = 0;
02098                 p = (char*)request;
02099                 between = 1;
02100                 argc = quoted = bSlashCount = 0;
02101             } else {
02102                 // Quit.
02103                 break;
02104             }
02105         }
02106     }
02107 
02108     nsCOMPtr<nsIComponentManager> compMgr;
02109     NS_GetComponentManager(getter_AddRefs(compMgr));
02110     rv = compMgr->CreateInstanceByContractID(
02111                     NS_COMMANDLINESERVICE_CONTRACTID,
02112                     nsnull, NS_GET_IID(nsICmdLineService),
02113                     (void**) aResult);
02114 
02115     if ( NS_FAILED( rv ) || NS_FAILED( ( rv = (*aResult)->Initialize( argc, argv ) ) ) ) {
02116 #if MOZ_DEBUG_DDE
02117         printf( "Error creating command line service = 0x%08X (argc=%d, argv=0x%08X)\n", (int)rv, (int)argc, (int)argv );
02118 #endif
02119     }
02120 
02121     // Cleanup.
02122     while ( argc ) {
02123         delete [] argv[ --argc ];
02124     }
02125     delete [] argv;
02126 
02127     return rv;
02128 }
02129 
02130 // Check to see if we have a profile. We will not have a profile
02131 // at this point if we were launched invisibly in -turbo mode, and
02132 // the profile mgr needed to show UI (to pick from multiple profiles).
02133 // At this point, we can show UI, so call DoProfileStartUp().
02134 nsresult
02135 nsNativeAppSupportOS2::EnsureProfile(nsICmdLineService* args)
02136 {
02137   nsresult rv;  
02138 
02139   nsCOMPtr<nsIProfileInternal> profileMgr(do_GetService(NS_PROFILE_CONTRACTID, &rv));
02140   if (NS_FAILED(rv)) return rv;
02141   nsCOMPtr<nsIAppStartup> appStartup(do_GetService(NS_APPSTARTUP_CONTRACTID, &rv));
02142   if (NS_FAILED(rv)) return rv;
02143 
02144   // If we have a profile, everything is fine.
02145   // unless mForceProfileStartup is TRUE. This flag is set when the
02146   // last window is closed in -turbo mode. When TRUE, it forces the
02147   // profile UI to come up at the beginning of the next -turbo session
02148   // even if we currently have a profile.
02149   PRBool haveProfile;
02150   rv = profileMgr->IsCurrentProfileAvailable(&haveProfile);
02151   if (!mForceProfileStartup && NS_SUCCEEDED(rv) && haveProfile)
02152       return NS_OK;
02153  
02154   // If the profile selection is happening, fail.
02155   PRBool doingProfileStartup;
02156   rv = profileMgr->GetIsStartingUp(&doingProfileStartup);
02157   if (NS_FAILED(rv) || doingProfileStartup) return NS_ERROR_FAILURE;
02158 
02159   // See if profile manager is being suppressed via -silent flag.
02160   PRBool canInteract = PR_TRUE;
02161   nsXPIDLCString arg;
02162   if (NS_SUCCEEDED(args->GetCmdLineValue("-silent", getter_Copies(arg)))) {
02163     if (!arg.IsEmpty()) {
02164       canInteract = PR_FALSE;
02165     }
02166   }
02167   rv = appStartup->DoProfileStartup(args, canInteract);
02168 
02169   mForceProfileStartup = PR_FALSE;
02170 
02171   return rv;
02172 }
02173 
02174 nsresult
02175 nsNativeAppSupportOS2::OpenWindow( const char*urlstr, const char *args, nsIDOMWindow **aResult ) {
02176 
02177   nsresult rv = NS_ERROR_FAILURE;
02178 
02179   nsCOMPtr<nsIWindowWatcher> wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID));
02180   nsCOMPtr<nsISupportsCString> sarg(do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID));
02181   if (sarg)
02182     sarg->SetData(nsDependentCString(args));
02183 
02184   if (wwatch && sarg) {
02185     rv = wwatch->OpenWindow(0, urlstr, "_blank", "chrome,dialog=no,all",
02186                    sarg, aResult);
02187 #if MOZ_DEBUG_DDE
02188   } else {
02189       printf("Get WindowWatcher (or create string) failed\n");
02190 #endif
02191   }
02192   return rv;
02193 }
02194 
02195 HWND hwndForDOMWindow( nsISupports *window ) {
02196     nsCOMPtr<nsIScriptGlobalObject> ppScriptGlobalObj( do_QueryInterface(window) );
02197     if ( !ppScriptGlobalObj ) {
02198         return 0;
02199     }
02200 
02201     nsCOMPtr<nsIBaseWindow> ppBaseWindow =
02202       do_QueryInterface( ppScriptGlobalObj->GetDocShell() );
02203     if ( !ppBaseWindow ) {
02204         return 0;
02205     }
02206 
02207     nsCOMPtr<nsIWidget> ppWidget;
02208     ppBaseWindow->GetMainWidget( getter_AddRefs( ppWidget ) );
02209 
02210     return (HWND)( ppWidget->GetNativeData( NS_NATIVE_WIDGET ) );
02211 }
02212 
02213 nsresult
02214 nsNativeAppSupportOS2::ReParent( nsISupports *window, HWND newParent ) {
02215     HWND hMainClient = hwndForDOMWindow( window );
02216     if ( !hMainClient ) {
02217         return NS_ERROR_FAILURE;
02218     }
02219     HWND hMainFrame = WinQueryWindow(hMainClient, QW_PARENT);
02220 
02221     // Reset the parent.
02222     WinSetParent( hMainFrame, newParent, FALSE );
02223 
02224     return NS_OK;
02225 }
02226 
02227 static const char sJSStackContractID[] = "@mozilla.org/js/xpc/ContextStack;1";
02228 
02229 class SafeJSContext {
02230 public:
02231   SafeJSContext();
02232   ~SafeJSContext();
02233 
02234   nsresult   Push();
02235   JSContext *get() { return mContext; }
02236 
02237 protected:
02238   nsCOMPtr<nsIThreadJSContextStack>  mService;
02239   JSContext                         *mContext;
02240 };
02241 
02242 SafeJSContext::SafeJSContext() : mContext(nsnull) {
02243 }
02244 
02245 SafeJSContext::~SafeJSContext() {
02246   JSContext *cx;
02247   nsresult   rv;
02248 
02249   if(mContext) {
02250     rv = mService->Pop(&cx);
02251     NS_ASSERTION(NS_SUCCEEDED(rv) && cx == mContext, "JSContext push/pop mismatch");
02252   }
02253 }
02254 
02255 nsresult SafeJSContext::Push() {
02256   if (mContext) // only once
02257     return NS_ERROR_FAILURE;
02258 
02259   mService = do_GetService(sJSStackContractID);
02260   if(mService) {
02261     JSContext *cx;
02262     if (NS_SUCCEEDED(mService->GetSafeJSContext(&cx)) &&
02263         cx &&
02264         NS_SUCCEEDED(mService->Push(cx))) {
02265       // Save cx in mContext to indicate need to pop.
02266       mContext = cx;
02267     }
02268   }
02269   return mContext ? NS_OK : NS_ERROR_FAILURE;
02270 }
02271 
02272 
02273 nsresult
02274 nsNativeAppSupportOS2::OpenBrowserWindow( const char *args, PRBool newWindow, nsIDOMWindow **aResult ) {
02275     nsresult rv = NS_OK;
02276     // Open the argument URL according to the external link preference.
02277     // If there is no Nav window, or newWindow is PR_TRUE, open a new one.
02278     
02279     // Get most recently used Nav window.
02280     nsCOMPtr<nsIDOMWindowInternal> navWin;
02281     GetMostRecentWindow( NS_LITERAL_STRING( "navigator:browser" ).get(), getter_AddRefs( navWin ) );
02282 
02283     // This isn't really a loop.  We just use "break" statements to fall
02284     // out to the OpenWindow call when things go awry.
02285     do {
02286         // If caller requires a new window, then don't use an existing one.
02287         if ( newWindow ) {
02288             break;
02289         }
02290         if ( !navWin ) {
02291             // Have to open a new one.
02292             break;
02293         }
02294         nsCOMPtr<nsIDOMChromeWindow> chromeWin( do_QueryInterface( navWin ) );
02295         if ( !chromeWin ) {
02296             break;
02297         }
02298         nsCOMPtr<nsIBrowserDOMWindow> bwin;
02299         chromeWin->GetBrowserDOMWindow( getter_AddRefs( bwin ) );
02300         if ( !bwin ) {
02301             break;
02302         }
02303         nsCOMPtr<nsIIOService> io( do_GetService( "@mozilla.org/network/io-service;1" ) );
02304         if ( !io ) {
02305             break;
02306         }
02307         nsCOMPtr<nsIURI> uri;
02308         io->NewURI( nsDependentCString( args ), nsnull, nsnull, getter_AddRefs( uri ) );
02309         if ( !uri ) {
02310             break;
02311         }
02312         return bwin->OpenURI( uri, nsnull, nsIBrowserDOMWindow::OPEN_DEFAULTWINDOW, nsIBrowserDOMWindow::OPEN_EXTERNAL, aResult );
02313     } while ( PR_FALSE );
02314 
02315     nsCOMPtr<nsICmdLineHandler> handler(do_GetService("@mozilla.org/commandlinehandler/general-startup;1?type=browser", &rv));
02316     if (NS_FAILED(rv)) return rv;
02317 
02318     nsXPIDLCString chromeUrlForTask;
02319     rv = handler->GetChromeUrlForTask(getter_Copies(chromeUrlForTask));
02320     if (NS_FAILED(rv)) return rv;
02321 
02322     // Last resort is to open a brand new window.
02323     return OpenWindow( chromeUrlForTask.get(), args, aResult );
02324 }
02325 
02326 //   This opens a special browser window for purposes of priming the pump for
02327 //   server mode (getting stuff into the caching, loading .dlls, etc.).  The
02328 //   window will have these attributes:
02329 //     - Load about:blank (no home page)
02330 //     - No toolbar (so there's no sidebar panels loaded, either)
02331 //     - Pass magic arg to cause window to close in onload handler.
02332 NS_IMETHODIMP
02333 nsNativeAppSupportOS2::StartServerMode() {
02334 
02335     if (mShouldShowUI) {
02336         // We dont have to anything anymore. The native UI
02337         // will create the window
02338         return NS_OK;
02339     } else {
02340         // Sometimes a window will have been opened even though mShouldShowUI is false
02341         // (e.g., "seamonkey -mail -turbo").  Detect that by testing whether there's a
02342         // window already open.
02343         nsCOMPtr<nsIDOMWindowInternal> win;
02344         GetMostRecentWindow( 0, getter_AddRefs( win ) );
02345         if ( win ) {
02346             // Window already opened, don't need this special Nav window.
02347             return NS_OK;
02348         }
02349     }
02350 
02351     // Since native UI wont create any window, we create a hidden window
02352     // so thing work alright.
02353 
02354     // Create some of the objects we'll need.
02355     nsCOMPtr<nsIWindowWatcher>   ww(do_GetService(NS_WINDOWWATCHER_CONTRACTID));
02356     nsCOMPtr<nsISupportsString> arg1(do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID));
02357     nsCOMPtr<nsISupportsString> arg2(do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID));
02358     if ( !ww || !arg1 || !arg2 ) {
02359         return NS_OK;
02360     }
02361 
02362     // Create the array for the arguments.
02363     nsCOMPtr<nsISupportsArray> argArray = do_CreateInstance(NS_SUPPORTSARRAY_CONTRACTID);
02364     if ( !argArray ) {
02365         return NS_OK;
02366     }
02367 
02368     // arg1 is the url to load.
02369     // arg2 is the string that tells navigator.js to auto-close.
02370     arg1->SetData( NS_LITERAL_STRING( "about:blank" ) );
02371     arg2->SetData( NS_LITERAL_STRING( "turbo=yes" ) );
02372 
02373     // Put args into array.
02374     if ( NS_FAILED( argArray->AppendElement( arg1 ) ) ||
02375         NS_FAILED( argArray->AppendElement( arg2 ) ) ) {
02376         return NS_OK;
02377     }
02378 
02379     // Now open the window.
02380     nsCOMPtr<nsIDOMWindow> newWindow;
02381     ww->OpenWindow( 0,
02382         "chrome://navigator/content",
02383         "_blank",
02384         "chrome,dialog=no,toolbar=no",
02385         argArray,
02386         getter_AddRefs( newWindow ) );
02387 
02388     if ( !newWindow ) {
02389         return NS_OK;
02390     }
02391     mInitialWindowActive = PR_TRUE;
02392 
02393     // Hide this window by re-parenting it (to ensure it doesn't appear).
02394     ReParent( newWindow, MessageWindow().getHWND() );
02395 
02396     return NS_OK;
02397 }
02398 
02399 NS_IMETHODIMP
02400 nsNativeAppSupportOS2::SetIsServerMode( PRBool isServerMode ) {
02401     return nsNativeAppSupportBase::SetIsServerMode( isServerMode );
02402 }
02403 
02404 NS_IMETHODIMP
02405 nsNativeAppSupportOS2::OnLastWindowClosing() {
02406  
02407     if ( !mServerMode )
02408         return NS_OK;
02409 
02410     // If the last window closed is our special "turbo" window made
02411     // in StartServerMode(), don't do anything.
02412     if ( mInitialWindowActive ) {
02413         mInitialWindowActive = PR_FALSE;
02414         return NS_OK;
02415     }
02416 
02417     nsresult rv;
02418 
02419     // If activated by the browser.turbo.singleProfileOnly pref,
02420     // check for multi-profile situation and turn off turbo mode
02421     // if there are multiple profiles.
02422     PRBool singleProfileOnly = PR_FALSE;
02423     nsCOMPtr<nsIPrefBranch> prefService( do_GetService( NS_PREFSERVICE_CONTRACTID, &rv ) );
02424     if ( NS_SUCCEEDED( rv ) ) {
02425         prefService->GetBoolPref( "browser.turbo.singleProfileOnly", &singleProfileOnly );
02426     }
02427     if ( singleProfileOnly ) {
02428         nsCOMPtr<nsIProfile> profileMgr( do_GetService( NS_PROFILE_CONTRACTID, &rv ) );
02429         if ( NS_SUCCEEDED( rv ) ) {
02430             PRInt32 profileCount = 0;
02431             if ( NS_SUCCEEDED( profileMgr->GetProfileCount( &profileCount ) ) &&
02432                  profileCount > 1 ) {
02433                 // Turn off turbo mode and quit the application.
02434                 SetIsServerMode( PR_FALSE );
02435                 nsCOMPtr<nsIAppStartup> appStartup
02436                     (do_GetService(NS_APPSTARTUP_CONTRACTID, &rv));
02437                 if ( NS_SUCCEEDED( rv ) ) {
02438                     appStartup->Quit(nsIAppStartup::eAttemptQuit);
02439                 }
02440                 return NS_OK;
02441             }
02442         }
02443     }
02444 
02445     nsCOMPtr<nsIObserverService> observerService(do_GetService("@mozilla.org/observer-service;1", &rv));
02446     if (NS_FAILED(rv)) return rv;
02447     observerService->NotifyObservers(nsnull, "session-logout", nsnull);
02448 
02449     mForceProfileStartup = PR_TRUE;
02450 
02451     return NS_OK;
02452 }
02453 
02454 // This is a public function called by nsAppRunner.cpp.  Its primary
02455 // purpose is to determine if any commandline options require a VIO
02456 // ("console") window.  If so and one isn't present, it will restart
02457 // the app in a VIO session.  It is intended to be called as early as
02458 // possible during startup and before any other commandline processing.
02459 // It returns TRUE if the current instance should continue running and
02460 // FALSE if it should terminate upon this function's return.
02461 
02462 PRBool     StartOS2App( int aArgc, char **aArgv)
02463 {
02464   PRBool    rv = PR_TRUE;
02465   PPIB      ppib;
02466   PTIB      ptib;
02467 
02468   DosGetInfoBlocks(&ptib, &ppib);
02469 
02470   // if this isn't a PM session, reset the session type to enable use
02471   // of PM functions;  if it is PM, look for args that require a VIO
02472   // session - however, ignore them if any of the turbo-mode args are
02473   // also present (why restart as VIO just to launch a detached process)
02474   if (ppib->pib_ultype != SSF_TYPE_PM)
02475     ppib->pib_ultype = SSF_TYPE_PM;
02476   else {
02477     for (int i = 1; i < aArgc; i++ ) {
02478       char *arg = aArgv[i];
02479       if (*arg != '-' && *arg != '/')
02480         continue;
02481       arg++;
02482 
02483       // as soon as we find a turbo-mode arg, we're done;  OTOH, if we
02484       // find a VIO-mode arg, keep looking so we don't miss a turbo arg
02485       if (stricmp("turbo", arg) == 0 ||
02486         stricmp("server", arg)  == 0 ||
02487         stricmp("kill", arg)    == 0 ||
02488         stricmp("killall", arg) == 0) {
02489         rv = PR_TRUE;
02490         break;
02491       }
02492       else {
02493         if (stricmp("?", arg)  == 0 ||
02494           stricmp("h", arg)    == 0 ||
02495           stricmp("v", arg)    == 0 ||
02496           stricmp("help", arg) == 0 ||
02497           stricmp("version", arg) == 0 ||
02498           stricmp("console", arg) == 0)
02499           rv = PR_FALSE;
02500       }
02501     }
02502   }
02503 
02504   // if the session type is OK, increase the number of 
02505   // file handles available to the app, then exit
02506   if (rv) {
02507     ULONG    ulMaxFH = 0;
02508     LONG     ulReqCount = 0;
02509 
02510     DosSetRelMaxFH(&ulReqCount, &ulMaxFH);
02511     if (ulMaxFH < 256)
02512       DosSetMaxFH(256);
02513 
02514     return rv;
02515   }
02516 
02517   // the app has to be restarted in a VIO session
02518   char        szErrObj[64] = "";
02519   STARTDATA   x;
02520 
02521   memset(&x, 0, sizeof(x));
02522   x.Length = sizeof(x);
02523   x.InheritOpt = SSF_INHERTOPT_PARENT;
02524   x.SessionType = SSF_TYPE_WINDOWABLEVIO;
02525   x.PgmControl = SSF_CONTROL_NOAUTOCLOSE;
02526   x.ObjectBuffer = szErrObj;
02527   x.ObjectBuffLen = sizeof(szErrObj);
02528 
02529   // the f/q exename is the string preceding ppib->pib_pchcmd;
02530   // the original commandline is the string following it
02531   char * ptr = ppib->pib_pchcmd - 2;
02532   while (*ptr)
02533     ptr--;
02534   x.PgmName = ptr + 1;
02535   x.PgmInputs = strchr(ppib->pib_pchcmd, 0) + 1;
02536 
02537   // restart the app;  if this session is in the background, trying
02538   // to start in the foreground will produce an error, but the app
02539   // will still start; if DosStartSession has a real failure, forget
02540   // the console and let the current instance keep running
02541   ULONG ulSession;
02542   PID   pid;
02543   ULONG rc = DosStartSession(&x, &ulSession, &pid);
02544   if (rc && rc != ERROR_SMG_START_IN_BACKGROUND)
02545     rv = PR_TRUE;
02546 
02547   return rv;
02548 }
02549