1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
15 * The Original Code is Mozilla Communicator client code.
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998
20 * the Initial Developer. All Rights Reserved.
23 * Bill Law law@netscape.com
25 * Rich Walsh dragtext@e-vertise.com
27 * Alternatively, the contents of this file may be used under the terms of
28 * either the GNU General Public License Version 2 or later (the "GPL"), or
29 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 * in which case the provisions of the GPL or the LGPL are applicable instead
31 * of those above. If you wish to allow use of your version of this file only
32 * under the terms of either the GPL or the LGPL, and not to allow others to
33 * use your version of this file under the terms of the MPL, indicate your
34 * decision by deleting the provisions above and replace them with the notice
35 * and other provisions required by the GPL or the LGPL. If you do not delete
36 * the provisions above, a recipient may use your version of this file under
37 * the terms of any one of the MPL, the GPL or the LGPL.
39 * ***** END LICENSE BLOCK ***** */
41 #ifdef MOZ_OS2_HIGH_MEMORY
42 // os2safe.h has to be included before os2.h, needed for high mem
49 #define INCL_DOSERRORS
52 #include "nsNativeAppSupportBase.h"
53 #include "nsNativeAppSupportOS2.h"
54 #include "nsAppRunner.h"
55 #include "nsXULAppAPI.h"
57 #include "nsIBrowserDOMWindow.h"
58 #include "nsICommandLineRunner.h"
60 #include "nsXPIDLString.h"
61 #include "nsIComponentManager.h"
62 #include "nsIServiceManager.h"
63 #include "nsPIDOMWindow.h"
64 #include "nsIDOMChromeWindow.h"
66 #include "nsISupportsPrimitives.h"
67 #include "nsISupportsArray.h"
68 #include "nsIWindowWatcher.h"
69 #include "nsIDocShell.h"
70 #include "nsIDocShellTreeItem.h"
71 #include "nsIBaseWindow.h"
72 #include "nsIWidget.h"
73 #include "nsIAppShellService.h"
74 #include "nsIXULWindow.h"
75 #include "nsIInterfaceRequestor.h"
76 #include "nsIInterfaceRequestorUtils.h"
77 #include "nsIPromptService.h"
79 #include "nsNetUtil.h"
80 #include "nsIObserver.h"
81 #include "nsIObserverService.h"
83 // These are needed to load a URL in a browser window.
84 #include "nsIDOMLocation.h"
85 #include "nsIJSContextStack.h"
86 #include "nsIWebNavigation.h"
87 #include "nsIWindowMediator.h"
92 #define kMailtoUrlScheme "mailto:"
94 /* trying to keep this like Window's COPYDATASTRUCT, but a compiler error is
95 * forcing me to add chBuff so that we can append the data to the end of the
98 typedef struct _COPYDATASTRUCT
104 }COPYDATASTRUCT
, *PCOPYDATASTRUCT
;
105 #define WM_COPYDATA (WM_USER + 60)
107 char szCommandLine
[2*CCHMAXPATH
];
109 static HWND
hwndForDOMWindow( nsISupports
* );
113 GetMostRecentWindow(const PRUnichar
* aType
, nsIDOMWindowInternal
** aWindow
) {
115 nsCOMPtr
<nsIWindowMediator
> med( do_GetService( NS_WINDOWMEDIATOR_CONTRACTID
, &rv
) );
116 if ( NS_FAILED( rv
) )
120 return med
->GetMostRecentWindow( aType
, aWindow
);
122 return NS_ERROR_FAILURE
;
127 activateWindow( nsIDOMWindowInternal
*win
) {
128 // Try to get native window handle.
129 HWND hwnd
= hwndForDOMWindow( win
);
132 /* if we are looking at a client window, then we really probably want
133 * the frame so that we can manipulate THAT
135 LONG id
= WinQueryWindowUShort( hwnd
, QWS_ID
);
136 if( id
== FID_CLIENT
)
138 hwnd
= WinQueryWindow( hwnd
, QW_PARENT
);
141 // Restore the window in case it is minimized.
142 // Use the OS call, if possible.
143 WinSetWindowPos( hwnd
, 0L, 0L, 0L, 0L, 0L,
144 SWP_SHOW
| SWP_RESTORE
| SWP_ACTIVATE
);
146 // Use internal method.
154 #define MOZ_DEBUG_DDE 1
157 // Simple Win32 mutex wrapper.
159 Mutex( const char *name
)
163 /* OS/2 named semaphores must begin with "\\SEM32\\" to be valid */
164 mName
.Insert("\\SEM32\\", 0);
165 APIRET rc
= DosCreateMutexSem(mName
.get(), &mHandle
, 0, FALSE
);
166 if (rc
!= NO_ERROR
) {
168 printf("CreateMutex error = 0x%08X\n", (int)rc
);
174 // Make sure we release it if we own it.
177 APIRET rc
= DosCloseMutexSem(mHandle
);
178 if (rc
!= NO_ERROR
) {
180 printf("CloseHandle error = 0x%08X\n", (int)rc
);
185 BOOL
Lock( DWORD timeout
) {
188 printf( "Waiting (%d msec) for DDE mutex...\n", (int)timeout
);
190 mState
= DosRequestMutexSem( mHandle
, timeout
);
192 printf( "...wait complete, result = 0x%08X\n", (int)mState
);
194 return (mState
== NO_ERROR
);
200 if ( mHandle
&& mState
== NO_ERROR
) {
202 printf( "Releasing DDE mutex\n" );
204 DosReleaseMutexSem( mHandle
);
216 * This section describes the Win32 DDE service implementation for
217 * Mozilla. DDE is used on Win32 platforms to communicate between
218 * separate instances of mozilla.exe (or other Mozilla-based
219 * executables), or, between the Win32 desktop shell and Mozilla.
221 * The first instance of Mozilla will become the "server" and
222 * subsequent executables (and the shell) will use DDE to send
223 * requests to that process. The requests are DDE "execute" requests
224 * that pass the command line arguments.
226 * Mozilla registers the DDE application "Mozilla" and currently
227 * supports only the "WWW_OpenURL" topic. This should be reasonably
228 * compatible with applications that interfaced with Netscape
229 * Communicator (and its predecessors?). Note that even that topic
230 * may not be supported in a compatible fashion as the command-line
231 * options for Mozilla are different than for Communiator.
233 * It is imperative that at most one instance of Mozilla execute in
234 * "server mode" at any one time. The "native app support" in Mozilla
235 * on Win32 ensures that only the server process performs XPCOM
236 * initialization (that is not required for subsequent client processes
237 * to communicate with the server process).
239 * To guarantee that only one server starts up, a Win32 "mutex" is used
240 * to ensure only one process executes the server-detection code. That
241 * code consists of initializing DDE and doing a DdeConnect to Mozilla's
242 * application/topic. If that connection succeeds, then a server process
243 * must be running already.
245 * Otherwise, no server has started. In that case, the current process
246 * calls DdeNameService to register that application/topic. Only at that
247 * point does the mutex get released.
249 * There are a couple of subtleties that one should be aware of:
251 * 1. It is imperative that DdeInitialize be called only after the mutex
252 * lock has been obtained. The reason is that at shutdown, DDE
253 * notifications go out to all initialized DDE processes. Thus, if
254 * the mutex is owned by a terminating intance of Mozilla, then
255 * calling DdeInitialize and then WaitForSingleObject will cause the
256 * DdeUninitialize from the terminating process to "hang" until the
257 * process waiting for the mutex times out (and can then service the
258 * notification that the DDE server is terminating). So, don't mess
259 * with the sequence of things in the startup/shutdown logic.
261 * 2. All mutex requests are made with a reasonably long timeout value and
262 * are designed to "fail safe" (i.e., a timeout is treated as failure).
264 * 3. An attempt has been made to minimize the degree to which the main
265 * Mozilla application logic needs to be aware of the DDE mechanisms
266 * implemented herein. As a result, this module surfaces a very
267 * large-grained interface, consisting of simple start/stop methods.
268 * As a consequence, details of certain scenarios can be "lost."
269 * Particularly, incoming DDE requests can arrive after this module
270 * initiates the DDE server, but before Mozilla is initialized to the
271 * point where those requests can be serviced (e.g., open a browser
272 * window to a particular URL). Since the client process sends the
273 * request early on, it may not be prepared to respond to that error.
274 * Thus, such situations may fail silently. The design goal is that
275 * they fail harmlessly. Refinements on this point will be made as
276 * details emerge (and time permits).
281 * A significant DDE bug in Windows is causing Mozilla to get wedged at
282 * startup. This is detailed in Bugzill bug 53952
283 * (http://bugzilla.mozilla.org/show_bug.cgi?id=53952).
285 * To resolve this, we are using a new strategy:
286 * o Use a "message window" to detect that Mozilla is already running and
287 * to pass requests from a second instance back to the first;
288 * o Run only as a "DDE server" (not as DDE client); this avoids the
289 * problematic call to DDEConnect().
291 * We still use the mutex semaphore to protect the code that detects
292 * whether Mozilla is already running.
295 class nsNativeAppSupportOS2
: public nsNativeAppSupportBase
,
300 NS_DECL_ISUPPORTS_INHERITED
302 // Overrides of base implementation.
303 NS_IMETHOD
Start( PRBool
*aResult
);
304 NS_IMETHOD
Stop( PRBool
*aResult
);
308 // The "old" Start method (renamed).
309 NS_IMETHOD
StartDDE();
311 // Utility function to handle a Win32-specific command line
312 // option: "-console", which dynamically creates a Windows
317 static HDDEDATA APIENTRY
HandleDDENotification( ULONG idInst
,
326 static void HandleCommandLine(const char* aCmdLineString
, nsIFile
* aWorkingDir
, PRUint32 aState
);
327 static void ParseDDEArg( HSZ args
, int index
, nsCString
& string
);
328 static void ParseDDEArg( const char* args
, int index
, nsCString
& aString
);
329 static void ActivateLastWindow();
330 static HDDEDATA
CreateDDEData( DWORD value
);
331 static HDDEDATA
CreateDDEData( LPBYTE value
, DWORD len
);
332 static PRBool
InitTopicStrings();
333 static int FindTopic( HSZ topic
);
334 static nsresult
OpenWindow( const char *urlstr
, const char *args
);
335 static nsresult
OpenBrowserWindow();
337 static int mConversations
;
344 topicUnRegisterViewer
,
346 // Note: Insert new values above this line!!!!!
347 topicCount
// Count of the number of real topics
350 static HSZ mApplication
, mTopics
[ topicCount
];
351 static DWORD mInstance
;
352 static PRBool mCanHandleRequests
;
353 static char mMutexName
[];
354 static PRBool mUseDDE
;
355 friend struct MessageWindow
;
356 }; // nsNativeAppSupportOS2
358 NS_INTERFACE_MAP_BEGIN(nsNativeAppSupportOS2
)
359 NS_INTERFACE_MAP_ENTRY(nsIObserver
)
360 NS_INTERFACE_MAP_END_INHERITING(nsNativeAppSupportBase
)
362 NS_IMPL_ADDREF_INHERITED(nsNativeAppSupportOS2
, nsNativeAppSupportBase
)
363 NS_IMPL_RELEASE_INHERITED(nsNativeAppSupportOS2
, nsNativeAppSupportBase
)
366 nsNativeAppSupportOS2::CheckConsole() {
371 // Create and return an instance of class nsNativeAppSupportOS2.
373 NS_CreateNativeAppSupport( nsINativeAppSupport
**aResult
) {
374 nsNativeAppSupportOS2
*pNative
= new nsNativeAppSupportOS2
;
375 if (!pNative
) return NS_ERROR_OUT_OF_MEMORY
;
377 // Check for dynamic console creation request.
378 pNative
->CheckConsole();
381 NS_ADDREF( *aResult
);
387 #define MOZ_DDE_APPLICATION "Mozilla"
388 #define MOZ_STARTUP_MUTEX_NAME "StartupMutex"
389 #define MOZ_DDE_START_TIMEOUT 30000
390 #define MOZ_DDE_STOP_TIMEOUT 15000
391 #define MOZ_DDE_EXEC_TIMEOUT 15000
393 // The array entries must match the enum ordering!
394 const char * const topicNames
[] = { "WWW_OpenURL",
396 "WWW_CancelProgress",
398 "WWW_RegisterViewer",
399 "WWW_UnRegisterViewer",
400 "WWW_GetWindowInfo" };
402 // Static member definitions.
403 int nsNativeAppSupportOS2::mConversations
= 0;
404 HSZ
nsNativeAppSupportOS2::mApplication
= 0;
405 HSZ
nsNativeAppSupportOS2::mTopics
[nsNativeAppSupportOS2::topicCount
] = { 0 };
406 DWORD
nsNativeAppSupportOS2::mInstance
= 0;
407 PRBool
nsNativeAppSupportOS2::mCanHandleRequests
= PR_FALSE
;
409 // Added this as pmddeml has no api like this
410 int DdeCmpStringHandles( HSZ hsz1
, HSZ hsz2
)
412 char chhsz1
[CCHMAXPATH
], chhsz2
[CCHMAXPATH
];
415 /* I am assuming that the strings will never be more that CCHMAXPATH in
416 * length. Safe bet (for now). To find true length, call WinDdeQueryString
417 * and pass in (hsz, NULL, 0L, 0) and it will return iLength. Passing 0
418 * for codepage will use the codepage of the current thread.
420 WinDdeQueryString( hsz1
, chhsz1
, sizeof( chhsz1
), 0 );
421 WinDdeQueryString( hsz2
, chhsz2
, sizeof( chhsz2
),0 );
423 rc
= stricmp( chhsz1
, chhsz2
);
429 char *GetCommandLine()
431 /* This function is meant to be like the Window's GetCommandLine() function.
432 * The value returned by pPIB->pib_pchcmd is of the format:
433 * <executable>\0<command line parameters>. So the first string is the
434 * .exe and the second string is what followed on the command line. Dorky,
435 * but I guess it'll have to do.
439 APIRET rc
= NO_ERROR
;
440 char *pchParam
= NULL
;
442 rc
= DosGetInfoBlocks( &pTIB
, &pPIB
);
446 char *pchTemp
= NULL
;
447 pchParam
= pPIB
->pib_pchcmd
;
448 strcpy( szCommandLine
, pchParam
);
449 iLen
= strlen( pchParam
);
451 /* szCommandLine[iLen] is '\0', so see what's next. Probably be another
452 * '\0', a space or start of the arguments
454 pchTemp
= &(pchParam
[iLen
+1]);
456 /* At this point, szCommandLine holds just the program name. Now we
457 * go for the parameters. If our next value is a space, ignore it
458 * and go for the next character
462 szCommandLine
[iLen
] = ' ';
464 if( *pchTemp
== ' ' )
468 strcpy( &(szCommandLine
[iLen
]), pchTemp
);
473 return( szCommandLine
);
476 typedef struct _DDEMLFN
480 } DDEMLFN
, *PDDEMLFN
;
482 DDEMLFN ddemlfnTable
[] =
484 { (PFN
*)&WinDdeAbandonTransaction
,100 },
485 { (PFN
*)&WinDdeAccessData
,101 },
486 { (PFN
*)&WinDdeAddData
,102 },
487 { (PFN
*)&WinDdeSubmitTransaction
,103 },
488 { (PFN
*)&WinDdeCompareStringHandles
,104 },
489 { (PFN
*)&WinDdeConnect
,105 },
490 { (PFN
*)&WinDdeConnectList
,106 },
491 { (PFN
*)&WinDdeCreateDataHandle
,107 },
492 { (PFN
*)&WinDdeCreateStringHandle
,108 },
493 { (PFN
*)&WinDdeDisconnect
,109 },
494 { (PFN
*)&WinDdeDisconnectList
,110 },
495 { (PFN
*)&WinDdeEnableCallback
,111 },
496 { (PFN
*)&WinDdeFreeDataHandle
,112 },
497 { (PFN
*)&WinDdeFreeStringHandle
,113 },
498 { (PFN
*)&WinDdeGetData
,114 },
499 { (PFN
*)&WinDdeInitialize
,116 },
500 { (PFN
*)&WinDdeKeepStringHandle
,117 },
501 { (PFN
*)&WinDdeNameService
,118 },
502 { (PFN
*)&WinDdePostAdvise
,119 },
503 { (PFN
*)&WinDdeQueryConvInfo
,120 },
504 { (PFN
*)&WinDdeQueryNextServer
,121 },
505 { (PFN
*)&WinDdeQueryString
,122 },
506 { (PFN
*)&WinDdeReconnect
,123 },
507 { (PFN
*)&WinDdeSetUserHandle
,124 },
508 { (PFN
*)&WinDdeUninitialize
,126 },
512 /* SetupOS2ddeml makes sure that we can get pointers to all of the DDEML
513 * functions in demlfnTable using entrypoints. If we can't find one of them
514 * or can't load pmddeml.dll, then returns FALSE
519 HMODULE hmodDDEML
= NULLHANDLE
;
520 APIRET rc
= NO_ERROR
;
522 rc
= DosLoadModule( NULL
, 0, "PMDDEML", &hmodDDEML
);
526 /* all of this had better work. Get ready to be a success! */
528 for( i
=0; ddemlfnTable
[i
].ord
!= 0; i
++ )
530 rc
= DosQueryProcAddr( hmodDDEML
, ddemlfnTable
[i
].ord
, NULL
,
531 ddemlfnTable
[i
].fn
);
534 /* we're horked. Return rc = horked */
544 char nsNativeAppSupportOS2::mMutexName
[ 128 ] = { 0 };
547 // Message window encapsulation.
548 struct MessageWindow
{
549 // ctor/dtor are simplistic
551 // Try to find window.
555 char classname
[CCHMAXPATH
];
557 mHandle
= NULLHANDLE
;
559 hatomtbl
= WinQuerySystemAtomTable();
560 mMsgWindowAtom
= WinFindAtom(hatomtbl
, className());
561 if (mMsgWindowAtom
== 0)
563 // If there is not atom in the system table for this class name, then
564 // we can assume that the app is not currently running.
565 mMsgWindowAtom
= WinAddAtom(hatomtbl
, className());
567 // Found an existing atom for this class name. Cycle through existing
568 // windows and see if one with our window id and class name already
570 henum
= WinBeginEnumWindows(HWND_OBJECT
);
571 while ((hwndNext
= WinGetNextWindow(henum
)) != NULLHANDLE
)
573 if (WinQueryWindowUShort(hwndNext
, QWS_ID
) == (USHORT
)mMsgWindowAtom
)
575 WinQueryClassName(hwndNext
, CCHMAXPATH
, classname
);
576 if (strcmp(classname
, className()) == 0)
590 // Class name: appName + "MessageWindow"
591 static const char *className() {
592 static char classNameBuffer
[128];
593 static char *mClassName
= 0;
595 sprintf( classNameBuffer
,
599 mClassName
= classNameBuffer
;
604 // Create: Register class and create window.
605 NS_IMETHOD
Create() {
607 // Register the window class.
608 NS_ENSURE_TRUE( WinRegisterClass( 0, className(),
609 (PFNWP
)&MessageWindow::WindowProc
,
613 /* Giving a size but offscreen so that won't be seen, even if all
614 * goes wrong. Giving a size so that can be seen and understood by
617 const char * pszClassName
= className();
618 mHandle
= WinCreateWindow( HWND_OBJECT
,
621 WS_DISABLED
, // style
624 HWND_DESKTOP
,// owner
625 HWND_BOTTOM
, // hwndbehind
626 (USHORT
)mMsgWindowAtom
, // id
628 NULL
); // pres params
631 printf( "Message window = 0x%08X\n", (int)mHandle
);
637 // Destory: Get rid of window and reset mHandle.
638 NS_IMETHOD
Destroy() {
639 nsresult retval
= NS_OK
;
642 HATOMTBL hatomtbl
= WinQuerySystemAtomTable();
643 WinDeleteAtom(hatomtbl
, mMsgWindowAtom
);
645 // DestroyWindow can only destroy windows created from
647 BOOL desRes
= WinDestroyWindow( mHandle
);
648 if ( FALSE
!= desRes
) {
652 retval
= NS_ERROR_FAILURE
;
659 // SendRequest: Construct a data buffer <commandline>\0<workingdir>\0,
660 // then pass the string via WM_COPYDATA to the message window.
662 NS_IMETHOD
SendRequest( const char *cmd
)
664 /* Nothing like WM_COPYDATA in OS/2, where the OS allows pointers to be
665 * passed to a different process and automatically accessible by that
666 * process. So we have to create shared mem on our side and then the
667 * process that gets the WM_COPYDATA message has to do a
668 * DosGetSharedMem on this pointer to be able to access the data
671 COPYDATASTRUCT
*pcds
;
675 return NS_ERROR_FAILURE
;
677 ULONG ulSize
= sizeof(COPYDATASTRUCT
)+strlen(cmd
)+1+CCHMAXPATH
;
678 #ifdef MOZ_OS2_HIGH_MEMORY
679 APIRET rc
= DosAllocSharedMem( &pvData
, NULL
, ulSize
,
680 PAG_COMMIT
| PAG_READ
| PAG_WRITE
| OBJ_GETTABLE
| OBJ_ANY
);
681 if (rc
!= NO_ERROR
) { // Did the kernel handle OBJ_ANY?
682 // Try again without OBJ_ANY and if the first failure was not caused
683 // by OBJ_ANY then we will get the same failure, else we have taken
684 // care of pre-FP13 systems where the kernel couldn't handle it.
685 rc
= DosAllocSharedMem( &pvData
, NULL
, ulSize
,
686 PAG_COMMIT
| PAG_READ
| PAG_WRITE
| OBJ_GETTABLE
);
687 if (rc
!= NO_ERROR
) {
688 return NS_ERROR_OUT_OF_MEMORY
;
692 if (DosAllocSharedMem( &pvData
, NULL
, ulSize
,
693 PAG_COMMIT
| PAG_READ
| PAG_WRITE
| OBJ_GETTABLE
| OBJ_ANY
))
694 return NS_ERROR_OUT_OF_MEMORY
;
697 // We used to set dwData to zero, when we didn't send the
698 // working dir. Now we're using it as a version number.
699 // The commandline goes in the buffer space immediately after
700 // the COPYDATASTRUCT; the current directory follows that.
701 pcds
= (COPYDATASTRUCT
*)(pvData
);
704 char * ptr
= &(pcds
->chBuff
);
707 pcds
->cbData
= strlen( ptr
) + 1;
710 if (DosQueryPathInfo( ".", FIL_QUERYFULLNAME
, ptr
, CCHMAXPATH
)) {
714 pcds
->cbData
+= strlen( ptr
) + 1;
716 WinSendMsg( mHandle
, WM_COPYDATA
, 0, (MPARAM
)pcds
);
717 DosFreeMem( pvData
);
724 MRESULT EXPENTRY
WindowProc( HWND msgWindow
, ULONG msg
, MPARAM wp
, MPARAM lp
)
726 // We have to return a FALSE from WM_CREATE
727 if ( msg
== WM_CREATE
)
728 return (MRESULT
)FALSE
;
730 if ( msg
!= WM_COPYDATA
)
731 return (MRESULT
)TRUE
;
733 // This is an incoming request.
734 COPYDATASTRUCT
*cds
= (COPYDATASTRUCT
*)lp
;
735 DosGetSharedMem( (PVOID
)cds
, PAG_READ
|PAG_WRITE
);
737 nsCOMPtr
<nsILocalFile
> workingDir
;
739 // a "1" or greater indicates that the other process's working
740 // directory follows the commandline string - locate & convert it
741 if (cds
->dwData
>= 1) {
742 char* wdpath
= strchr( (char*)cds
->lpData
, 0) + 1;
743 NS_NewNativeLocalFile(nsDependentCString(wdpath
),
744 PR_FALSE
, getter_AddRefs(workingDir
));
747 nsNativeAppSupportOS2::HandleCommandLine((char*)cds
->lpData
,
748 workingDir
, nsICommandLine::STATE_REMOTE_AUTO
);
750 return (MRESULT
)TRUE
;
755 USHORT mMsgWindowAtom
;
756 }; // struct MessageWindow
758 PRBool
nsNativeAppSupportOS2::mUseDDE
= PR_FALSE
;
760 /* Start: Tries to find the "message window" to determine if it
761 * exists. If so, then Mozilla is already running. In that
762 * case, we use the handle to the "message" window and send
763 * a request corresponding to this process's command line
766 * If not, then this is the first instance of Mozilla. In
767 * that case, we create and set up the message window.
769 * The checking for existence of the message window must
770 * be protected by use of a mutex semaphore.
773 nsNativeAppSupportOS2::Start( PRBool
*aResult
) {
774 NS_ENSURE_ARG( aResult
);
775 NS_ENSURE_TRUE( mInstance
== 0, NS_ERROR_NOT_INITIALIZED
);
777 nsresult rv
= NS_ERROR_FAILURE
;
780 // see if DDE should be enabled AND remove OS/2-specific
781 // options the app's commandline handler won't recognize
782 // (-console was handled earlier by StartOS2App())
783 for (int i
= 1; i
< gArgc
; i
++) {
784 if (stricmp("-dde", gArgv
[i
]) == 0 ||
785 stricmp("/dde", gArgv
[i
]) == 0)
788 if (stricmp("-console", gArgv
[i
]) != 0 &&
789 stricmp("/console", gArgv
[i
]) != 0)
792 for (int j
= i
; j
< gArgc
; j
++)
793 gArgv
[j
] = gArgv
[j
+1];
798 // if this is a standalone instance, turn off DDE regardless of the
799 // commandline, then skip out before we look for another instance
800 if (getenv("MOZ_NO_REMOTE")) {
808 // Build mutex name from app name.
809 PR_snprintf( mMutexName
, sizeof mMutexName
, "%s%s", gAppData
->name
, MOZ_STARTUP_MUTEX_NAME
);
810 Mutex startupLock
= Mutex( mMutexName
);
812 NS_ENSURE_TRUE( startupLock
.Lock( MOZ_DDE_START_TIMEOUT
), NS_ERROR_FAILURE
);
814 /* We need to have a message queue to do the MessageWindow stuff (for both
815 * Create() and SendRequest()). If we don't have one, make it now.
816 * If we are going to end up going away right after ::Start() returns,
817 * then make sure to clean up the message queue.
821 HMQ hmqCurrent
= WinQueryQueueInfo( HMQ_CURRENT
, &mqinfo
,
824 hab
= WinInitialize( 0 );
825 hmqCurrent
= WinCreateMsgQueue( hab
, 0 );
828 // Search for existing message window.
829 MessageWindow msgWindow
;
830 if ( msgWindow
.getHWND() ) {
831 // We are a client process. Pass request to message window.
832 char *cmd
= GetCommandLine();
833 rv
= msgWindow
.SendRequest( cmd
);
835 // We will be server.
836 rv
= msgWindow
.Create();
837 if ( NS_SUCCEEDED( rv
) ) {
838 // Start up DDE server
841 // Tell caller to spin message loop.
846 startupLock
.Unlock();
848 if( *aResult
== PR_FALSE
)
850 /* This process isn't going to continue much longer. Make sure that we
851 * clean up the message queue
854 WinDestroyMsgQueue(hmqCurrent
);
863 nsNativeAppSupportOS2::InitTopicStrings() {
864 for ( int i
= 0; i
< topicCount
; i
++ ) {
865 if ( !( mTopics
[ i
] = WinDdeCreateStringHandle( (PSZ
)topicNames
[ i
], CP_WINANSI
) ) ) {
873 nsNativeAppSupportOS2::FindTopic( HSZ topic
) {
874 for ( int i
= 0; i
< topicCount
; i
++ ) {
875 if ( DdeCmpStringHandles( topic
, mTopics
[i
] ) == 0 ) {
884 // This used to be the Start() method when we were using DDE as the
885 // primary IPC mechanism between secondary Mozilla processes and the
886 // initial "server" process.
888 // Now, it simply initializes the DDE server. The caller must check
889 // that this process is to be the server, and, must acquire the DDE
890 // startup mutex semaphore prior to calling this routine. See ::Start(),
893 nsNativeAppSupportOS2::StartDDE() {
894 NS_ENSURE_TRUE( mInstance
== 0, NS_ERROR_NOT_INITIALIZED
);
896 /* Get entrypoints for PMDDEML */
897 BOOL bDDEML
= SetupOS2ddeml();
899 /* If we couldn't initialize DDEML, set mUSEDDE to PR_FALSE */
900 /* so we don't do anything else DDE related */
907 NS_ENSURE_TRUE( DDEERR_NO_ERROR
== WinDdeInitialize( &mInstance
,
908 nsNativeAppSupportOS2::HandleDDENotification
,
913 // Allocate DDE strings.
914 NS_ENSURE_TRUE( ( mApplication
= WinDdeCreateStringHandle( (char*) gAppData
->name
, CP_WINANSI
) ) && InitTopicStrings(),
917 // Next step is to register a DDE service.
918 NS_ENSURE_TRUE( WinDdeNameService( mInstance
, mApplication
, 0, DNS_REGISTER
), NS_ERROR_FAILURE
);
921 printf( "DDE server started\n" );
927 // If no DDE conversations are pending, terminate DDE.
929 nsNativeAppSupportOS2::Stop( PRBool
*aResult
) {
930 NS_ENSURE_ARG( aResult
);
931 NS_ENSURE_TRUE( mInstance
, NS_ERROR_NOT_INITIALIZED
);
940 Mutex
ddeLock( mMutexName
);
942 if ( ddeLock
.Lock( MOZ_DDE_STOP_TIMEOUT
) ) {
943 if ( mConversations
== 0 ) {
952 // No DDE application name specified, but that's OK. Just
961 nsNativeAppSupportOS2::Observe(nsISupports
* aSubject
, const char* aTopic
,
962 const PRUnichar
* aData
)
964 if (strcmp(aTopic
, "quit-application") == 0) {
967 NS_ERROR("Unexpected observer topic.");
973 // Terminate DDE regardless.
975 nsNativeAppSupportOS2::Quit() {
976 // If another process wants to look for the message window, they need
977 // to wait to hold the lock, in which case they will not find the
978 // window as we will destroy ours under our lock.
979 // When the mutex goes off the stack, it is unlocked via destructor.
980 Mutex
mutexLock(mMutexName
);
981 NS_ENSURE_TRUE(mutexLock
.Lock(MOZ_DDE_START_TIMEOUT
), NS_ERROR_FAILURE
);
983 // If we've got a message window to receive IPC or new window requests,
984 // get rid of it as we are shutting down.
985 // Note: Destroy calls DestroyWindow, which will only work on a window
986 // created by the same thread.
991 // Unregister application name.
992 WinDdeNameService( mInstance
, mApplication
, 0, DNS_UNREGISTER
);
994 if ( mApplication
) {
995 WinDdeFreeStringHandle( mApplication
);
998 for ( int i
= 0; i
< topicCount
; i
++ ) {
1000 WinDdeFreeStringHandle( mTopics
[i
] );
1004 WinDdeUninitialize( mInstance
);
1012 nsNativeAppSupportOS2::Enable()
1014 mCanHandleRequests
= PR_TRUE
;
1015 nsCOMPtr
<nsIObserverService
> obs
= mozilla::services::GetObserverService();
1017 obs
->AddObserver(this, "quit-application", PR_FALSE
);
1019 NS_ERROR("No observer service?");
1026 // Macro to generate case statement for a given XTYP value.
1027 #define XTYP_CASE(t) \
1028 case t: result = #t; break
1030 static nsCString
uTypeDesc( UINT uType
) {
1033 XTYP_CASE(XTYP_ADVSTART
);
1034 XTYP_CASE(XTYP_CONNECT
);
1035 XTYP_CASE(XTYP_ADVREQ
);
1036 XTYP_CASE(XTYP_REQUEST
);
1037 XTYP_CASE(XTYP_WILDCONNECT
);
1038 XTYP_CASE(XTYP_ADVDATA
);
1039 XTYP_CASE(XTYP_EXECUTE
);
1040 XTYP_CASE(XTYP_POKE
);
1041 XTYP_CASE(XTYP_ADVSTOP
);
1042 XTYP_CASE(XTYP_CONNECT_CONFIRM
);
1043 XTYP_CASE(XTYP_DISCONNECT
);
1044 XTYP_CASE(XTYP_ERROR
);
1045 XTYP_CASE(XTYP_MONITOR
);
1046 XTYP_CASE(XTYP_REGISTER
);
1047 XTYP_CASE(XTYP_XACT_COMPLETE
);
1048 XTYP_CASE(XTYP_UNREGISTER
);
1049 default: result
= "XTYP_?????";
1054 static nsCString
hszValue( DWORD instance
, HSZ hsz
) {
1055 // Extract string from HSZ.
1056 nsCString
result("[");
1057 DWORD len
= WinDdeQueryString( hsz
, NULL
, NULL
, CP_WINANSI
);
1060 WinDdeQueryString( hsz
, buffer
, sizeof buffer
, CP_WINANSI
);
1069 // Utility function to escape double-quotes within a string.
1070 static void escapeQuotes( nsAString
&aString
) {
1071 PRInt32 offset
= -1;
1074 offset
= aString
.FindChar( '"', ++offset
);
1075 if ( offset
== kNotFound
) {
1076 // No more quotes, exit.
1079 // Insert back-slash ahead of the '"'.
1080 aString
.Insert( PRUnichar('\\'), offset
);
1081 // Increment offset because we just inserted a slash
1090 nsNativeAppSupportOS2::HandleDDENotification( ULONG idInst
, // DDEML instance
1091 USHORT uType
, // transaction type
1092 USHORT uFmt
, // clipboard data format
1093 HCONV hconv
, // handle to the conversation
1094 HSZ hsz1
, // handle to a string
1095 HSZ hsz2
, // handle to a string
1096 HDDEDATA hdata
, // handle to a global memory object
1097 ULONG dwData1
, // transaction-specific data
1098 ULONG dwData2
) { // transaction-specific data
1101 printf( "DDE: uType =%s\n", uTypeDesc( uType
).get() );
1102 printf( " uFmt =%u\n", (unsigned)uFmt
);
1103 printf( " hconv =%08x\n", (int)hconv
);
1104 printf( " hsz1 =%08x:%s\n", (int)hsz1
, hszValue( mInstance
, hsz1
).get() );
1105 printf( " hsz2 =%08x:%s\n", (int)hsz2
, hszValue( mInstance
, hsz2
).get() );
1106 printf( " hdata =%08x\n", (int)hdata
);
1107 printf( " dwData1=%08x\n", (int)dwData1
);
1108 printf( " dwData2=%08x\n", (int)dwData2
);
1111 HDDEDATA result
= 0;
1112 if ( uType
& XCLASS_BOOL
) {
1115 // Make sure its for our service/topic.
1116 if ( FindTopic( hsz1
) != -1 ) {
1117 // We support this connection.
1118 result
= (HDDEDATA
)1;
1121 case XTYP_CONNECT_CONFIRM
:
1122 // We don't care about the conversation handle, at this point.
1123 result
= (HDDEDATA
)1;
1126 } else if ( uType
& XCLASS_DATA
) {
1127 if ( uType
== XTYP_REQUEST
) {
1128 switch ( FindTopic( hsz1
) ) {
1129 case topicOpenURL
: {
1130 // Open a given URL...
1132 // Get the URL from the first argument in the command.
1134 ParseDDEArg(hsz2
, 0, url
);
1136 // Read the 3rd argument in the command to determine if a
1137 // new window is to be used.
1138 nsCAutoString windowID
;
1139 ParseDDEArg(hsz2
, 2, windowID
);
1140 // to open the URL in a new window, the old OS/2 code
1141 // looks for "0" while the new Win32 code looks for "";
1142 // not knowing which is correct, we'll look for both
1143 if (windowID
.Equals( "0" ) || windowID
.Equals( "" ))
1144 url
.Insert("mozilla -new-window ", 0);
1146 url
.Insert("mozilla -url ", 0);
1149 printf( "Handling dde XTYP_REQUEST request: [%s]...\n", url
.get() );
1152 HandleCommandLine(url
.get(), nsnull
, nsICommandLine::STATE_REMOTE_EXPLICIT
);
1153 // Return pseudo window ID.
1154 result
= CreateDDEData( 1 );
1157 case topicGetWindowInfo
: {
1158 // This topic has to get the current URL, get the current
1159 // page title and then format the output into the DDE
1160 // return string. The return value is "URL","Page Title",
1161 // "Window ID" however the window ID is not used for this
1162 // command, therefore it is returned as a null string
1164 // This isn't really a loop. We just use "break"
1165 // statements to bypass the remaining steps when
1166 // something goes wrong.
1168 // Get most recently used Nav window.
1169 nsCOMPtr
<nsIDOMWindowInternal
> navWin
;
1170 GetMostRecentWindow( NS_LITERAL_STRING( "navigator:browser" ).get(),
1171 getter_AddRefs( navWin
) );
1173 // There is not a window open
1176 // Get content window.
1177 nsCOMPtr
<nsIDOMWindow
> content
;
1178 navWin
->GetContent( getter_AddRefs( content
) );
1182 // Convert that to internal interface.
1183 nsCOMPtr
<nsPIDOMWindow
> internalContent( do_QueryInterface( content
) );
1184 if ( !internalContent
) {
1188 nsCOMPtr
<nsIDOMLocation
> location
;
1189 internalContent
->GetLocation( getter_AddRefs( location
) );
1193 // Get href for URL.
1195 if ( NS_FAILED( location
->GetHref( url
) ) ) {
1198 // Escape any double-quotes.
1199 escapeQuotes( url
);
1201 // Now for the title...
1203 // Get the base window from the doc shell...
1204 nsCOMPtr
<nsIBaseWindow
> baseWindow
=
1205 do_QueryInterface( internalContent
->GetDocShell() );
1206 if ( !baseWindow
) {
1209 // And from the base window we can get the title.
1210 nsXPIDLString title
;
1214 baseWindow
->GetTitle(getter_Copies(title
));
1215 // Escape any double-quotes in the title.
1216 escapeQuotes( title
);
1218 // Use a string buffer for the output data, first
1220 nsCAutoString
outpt( NS_LITERAL_CSTRING("\"") );
1221 // Now copy the URL converting the Unicode string
1222 // to a single-byte ASCII string
1223 outpt
.Append( NS_LossyConvertUTF16toASCII( url
) );
1224 // Add the "," used to separate the URL and the page
1226 outpt
.Append( NS_LITERAL_CSTRING("\",\"") );
1227 // Now copy the current page title to the return string
1228 outpt
.Append( NS_LossyConvertUTF16toASCII( title
.get() ));
1229 // Fill out the return string with the remainin ",""
1230 outpt
.Append( NS_LITERAL_CSTRING( "\",\"\"" ));
1232 // Create a DDE handle to a char string for the data
1233 // being returned, this copies and creates a "shared"
1234 // copy of the DDE response until the calling APP
1235 // reads it and says it can be freed.
1236 result
= CreateDDEData( (LPBYTE
)(const char*)outpt
.get(),
1237 outpt
.Length() + 1 );
1239 printf( "WWW_GetWindowInfo->%s\n", outpt
.get() );
1241 } while ( PR_FALSE
);
1244 case topicActivate
: {
1245 // Activate a Nav window...
1246 nsCAutoString windowID
;
1247 ParseDDEArg(hsz2
, 0, windowID
);
1248 // 4294967295 is decimal for 0xFFFFFFFF which is also a
1249 // correct value to do that Activate last window stuff
1250 if ( windowID
.Equals( "-1" ) ||
1251 windowID
.Equals( "4294967295" ) ) {
1252 // We only support activating the most recent window (or a new one).
1253 ActivateLastWindow();
1254 // Return pseudo window ID.
1255 result
= CreateDDEData( 1 );
1259 case topicVersion
: {
1260 // Return version. We're restarting at 1.0!
1261 DWORD version
= 1 << 16; // "1.0"
1262 result
= CreateDDEData( version
);
1265 case topicRegisterViewer
: {
1266 // Register new viewer (not implemented).
1267 result
= CreateDDEData( PR_FALSE
);
1270 case topicUnRegisterViewer
: {
1271 // Unregister new viewer (not implemented).
1272 result
= CreateDDEData( PR_FALSE
);
1278 } else if ( uType
& XTYP_POKE
) {
1279 switch ( FindTopic( hsz1
) ) {
1280 case topicCancelProgress
: {
1281 // "Handle" progress cancel (actually, pretty much ignored).
1282 result
= (HDDEDATA
)DDE_FACK
;
1289 } else if ( uType
& XCLASS_FLAGS
) {
1290 if ( uType
== XTYP_EXECUTE
) {
1291 // Prove that we received the request.
1293 LPBYTE request
= (LPBYTE
)WinDdeAccessData( hdata
, &bytes
);
1295 printf( "Handling dde request: [%s]...\n", (char*)request
);
1298 ParseDDEArg((const char*) request
, 0, url
);
1300 // Read the 3rd argument in the command to determine if a
1301 // new window is to be used.
1302 nsCAutoString windowID
;
1303 ParseDDEArg((const char*) request
, 2, windowID
);
1305 // to open the URL in a new window, the old OS/2 code
1306 // looks for "0" while the new Win32 code looks for "";
1307 // not knowing which is correct, we'll look for both
1308 if (windowID
.Equals( "0" ) || windowID
.Equals( "" ))
1309 url
.Insert("mozilla -new-window ", 0);
1311 url
.Insert("mozilla -url ", 0);
1314 printf( "Handling dde XTYP_REQUEST request: [%s]...\n", url
.get() );
1317 HandleCommandLine(url
.get(), nsnull
, nsICommandLine::STATE_REMOTE_EXPLICIT
);
1319 // Release the data.
1320 // DdeUnaccessData( hdata );
1321 result
= (HDDEDATA
)DDE_FACK
;
1323 result
= (HDDEDATA
)DDE_NOTPROCESSED
;
1325 } else if ( uType
& XCLASS_NOTIFICATION
) {
1328 printf( "DDE result=%d (0x%08X)\n", (int)result
, (int)result
);
1334 // Utility function to advance to end of quoted string.
1335 // p+offset must point to the comma preceding the arg on entry.
1336 // On return, p+result points to the closing '"' (or end of the string
1337 // if the closing '"' is missing) if the arg is quoted. If the arg
1338 // is not quoted, then p+result will point to the first character
1340 static PRInt32
advanceToEndOfQuotedArg( const char *p
, PRInt32 offset
, PRInt32 len
) {
1341 // Check whether the current arg is quoted.
1342 if ( p
[++offset
] == '"' ) {
1343 // Advance past the closing quote.
1344 while ( offset
< len
&& p
[++offset
] != '"' ) {
1345 // If the current character is a backslash, then the
1346 // next character can't be a *real* '"', so skip it.
1347 if ( p
[offset
] == '\\' ) {
1355 void nsNativeAppSupportOS2::ParseDDEArg( const char* args
, int index
, nsCString
& aString
) {
1357 int argLen
= strlen(args
);
1358 nsDependentCString
temp(args
, argLen
);
1360 // offset points to the comma preceding the desired arg.
1361 PRInt32 offset
= -1;
1362 // Skip commas till we get to the arg we want.
1364 // If this arg is quoted, then go to closing quote.
1365 offset
= advanceToEndOfQuotedArg( args
, offset
, argLen
);
1367 offset
= temp
.FindChar( ',', offset
);
1368 if ( offset
== kNotFound
) {
1369 // No more commas, give up.
1374 // The desired argument starts just past the preceding comma,
1375 // which offset points to, and extends until the following
1376 // comma (or the end of the string).
1378 // Since the argument might be enclosed in quotes, we need to
1379 // deal with that before searching for the terminating comma.
1380 // We advance offset so it ends up pointing to the start of
1381 // the argument we want.
1382 PRInt32 end
= advanceToEndOfQuotedArg( args
, offset
++, argLen
);
1383 // Find next comma (or end of string).
1384 end
= temp
.FindChar( ',', end
);
1385 if ( end
== kNotFound
) {
1386 // Arg is the rest of the string.
1390 aString
.Assign( args
+ offset
, end
- offset
);
1395 // Utility to parse out argument from a DDE item string.
1396 void nsNativeAppSupportOS2::ParseDDEArg( HSZ args
, int index
, nsCString
& aString
) {
1397 DWORD argLen
= WinDdeQueryString( args
, NULL
, NULL
, CP_WINANSI
);
1398 // there wasn't any string, so return empty string
1399 if ( !argLen
) return;
1401 // Ensure result's buffer is sufficiently big.
1402 temp
.SetLength( argLen
);
1403 // Now get the string contents.
1404 WinDdeQueryString( args
, temp
.BeginWriting(), temp
.Length(), CP_WINANSI
);
1405 // Parse out the given arg.
1406 ParseDDEArg(temp
.get(), index
, aString
);
1410 void nsNativeAppSupportOS2::ActivateLastWindow() {
1411 nsCOMPtr
<nsIDOMWindowInternal
> navWin
;
1412 GetMostRecentWindow( NS_LITERAL_STRING("navigator:browser").get(), getter_AddRefs( navWin
) );
1414 // Activate that window.
1415 activateWindow( navWin
);
1417 // Need to create a Navigator window, then.
1418 OpenBrowserWindow();
1421 HDDEDATA
nsNativeAppSupportOS2::CreateDDEData( DWORD value
) {
1422 return CreateDDEData( (LPBYTE
)&value
, sizeof value
);
1425 HDDEDATA
nsNativeAppSupportOS2::CreateDDEData( LPBYTE value
, DWORD len
) {
1426 HDDEDATA result
= WinDdeCreateDataHandle( value
,
1437 nsNativeAppSupportOS2::HandleCommandLine(const char* aCmdLineString
,
1438 nsIFile
* aWorkingDir
,
1443 int justCounting
= 1;
1447 int between
, quoted
, bSlashCount
;
1452 nsCOMPtr
<nsICommandLineRunner
> cmdLine
1453 (do_CreateInstance("@mozilla.org/toolkit/command-line;1"));
1455 NS_ERROR("Couldn't create command line!");
1459 // We loop if we've not finished the second pass through.
1461 // Initialize if required.
1465 argc
= quoted
= bSlashCount
= 0;
1470 // We are traversing whitespace between args.
1471 // Check for start of next arg.
1472 if ( *p
!= 0 && !isspace( *p
) ) {
1473 // Start of another arg.
1478 // Count the backslash.
1482 // Remember we're inside quotes.
1486 // Add character to arg.
1491 // Another space between args, ignore it.
1494 // We are processing the contents of an argument.
1495 // Check for whitespace or end.
1497 // if the argument we are parsing is a mailto url then all
1498 // of the remaining command line data needs to be part of
1499 // the mailto url even if it has spaces. See Bug #231032
1500 nsDependentCString
mailtoUrlScheme (kMailtoUrlScheme
);
1502 if ( *p
== 0 || ( !quoted
&& isspace( *p
) && !StringBeginsWith(arg
, mailtoUrlScheme
, nsCaseInsensitiveCStringComparator()) ) ) {
1503 // Process pending backslashes (interpret them
1504 // literally since they're not followed by a ").
1505 while( bSlashCount
) {
1510 if ( !justCounting
) {
1511 argv
[argc
] = new char[ arg
.Length() + 1 ];
1512 strcpy( argv
[argc
], arg
.get() );
1515 // We're now between args.
1518 // Still inside argument, process the character.
1521 // First, digest preceding backslashes (if any).
1522 while ( bSlashCount
> 1 ) {
1523 // Put one backsplash in arg for each pair.
1527 if ( bSlashCount
) {
1528 // Quote is literal.
1532 // Quote starts or ends a quoted section.
1534 // Check for special case of consecutive double
1535 // quotes inside a quoted section.
1536 if ( *(p
+1) == '"' ) {
1537 // This implies a literal double-quote. Fake that
1538 // out by causing next double-quote to look as
1539 // if it was preceded by a backslash.
1554 // Accept any preceding backslashes literally.
1555 while ( bSlashCount
) {
1559 // Just add next char to the current arg.
1565 // Check for end of input.
1567 // Go to next character.
1570 // If on first pass, go on to second.
1571 if ( justCounting
) {
1572 // Allocate argv array.
1573 argv
= new char*[ argc
];
1575 // Start second pass
1585 rv
= cmdLine
->Init(argc
, argv
, aWorkingDir
, aState
);
1587 // if these OS/2-specific flags aren't removed,
1588 // any URL following them will be ignored
1590 cmdLine
->HandleFlag(NS_LITERAL_STRING("console"), PR_FALSE
, &found
);
1591 cmdLine
->HandleFlag(NS_LITERAL_STRING("dde"), PR_FALSE
, &found
);
1595 delete [] argv
[ --argc
];
1599 if (NS_FAILED(rv
)) {
1600 NS_ERROR("Error initializing command line.");
1609 nsNativeAppSupportOS2::OpenWindow( const char*urlstr
, const char *args
) {
1611 nsresult rv
= NS_ERROR_FAILURE
;
1613 nsCOMPtr
<nsIWindowWatcher
> wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID
));
1614 nsCOMPtr
<nsISupportsCString
> sarg(do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID
));
1616 sarg
->SetData(nsDependentCString(args
));
1618 if (wwatch
&& sarg
) {
1619 nsCOMPtr
<nsIDOMWindow
> newWindow
;
1620 rv
= wwatch
->OpenWindow(0, urlstr
, "_blank", "chrome,dialog=no,all",
1621 sarg
, getter_AddRefs(newWindow
));
1624 printf("Get WindowWatcher (or create string) failed\n");
1630 HWND
hwndForDOMWindow( nsISupports
*window
) {
1631 nsCOMPtr
<nsPIDOMWindow
> pidomwindow( do_QueryInterface(window
) );
1632 if ( !pidomwindow
) {
1636 nsCOMPtr
<nsIBaseWindow
> ppBaseWindow
=
1637 do_QueryInterface( pidomwindow
->GetDocShell() );
1638 if ( !ppBaseWindow
) {
1642 nsCOMPtr
<nsIWidget
> ppWidget
;
1643 ppBaseWindow
->GetMainWidget( getter_AddRefs( ppWidget
) );
1645 return (HWND
)( ppWidget
->GetNativeData( NS_NATIVE_WIDGET
) );
1648 static const char sJSStackContractID
[] = "@mozilla.org/js/xpc/ContextStack;1";
1650 class SafeJSContext
{
1656 JSContext
*get() { return mContext
; }
1659 nsCOMPtr
<nsIThreadJSContextStack
> mService
;
1660 JSContext
*mContext
;
1663 SafeJSContext::SafeJSContext() : mContext(nsnull
) {
1666 SafeJSContext::~SafeJSContext() {
1671 rv
= mService
->Pop(&cx
);
1672 NS_ASSERTION(NS_SUCCEEDED(rv
) && cx
== mContext
, "JSContext push/pop mismatch");
1676 nsresult
SafeJSContext::Push() {
1677 if (mContext
) // only once
1678 return NS_ERROR_FAILURE
;
1680 mService
= do_GetService(sJSStackContractID
);
1683 if (NS_SUCCEEDED(mService
->GetSafeJSContext(&cx
)) &&
1685 NS_SUCCEEDED(mService
->Push(cx
))) {
1686 // Save cx in mContext to indicate need to pop.
1690 return mContext
? NS_OK
: NS_ERROR_FAILURE
;
1694 // As of Jan, 2005, most of the code in this method is pointless and
1695 // will never be used. It is only called by ActivateLastWindow() and
1696 // only when there is no existing window. Consequently, the first test
1697 // below [if ( !navWin )] will always fail, bypassing everything in the
1698 // dummy do..while() loop. Given that this file is largely a port of
1699 // the Win32 version, it is left in place to maintain concurrency.
1702 nsNativeAppSupportOS2::OpenBrowserWindow()
1704 nsresult rv
= NS_OK
;
1706 // Open the argument URL in the most recently used Navigator window.
1707 // If there is no Nav window, open a new one.
1709 // If at all possible, hand the request off to the most recent
1712 nsCOMPtr
<nsIDOMWindowInternal
> navWin
;
1713 GetMostRecentWindow( NS_LITERAL_STRING( "navigator:browser" ).get(), getter_AddRefs( navWin
) );
1715 // This isn't really a loop. We just use "break" statements to fall
1716 // out to the OpenWindow call when things go awry.
1718 // If caller requires a new window, then don't use an existing one.
1720 // Have to open a new one.
1724 nsCOMPtr
<nsIBrowserDOMWindow
> bwin
;
1725 { // scope a bunch of temporary cruft used to generate bwin
1726 nsCOMPtr
<nsIWebNavigation
> navNav( do_GetInterface( navWin
) );
1727 nsCOMPtr
<nsIDocShellTreeItem
> navItem( do_QueryInterface( navNav
) );
1729 nsCOMPtr
<nsIDocShellTreeItem
> rootItem
;
1730 navItem
->GetRootTreeItem( getter_AddRefs( rootItem
) );
1731 nsCOMPtr
<nsIDOMWindow
> rootWin( do_GetInterface( rootItem
) );
1732 nsCOMPtr
<nsIDOMChromeWindow
> chromeWin(do_QueryInterface(rootWin
));
1734 chromeWin
->GetBrowserDOMWindow( getter_AddRefs ( bwin
) );
1738 nsCOMPtr
<nsIURI
> uri
;
1739 NS_NewURI( getter_AddRefs( uri
), NS_LITERAL_CSTRING("about:blank"), 0, 0 );
1741 nsCOMPtr
<nsIDOMWindow
> container
;
1742 rv
= bwin
->OpenURI( uri
, 0,
1743 nsIBrowserDOMWindow::OPEN_DEFAULTWINDOW
,
1744 nsIBrowserDOMWindow::OPEN_EXTERNAL
,
1745 getter_AddRefs( container
) );
1746 if ( NS_SUCCEEDED( rv
) )
1751 NS_ERROR("failed to hand off external URL to extant window");
1752 } while ( PR_FALSE
);
1754 // open a new window if caller requested it or if anything above failed
1756 char* argv
[] = { 0 };
1757 nsCOMPtr
<nsICommandLineRunner
> cmdLine
1758 (do_CreateInstance("@mozilla.org/toolkit/command-line;1"));
1759 NS_ENSURE_TRUE(cmdLine
, NS_ERROR_FAILURE
);
1761 rv
= cmdLine
->Init(0, argv
, nsnull
, nsICommandLine::STATE_REMOTE_EXPLICIT
);
1762 NS_ENSURE_SUCCESS(rv
, rv
);
1764 return cmdLine
->Run();
1767 // This is a public function called by nsAppRunner.cpp. Its primary
1768 // purpose is to determine if any commandline options require a VIO
1769 // ("console") window. If so and one isn't present, it will restart
1770 // the app in a VIO session. It is intended to be called as early as
1771 // possible during startup and before any other commandline processing.
1772 // It returns TRUE if the current instance should continue running and
1773 // FALSE if it should terminate upon this function's return.
1775 PRBool
StartOS2App(int aArgc
, char **aArgv
)
1777 PRBool rv
= PR_TRUE
;
1781 DosGetInfoBlocks(&ptib
, &ppib
);
1783 // if this isn't a PM session, reset the session type to enable use
1784 // of PM functions; if it is PM, see if a VIO session is required
1785 if (ppib
->pib_ultype
!= SSF_TYPE_PM
)
1786 ppib
->pib_ultype
= SSF_TYPE_PM
;
1788 for (int i
= 1; i
< aArgc
; i
++ ) {
1789 char *arg
= aArgv
[i
];
1790 if (*arg
!= '-' && *arg
!= '/')
1793 if (stricmp("?", arg
) == 0 ||
1794 stricmp("h", arg
) == 0 ||
1795 stricmp("v", arg
) == 0 ||
1796 stricmp("help", arg
) == 0 ||
1797 stricmp("version", arg
) == 0 ||
1798 stricmp("console", arg
) == 0) {
1805 // if the session type is OK, increase the number of
1806 // file handles available to the app, then exit
1809 LONG ulReqCount
= 0;
1811 DosSetRelMaxFH(&ulReqCount
, &ulMaxFH
);
1818 // the app has to be restarted in a VIO session
1819 char szErrObj
[64] = "";
1822 memset(&x
, 0, sizeof(x
));
1823 x
.Length
= sizeof(x
);
1824 x
.PgmTitle
= (PSZ
)gAppData
->name
;
1825 x
.InheritOpt
= SSF_INHERTOPT_PARENT
;
1826 x
.SessionType
= SSF_TYPE_WINDOWABLEVIO
;
1827 x
.PgmControl
= SSF_CONTROL_NOAUTOCLOSE
;
1828 x
.ObjectBuffer
= szErrObj
;
1829 x
.ObjectBuffLen
= sizeof(szErrObj
);
1831 // the f/q exename is the string preceding ppib->pib_pchcmd;
1832 // the original commandline is the string following it
1833 char * ptr
= ppib
->pib_pchcmd
- 2;
1836 x
.PgmName
= ptr
+ 1;
1837 x
.PgmInputs
= strchr(ppib
->pib_pchcmd
, 0) + 1;
1839 // restart the app; if this session is in the background, trying
1840 // to start in the foreground will produce an error, but the app
1841 // will still start; if DosStartSession has a real failure, forget
1842 // the console and let the current instance keep running
1845 ULONG rc
= DosStartSession(&x
, &ulSession
, &pid
);
1846 if (rc
&& rc
!= ERROR_SMG_START_IN_BACKGROUND
)