Back to index

lightning-sunbird  0.9+nobinonly
nsNativeAppSupportOS2.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 the GNU General Public License Version 2 or later (the "GPL"), or
00029  * 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 "nsNativeAppSupportBase.h"
00048 #include "nsNativeAppSupportOS2.h"
00049 #include "nsAppRunner.h"
00050 #include "nsXULAppAPI.h"
00051 #include "nsString.h"
00052 #include "nsIBrowserDOMWindow.h"
00053 #include "nsICommandLineRunner.h"
00054 #include "nsCOMPtr.h"
00055 #include "nsXPIDLString.h"
00056 #include "nsIComponentManager.h"
00057 #include "nsIServiceManager.h"
00058 #include "nsIDOMWindow.h"
00059 #include "nsIDOMChromeWindow.h"
00060 #include "nsXPCOM.h"
00061 #include "nsISupportsPrimitives.h"
00062 #include "nsISupportsArray.h"
00063 #include "nsIWindowWatcher.h"
00064 #include "nsIDOMWindowInternal.h"
00065 #include "nsIScriptGlobalObject.h"
00066 #include "nsIDocShell.h"
00067 #include "nsIDocShellTreeItem.h"
00068 #include "nsIBaseWindow.h"
00069 #include "nsIWidget.h"
00070 #include "nsIAppShellService.h"
00071 #include "nsIProfileInternal.h"
00072 #include "nsIXULWindow.h"
00073 #include "nsIInterfaceRequestor.h"
00074 #include "nsIInterfaceRequestorUtils.h"
00075 #include "nsIPromptService.h"
00076 #include "nsNetCID.h"
00077 #include "nsNetUtil.h"
00078 #include "nsIObserver.h"
00079 #include "nsIObserverService.h"
00080 //#include "nsXPCOM.h"
00081 #ifdef MOZ_PHOENIX
00082 #include "nsIShellService.h"
00083 #endif
00084 
00085 // These are needed to load a URL in a browser window.
00086 #include "nsIDOMLocation.h"
00087 #include "nsIJSContextStack.h"
00088 #include "nsIWebNavigation.h"
00089 #include "nsIWindowMediator.h"
00090 
00091 #include <stdlib.h>
00092 #include "prprf.h"
00093 
00094 #define kMailtoUrlScheme "mailto:"
00095 
00096 /* trying to keep this like Window's COPYDATASTRUCT, but a compiler error is
00097  * forcing me to add chBuff so that we can append the data to the end of the
00098  * structure
00099  */
00100 typedef struct _COPYDATASTRUCT
00101 {
00102    ULONG   dwData;
00103    ULONG   cbData;
00104    PVOID   lpData;
00105    CHAR    chBuff;
00106 }COPYDATASTRUCT, *PCOPYDATASTRUCT;
00107 #define WM_COPYDATA             (WM_USER + 60)
00108 
00109 char szCommandLine[2*CCHMAXPATH];
00110 
00111 static HWND hwndForDOMWindow( nsISupports * );
00112 
00113 static
00114 nsresult
00115 GetMostRecentWindow(const PRUnichar* aType, nsIDOMWindowInternal** aWindow) {
00116     nsresult rv;
00117     nsCOMPtr<nsIWindowMediator> med( do_GetService( NS_WINDOWMEDIATOR_CONTRACTID, &rv ) );
00118     if ( NS_FAILED( rv ) )
00119         return rv;
00120 
00121     if ( med )
00122         return med->GetMostRecentWindow( aType, aWindow );
00123 
00124     return NS_ERROR_FAILURE;
00125 }
00126 
00127 static
00128 void
00129 activateWindow( nsIDOMWindowInternal *win ) {
00130     // Try to get native window handle.
00131     HWND hwnd = hwndForDOMWindow( win );
00132     if ( hwnd ) {
00133 
00134         /* if we are looking at a client window, then we really probably want
00135          * the frame so that we can manipulate THAT
00136          */
00137         LONG id = WinQueryWindowUShort( hwnd, QWS_ID );
00138         if( id == FID_CLIENT )
00139         {
00140            hwnd = WinQueryWindow( hwnd, QW_PARENT );
00141         }
00142 
00143         // Restore the window in case it is minimized.
00144         // Use the OS call, if possible.
00145         WinSetWindowPos( hwnd, 0L, 0L, 0L, 0L, 0L, 
00146                          SWP_SHOW | SWP_RESTORE | SWP_ACTIVATE );
00147     } else {
00148         // Use internal method.
00149         win->Focus();
00150     }
00151 }
00152 
00153 
00154 #ifdef DEBUG_law
00155 #undef MOZ_DEBUG_DDE
00156 #define MOZ_DEBUG_DDE 1
00157 #endif
00158 
00159 // Simple Win32 mutex wrapper.
00160 struct Mutex {
00161     Mutex( const char *name )
00162         : mName( name ),
00163           mHandle( 0 ),
00164           mState( 0xFFFF ) {
00165         /* OS/2 named semaphores must begin with "\\SEM32\\" to be valid */
00166         mName.Insert("\\SEM32\\", 0);
00167         APIRET rc = DosCreateMutexSem(mName.get(), &mHandle, 0, FALSE);
00168         if (rc != NO_ERROR) {
00169 #if MOZ_DEBUG_DDE
00170             printf("CreateMutex error = 0x%08X\n", (int)rc);
00171 #endif
00172         }
00173     }
00174     ~Mutex() {
00175         if ( mHandle ) {
00176             // Make sure we release it if we own it.
00177             Unlock();
00178 
00179             APIRET rc = DosCloseMutexSem(mHandle);
00180             if (rc != NO_ERROR) {
00181 #if MOZ_DEBUG_DDE
00182                 printf("CloseHandle error = 0x%08X\n", (int)rc);
00183 #endif
00184             }
00185         }
00186     }
00187     BOOL Lock( DWORD timeout ) {
00188         if ( mHandle ) {
00189 #if MOZ_DEBUG_DDE
00190             printf( "Waiting (%d msec) for DDE mutex...\n", (int)timeout );
00191 #endif
00192             mState = DosRequestMutexSem( mHandle, timeout );
00193 #if MOZ_DEBUG_DDE
00194             printf( "...wait complete, result = 0x%08X\n", (int)mState );
00195 #endif
00196             return (mState == NO_ERROR);
00197         } else {
00198             return FALSE;
00199         }
00200     }
00201     void Unlock() {
00202         if ( mHandle && mState == NO_ERROR ) {
00203 #if MOZ_DEBUG_DDE
00204             printf( "Releasing DDE mutex\n" );
00205 #endif
00206             DosReleaseMutexSem( mHandle );
00207             mState = 0xFFFF;
00208         }
00209     }
00210 private:
00211     nsCString mName;
00212     HMTX      mHandle;
00213     DWORD     mState;
00214 };
00215 
00216 /* DDE Notes
00217  *
00218  * This section describes the Win32 DDE service implementation for
00219  * Mozilla.  DDE is used on Win32 platforms to communicate between
00220  * separate instances of mozilla.exe (or other Mozilla-based
00221  * executables), or, between the Win32 desktop shell and Mozilla.
00222  *
00223  * The first instance of Mozilla will become the "server" and
00224  * subsequent executables (and the shell) will use DDE to send
00225  * requests to that process.  The requests are DDE "execute" requests
00226  * that pass the command line arguments.
00227  *
00228  * Mozilla registers the DDE application "Mozilla" and currently
00229  * supports only the "WWW_OpenURL" topic.  This should be reasonably
00230  * compatible with applications that interfaced with Netscape
00231  * Communicator (and its predecessors?).  Note that even that topic
00232  * may not be supported in a compatible fashion as the command-line
00233  * options for Mozilla are different than for Communiator.
00234  *
00235  * It is imperative that at most one instance of Mozilla execute in
00236  * "server mode" at any one time.  The "native app support" in Mozilla
00237  * on Win32 ensures that only the server process performs XPCOM
00238  * initialization (that is not required for subsequent client processes
00239  * to communicate with the server process).
00240  *
00241  * To guarantee that only one server starts up, a Win32 "mutex" is used
00242  * to ensure only one process executes the server-detection code.  That
00243  * code consists of initializing DDE and doing a DdeConnect to Mozilla's
00244  * application/topic.  If that connection succeeds, then a server process
00245  * must be running already.
00246  *
00247  * Otherwise, no server has started.  In that case, the current process
00248  * calls DdeNameService to register that application/topic.  Only at that
00249  * point does the mutex get released.
00250  *
00251  * There are a couple of subtleties that one should be aware of:
00252  *
00253  * 1. It is imperative that DdeInitialize be called only after the mutex
00254  *    lock has been obtained.  The reason is that at shutdown, DDE
00255  *    notifications go out to all initialized DDE processes.  Thus, if
00256  *    the mutex is owned by a terminating intance of Mozilla, then
00257  *    calling DdeInitialize and then WaitForSingleObject will cause the
00258  *    DdeUninitialize from the terminating process to "hang" until the
00259  *    process waiting for the mutex times out (and can then service the
00260  *    notification that the DDE server is terminating).  So, don't mess
00261  *    with the sequence of things in the startup/shutdown logic.
00262  *
00263  * 2. All mutex requests are made with a reasonably long timeout value and
00264  *    are designed to "fail safe" (i.e., a timeout is treated as failure).
00265  *
00266  * 3. An attempt has been made to minimize the degree to which the main
00267  *    Mozilla application logic needs to be aware of the DDE mechanisms
00268  *    implemented herein.  As a result, this module surfaces a very
00269  *    large-grained interface, consisting of simple start/stop methods.
00270  *    As a consequence, details of certain scenarios can be "lost."
00271  *    Particularly, incoming DDE requests can arrive after this module
00272  *    initiates the DDE server, but before Mozilla is initialized to the
00273  *    point where those requests can be serviced (e.g., open a browser
00274  *    window to a particular URL).  Since the client process sends the
00275  *    request early on, it may not be prepared to respond to that error.
00276  *    Thus, such situations may fail silently.  The design goal is that
00277  *    they fail harmlessly.  Refinements on this point will be made as
00278  *    details emerge (and time permits).
00279  */
00280 
00281 /* Update 2001 March
00282  *
00283  * A significant DDE bug in Windows is causing Mozilla to get wedged at
00284  * startup.  This is detailed in Bugzill bug 53952
00285  * (http://bugzilla.mozilla.org/show_bug.cgi?id=53952).
00286  *
00287  * To resolve this, we are using a new strategy:
00288  *   o Use a "message window" to detect that Mozilla is already running and
00289  *     to pass requests from a second instance back to the first;
00290  *   o Run only as a "DDE server" (not as DDE client); this avoids the
00291  *     problematic call to DDEConnect().
00292  *
00293  * We still use the mutex semaphore to protect the code that detects
00294  * whether Mozilla is already running.
00295  */
00296 
00297 class nsNativeAppSupportOS2 : public nsNativeAppSupportBase,
00298                               public nsIObserver
00299 {
00300 public:
00301     NS_DECL_NSIOBSERVER
00302     NS_DECL_ISUPPORTS_INHERITED
00303 
00304     // Overrides of base implementation.
00305     NS_IMETHOD Start( PRBool *aResult );
00306     NS_IMETHOD Stop( PRBool *aResult );
00307     NS_IMETHOD Quit();
00308     NS_IMETHOD Enable();
00309 
00310     // The "old" Start method (renamed).
00311     NS_IMETHOD StartDDE();
00312 
00313     // Utility function to handle a Win32-specific command line
00314     // option: "-console", which dynamically creates a Windows
00315     // console.
00316     void CheckConsole();
00317 
00318 private:
00319     static HDDEDATA APIENTRY HandleDDENotification( ULONG    idInst,
00320                                                     USHORT   uType,
00321                                                     USHORT   uFmt,
00322                                                     HCONV    hconv,
00323                                                     HSZ      hsz1,
00324                                                     HSZ      hsz2,
00325                                                     HDDEDATA hdata,
00326                                                     ULONG    dwData1,
00327                                                     ULONG    dwData2 );
00328     static void HandleCommandLine(const char* aCmdLineString, nsIFile* aWorkingDir, PRUint32 aState);
00329     static void ParseDDEArg( HSZ args, int index, nsCString& string);
00330     static void ParseDDEArg( const char* args, int index, nsCString& aString);
00331     static void ActivateLastWindow();
00332     static HDDEDATA CreateDDEData( DWORD value );
00333     static HDDEDATA CreateDDEData( LPBYTE value, DWORD len );
00334     static PRBool   InitTopicStrings();
00335     static int      FindTopic( HSZ topic );
00336     static nsresult OpenWindow( const char *urlstr, const char *args );
00337     static nsresult OpenBrowserWindow();
00338 
00339     static int   mConversations;
00340     enum {
00341         topicOpenURL,
00342         topicActivate,
00343         topicCancelProgress,
00344         topicVersion,
00345         topicRegisterViewer,
00346         topicUnRegisterViewer,
00347         topicGetWindowInfo,
00348         // Note: Insert new values above this line!!!!!
00349         topicCount // Count of the number of real topics
00350     };
00351 
00352     static HSZ   mApplication, mTopics[ topicCount ];
00353     static DWORD mInstance;
00354     static PRBool mCanHandleRequests;
00355     static char mMutexName[];
00356     static PRBool mUseDDE;
00357     friend struct MessageWindow;
00358 }; // nsNativeAppSupportOS2
00359 
00360 NS_INTERFACE_MAP_BEGIN(nsNativeAppSupportOS2)
00361     NS_INTERFACE_MAP_ENTRY(nsIObserver)
00362 NS_INTERFACE_MAP_END_INHERITING(nsNativeAppSupportBase)
00363 
00364 NS_IMPL_ADDREF_INHERITED(nsNativeAppSupportOS2, nsNativeAppSupportBase)
00365 NS_IMPL_RELEASE_INHERITED(nsNativeAppSupportOS2, nsNativeAppSupportBase)
00366 
00367 void
00368 nsNativeAppSupportOS2::CheckConsole() {
00369     return;
00370 }
00371 
00372 
00373 // Create and return an instance of class nsNativeAppSupportOS2.
00374 nsresult
00375 NS_CreateNativeAppSupport( nsINativeAppSupport **aResult ) {
00376     nsNativeAppSupportOS2 *pNative = new nsNativeAppSupportOS2;
00377     if (!pNative) return NS_ERROR_OUT_OF_MEMORY;
00378 
00379     // Check for dynamic console creation request.
00380     pNative->CheckConsole();
00381 
00382     *aResult = pNative;
00383     NS_ADDREF( *aResult );
00384 
00385     return NS_OK;
00386 }
00387 
00388 // Constants
00389 #define MOZ_DDE_APPLICATION    "Mozilla"
00390 #define MOZ_STARTUP_MUTEX_NAME "StartupMutex"
00391 #define MOZ_DDE_START_TIMEOUT 30000
00392 #define MOZ_DDE_STOP_TIMEOUT  15000
00393 #define MOZ_DDE_EXEC_TIMEOUT  15000
00394 
00395 // The array entries must match the enum ordering!
00396 const char * const topicNames[] = { "WWW_OpenURL",
00397                                     "WWW_Activate",
00398                                     "WWW_CancelProgress",
00399                                     "WWW_Version",
00400                                     "WWW_RegisterViewer",
00401                                     "WWW_UnRegisterViewer",
00402                                     "WWW_GetWindowInfo" };
00403 
00404 // Static member definitions.
00405 int   nsNativeAppSupportOS2::mConversations = 0;
00406 HSZ   nsNativeAppSupportOS2::mApplication   = 0;
00407 HSZ   nsNativeAppSupportOS2::mTopics[nsNativeAppSupportOS2::topicCount] = { 0 };
00408 DWORD nsNativeAppSupportOS2::mInstance      = 0;
00409 PRBool nsNativeAppSupportOS2::mCanHandleRequests   = PR_FALSE;
00410 
00411 // Added this as pmddeml has no api like this
00412 int DdeCmpStringHandles( HSZ hsz1, HSZ hsz2 )
00413 {
00414   char chhsz1[CCHMAXPATH], chhsz2[CCHMAXPATH];
00415   int rc = -1;
00416 
00417   /* I am assuming that the strings will never be more that CCHMAXPATH in
00418    * length.  Safe bet (for now).  To find true length, call WinDdeQueryString
00419    * and pass in (hsz, NULL, 0L, 0) and it will return iLength.  Passing 0
00420    * for codepage will use the codepage of the current thread.
00421    */
00422   WinDdeQueryString( hsz1, chhsz1, sizeof( chhsz1 ), 0 );
00423   WinDdeQueryString( hsz2, chhsz2, sizeof( chhsz2 ),0 );
00424 
00425   rc = stricmp( chhsz1, chhsz2 );
00426 
00427   return(rc);
00428 }
00429 
00430 
00431 char *GetCommandLine()
00432 {
00433    /* This function is meant to be like the Window's GetCommandLine() function.
00434     * The value returned by pPIB->pib_pchcmd is of the format:
00435     * <executable>\0<command line parameters>.  So the first string is the
00436     * .exe and the second string is what followed on the command line.  Dorky,
00437     * but I guess it'll have to do.
00438     */
00439    PTIB pTIB = NULL;
00440    PPIB pPIB = NULL;
00441    APIRET rc = NO_ERROR;
00442    char *pchParam = NULL;
00443 
00444    rc = DosGetInfoBlocks( &pTIB, &pPIB );
00445    if( rc == NO_ERROR )
00446    {
00447       INT iLen = 0;
00448       char *pchTemp = NULL;
00449       pchParam = pPIB->pib_pchcmd;
00450       strcpy( szCommandLine, pchParam );
00451       iLen = strlen( pchParam );
00452 
00453       /* szCommandLine[iLen] is '\0', so see what's next.  Probably be another
00454        * '\0', a space or start of the arguments
00455        */
00456       pchTemp = &(pchParam[iLen+1]);
00457 
00458       /* At this point, szCommandLine holds just the program name.  Now we
00459        * go for the parameters.  If our next value is a space, ignore it
00460        * and go for the next character
00461        */
00462       if( *pchTemp )
00463       {
00464          szCommandLine[iLen] = ' ';
00465          iLen++;
00466          if( *pchTemp == ' ' )
00467          {
00468             pchTemp++;
00469          }
00470          strcpy( &(szCommandLine[iLen]), pchTemp );
00471       }
00472 
00473    }
00474 
00475    return( szCommandLine );
00476 }
00477 
00478 typedef struct _DDEMLFN
00479 {
00480    PFN   *fn;
00481    ULONG ord; 
00482 } DDEMLFN, *PDDEMLFN;
00483 
00484 DDEMLFN ddemlfnTable[] = 
00485 {
00486    { (PFN *)&WinDdeAbandonTransaction   ,100 },
00487    { (PFN *)&WinDdeAccessData           ,101 },
00488    { (PFN *)&WinDdeAddData              ,102 },
00489    { (PFN *)&WinDdeSubmitTransaction    ,103 },
00490    { (PFN *)&WinDdeCompareStringHandles ,104 },
00491    { (PFN *)&WinDdeConnect              ,105 },
00492    { (PFN *)&WinDdeConnectList          ,106 },
00493    { (PFN *)&WinDdeCreateDataHandle     ,107 },
00494    { (PFN *)&WinDdeCreateStringHandle   ,108 },
00495    { (PFN *)&WinDdeDisconnect           ,109 },
00496    { (PFN *)&WinDdeDisconnectList       ,110 },
00497    { (PFN *)&WinDdeEnableCallback       ,111 },
00498    { (PFN *)&WinDdeFreeDataHandle       ,112 },
00499    { (PFN *)&WinDdeFreeStringHandle     ,113 },
00500    { (PFN *)&WinDdeGetData              ,114 },
00501    { (PFN *)&WinDdeInitialize           ,116 },
00502    { (PFN *)&WinDdeKeepStringHandle     ,117 },
00503    { (PFN *)&WinDdeNameService          ,118 },
00504    { (PFN *)&WinDdePostAdvise           ,119 },
00505    { (PFN *)&WinDdeQueryConvInfo        ,120 },
00506    { (PFN *)&WinDdeQueryNextServer      ,121 },
00507    { (PFN *)&WinDdeQueryString          ,122 },
00508    { (PFN *)&WinDdeReconnect            ,123 },
00509    { (PFN *)&WinDdeSetUserHandle        ,124 },
00510    { (PFN *)&WinDdeUninitialize         ,126 },
00511    { (PFN *)NULL                           ,  0 }
00512 };
00513 
00514 /* SetupOS2ddeml makes sure that we can get pointers to all of the DDEML 
00515  * functions in demlfnTable using entrypoints.  If we can't find one of them
00516  * or can't load pmddeml.dll, then returns FALSE
00517  */
00518 BOOL SetupOS2ddeml()
00519 {
00520     BOOL bRC = FALSE;
00521     HMODULE hmodDDEML = NULLHANDLE;
00522     APIRET rc = NO_ERROR;
00523 
00524     rc = DosLoadModule( NULL, 0, "PMDDEML", &hmodDDEML );
00525     if( rc == NO_ERROR )
00526     {
00527        int i;
00528        /* all of this had better work.  Get ready to be a success! */
00529        bRC = TRUE;
00530        for( i=0; ddemlfnTable[i].ord != 0; i++ )
00531        {
00532           rc = DosQueryProcAddr( hmodDDEML, ddemlfnTable[i].ord, NULL,
00533                                  ddemlfnTable[i].fn );
00534           if( rc != NO_ERROR )
00535           {
00536              /* we're horked.  Return rc = horked */
00537              bRC = FALSE;
00538              break;
00539           }
00540        }
00541     } /* load PMDDEML */
00542 
00543     return( bRC );
00544 }
00545 
00546 char nsNativeAppSupportOS2::mMutexName[ 128 ] = { 0 };
00547 
00548 
00549 // Message window encapsulation.
00550 struct MessageWindow {
00551     // ctor/dtor are simplistic
00552     MessageWindow() {
00553         // Try to find window.
00554         HATOMTBL  hatomtbl;
00555         HENUM     henum;
00556         HWND      hwndNext;
00557         char      classname[CCHMAXPATH];
00558 
00559         mHandle = NULLHANDLE;
00560 
00561         hatomtbl = WinQuerySystemAtomTable();
00562         mMsgWindowAtom = WinFindAtom(hatomtbl, className());
00563         if (mMsgWindowAtom == 0)
00564         {
00565           // If there is not atom in the system table for this class name, then
00566           // we can assume that the app is not currently running.
00567           mMsgWindowAtom = WinAddAtom(hatomtbl, className());
00568         } else {
00569           // Found an existing atom for this class name.  Cycle through existing
00570           // windows and see if one with our window id and class name already 
00571           // exists
00572           henum = WinBeginEnumWindows(HWND_OBJECT);
00573           while ((hwndNext = WinGetNextWindow(henum)) != NULLHANDLE)
00574           {
00575             if (WinQueryWindowUShort(hwndNext, QWS_ID) == (USHORT)mMsgWindowAtom)
00576             {
00577               WinQueryClassName(hwndNext, CCHMAXPATH, classname);
00578               if (strcmp(classname, className()) == 0)
00579               {
00580                 mHandle = hwndNext;
00581                 break;
00582               }
00583             }
00584           }
00585         }
00586     }
00587 
00588     HWND getHWND() {
00589         return mHandle;
00590     }
00591 
00592     // Class name: appName + "MessageWindow"
00593     static const char *className() {
00594         static char classNameBuffer[128];
00595         static char *mClassName = 0;
00596         if ( !mClassName ) { 
00597             sprintf( classNameBuffer,
00598                          "%s%s",
00599                          gAppData->name,
00600                          "MessageWindow" );
00601             mClassName = classNameBuffer;
00602         }
00603         return mClassName;
00604     }
00605 
00606     // Create: Register class and create window.
00607     NS_IMETHOD Create() {
00608 
00609         // Register the window class.
00610         NS_ENSURE_TRUE( WinRegisterClass( 0, className(), 
00611                                           (PFNWP)&MessageWindow::WindowProc, 
00612                                           0L, 0 ), 
00613                         NS_ERROR_FAILURE );
00614 
00615         /* Giving a size but offscreen so that won't be seen, even if all
00616          * goes wrong.  Giving a size so that can be seen and understood by
00617          * tools
00618          */
00619         const char * pszClassName = className();
00620         mHandle = WinCreateWindow( HWND_OBJECT,
00621                                    pszClassName,
00622                                    NULL,        // name
00623                                    WS_DISABLED, // style
00624                                    0,0,     // x, y
00625                                    0,0,       // cx,cy
00626                                    HWND_DESKTOP,// owner
00627                                    HWND_BOTTOM,  // hwndbehind
00628                                    (USHORT)mMsgWindowAtom, // id
00629                                    NULL,        // pCtlData
00630                                    NULL );      // pres params
00631 
00632 #if MOZ_DEBUG_DDE
00633         printf( "Message window = 0x%08X\n", (int)mHandle );
00634 #endif
00635 
00636         return NS_OK;
00637     }
00638 
00639     // Destory:  Get rid of window and reset mHandle.
00640     NS_IMETHOD Destroy() {
00641         nsresult retval = NS_OK;
00642 
00643         if ( mHandle ) {
00644            HATOMTBL hatomtbl = WinQuerySystemAtomTable();
00645            WinDeleteAtom(hatomtbl, mMsgWindowAtom);
00646            
00647             // DestroyWindow can only destroy windows created from
00648             //  the same thread.
00649             BOOL desRes = WinDestroyWindow( mHandle );
00650             if ( FALSE != desRes ) {
00651                 mHandle = NULL;
00652             }
00653             else {
00654                 retval = NS_ERROR_FAILURE;
00655             }
00656         }
00657 
00658         return retval;
00659     }
00660 
00661     // SendRequest: Construct a data buffer <commandline>\0<workingdir>\0,
00662     // then pass the string via WM_COPYDATA to the message window.
00663 
00664     NS_IMETHOD SendRequest( const char *cmd )
00665     {
00666     /* Nothing like WM_COPYDATA in OS/2, where the OS allows pointers to be
00667      * passed to a different process and automatically accessible by that
00668      * process.  So we have to create shared mem on our side and then the
00669      * process that gets the WM_COPYDATA message has to do a 
00670      * DosGetSharedMem on this pointer to be able to access the data
00671     */
00672 
00673         COPYDATASTRUCT *pcds;
00674         PVOID pvData = NULL;
00675 
00676         if (!cmd)
00677             return NS_ERROR_FAILURE;
00678 
00679         ULONG ulSize = sizeof(COPYDATASTRUCT)+strlen(cmd)+1+CCHMAXPATH;
00680         if (DosAllocSharedMem( &pvData, NULL, ulSize,
00681                                (PAG_COMMIT|PAG_READ|PAG_WRITE|OBJ_GETTABLE)))
00682             return NS_ERROR_OUT_OF_MEMORY;
00683 
00684         // We used to set dwData to zero, when we didn't send the
00685         // working dir.  Now we're using it as a version number.
00686         // The commandline goes in the buffer space immediately after
00687         // the COPYDATASTRUCT;  the current directory follows that.
00688         pcds = (COPYDATASTRUCT *)(pvData);
00689         pcds->dwData = 1;
00690 
00691         char * ptr = &(pcds->chBuff);
00692         pcds->lpData = ptr;
00693         strcpy( ptr, cmd);
00694         pcds->cbData = strlen( ptr) + 1;
00695         ptr += pcds->cbData;
00696 
00697         if (DosQueryPathInfo( ".", FIL_QUERYFULLNAME, ptr, CCHMAXPATH)) {
00698             ptr[0] = '.';
00699             ptr[1] = '\0';
00700         }
00701         pcds->cbData += strlen( ptr) + 1;
00702 
00703         WinSendMsg( mHandle, WM_COPYDATA, 0, (MPARAM)pcds );
00704         DosFreeMem( pvData );
00705 
00706         return NS_OK;
00707     }
00708 
00709     // Window proc.
00710     static
00711     MRESULT EXPENTRY WindowProc( HWND msgWindow, ULONG msg, MPARAM wp, MPARAM lp )
00712     {
00713         // We have to return a FALSE from WM_CREATE
00714         if ( msg == WM_CREATE )
00715             return (MRESULT)FALSE;
00716 
00717         if ( msg != WM_COPYDATA ) 
00718             return (MRESULT)TRUE;
00719 
00720         // This is an incoming request.
00721         COPYDATASTRUCT *cds = (COPYDATASTRUCT*)lp;
00722         DosGetSharedMem( (PVOID)cds, PAG_READ|PAG_WRITE );
00723 
00724         nsCOMPtr<nsILocalFile> workingDir;
00725 
00726         // a "1" or greater indicates that the other process's working
00727         // directory follows the commandline string - locate & convert it
00728         if (cds->dwData >= 1) {
00729             char* wdpath = strchr( (char*)cds->lpData, 0) + 1;
00730             NS_NewNativeLocalFile(nsDependentCString(wdpath),
00731                                   PR_FALSE, getter_AddRefs(workingDir));
00732         }
00733 
00734         nsNativeAppSupportOS2::HandleCommandLine((char*)cds->lpData,
00735                                 workingDir, nsICommandLine::STATE_REMOTE_AUTO);
00736 
00737         return (MRESULT)TRUE;
00738     }
00739 
00740 private:
00741     HWND mHandle;
00742     USHORT   mMsgWindowAtom;
00743 }; // struct MessageWindow
00744 
00745 PRBool nsNativeAppSupportOS2::mUseDDE = PR_FALSE;
00746 
00747 /* Start: Tries to find the "message window" to determine if it
00748  *        exists.  If so, then Mozilla is already running.  In that
00749  *        case, we use the handle to the "message" window and send
00750  *        a request corresponding to this process's command line
00751  *        options.
00752  *
00753  *        If not, then this is the first instance of Mozilla.  In
00754  *        that case, we create and set up the message window.
00755  *
00756  *        The checking for existance of the message window must
00757  *        be protected by use of a mutex semaphore.
00758  */
00759 NS_IMETHODIMP
00760 nsNativeAppSupportOS2::Start( PRBool *aResult ) {
00761     NS_ENSURE_ARG( aResult );
00762     NS_ENSURE_TRUE( mInstance == 0, NS_ERROR_NOT_INITIALIZED );
00763 
00764     nsresult rv = NS_ERROR_FAILURE;
00765     *aResult = PR_FALSE;
00766 
00767     // see if DDE should be enabled AND remove OS/2-specific
00768     // options the app's commandline handler won't recognize
00769     // (-console was handled earlier by StartOS2App())
00770     for (int i = 1; i < gArgc; i++) {
00771         if (stricmp("-dde", gArgv[i]) == 0 ||
00772             stricmp("/dde", gArgv[i]) == 0)
00773             mUseDDE = PR_TRUE;
00774         else
00775             if (stricmp("-console", gArgv[i]) != 0 &&
00776                 stricmp("/console", gArgv[i]) != 0)
00777                 continue;
00778 
00779         for (int j = i; j < gArgc; j++)
00780             gArgv[j] = gArgv[j+1];
00781         gArgc--;
00782         i--;
00783     }
00784 
00785     // if this is a standalone instance, turn off DDE regardless of the
00786     // commandline, then skip out before we look for another instance
00787     if (getenv("MOZ_NO_REMOTE")) {
00788         mUseDDE = PR_FALSE;
00789         *aResult = PR_TRUE;
00790         return NS_OK;
00791     }
00792 
00793     // Grab mutex first.
00794 
00795     // Build mutex name from app name.
00796     PR_snprintf( mMutexName, sizeof mMutexName, "%s%s", gAppData->name, MOZ_STARTUP_MUTEX_NAME );
00797     Mutex startupLock = Mutex( mMutexName );
00798 
00799     NS_ENSURE_TRUE( startupLock.Lock( MOZ_DDE_START_TIMEOUT ), NS_ERROR_FAILURE );
00800 
00801     /* We need to have a message queue to do the MessageWindow stuff (for both
00802      * Create() and SendRequest()).  If we don't have one, make it now.
00803      * If we are going to end up going away right after ::Start() returns,
00804      * then make sure to clean up the message queue.
00805      */
00806     MQINFO mqinfo;
00807     HAB hab;
00808     HMQ hmqCurrent = WinQueryQueueInfo( HMQ_CURRENT, &mqinfo, 
00809                                         sizeof( MQINFO ) );
00810     if( !hmqCurrent ) {
00811         hab = WinInitialize( 0 );
00812         hmqCurrent = WinCreateMsgQueue( hab, 0 );
00813     }
00814 
00815     // Search for existing message window.
00816     MessageWindow msgWindow;
00817     if ( msgWindow.getHWND() ) {
00818         // We are a client process.  Pass request to message window.
00819         char *cmd = GetCommandLine();
00820         rv = msgWindow.SendRequest( cmd );
00821     } else {
00822         // We will be server.
00823         rv = msgWindow.Create();
00824         if ( NS_SUCCEEDED( rv ) ) {
00825             // Start up DDE server
00826             if (mUseDDE)
00827                 this->StartDDE();
00828             // Tell caller to spin message loop.
00829             *aResult = PR_TRUE;
00830         }
00831     }
00832 
00833     startupLock.Unlock();
00834 
00835     if( *aResult == PR_FALSE )
00836     {
00837         /* This process isn't going to continue much longer.  Make sure that we
00838          * clean up the message queue
00839          */
00840         if (hmqCurrent)
00841            WinDestroyMsgQueue(hmqCurrent);
00842         if (hab)
00843            WinTerminate(hab);
00844     }
00845 
00846     return rv;
00847 }
00848 
00849 PRBool
00850 nsNativeAppSupportOS2::InitTopicStrings() {
00851     for ( int i = 0; i < topicCount; i++ ) {
00852         if ( !( mTopics[ i ] = WinDdeCreateStringHandle( (PSZ)topicNames[ i ], CP_WINANSI ) ) ) {
00853             return PR_FALSE;
00854         }
00855     }
00856     return PR_TRUE;
00857 }
00858 
00859 int
00860 nsNativeAppSupportOS2::FindTopic( HSZ topic ) {
00861     for ( int i = 0; i < topicCount; i++ ) {
00862         if ( DdeCmpStringHandles( topic, mTopics[i] ) == 0 ) {
00863             return i;
00864         }
00865     }
00866     return -1;
00867 }
00868 
00869 // Start DDE server.
00870 //
00871 // This used to be the Start() method when we were using DDE as the
00872 // primary IPC mechanism between secondary Mozilla processes and the
00873 // initial "server" process.
00874 //
00875 // Now, it simply initializes the DDE server.  The caller must check
00876 // that this process is to be the server, and, must acquire the DDE
00877 // startup mutex semaphore prior to calling this routine.  See ::Start(),
00878 // above.
00879 NS_IMETHODIMP
00880 nsNativeAppSupportOS2::StartDDE() {
00881     NS_ENSURE_TRUE( mInstance == 0, NS_ERROR_NOT_INITIALIZED );
00882 
00883     /* Get entrypoints for PMDDEML */
00884     BOOL bDDEML = SetupOS2ddeml();
00885 
00886     /* If we couldn't initialize DDEML, set mUSEDDE to PR_FALSE */
00887     /* so we don't do anything else DDE related */
00888     if (!bDDEML) {
00889        mUseDDE = PR_FALSE;
00890        return NS_OK;
00891     }
00892 
00893     // Initialize DDE.
00894     NS_ENSURE_TRUE( DDEERR_NO_ERROR == WinDdeInitialize( &mInstance,
00895                                                          nsNativeAppSupportOS2::HandleDDENotification,
00896                                                          APPCLASS_STANDARD,
00897                                                          0 ),
00898                     NS_ERROR_FAILURE );
00899 
00900     // Allocate DDE strings.
00901     NS_ENSURE_TRUE( ( mApplication = WinDdeCreateStringHandle( (char*) gAppData->name, CP_WINANSI ) ) && InitTopicStrings(),
00902                     NS_ERROR_FAILURE );
00903 
00904     // Next step is to register a DDE service.
00905     NS_ENSURE_TRUE( WinDdeNameService( mInstance, mApplication, 0, DNS_REGISTER ), NS_ERROR_FAILURE );
00906 
00907 #if MOZ_DEBUG_DDE
00908     printf( "DDE server started\n" );
00909 #endif
00910 
00911     return NS_OK;
00912 }
00913 
00914 // If no DDE conversations are pending, terminate DDE.
00915 NS_IMETHODIMP
00916 nsNativeAppSupportOS2::Stop( PRBool *aResult ) {
00917     NS_ENSURE_ARG( aResult );
00918     NS_ENSURE_TRUE( mInstance, NS_ERROR_NOT_INITIALIZED );
00919 
00920     nsresult rv = NS_OK;
00921     *aResult = PR_TRUE;
00922 
00923     if (!mUseDDE) {
00924        return rv;
00925     }
00926 
00927     Mutex ddeLock( mMutexName );
00928 
00929     if ( ddeLock.Lock( MOZ_DDE_STOP_TIMEOUT ) ) {
00930         if ( mConversations == 0 ) {
00931             this->Quit();
00932         } else {
00933             *aResult = PR_FALSE;
00934         }
00935 
00936         ddeLock.Unlock();
00937     }
00938     else {
00939         // No DDE application name specified, but that's OK.  Just
00940         // forge ahead.
00941         *aResult = PR_TRUE;
00942     }
00943 
00944     return rv;
00945 }
00946 
00947 NS_IMETHODIMP
00948 nsNativeAppSupportOS2::Observe(nsISupports* aSubject, const char* aTopic,
00949                                const PRUnichar* aData)
00950 {
00951     if (strcmp(aTopic, "quit-application") == 0) {
00952         Quit();
00953     } else {
00954         NS_ERROR("Unexpected observer topic.");
00955     }
00956 
00957     return NS_OK;
00958 }
00959 
00960 // Terminate DDE regardless.
00961 NS_IMETHODIMP
00962 nsNativeAppSupportOS2::Quit() {
00963     // If another process wants to look for the message window, they need
00964     // to wait to hold the lock, in which case they will not find the
00965     // window as we will destroy ours under our lock.
00966     // When the mutex goes off the stack, it is unlocked via destructor.
00967     Mutex mutexLock(mMutexName);
00968     NS_ENSURE_TRUE(mutexLock.Lock(MOZ_DDE_START_TIMEOUT), NS_ERROR_FAILURE);
00969 
00970     // If we've got a message window to receive IPC or new window requests,
00971     // get rid of it as we are shutting down.
00972     // Note:  Destroy calls DestroyWindow, which will only work on a window
00973     //  created by the same thread.
00974     MessageWindow mw;
00975     mw.Destroy();
00976 
00977     if ( mInstance ) {
00978         // Unregister application name.
00979         WinDdeNameService( mInstance, mApplication, 0, DNS_UNREGISTER );
00980         // Clean up strings.
00981         if ( mApplication ) {
00982             WinDdeFreeStringHandle( mApplication );
00983             mApplication = 0;
00984         }
00985         for ( int i = 0; i < topicCount; i++ ) {
00986             if ( mTopics[i] ) {
00987                 WinDdeFreeStringHandle( mTopics[i] );
00988                 mTopics[i] = 0;
00989             }
00990         }
00991         WinDdeUninitialize( mInstance );
00992         mInstance = 0;
00993     }
00994 
00995     return NS_OK;
00996 }
00997 
00998 NS_IMETHODIMP
00999 nsNativeAppSupportOS2::Enable()
01000 {
01001     mCanHandleRequests = PR_TRUE;
01002     nsCOMPtr<nsIObserverService> obs
01003         (do_GetService("@mozilla.org/observer-service;1"));
01004     if (obs) {
01005         obs->AddObserver(this, "quit-application", PR_FALSE);
01006     } else {
01007         NS_ERROR("No observer service?");
01008     }
01009 
01010     return NS_OK;
01011 }
01012 
01013 #if MOZ_DEBUG_DDE
01014 // Macro to generate case statement for a given XTYP value.
01015 #define XTYP_CASE(t) \
01016     case t: result = #t; break
01017 
01018 static nsCString uTypeDesc( UINT uType ) {
01019     nsCString result;
01020     switch ( uType ) {
01021     XTYP_CASE(XTYP_ADVSTART);
01022     XTYP_CASE(XTYP_CONNECT);
01023     XTYP_CASE(XTYP_ADVREQ);
01024     XTYP_CASE(XTYP_REQUEST);
01025     XTYP_CASE(XTYP_WILDCONNECT);
01026     XTYP_CASE(XTYP_ADVDATA);
01027     XTYP_CASE(XTYP_EXECUTE);
01028     XTYP_CASE(XTYP_POKE);
01029     XTYP_CASE(XTYP_ADVSTOP);
01030     XTYP_CASE(XTYP_CONNECT_CONFIRM);
01031     XTYP_CASE(XTYP_DISCONNECT);
01032     XTYP_CASE(XTYP_ERROR);
01033     XTYP_CASE(XTYP_MONITOR);
01034     XTYP_CASE(XTYP_REGISTER);
01035     XTYP_CASE(XTYP_XACT_COMPLETE);
01036     XTYP_CASE(XTYP_UNREGISTER);
01037     default: result = "XTYP_?????";
01038     }
01039     return result;
01040 }
01041 
01042 static nsCString hszValue( DWORD instance, HSZ hsz ) {
01043     // Extract string from HSZ.
01044     nsCString result("[");
01045     DWORD len = WinDdeQueryString( hsz, NULL, NULL, CP_WINANSI );
01046     if ( len ) {
01047         char buffer[ 256 ];
01048         WinDdeQueryString( hsz, buffer, sizeof buffer, CP_WINANSI );
01049         result += buffer;
01050     }
01051     result += "]";
01052     return result;
01053 }
01054 #endif
01055 
01056 
01057 // Utility function to escape double-quotes within a string.
01058 static void escapeQuotes( nsAString &aString ) {
01059     PRInt32 offset = -1;
01060     while( 1 ) {
01061        // Find next '"'.
01062        offset = aString.FindChar( '"', ++offset );
01063        if ( offset == kNotFound ) {
01064            // No more quotes, exit.
01065            break;
01066        } else {
01067            // Insert back-slash ahead of the '"'.
01068            aString.Insert( PRUnichar('\\'), offset );
01069            // Increment offset because we just inserted a slash
01070            offset++;
01071        }
01072     }
01073     return;
01074 }
01075 
01076 
01077 HDDEDATA APIENTRY
01078 nsNativeAppSupportOS2::HandleDDENotification( ULONG idInst,     // DDEML instance
01079                                               USHORT uType,     // transaction type
01080                                               USHORT uFmt,      // clipboard data format
01081                                               HCONV hconv,      // handle to the conversation
01082                                               HSZ hsz1,         // handle to a string
01083                                               HSZ hsz2,         // handle to a string
01084                                               HDDEDATA hdata,   // handle to a global memory object
01085                                               ULONG dwData1,    // transaction-specific data
01086                                               ULONG dwData2 ) { // transaction-specific data
01087 
01088 #if MOZ_DEBUG_DDE
01089     printf( "DDE: uType  =%s\n",      uTypeDesc( uType ).get() );
01090     printf( "     uFmt   =%u\n",      (unsigned)uFmt );
01091     printf( "     hconv  =%08x\n",    (int)hconv );
01092     printf( "     hsz1   =%08x:%s\n", (int)hsz1, hszValue( mInstance, hsz1 ).get() );
01093     printf( "     hsz2   =%08x:%s\n", (int)hsz2, hszValue( mInstance, hsz2 ).get() );
01094     printf( "     hdata  =%08x\n",    (int)hdata );
01095     printf( "     dwData1=%08x\n",    (int)dwData1 );
01096     printf( "     dwData2=%08x\n",    (int)dwData2 );
01097 #endif
01098 
01099     HDDEDATA result = 0;
01100     if ( uType & XCLASS_BOOL ) {
01101         switch ( uType ) {
01102             case XTYP_CONNECT:
01103                 // Make sure its for our service/topic.
01104                 if ( FindTopic( hsz1 ) != -1 ) {
01105                     // We support this connection.
01106                     result = (HDDEDATA)1;
01107                 }
01108                 break;
01109             case XTYP_CONNECT_CONFIRM:
01110                 // We don't care about the conversation handle, at this point.
01111                 result = (HDDEDATA)1;
01112                 break;
01113         }
01114     } else if ( uType & XCLASS_DATA ) {
01115         if ( uType == XTYP_REQUEST ) {
01116             switch ( FindTopic( hsz1 ) ) {
01117                 case topicOpenURL: {
01118                     // Open a given URL...
01119 
01120                     // Get the URL from the first argument in the command.
01121                     nsCAutoString url;
01122                     ParseDDEArg(hsz2, 0, url);
01123 
01124                     // Read the 3rd argument in the command to determine if a
01125                     // new window is to be used.
01126                     nsCAutoString windowID;
01127                     ParseDDEArg(hsz2, 2, windowID);
01128                     // to open the URL in a new window, the old OS/2 code
01129                     // looks for "0" while the new Win32 code looks for "";
01130                     // not knowing which is correct, we'll look for both
01131                     if (windowID.Equals( "0" ) || windowID.Equals( "" ))
01132                         url.Insert("mozilla -new-window ", 0);
01133                     else
01134                         url.Insert("mozilla -url ", 0);
01135 
01136 #if MOZ_DEBUG_DDE
01137                     printf( "Handling dde XTYP_REQUEST request: [%s]...\n", url.get() );
01138 #endif
01139                     // Now handle it.
01140                     HandleCommandLine(url.get(), nsnull, nsICommandLine::STATE_REMOTE_EXPLICIT);
01141                     // Return pseudo window ID.
01142                     result = CreateDDEData( 1 );
01143                     break;
01144                 }
01145                 case topicGetWindowInfo: {
01146                     // This topic has to get the current URL, get the current
01147                     // page title and then format the output into the DDE
01148                     // return string.  The return value is "URL","Page Title",
01149                     // "Window ID" however the window ID is not used for this
01150                     // command, therefore it is returned as a null string
01151 
01152                     // This isn't really a loop.  We just use "break"
01153                     // statements to bypass the remaining steps when
01154                     // something goes wrong.
01155                     do {
01156                         // Get most recently used Nav window.
01157                         nsCOMPtr<nsIDOMWindowInternal> navWin;
01158                         GetMostRecentWindow( NS_LITERAL_STRING( "navigator:browser" ).get(),
01159                                              getter_AddRefs( navWin ) );
01160                         if ( !navWin ) {
01161                             // There is not a window open
01162                             break;
01163                         }
01164                         // Get content window.
01165                         nsCOMPtr<nsIDOMWindow> content;
01166                         navWin->GetContent( getter_AddRefs( content ) );
01167                         if ( !content ) {
01168                             break;
01169                         }
01170                         // Convert that to internal interface.
01171                         nsCOMPtr<nsIDOMWindowInternal> internalContent( do_QueryInterface( content ) );
01172                         if ( !internalContent ) {
01173                             break;
01174                         }
01175                         // Get location.
01176                         nsCOMPtr<nsIDOMLocation> location;
01177                         internalContent->GetLocation( getter_AddRefs( location ) );
01178                         if ( !location ) {
01179                             break;
01180                         }
01181                         // Get href for URL.
01182                         nsAutoString url;
01183                         if ( NS_FAILED( location->GetHref( url ) ) ) {
01184                             break;
01185                         }
01186                         // Escape any double-quotes.
01187                         escapeQuotes( url );
01188 
01189                         // Now for the title; first, get the "window" script global object.
01190                         nsCOMPtr<nsIScriptGlobalObject> scrGlobalObj( do_QueryInterface( internalContent ) );
01191                         if ( !scrGlobalObj ) {
01192                             break;
01193                         }
01194                         // Then from its doc shell get the base window...
01195                         nsCOMPtr<nsIBaseWindow> baseWindow =
01196                           do_QueryInterface( scrGlobalObj->GetDocShell() );
01197                         if ( !baseWindow ) {
01198                             break;
01199                         }
01200                         // And from the base window we can get the title.
01201                         nsXPIDLString title;
01202                         if(!baseWindow) {
01203                             break;
01204                         }
01205                         baseWindow->GetTitle(getter_Copies(title));
01206                         // Escape any double-quotes in the title.
01207                         escapeQuotes( title );
01208 
01209                         // Use a string buffer for the output data, first
01210                         // save a quote.
01211                         nsCAutoString   outpt( NS_LITERAL_CSTRING("\"") );
01212                         // Now copy the URL converting the Unicode string
01213                         // to a single-byte ASCII string
01214                         outpt.Append( NS_LossyConvertUCS2toASCII( url ) );
01215                         // Add the "," used to separate the URL and the page
01216                         // title
01217                         outpt.Append( NS_LITERAL_CSTRING("\",\"") );
01218                         // Now copy the current page title to the return string
01219                         outpt.Append( NS_LossyConvertUCS2toASCII( title.get() ));
01220                         // Fill out the return string with the remainin ",""
01221                         outpt.Append( NS_LITERAL_CSTRING( "\",\"\"" ));
01222 
01223                         // Create a DDE handle to a char string for the data
01224                         // being returned, this copies and creates a "shared"
01225                         // copy of the DDE response until the calling APP
01226                         // reads it and says it can be freed.
01227                         result = CreateDDEData( (LPBYTE)(const char*)outpt.get(),
01228                                                 outpt.Length() + 1 );
01229 #if MOZ_DEBUG_DDE
01230                         printf( "WWW_GetWindowInfo->%s\n", outpt.get() );
01231 #endif
01232                     } while ( PR_FALSE );
01233                     break;
01234                 }
01235                 case topicActivate: {
01236                     // Activate a Nav window...
01237                     nsCAutoString windowID;
01238                     ParseDDEArg(hsz2, 0, windowID);
01239                     // 4294967295 is decimal for 0xFFFFFFFF which is also a
01240                     //   correct value to do that Activate last window stuff
01241                     if ( windowID.Equals( "-1" ) ||
01242                          windowID.Equals( "4294967295" ) ) {
01243                         // We only support activating the most recent window (or a new one).
01244                         ActivateLastWindow();
01245                         // Return pseudo window ID.
01246                         result = CreateDDEData( 1 );
01247                     }
01248                     break;
01249                 }
01250                 case topicVersion: {
01251                     // Return version.  We're restarting at 1.0!
01252                     DWORD version = 1 << 16; // "1.0"
01253                     result = CreateDDEData( version );
01254                     break;
01255                 }
01256                 case topicRegisterViewer: {
01257                     // Register new viewer (not implemented).
01258                     result = CreateDDEData( PR_FALSE );
01259                     break;
01260                 }
01261                 case topicUnRegisterViewer: {
01262                     // Unregister new viewer (not implemented).
01263                     result = CreateDDEData( PR_FALSE );
01264                     break;
01265                 }
01266                 default:
01267                     break;
01268             }
01269         } else if ( uType & XTYP_POKE ) {
01270             switch ( FindTopic( hsz1 ) ) {
01271                 case topicCancelProgress: {
01272                     // "Handle" progress cancel (actually, pretty much ignored).
01273                     result = (HDDEDATA)DDE_FACK;
01274                     break;
01275                 }
01276                 default:
01277                     break;
01278             }
01279         }
01280     } else if ( uType & XCLASS_FLAGS ) {
01281         if ( uType == XTYP_EXECUTE ) {
01282             // Prove that we received the request.
01283             DWORD bytes;
01284             LPBYTE request = (LPBYTE)WinDdeAccessData( hdata, &bytes );
01285 #if MOZ_DEBUG_DDE
01286             printf( "Handling dde request: [%s]...\n", (char*)request );
01287 #endif
01288             nsCAutoString url;
01289             ParseDDEArg((const char*) request, 0, url);
01290 
01291             // Read the 3rd argument in the command to determine if a
01292             // new window is to be used.
01293             nsCAutoString windowID;
01294             ParseDDEArg((const char*) request, 2, windowID);
01295 
01296             // to open the URL in a new window, the old OS/2 code
01297             // looks for "0" while the new Win32 code looks for "";
01298             // not knowing which is correct, we'll look for both
01299             if (windowID.Equals( "0" ) || windowID.Equals( "" ))
01300                 url.Insert("mozilla -new-window ", 0);
01301             else
01302                 url.Insert("mozilla -url ", 0);
01303 
01304 #if MOZ_DEBUG_DDE
01305             printf( "Handling dde XTYP_REQUEST request: [%s]...\n", url.get() );
01306 #endif
01307             // Now handle it.
01308             HandleCommandLine(url.get(), nsnull, nsICommandLine::STATE_REMOTE_EXPLICIT);
01309 
01310             // Release the data.
01311 //            DdeUnaccessData( hdata );
01312             result = (HDDEDATA)DDE_FACK;
01313         } else {
01314             result = (HDDEDATA)DDE_NOTPROCESSED;
01315         }
01316     } else if ( uType & XCLASS_NOTIFICATION ) {
01317     }
01318 #if MOZ_DEBUG_DDE
01319     printf( "DDE result=%d (0x%08X)\n", (int)result, (int)result );
01320 #endif
01321     return result;
01322 }
01323 
01324 
01325 // Utility function to advance to end of quoted string.
01326 // p+offset must point to the comma preceding the arg on entry.
01327 // On return, p+result points to the closing '"' (or end of the string
01328 // if the closing '"' is missing) if the arg is quoted.  If the arg
01329 // is not quoted, then p+result will point to the first character
01330 // of the arg.
01331 static PRInt32 advanceToEndOfQuotedArg( const char *p, PRInt32 offset, PRInt32 len ) {
01332     // Check whether the current arg is quoted.
01333     if ( p[++offset] == '"' ) {
01334         // Advance past the closing quote.
01335         while ( offset < len && p[++offset] != '"' ) {
01336             // If the current character is a backslash, then the
01337             // next character can't be a *real* '"', so skip it.
01338             if ( p[offset] == '\\' ) {
01339                 offset++;
01340             }
01341         }
01342     }
01343     return offset;
01344 }
01345 
01346 void nsNativeAppSupportOS2::ParseDDEArg( const char* args, int index, nsCString& aString) {
01347     if ( args ) {
01348         int argLen = strlen(args);
01349         nsDependentCString temp(args, argLen);
01350 
01351         // offset points to the comma preceding the desired arg.
01352         PRInt32 offset = -1;
01353         // Skip commas till we get to the arg we want.
01354         while( index-- ) {
01355             // If this arg is quoted, then go to closing quote.
01356             offset = advanceToEndOfQuotedArg( args, offset, argLen);
01357             // Find next comma.
01358             offset = temp.FindChar( ',', offset );
01359             if ( offset == kNotFound ) {
01360                 // No more commas, give up.
01361                 aString = args;
01362                 return;
01363             }
01364         }
01365         // The desired argument starts just past the preceding comma,
01366         // which offset points to, and extends until the following
01367         // comma (or the end of the string).
01368         //
01369         // Since the argument might be enclosed in quotes, we need to
01370         // deal with that before searching for the terminating comma.
01371         // We advance offset so it ends up pointing to the start of
01372         // the argument we want.
01373         PRInt32 end = advanceToEndOfQuotedArg( args, offset++, argLen );
01374         // Find next comma (or end of string).
01375         end = temp.FindChar( ',', end );
01376         if ( end == kNotFound ) {
01377             // Arg is the rest of the string.
01378             end = argLen;
01379         }
01380         // Extract result.
01381         aString.Assign( args + offset, end - offset );
01382     }
01383     return;
01384 }
01385 
01386 // Utility to parse out argument from a DDE item string.
01387 void nsNativeAppSupportOS2::ParseDDEArg( HSZ args, int index, nsCString& aString) {
01388     DWORD argLen = WinDdeQueryString( args, NULL, NULL, CP_WINANSI );
01389     // there wasn't any string, so return empty string
01390     if ( !argLen ) return;
01391     nsCAutoString temp;
01392     // Ensure result's buffer is sufficiently big.
01393     temp.SetLength( argLen );
01394     // Now get the string contents.
01395     WinDdeQueryString( args, temp.BeginWriting(), temp.Length(), CP_WINANSI );
01396     // Parse out the given arg.
01397     ParseDDEArg(temp.get(), index, aString);
01398     return;
01399 }
01400 
01401 void nsNativeAppSupportOS2::ActivateLastWindow() {
01402     nsCOMPtr<nsIDOMWindowInternal> navWin;
01403     GetMostRecentWindow( NS_LITERAL_STRING("navigator:browser").get(), getter_AddRefs( navWin ) );
01404     if ( navWin )
01405         // Activate that window.
01406         activateWindow( navWin );
01407     else
01408         // Need to create a Navigator window, then.
01409         OpenBrowserWindow();
01410 }
01411 
01412 HDDEDATA nsNativeAppSupportOS2::CreateDDEData( DWORD value ) {
01413     return CreateDDEData( (LPBYTE)&value, sizeof value );
01414 }
01415 
01416 HDDEDATA nsNativeAppSupportOS2::CreateDDEData( LPBYTE value, DWORD len ) {
01417     HDDEDATA result = WinDdeCreateDataHandle( value,
01418                                               len,
01419                                               0,
01420                                               mApplication,
01421                                               CF_TEXT,
01422                                               0 );
01423     return result;
01424 }
01425 
01426 
01427 void
01428 nsNativeAppSupportOS2::HandleCommandLine(const char* aCmdLineString,
01429                                          nsIFile* aWorkingDir,
01430                                          PRUint32 aState)
01431 {
01432     nsresult rv;
01433 
01434     int justCounting = 1;
01435     char **argv = 0;
01436     // Flags, etc.
01437     int init = 1;
01438     int between, quoted, bSlashCount;
01439     int argc;
01440     const char *p;
01441     nsCAutoString arg;
01442 
01443     nsCOMPtr<nsICommandLineRunner> cmdLine
01444         (do_CreateInstance("@mozilla.org/toolkit/command-line;1"));
01445     if (!cmdLine) {
01446         NS_ERROR("Couldn't create command line!");
01447         return;
01448     }
01449 
01450     // We loop if we've not finished the second pass through.
01451     while ( 1 ) {
01452         // Initialize if required.
01453         if ( init ) {
01454             p = aCmdLineString;
01455             between = 1;
01456             argc = quoted = bSlashCount = 0;
01457 
01458             init = 0;
01459         }
01460         if ( between ) {
01461             // We are traversing whitespace between args.
01462             // Check for start of next arg.
01463             if (  *p != 0 && !isspace( *p ) ) {
01464                 // Start of another arg.
01465                 between = 0;
01466                 arg = "";
01467                 switch ( *p ) {
01468                     case '\\':
01469                         // Count the backslash.
01470                         bSlashCount = 1;
01471                         break;
01472                     case '"':
01473                         // Remember we're inside quotes.
01474                         quoted = 1;
01475                         break;
01476                     default:
01477                         // Add character to arg.
01478                         arg += *p;
01479                         break;
01480                 }
01481             } else {
01482                 // Another space between args, ignore it.
01483             }
01484         } else {
01485             // We are processing the contents of an argument.
01486             // Check for whitespace or end.
01487 
01488             // if the argument we are parsing is a mailto url then all
01489             // of the remaining command line data needs to be part of
01490             // the mailto url even if it has spaces. See Bug #231032
01491             nsDependentCString mailtoUrlScheme (kMailtoUrlScheme);
01492 
01493             if ( *p == 0 || ( !quoted && isspace( *p ) && !StringBeginsWith(arg, mailtoUrlScheme, nsCaseInsensitiveCStringComparator()) ) ) {
01494                 // Process pending backslashes (interpret them
01495                 // literally since they're not followed by a ").
01496                 while( bSlashCount ) {
01497                     arg += '\\';
01498                     bSlashCount--;
01499                 }
01500                 // End current arg.
01501                 if ( !justCounting ) {
01502                     argv[argc] = new char[ arg.Length() + 1 ];
01503                     strcpy( argv[argc], arg.get() );
01504                 }
01505                 argc++;
01506                 // We're now between args.
01507                 between = 1;
01508             } else {
01509                 // Still inside argument, process the character.
01510                 switch ( *p ) {
01511                     case '"':
01512                         // First, digest preceding backslashes (if any).
01513                         while ( bSlashCount > 1 ) {
01514                             // Put one backsplash in arg for each pair.
01515                             arg += '\\';
01516                             bSlashCount -= 2;
01517                         }
01518                         if ( bSlashCount ) {
01519                             // Quote is literal.
01520                             arg += '"';
01521                             bSlashCount = 0;
01522                         } else {
01523                             // Quote starts or ends a quoted section.
01524                             if ( quoted ) {
01525                                 // Check for special case of consecutive double
01526                                 // quotes inside a quoted section.
01527                                 if ( *(p+1) == '"' ) {
01528                                     // This implies a literal double-quote.  Fake that
01529                                     // out by causing next double-quote to look as
01530                                     // if it was preceded by a backslash.
01531                                     bSlashCount = 1;
01532                                 } else {
01533                                     quoted = 0;
01534                                 }
01535                             } else {
01536                                 quoted = 1;
01537                             }
01538                         }
01539                         break;
01540                     case '\\':
01541                         // Add to count.
01542                         bSlashCount++;
01543                         break;
01544                     default:
01545                         // Accept any preceding backslashes literally.
01546                         while ( bSlashCount ) {
01547                             arg += '\\';
01548                             bSlashCount--;
01549                         }
01550                         // Just add next char to the current arg.
01551                         arg += *p;
01552                         break;
01553                 }
01554             }
01555         }
01556         // Check for end of input.
01557         if ( *p ) {
01558             // Go to next character.
01559             p++;
01560         } else {
01561             // If on first pass, go on to second.
01562             if ( justCounting ) {
01563                 // Allocate argv array.
01564                 argv = new char*[ argc ];
01565 
01566                 // Start second pass
01567                 justCounting = 0;
01568                 init = 1;
01569             } else {
01570                 // Quit.
01571                 break;
01572             }
01573         }
01574     }
01575 
01576     rv = cmdLine->Init(argc, argv, aWorkingDir, aState);
01577 
01578     // if these OS/2-specific flags aren't removed,
01579     // any URL following them will be ignored
01580     PRBool found;
01581     cmdLine->HandleFlag(NS_LITERAL_STRING("console"), PR_FALSE, &found);
01582     cmdLine->HandleFlag(NS_LITERAL_STRING("dde"), PR_FALSE, &found);
01583 
01584     // Cleanup.
01585     while ( argc ) {
01586         delete [] argv[ --argc ];
01587     }
01588     delete [] argv;
01589 
01590     if (NS_FAILED(rv)) {
01591         NS_ERROR("Error initializing command line.");
01592         return;
01593     }
01594 
01595     cmdLine->Run();
01596 }
01597 
01598 
01599 nsresult
01600 nsNativeAppSupportOS2::OpenWindow( const char*urlstr, const char *args ) {
01601 
01602   nsresult rv = NS_ERROR_FAILURE;
01603 
01604   nsCOMPtr<nsIWindowWatcher> wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID));
01605   nsCOMPtr<nsISupportsCString> sarg(do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID));
01606   if (sarg)
01607     sarg->SetData(nsDependentCString(args));
01608 
01609   if (wwatch && sarg) {
01610     nsCOMPtr<nsIDOMWindow> newWindow;
01611     rv = wwatch->OpenWindow(0, urlstr, "_blank", "chrome,dialog=no,all",
01612                    sarg, getter_AddRefs(newWindow));
01613 #if MOZ_DEBUG_DDE
01614   } else {
01615       printf("Get WindowWatcher (or create string) failed\n");
01616 #endif
01617   }
01618   return rv;
01619 }
01620 
01621 HWND hwndForDOMWindow( nsISupports *window ) {
01622     nsCOMPtr<nsIScriptGlobalObject> ppScriptGlobalObj( do_QueryInterface(window) );
01623     if ( !ppScriptGlobalObj ) {
01624         return 0;
01625     }
01626 
01627     nsCOMPtr<nsIBaseWindow> ppBaseWindow =
01628         do_QueryInterface( ppScriptGlobalObj->GetDocShell() );
01629     if ( !ppBaseWindow ) {
01630         return 0;
01631     }
01632 
01633     nsCOMPtr<nsIWidget> ppWidget;
01634     ppBaseWindow->GetMainWidget( getter_AddRefs( ppWidget ) );
01635 
01636     return (HWND)( ppWidget->GetNativeData( NS_NATIVE_WIDGET ) );
01637 }
01638 
01639 static const char sJSStackContractID[] = "@mozilla.org/js/xpc/ContextStack;1";
01640 
01641 class SafeJSContext {
01642 public:
01643   SafeJSContext();
01644   ~SafeJSContext();
01645 
01646   nsresult   Push();
01647   JSContext *get() { return mContext; }
01648 
01649 protected:
01650   nsCOMPtr<nsIThreadJSContextStack>  mService;
01651   JSContext                         *mContext;
01652 };
01653 
01654 SafeJSContext::SafeJSContext() : mContext(nsnull) {
01655 }
01656 
01657 SafeJSContext::~SafeJSContext() {
01658   JSContext *cx;
01659   nsresult   rv;
01660 
01661   if(mContext) {
01662     rv = mService->Pop(&cx);
01663     NS_ASSERTION(NS_SUCCEEDED(rv) && cx == mContext, "JSContext push/pop mismatch");
01664   }
01665 }
01666 
01667 nsresult SafeJSContext::Push() {
01668   if (mContext) // only once
01669     return NS_ERROR_FAILURE;
01670 
01671   mService = do_GetService(sJSStackContractID);
01672   if(mService) {
01673     JSContext *cx;
01674     if (NS_SUCCEEDED(mService->GetSafeJSContext(&cx)) &&
01675         cx &&
01676         NS_SUCCEEDED(mService->Push(cx))) {
01677       // Save cx in mContext to indicate need to pop.
01678       mContext = cx;
01679     }
01680   }
01681   return mContext ? NS_OK : NS_ERROR_FAILURE;
01682 }
01683 
01684 
01685 // As of Jan, 2005, most of the code in this method is pointless and
01686 // will never be used.  It is only called by ActivateLastWindow() and
01687 // only when there is no existing window.  Consequently, the first test
01688 // below [if ( !navWin )] will always fail, bypassing everything in the
01689 // dummy do..while() loop.  Given that this file is largely a port of
01690 // the Win32 version, it is left in place to maintain concurrency.
01691 
01692 nsresult
01693 nsNativeAppSupportOS2::OpenBrowserWindow()
01694 {
01695     nsresult rv = NS_OK;
01696 
01697     // Open the argument URL in the most recently used Navigator window.
01698     // If there is no Nav window, open a new one.
01699 
01700     // If at all possible, hand the request off to the most recent
01701     // browser window.
01702 
01703     nsCOMPtr<nsIDOMWindowInternal> navWin;
01704     GetMostRecentWindow( NS_LITERAL_STRING( "navigator:browser" ).get(), getter_AddRefs( navWin ) );
01705 
01706     // This isn't really a loop.  We just use "break" statements to fall
01707     // out to the OpenWindow call when things go awry.
01708     do {
01709         // If caller requires a new window, then don't use an existing one.
01710         if ( !navWin ) {
01711             // Have to open a new one.
01712             break;
01713         }
01714 
01715         nsCOMPtr<nsIBrowserDOMWindow> bwin;
01716         { // scope a bunch of temporary cruft used to generate bwin
01717           nsCOMPtr<nsIWebNavigation> navNav( do_GetInterface( navWin ) );
01718           nsCOMPtr<nsIDocShellTreeItem> navItem( do_QueryInterface( navNav ) );
01719           if ( navItem ) {
01720             nsCOMPtr<nsIDocShellTreeItem> rootItem;
01721             navItem->GetRootTreeItem( getter_AddRefs( rootItem ) );
01722             nsCOMPtr<nsIDOMWindow> rootWin( do_GetInterface( rootItem ) );
01723             nsCOMPtr<nsIDOMChromeWindow> chromeWin(do_QueryInterface(rootWin));
01724             if ( chromeWin )
01725               chromeWin->GetBrowserDOMWindow( getter_AddRefs ( bwin ) );
01726           }
01727         }
01728         if ( bwin ) {
01729           nsCOMPtr<nsIURI> uri;
01730           NS_NewURI( getter_AddRefs( uri ), NS_LITERAL_CSTRING("about:blank"), 0, 0 );
01731           if ( uri ) {
01732             nsCOMPtr<nsIDOMWindow> container;
01733             rv = bwin->OpenURI( uri, 0,
01734                                 nsIBrowserDOMWindow::OPEN_DEFAULTWINDOW,
01735                                 nsIBrowserDOMWindow::OPEN_EXTERNAL,
01736                                 getter_AddRefs( container ) );
01737             if ( NS_SUCCEEDED( rv ) )
01738               return NS_OK;
01739           }
01740         }
01741 
01742         NS_ERROR("failed to hand off external URL to extant window\n");
01743     } while ( PR_FALSE );
01744 
01745     // open a new window if caller requested it or if anything above failed
01746 
01747     char* argv[] = { 0 };
01748     nsCOMPtr<nsICommandLineRunner> cmdLine
01749         (do_CreateInstance("@mozilla.org/toolkit/command-line;1"));
01750     NS_ENSURE_TRUE(cmdLine, NS_ERROR_FAILURE);
01751 
01752     rv = cmdLine->Init(0, argv, nsnull, nsICommandLine::STATE_REMOTE_EXPLICIT);
01753     NS_ENSURE_SUCCESS(rv, rv);
01754 
01755     return cmdLine->Run();
01756 }
01757 
01758 // This is a public function called by nsAppRunner.cpp.  Its primary
01759 // purpose is to determine if any commandline options require a VIO
01760 // ("console") window.  If so and one isn't present, it will restart
01761 // the app in a VIO session.  It is intended to be called as early as
01762 // possible during startup and before any other commandline processing.
01763 // It returns TRUE if the current instance should continue running and
01764 // FALSE if it should terminate upon this function's return.
01765 
01766 PRBool     StartOS2App(int aArgc, char **aArgv)
01767 {
01768   PRBool    rv = PR_TRUE;
01769   PPIB      ppib;
01770   PTIB      ptib;
01771 
01772   DosGetInfoBlocks(&ptib, &ppib);
01773 
01774   // if this isn't a PM session, reset the session type to enable use
01775   // of PM functions;  if it is PM, see if a VIO session is required
01776   if (ppib->pib_ultype != SSF_TYPE_PM)
01777     ppib->pib_ultype = SSF_TYPE_PM;
01778   else {
01779     for (int i = 1; i < aArgc; i++ ) {
01780       char *arg = aArgv[i];
01781       if (*arg != '-' && *arg != '/')
01782         continue;
01783       arg++;
01784       if (stricmp("?", arg) == 0 ||
01785         stricmp("h", arg) == 0 ||
01786         stricmp("v", arg) == 0 ||
01787         stricmp("help", arg) == 0 ||
01788         stricmp("version", arg) == 0 ||
01789         stricmp("console", arg) == 0) {
01790         rv = PR_FALSE;
01791         break;
01792       }
01793     }
01794   }
01795 
01796   // if the session type is OK, increase the number of 
01797   // file handles available to the app, then exit
01798   if (rv) {
01799     ULONG    ulMaxFH = 0;
01800     LONG     ulReqCount = 0;
01801 
01802     DosSetRelMaxFH(&ulReqCount, &ulMaxFH);
01803     if (ulMaxFH < 256)
01804       DosSetMaxFH(256);
01805 
01806     return rv;
01807   }
01808 
01809   // the app has to be restarted in a VIO session
01810   char        szErrObj[64] = "";
01811   STARTDATA   x;
01812 
01813   memset(&x, 0, sizeof(x));
01814   x.Length = sizeof(x);
01815   (const char* const)(x.PgmTitle) = gAppData->name;
01816   x.InheritOpt = SSF_INHERTOPT_PARENT;
01817   x.SessionType = SSF_TYPE_WINDOWABLEVIO;
01818   x.PgmControl = SSF_CONTROL_NOAUTOCLOSE;
01819   x.ObjectBuffer = szErrObj;
01820   x.ObjectBuffLen = sizeof(szErrObj);
01821 
01822   // the f/q exename is the string preceding ppib->pib_pchcmd;
01823   // the original commandline is the string following it
01824   char * ptr = ppib->pib_pchcmd - 2;
01825   while (*ptr)
01826     ptr--;
01827   x.PgmName = ptr + 1;
01828   x.PgmInputs = strchr(ppib->pib_pchcmd, 0) + 1;
01829 
01830   // restart the app;  if this session is in the background, trying
01831   // to start in the foreground will produce an error, but the app
01832   // will still start; if DosStartSession has a real failure, forget
01833   // the console and let the current instance keep running
01834   ULONG ulSession;
01835   PID   pid;
01836   ULONG rc = DosStartSession(&x, &ulSession, &pid);
01837   if (rc && rc != ERROR_SMG_START_IN_BACKGROUND)
01838     rv = PR_TRUE;
01839 
01840   return rv;
01841 }
01842