Avoid wildman storage-punning hacks for non-unit static strings (515273, r=gal/gwagner).
[mozilla-central.git] / toolkit / xre / nsNativeAppSupportOS2.cpp
blobadab9b211580d0f8cfd4df5f92ec1f7bbb5d34ba
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
13 * License.
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.
22 * Contributor(s):
23 * Bill Law law@netscape.com
24 * IBM Corp.
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
43 #include <os2safe.h>
44 #endif
46 #define INCL_PM
47 #define INCL_GPI
48 #define INCL_DOS
49 #define INCL_DOSERRORS
50 #include <os2.h>
52 #include "nsNativeAppSupportBase.h"
53 #include "nsNativeAppSupportOS2.h"
54 #include "nsAppRunner.h"
55 #include "nsXULAppAPI.h"
56 #include "nsString.h"
57 #include "nsIBrowserDOMWindow.h"
58 #include "nsICommandLineRunner.h"
59 #include "nsCOMPtr.h"
60 #include "nsXPIDLString.h"
61 #include "nsIComponentManager.h"
62 #include "nsIServiceManager.h"
63 #include "nsPIDOMWindow.h"
64 #include "nsIDOMChromeWindow.h"
65 #include "nsXPCOM.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"
78 #include "nsNetCID.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"
89 #include <stdlib.h>
90 #include "prprf.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
96 * structure
98 typedef struct _COPYDATASTRUCT
100 ULONG dwData;
101 ULONG cbData;
102 PVOID lpData;
103 CHAR chBuff;
104 }COPYDATASTRUCT, *PCOPYDATASTRUCT;
105 #define WM_COPYDATA (WM_USER + 60)
107 char szCommandLine[2*CCHMAXPATH];
109 static HWND hwndForDOMWindow( nsISupports * );
111 static
112 nsresult
113 GetMostRecentWindow(const PRUnichar* aType, nsIDOMWindowInternal** aWindow) {
114 nsresult rv;
115 nsCOMPtr<nsIWindowMediator> med( do_GetService( NS_WINDOWMEDIATOR_CONTRACTID, &rv ) );
116 if ( NS_FAILED( rv ) )
117 return rv;
119 if ( med )
120 return med->GetMostRecentWindow( aType, aWindow );
122 return NS_ERROR_FAILURE;
125 static
126 void
127 activateWindow( nsIDOMWindowInternal *win ) {
128 // Try to get native window handle.
129 HWND hwnd = hwndForDOMWindow( win );
130 if ( hwnd ) {
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 );
145 } else {
146 // Use internal method.
147 win->Focus();
152 #ifdef DEBUG_law
153 #undef MOZ_DEBUG_DDE
154 #define MOZ_DEBUG_DDE 1
155 #endif
157 // Simple Win32 mutex wrapper.
158 struct Mutex {
159 Mutex( const char *name )
160 : mName( name ),
161 mHandle( 0 ),
162 mState( 0xFFFF ) {
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) {
167 #if MOZ_DEBUG_DDE
168 printf("CreateMutex error = 0x%08X\n", (int)rc);
169 #endif
172 ~Mutex() {
173 if ( mHandle ) {
174 // Make sure we release it if we own it.
175 Unlock();
177 APIRET rc = DosCloseMutexSem(mHandle);
178 if (rc != NO_ERROR) {
179 #if MOZ_DEBUG_DDE
180 printf("CloseHandle error = 0x%08X\n", (int)rc);
181 #endif
185 BOOL Lock( DWORD timeout ) {
186 if ( mHandle ) {
187 #if MOZ_DEBUG_DDE
188 printf( "Waiting (%d msec) for DDE mutex...\n", (int)timeout );
189 #endif
190 mState = DosRequestMutexSem( mHandle, timeout );
191 #if MOZ_DEBUG_DDE
192 printf( "...wait complete, result = 0x%08X\n", (int)mState );
193 #endif
194 return (mState == NO_ERROR);
195 } else {
196 return FALSE;
199 void Unlock() {
200 if ( mHandle && mState == NO_ERROR ) {
201 #if MOZ_DEBUG_DDE
202 printf( "Releasing DDE mutex\n" );
203 #endif
204 DosReleaseMutexSem( mHandle );
205 mState = 0xFFFF;
208 private:
209 nsCString mName;
210 HMTX mHandle;
211 DWORD mState;
214 /* DDE Notes
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).
279 /* Update 2001 March
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,
296 public nsIObserver
298 public:
299 NS_DECL_NSIOBSERVER
300 NS_DECL_ISUPPORTS_INHERITED
302 // Overrides of base implementation.
303 NS_IMETHOD Start( PRBool *aResult );
304 NS_IMETHOD Stop( PRBool *aResult );
305 NS_IMETHOD Quit();
306 NS_IMETHOD Enable();
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
313 // console.
314 void CheckConsole();
316 private:
317 static HDDEDATA APIENTRY HandleDDENotification( ULONG idInst,
318 USHORT uType,
319 USHORT uFmt,
320 HCONV hconv,
321 HSZ hsz1,
322 HSZ hsz2,
323 HDDEDATA hdata,
324 ULONG dwData1,
325 ULONG dwData2 );
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;
338 enum {
339 topicOpenURL,
340 topicActivate,
341 topicCancelProgress,
342 topicVersion,
343 topicRegisterViewer,
344 topicUnRegisterViewer,
345 topicGetWindowInfo,
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)
365 void
366 nsNativeAppSupportOS2::CheckConsole() {
367 return;
371 // Create and return an instance of class nsNativeAppSupportOS2.
372 nsresult
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();
380 *aResult = pNative;
381 NS_ADDREF( *aResult );
383 return NS_OK;
386 // Constants
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",
395 "WWW_Activate",
396 "WWW_CancelProgress",
397 "WWW_Version",
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];
413 int rc = -1;
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 );
425 return(rc);
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.
437 PTIB pTIB = NULL;
438 PPIB pPIB = NULL;
439 APIRET rc = NO_ERROR;
440 char *pchParam = NULL;
442 rc = DosGetInfoBlocks( &pTIB, &pPIB );
443 if( rc == NO_ERROR )
445 INT iLen = 0;
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
460 if( *pchTemp )
462 szCommandLine[iLen] = ' ';
463 iLen++;
464 if( *pchTemp == ' ' )
466 pchTemp++;
468 strcpy( &(szCommandLine[iLen]), pchTemp );
473 return( szCommandLine );
476 typedef struct _DDEMLFN
478 PFN *fn;
479 ULONG ord;
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 },
509 { (PFN *)NULL , 0 }
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
516 BOOL SetupOS2ddeml()
518 BOOL bRC = FALSE;
519 HMODULE hmodDDEML = NULLHANDLE;
520 APIRET rc = NO_ERROR;
522 rc = DosLoadModule( NULL, 0, "PMDDEML", &hmodDDEML );
523 if( rc == NO_ERROR )
525 int i;
526 /* all of this had better work. Get ready to be a success! */
527 bRC = TRUE;
528 for( i=0; ddemlfnTable[i].ord != 0; i++ )
530 rc = DosQueryProcAddr( hmodDDEML, ddemlfnTable[i].ord, NULL,
531 ddemlfnTable[i].fn );
532 if( rc != NO_ERROR )
534 /* we're horked. Return rc = horked */
535 bRC = FALSE;
536 break;
539 } /* load PMDDEML */
541 return( bRC );
544 char nsNativeAppSupportOS2::mMutexName[ 128 ] = { 0 };
547 // Message window encapsulation.
548 struct MessageWindow {
549 // ctor/dtor are simplistic
550 MessageWindow() {
551 // Try to find window.
552 HATOMTBL hatomtbl;
553 HENUM henum;
554 HWND hwndNext;
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());
566 } else {
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
569 // exists
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)
578 mHandle = hwndNext;
579 break;
586 HWND getHWND() {
587 return mHandle;
590 // Class name: appName + "MessageWindow"
591 static const char *className() {
592 static char classNameBuffer[128];
593 static char *mClassName = 0;
594 if ( !mClassName ) {
595 sprintf( classNameBuffer,
596 "%s%s",
597 gAppData->name,
598 "MessageWindow" );
599 mClassName = classNameBuffer;
601 return mClassName;
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,
610 0L, 0 ),
611 NS_ERROR_FAILURE );
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
615 * tools
617 const char * pszClassName = className();
618 mHandle = WinCreateWindow( HWND_OBJECT,
619 pszClassName,
620 NULL, // name
621 WS_DISABLED, // style
622 0,0, // x, y
623 0,0, // cx,cy
624 HWND_DESKTOP,// owner
625 HWND_BOTTOM, // hwndbehind
626 (USHORT)mMsgWindowAtom, // id
627 NULL, // pCtlData
628 NULL ); // pres params
630 #if MOZ_DEBUG_DDE
631 printf( "Message window = 0x%08X\n", (int)mHandle );
632 #endif
634 return NS_OK;
637 // Destory: Get rid of window and reset mHandle.
638 NS_IMETHOD Destroy() {
639 nsresult retval = NS_OK;
641 if ( mHandle ) {
642 HATOMTBL hatomtbl = WinQuerySystemAtomTable();
643 WinDeleteAtom(hatomtbl, mMsgWindowAtom);
645 // DestroyWindow can only destroy windows created from
646 // the same thread.
647 BOOL desRes = WinDestroyWindow( mHandle );
648 if ( FALSE != desRes ) {
649 mHandle = NULL;
651 else {
652 retval = NS_ERROR_FAILURE;
656 return retval;
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;
672 PVOID pvData = NULL;
674 if (!cmd)
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;
691 #else
692 if (DosAllocSharedMem( &pvData, NULL, ulSize,
693 PAG_COMMIT | PAG_READ | PAG_WRITE | OBJ_GETTABLE | OBJ_ANY))
694 return NS_ERROR_OUT_OF_MEMORY;
695 #endif
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);
702 pcds->dwData = 1;
704 char * ptr = &(pcds->chBuff);
705 pcds->lpData = ptr;
706 strcpy( ptr, cmd);
707 pcds->cbData = strlen( ptr) + 1;
708 ptr += pcds->cbData;
710 if (DosQueryPathInfo( ".", FIL_QUERYFULLNAME, ptr, CCHMAXPATH)) {
711 ptr[0] = '.';
712 ptr[1] = '\0';
714 pcds->cbData += strlen( ptr) + 1;
716 WinSendMsg( mHandle, WM_COPYDATA, 0, (MPARAM)pcds );
717 DosFreeMem( pvData );
719 return NS_OK;
722 // Window proc.
723 static
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;
753 private:
754 HWND mHandle;
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
764 * options.
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 existance of the message window must
770 * be protected by use of a mutex semaphore.
772 NS_IMETHODIMP
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;
778 *aResult = PR_FALSE;
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)
786 mUseDDE = PR_TRUE;
787 else
788 if (stricmp("-console", gArgv[i]) != 0 &&
789 stricmp("/console", gArgv[i]) != 0)
790 continue;
792 for (int j = i; j < gArgc; j++)
793 gArgv[j] = gArgv[j+1];
794 gArgc--;
795 i--;
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")) {
801 mUseDDE = PR_FALSE;
802 *aResult = PR_TRUE;
803 return NS_OK;
806 // Grab mutex first.
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.
819 MQINFO mqinfo;
820 HAB hab;
821 HMQ hmqCurrent = WinQueryQueueInfo( HMQ_CURRENT, &mqinfo,
822 sizeof( MQINFO ) );
823 if( !hmqCurrent ) {
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 );
834 } else {
835 // We will be server.
836 rv = msgWindow.Create();
837 if ( NS_SUCCEEDED( rv ) ) {
838 // Start up DDE server
839 if (mUseDDE)
840 this->StartDDE();
841 // Tell caller to spin message loop.
842 *aResult = PR_TRUE;
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
853 if (hmqCurrent)
854 WinDestroyMsgQueue(hmqCurrent);
855 if (hab)
856 WinTerminate(hab);
859 return rv;
862 PRBool
863 nsNativeAppSupportOS2::InitTopicStrings() {
864 for ( int i = 0; i < topicCount; i++ ) {
865 if ( !( mTopics[ i ] = WinDdeCreateStringHandle( (PSZ)topicNames[ i ], CP_WINANSI ) ) ) {
866 return PR_FALSE;
869 return PR_TRUE;
873 nsNativeAppSupportOS2::FindTopic( HSZ topic ) {
874 for ( int i = 0; i < topicCount; i++ ) {
875 if ( DdeCmpStringHandles( topic, mTopics[i] ) == 0 ) {
876 return i;
879 return -1;
882 // Start DDE server.
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(),
891 // above.
892 NS_IMETHODIMP
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 */
901 if (!bDDEML) {
902 mUseDDE = PR_FALSE;
903 return NS_OK;
906 // Initialize DDE.
907 NS_ENSURE_TRUE( DDEERR_NO_ERROR == WinDdeInitialize( &mInstance,
908 nsNativeAppSupportOS2::HandleDDENotification,
909 APPCLASS_STANDARD,
910 0 ),
911 NS_ERROR_FAILURE );
913 // Allocate DDE strings.
914 NS_ENSURE_TRUE( ( mApplication = WinDdeCreateStringHandle( (char*) gAppData->name, CP_WINANSI ) ) && InitTopicStrings(),
915 NS_ERROR_FAILURE );
917 // Next step is to register a DDE service.
918 NS_ENSURE_TRUE( WinDdeNameService( mInstance, mApplication, 0, DNS_REGISTER ), NS_ERROR_FAILURE );
920 #if MOZ_DEBUG_DDE
921 printf( "DDE server started\n" );
922 #endif
924 return NS_OK;
927 // If no DDE conversations are pending, terminate DDE.
928 NS_IMETHODIMP
929 nsNativeAppSupportOS2::Stop( PRBool *aResult ) {
930 NS_ENSURE_ARG( aResult );
931 NS_ENSURE_TRUE( mInstance, NS_ERROR_NOT_INITIALIZED );
933 nsresult rv = NS_OK;
934 *aResult = PR_TRUE;
936 if (!mUseDDE) {
937 return rv;
940 Mutex ddeLock( mMutexName );
942 if ( ddeLock.Lock( MOZ_DDE_STOP_TIMEOUT ) ) {
943 if ( mConversations == 0 ) {
944 this->Quit();
945 } else {
946 *aResult = PR_FALSE;
949 ddeLock.Unlock();
951 else {
952 // No DDE application name specified, but that's OK. Just
953 // forge ahead.
954 *aResult = PR_TRUE;
957 return rv;
960 NS_IMETHODIMP
961 nsNativeAppSupportOS2::Observe(nsISupports* aSubject, const char* aTopic,
962 const PRUnichar* aData)
964 if (strcmp(aTopic, "quit-application") == 0) {
965 Quit();
966 } else {
967 NS_ERROR("Unexpected observer topic.");
970 return NS_OK;
973 // Terminate DDE regardless.
974 NS_IMETHODIMP
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.
987 MessageWindow mw;
988 mw.Destroy();
990 if ( mInstance ) {
991 // Unregister application name.
992 WinDdeNameService( mInstance, mApplication, 0, DNS_UNREGISTER );
993 // Clean up strings.
994 if ( mApplication ) {
995 WinDdeFreeStringHandle( mApplication );
996 mApplication = 0;
998 for ( int i = 0; i < topicCount; i++ ) {
999 if ( mTopics[i] ) {
1000 WinDdeFreeStringHandle( mTopics[i] );
1001 mTopics[i] = 0;
1004 WinDdeUninitialize( mInstance );
1005 mInstance = 0;
1008 return NS_OK;
1011 NS_IMETHODIMP
1012 nsNativeAppSupportOS2::Enable()
1014 mCanHandleRequests = PR_TRUE;
1015 nsCOMPtr<nsIObserverService> obs
1016 (do_GetService("@mozilla.org/observer-service;1"));
1017 if (obs) {
1018 obs->AddObserver(this, "quit-application", PR_FALSE);
1019 } else {
1020 NS_ERROR("No observer service?");
1023 return NS_OK;
1026 #if MOZ_DEBUG_DDE
1027 // Macro to generate case statement for a given XTYP value.
1028 #define XTYP_CASE(t) \
1029 case t: result = #t; break
1031 static nsCString uTypeDesc( UINT uType ) {
1032 nsCString result;
1033 switch ( uType ) {
1034 XTYP_CASE(XTYP_ADVSTART);
1035 XTYP_CASE(XTYP_CONNECT);
1036 XTYP_CASE(XTYP_ADVREQ);
1037 XTYP_CASE(XTYP_REQUEST);
1038 XTYP_CASE(XTYP_WILDCONNECT);
1039 XTYP_CASE(XTYP_ADVDATA);
1040 XTYP_CASE(XTYP_EXECUTE);
1041 XTYP_CASE(XTYP_POKE);
1042 XTYP_CASE(XTYP_ADVSTOP);
1043 XTYP_CASE(XTYP_CONNECT_CONFIRM);
1044 XTYP_CASE(XTYP_DISCONNECT);
1045 XTYP_CASE(XTYP_ERROR);
1046 XTYP_CASE(XTYP_MONITOR);
1047 XTYP_CASE(XTYP_REGISTER);
1048 XTYP_CASE(XTYP_XACT_COMPLETE);
1049 XTYP_CASE(XTYP_UNREGISTER);
1050 default: result = "XTYP_?????";
1052 return result;
1055 static nsCString hszValue( DWORD instance, HSZ hsz ) {
1056 // Extract string from HSZ.
1057 nsCString result("[");
1058 DWORD len = WinDdeQueryString( hsz, NULL, NULL, CP_WINANSI );
1059 if ( len ) {
1060 char buffer[ 256 ];
1061 WinDdeQueryString( hsz, buffer, sizeof buffer, CP_WINANSI );
1062 result += buffer;
1064 result += "]";
1065 return result;
1067 #endif
1070 // Utility function to escape double-quotes within a string.
1071 static void escapeQuotes( nsAString &aString ) {
1072 PRInt32 offset = -1;
1073 while( 1 ) {
1074 // Find next '"'.
1075 offset = aString.FindChar( '"', ++offset );
1076 if ( offset == kNotFound ) {
1077 // No more quotes, exit.
1078 break;
1079 } else {
1080 // Insert back-slash ahead of the '"'.
1081 aString.Insert( PRUnichar('\\'), offset );
1082 // Increment offset because we just inserted a slash
1083 offset++;
1086 return;
1090 HDDEDATA APIENTRY
1091 nsNativeAppSupportOS2::HandleDDENotification( ULONG idInst, // DDEML instance
1092 USHORT uType, // transaction type
1093 USHORT uFmt, // clipboard data format
1094 HCONV hconv, // handle to the conversation
1095 HSZ hsz1, // handle to a string
1096 HSZ hsz2, // handle to a string
1097 HDDEDATA hdata, // handle to a global memory object
1098 ULONG dwData1, // transaction-specific data
1099 ULONG dwData2 ) { // transaction-specific data
1101 #if MOZ_DEBUG_DDE
1102 printf( "DDE: uType =%s\n", uTypeDesc( uType ).get() );
1103 printf( " uFmt =%u\n", (unsigned)uFmt );
1104 printf( " hconv =%08x\n", (int)hconv );
1105 printf( " hsz1 =%08x:%s\n", (int)hsz1, hszValue( mInstance, hsz1 ).get() );
1106 printf( " hsz2 =%08x:%s\n", (int)hsz2, hszValue( mInstance, hsz2 ).get() );
1107 printf( " hdata =%08x\n", (int)hdata );
1108 printf( " dwData1=%08x\n", (int)dwData1 );
1109 printf( " dwData2=%08x\n", (int)dwData2 );
1110 #endif
1112 HDDEDATA result = 0;
1113 if ( uType & XCLASS_BOOL ) {
1114 switch ( uType ) {
1115 case XTYP_CONNECT:
1116 // Make sure its for our service/topic.
1117 if ( FindTopic( hsz1 ) != -1 ) {
1118 // We support this connection.
1119 result = (HDDEDATA)1;
1121 break;
1122 case XTYP_CONNECT_CONFIRM:
1123 // We don't care about the conversation handle, at this point.
1124 result = (HDDEDATA)1;
1125 break;
1127 } else if ( uType & XCLASS_DATA ) {
1128 if ( uType == XTYP_REQUEST ) {
1129 switch ( FindTopic( hsz1 ) ) {
1130 case topicOpenURL: {
1131 // Open a given URL...
1133 // Get the URL from the first argument in the command.
1134 nsCAutoString url;
1135 ParseDDEArg(hsz2, 0, url);
1137 // Read the 3rd argument in the command to determine if a
1138 // new window is to be used.
1139 nsCAutoString windowID;
1140 ParseDDEArg(hsz2, 2, windowID);
1141 // to open the URL in a new window, the old OS/2 code
1142 // looks for "0" while the new Win32 code looks for "";
1143 // not knowing which is correct, we'll look for both
1144 if (windowID.Equals( "0" ) || windowID.Equals( "" ))
1145 url.Insert("mozilla -new-window ", 0);
1146 else
1147 url.Insert("mozilla -url ", 0);
1149 #if MOZ_DEBUG_DDE
1150 printf( "Handling dde XTYP_REQUEST request: [%s]...\n", url.get() );
1151 #endif
1152 // Now handle it.
1153 HandleCommandLine(url.get(), nsnull, nsICommandLine::STATE_REMOTE_EXPLICIT);
1154 // Return pseudo window ID.
1155 result = CreateDDEData( 1 );
1156 break;
1158 case topicGetWindowInfo: {
1159 // This topic has to get the current URL, get the current
1160 // page title and then format the output into the DDE
1161 // return string. The return value is "URL","Page Title",
1162 // "Window ID" however the window ID is not used for this
1163 // command, therefore it is returned as a null string
1165 // This isn't really a loop. We just use "break"
1166 // statements to bypass the remaining steps when
1167 // something goes wrong.
1168 do {
1169 // Get most recently used Nav window.
1170 nsCOMPtr<nsIDOMWindowInternal> navWin;
1171 GetMostRecentWindow( NS_LITERAL_STRING( "navigator:browser" ).get(),
1172 getter_AddRefs( navWin ) );
1173 if ( !navWin ) {
1174 // There is not a window open
1175 break;
1177 // Get content window.
1178 nsCOMPtr<nsIDOMWindow> content;
1179 navWin->GetContent( getter_AddRefs( content ) );
1180 if ( !content ) {
1181 break;
1183 // Convert that to internal interface.
1184 nsCOMPtr<nsPIDOMWindow> internalContent( do_QueryInterface( content ) );
1185 if ( !internalContent ) {
1186 break;
1188 // Get location.
1189 nsCOMPtr<nsIDOMLocation> location;
1190 internalContent->GetLocation( getter_AddRefs( location ) );
1191 if ( !location ) {
1192 break;
1194 // Get href for URL.
1195 nsAutoString url;
1196 if ( NS_FAILED( location->GetHref( url ) ) ) {
1197 break;
1199 // Escape any double-quotes.
1200 escapeQuotes( url );
1202 // Now for the title...
1204 // Get the base window from the doc shell...
1205 nsCOMPtr<nsIBaseWindow> baseWindow =
1206 do_QueryInterface( internalContent->GetDocShell() );
1207 if ( !baseWindow ) {
1208 break;
1210 // And from the base window we can get the title.
1211 nsXPIDLString title;
1212 if(!baseWindow) {
1213 break;
1215 baseWindow->GetTitle(getter_Copies(title));
1216 // Escape any double-quotes in the title.
1217 escapeQuotes( title );
1219 // Use a string buffer for the output data, first
1220 // save a quote.
1221 nsCAutoString outpt( NS_LITERAL_CSTRING("\"") );
1222 // Now copy the URL converting the Unicode string
1223 // to a single-byte ASCII string
1224 outpt.Append( NS_LossyConvertUTF16toASCII( url ) );
1225 // Add the "," used to separate the URL and the page
1226 // title
1227 outpt.Append( NS_LITERAL_CSTRING("\",\"") );
1228 // Now copy the current page title to the return string
1229 outpt.Append( NS_LossyConvertUTF16toASCII( title.get() ));
1230 // Fill out the return string with the remainin ",""
1231 outpt.Append( NS_LITERAL_CSTRING( "\",\"\"" ));
1233 // Create a DDE handle to a char string for the data
1234 // being returned, this copies and creates a "shared"
1235 // copy of the DDE response until the calling APP
1236 // reads it and says it can be freed.
1237 result = CreateDDEData( (LPBYTE)(const char*)outpt.get(),
1238 outpt.Length() + 1 );
1239 #if MOZ_DEBUG_DDE
1240 printf( "WWW_GetWindowInfo->%s\n", outpt.get() );
1241 #endif
1242 } while ( PR_FALSE );
1243 break;
1245 case topicActivate: {
1246 // Activate a Nav window...
1247 nsCAutoString windowID;
1248 ParseDDEArg(hsz2, 0, windowID);
1249 // 4294967295 is decimal for 0xFFFFFFFF which is also a
1250 // correct value to do that Activate last window stuff
1251 if ( windowID.Equals( "-1" ) ||
1252 windowID.Equals( "4294967295" ) ) {
1253 // We only support activating the most recent window (or a new one).
1254 ActivateLastWindow();
1255 // Return pseudo window ID.
1256 result = CreateDDEData( 1 );
1258 break;
1260 case topicVersion: {
1261 // Return version. We're restarting at 1.0!
1262 DWORD version = 1 << 16; // "1.0"
1263 result = CreateDDEData( version );
1264 break;
1266 case topicRegisterViewer: {
1267 // Register new viewer (not implemented).
1268 result = CreateDDEData( PR_FALSE );
1269 break;
1271 case topicUnRegisterViewer: {
1272 // Unregister new viewer (not implemented).
1273 result = CreateDDEData( PR_FALSE );
1274 break;
1276 default:
1277 break;
1279 } else if ( uType & XTYP_POKE ) {
1280 switch ( FindTopic( hsz1 ) ) {
1281 case topicCancelProgress: {
1282 // "Handle" progress cancel (actually, pretty much ignored).
1283 result = (HDDEDATA)DDE_FACK;
1284 break;
1286 default:
1287 break;
1290 } else if ( uType & XCLASS_FLAGS ) {
1291 if ( uType == XTYP_EXECUTE ) {
1292 // Prove that we received the request.
1293 DWORD bytes;
1294 LPBYTE request = (LPBYTE)WinDdeAccessData( hdata, &bytes );
1295 #if MOZ_DEBUG_DDE
1296 printf( "Handling dde request: [%s]...\n", (char*)request );
1297 #endif
1298 nsCAutoString url;
1299 ParseDDEArg((const char*) request, 0, url);
1301 // Read the 3rd argument in the command to determine if a
1302 // new window is to be used.
1303 nsCAutoString windowID;
1304 ParseDDEArg((const char*) request, 2, windowID);
1306 // to open the URL in a new window, the old OS/2 code
1307 // looks for "0" while the new Win32 code looks for "";
1308 // not knowing which is correct, we'll look for both
1309 if (windowID.Equals( "0" ) || windowID.Equals( "" ))
1310 url.Insert("mozilla -new-window ", 0);
1311 else
1312 url.Insert("mozilla -url ", 0);
1314 #if MOZ_DEBUG_DDE
1315 printf( "Handling dde XTYP_REQUEST request: [%s]...\n", url.get() );
1316 #endif
1317 // Now handle it.
1318 HandleCommandLine(url.get(), nsnull, nsICommandLine::STATE_REMOTE_EXPLICIT);
1320 // Release the data.
1321 // DdeUnaccessData( hdata );
1322 result = (HDDEDATA)DDE_FACK;
1323 } else {
1324 result = (HDDEDATA)DDE_NOTPROCESSED;
1326 } else if ( uType & XCLASS_NOTIFICATION ) {
1328 #if MOZ_DEBUG_DDE
1329 printf( "DDE result=%d (0x%08X)\n", (int)result, (int)result );
1330 #endif
1331 return result;
1335 // Utility function to advance to end of quoted string.
1336 // p+offset must point to the comma preceding the arg on entry.
1337 // On return, p+result points to the closing '"' (or end of the string
1338 // if the closing '"' is missing) if the arg is quoted. If the arg
1339 // is not quoted, then p+result will point to the first character
1340 // of the arg.
1341 static PRInt32 advanceToEndOfQuotedArg( const char *p, PRInt32 offset, PRInt32 len ) {
1342 // Check whether the current arg is quoted.
1343 if ( p[++offset] == '"' ) {
1344 // Advance past the closing quote.
1345 while ( offset < len && p[++offset] != '"' ) {
1346 // If the current character is a backslash, then the
1347 // next character can't be a *real* '"', so skip it.
1348 if ( p[offset] == '\\' ) {
1349 offset++;
1353 return offset;
1356 void nsNativeAppSupportOS2::ParseDDEArg( const char* args, int index, nsCString& aString) {
1357 if ( args ) {
1358 int argLen = strlen(args);
1359 nsDependentCString temp(args, argLen);
1361 // offset points to the comma preceding the desired arg.
1362 PRInt32 offset = -1;
1363 // Skip commas till we get to the arg we want.
1364 while( index-- ) {
1365 // If this arg is quoted, then go to closing quote.
1366 offset = advanceToEndOfQuotedArg( args, offset, argLen);
1367 // Find next comma.
1368 offset = temp.FindChar( ',', offset );
1369 if ( offset == kNotFound ) {
1370 // No more commas, give up.
1371 aString = args;
1372 return;
1375 // The desired argument starts just past the preceding comma,
1376 // which offset points to, and extends until the following
1377 // comma (or the end of the string).
1379 // Since the argument might be enclosed in quotes, we need to
1380 // deal with that before searching for the terminating comma.
1381 // We advance offset so it ends up pointing to the start of
1382 // the argument we want.
1383 PRInt32 end = advanceToEndOfQuotedArg( args, offset++, argLen );
1384 // Find next comma (or end of string).
1385 end = temp.FindChar( ',', end );
1386 if ( end == kNotFound ) {
1387 // Arg is the rest of the string.
1388 end = argLen;
1390 // Extract result.
1391 aString.Assign( args + offset, end - offset );
1393 return;
1396 // Utility to parse out argument from a DDE item string.
1397 void nsNativeAppSupportOS2::ParseDDEArg( HSZ args, int index, nsCString& aString) {
1398 DWORD argLen = WinDdeQueryString( args, NULL, NULL, CP_WINANSI );
1399 // there wasn't any string, so return empty string
1400 if ( !argLen ) return;
1401 nsCAutoString temp;
1402 // Ensure result's buffer is sufficiently big.
1403 temp.SetLength( argLen );
1404 // Now get the string contents.
1405 WinDdeQueryString( args, temp.BeginWriting(), temp.Length(), CP_WINANSI );
1406 // Parse out the given arg.
1407 ParseDDEArg(temp.get(), index, aString);
1408 return;
1411 void nsNativeAppSupportOS2::ActivateLastWindow() {
1412 nsCOMPtr<nsIDOMWindowInternal> navWin;
1413 GetMostRecentWindow( NS_LITERAL_STRING("navigator:browser").get(), getter_AddRefs( navWin ) );
1414 if ( navWin )
1415 // Activate that window.
1416 activateWindow( navWin );
1417 else
1418 // Need to create a Navigator window, then.
1419 OpenBrowserWindow();
1422 HDDEDATA nsNativeAppSupportOS2::CreateDDEData( DWORD value ) {
1423 return CreateDDEData( (LPBYTE)&value, sizeof value );
1426 HDDEDATA nsNativeAppSupportOS2::CreateDDEData( LPBYTE value, DWORD len ) {
1427 HDDEDATA result = WinDdeCreateDataHandle( value,
1428 len,
1430 mApplication,
1431 CF_TEXT,
1432 0 );
1433 return result;
1437 void
1438 nsNativeAppSupportOS2::HandleCommandLine(const char* aCmdLineString,
1439 nsIFile* aWorkingDir,
1440 PRUint32 aState)
1442 nsresult rv;
1444 int justCounting = 1;
1445 char **argv = 0;
1446 // Flags, etc.
1447 int init = 1;
1448 int between, quoted, bSlashCount;
1449 int argc;
1450 const char *p;
1451 nsCAutoString arg;
1453 nsCOMPtr<nsICommandLineRunner> cmdLine
1454 (do_CreateInstance("@mozilla.org/toolkit/command-line;1"));
1455 if (!cmdLine) {
1456 NS_ERROR("Couldn't create command line!");
1457 return;
1460 // We loop if we've not finished the second pass through.
1461 while ( 1 ) {
1462 // Initialize if required.
1463 if ( init ) {
1464 p = aCmdLineString;
1465 between = 1;
1466 argc = quoted = bSlashCount = 0;
1468 init = 0;
1470 if ( between ) {
1471 // We are traversing whitespace between args.
1472 // Check for start of next arg.
1473 if ( *p != 0 && !isspace( *p ) ) {
1474 // Start of another arg.
1475 between = 0;
1476 arg = "";
1477 switch ( *p ) {
1478 case '\\':
1479 // Count the backslash.
1480 bSlashCount = 1;
1481 break;
1482 case '"':
1483 // Remember we're inside quotes.
1484 quoted = 1;
1485 break;
1486 default:
1487 // Add character to arg.
1488 arg += *p;
1489 break;
1491 } else {
1492 // Another space between args, ignore it.
1494 } else {
1495 // We are processing the contents of an argument.
1496 // Check for whitespace or end.
1498 // if the argument we are parsing is a mailto url then all
1499 // of the remaining command line data needs to be part of
1500 // the mailto url even if it has spaces. See Bug #231032
1501 nsDependentCString mailtoUrlScheme (kMailtoUrlScheme);
1503 if ( *p == 0 || ( !quoted && isspace( *p ) && !StringBeginsWith(arg, mailtoUrlScheme, nsCaseInsensitiveCStringComparator()) ) ) {
1504 // Process pending backslashes (interpret them
1505 // literally since they're not followed by a ").
1506 while( bSlashCount ) {
1507 arg += '\\';
1508 bSlashCount--;
1510 // End current arg.
1511 if ( !justCounting ) {
1512 argv[argc] = new char[ arg.Length() + 1 ];
1513 strcpy( argv[argc], arg.get() );
1515 argc++;
1516 // We're now between args.
1517 between = 1;
1518 } else {
1519 // Still inside argument, process the character.
1520 switch ( *p ) {
1521 case '"':
1522 // First, digest preceding backslashes (if any).
1523 while ( bSlashCount > 1 ) {
1524 // Put one backsplash in arg for each pair.
1525 arg += '\\';
1526 bSlashCount -= 2;
1528 if ( bSlashCount ) {
1529 // Quote is literal.
1530 arg += '"';
1531 bSlashCount = 0;
1532 } else {
1533 // Quote starts or ends a quoted section.
1534 if ( quoted ) {
1535 // Check for special case of consecutive double
1536 // quotes inside a quoted section.
1537 if ( *(p+1) == '"' ) {
1538 // This implies a literal double-quote. Fake that
1539 // out by causing next double-quote to look as
1540 // if it was preceded by a backslash.
1541 bSlashCount = 1;
1542 } else {
1543 quoted = 0;
1545 } else {
1546 quoted = 1;
1549 break;
1550 case '\\':
1551 // Add to count.
1552 bSlashCount++;
1553 break;
1554 default:
1555 // Accept any preceding backslashes literally.
1556 while ( bSlashCount ) {
1557 arg += '\\';
1558 bSlashCount--;
1560 // Just add next char to the current arg.
1561 arg += *p;
1562 break;
1566 // Check for end of input.
1567 if ( *p ) {
1568 // Go to next character.
1569 p++;
1570 } else {
1571 // If on first pass, go on to second.
1572 if ( justCounting ) {
1573 // Allocate argv array.
1574 argv = new char*[ argc ];
1576 // Start second pass
1577 justCounting = 0;
1578 init = 1;
1579 } else {
1580 // Quit.
1581 break;
1586 rv = cmdLine->Init(argc, argv, aWorkingDir, aState);
1588 // if these OS/2-specific flags aren't removed,
1589 // any URL following them will be ignored
1590 PRBool found;
1591 cmdLine->HandleFlag(NS_LITERAL_STRING("console"), PR_FALSE, &found);
1592 cmdLine->HandleFlag(NS_LITERAL_STRING("dde"), PR_FALSE, &found);
1594 // Cleanup.
1595 while ( argc ) {
1596 delete [] argv[ --argc ];
1598 delete [] argv;
1600 if (NS_FAILED(rv)) {
1601 NS_ERROR("Error initializing command line.");
1602 return;
1605 cmdLine->Run();
1609 nsresult
1610 nsNativeAppSupportOS2::OpenWindow( const char*urlstr, const char *args ) {
1612 nsresult rv = NS_ERROR_FAILURE;
1614 nsCOMPtr<nsIWindowWatcher> wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID));
1615 nsCOMPtr<nsISupportsCString> sarg(do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID));
1616 if (sarg)
1617 sarg->SetData(nsDependentCString(args));
1619 if (wwatch && sarg) {
1620 nsCOMPtr<nsIDOMWindow> newWindow;
1621 rv = wwatch->OpenWindow(0, urlstr, "_blank", "chrome,dialog=no,all",
1622 sarg, getter_AddRefs(newWindow));
1623 #if MOZ_DEBUG_DDE
1624 } else {
1625 printf("Get WindowWatcher (or create string) failed\n");
1626 #endif
1628 return rv;
1631 HWND hwndForDOMWindow( nsISupports *window ) {
1632 nsCOMPtr<nsPIDOMWindow> pidomwindow( do_QueryInterface(window) );
1633 if ( !pidomwindow ) {
1634 return 0;
1637 nsCOMPtr<nsIBaseWindow> ppBaseWindow =
1638 do_QueryInterface( pidomwindow->GetDocShell() );
1639 if ( !ppBaseWindow ) {
1640 return 0;
1643 nsCOMPtr<nsIWidget> ppWidget;
1644 ppBaseWindow->GetMainWidget( getter_AddRefs( ppWidget ) );
1646 return (HWND)( ppWidget->GetNativeData( NS_NATIVE_WIDGET ) );
1649 static const char sJSStackContractID[] = "@mozilla.org/js/xpc/ContextStack;1";
1651 class SafeJSContext {
1652 public:
1653 SafeJSContext();
1654 ~SafeJSContext();
1656 nsresult Push();
1657 JSContext *get() { return mContext; }
1659 protected:
1660 nsCOMPtr<nsIThreadJSContextStack> mService;
1661 JSContext *mContext;
1664 SafeJSContext::SafeJSContext() : mContext(nsnull) {
1667 SafeJSContext::~SafeJSContext() {
1668 JSContext *cx;
1669 nsresult rv;
1671 if(mContext) {
1672 rv = mService->Pop(&cx);
1673 NS_ASSERTION(NS_SUCCEEDED(rv) && cx == mContext, "JSContext push/pop mismatch");
1677 nsresult SafeJSContext::Push() {
1678 if (mContext) // only once
1679 return NS_ERROR_FAILURE;
1681 mService = do_GetService(sJSStackContractID);
1682 if(mService) {
1683 JSContext *cx;
1684 if (NS_SUCCEEDED(mService->GetSafeJSContext(&cx)) &&
1685 cx &&
1686 NS_SUCCEEDED(mService->Push(cx))) {
1687 // Save cx in mContext to indicate need to pop.
1688 mContext = cx;
1691 return mContext ? NS_OK : NS_ERROR_FAILURE;
1695 // As of Jan, 2005, most of the code in this method is pointless and
1696 // will never be used. It is only called by ActivateLastWindow() and
1697 // only when there is no existing window. Consequently, the first test
1698 // below [if ( !navWin )] will always fail, bypassing everything in the
1699 // dummy do..while() loop. Given that this file is largely a port of
1700 // the Win32 version, it is left in place to maintain concurrency.
1702 nsresult
1703 nsNativeAppSupportOS2::OpenBrowserWindow()
1705 nsresult rv = NS_OK;
1707 // Open the argument URL in the most recently used Navigator window.
1708 // If there is no Nav window, open a new one.
1710 // If at all possible, hand the request off to the most recent
1711 // browser window.
1713 nsCOMPtr<nsIDOMWindowInternal> navWin;
1714 GetMostRecentWindow( NS_LITERAL_STRING( "navigator:browser" ).get(), getter_AddRefs( navWin ) );
1716 // This isn't really a loop. We just use "break" statements to fall
1717 // out to the OpenWindow call when things go awry.
1718 do {
1719 // If caller requires a new window, then don't use an existing one.
1720 if ( !navWin ) {
1721 // Have to open a new one.
1722 break;
1725 nsCOMPtr<nsIBrowserDOMWindow> bwin;
1726 { // scope a bunch of temporary cruft used to generate bwin
1727 nsCOMPtr<nsIWebNavigation> navNav( do_GetInterface( navWin ) );
1728 nsCOMPtr<nsIDocShellTreeItem> navItem( do_QueryInterface( navNav ) );
1729 if ( navItem ) {
1730 nsCOMPtr<nsIDocShellTreeItem> rootItem;
1731 navItem->GetRootTreeItem( getter_AddRefs( rootItem ) );
1732 nsCOMPtr<nsIDOMWindow> rootWin( do_GetInterface( rootItem ) );
1733 nsCOMPtr<nsIDOMChromeWindow> chromeWin(do_QueryInterface(rootWin));
1734 if ( chromeWin )
1735 chromeWin->GetBrowserDOMWindow( getter_AddRefs ( bwin ) );
1738 if ( bwin ) {
1739 nsCOMPtr<nsIURI> uri;
1740 NS_NewURI( getter_AddRefs( uri ), NS_LITERAL_CSTRING("about:blank"), 0, 0 );
1741 if ( uri ) {
1742 nsCOMPtr<nsIDOMWindow> container;
1743 rv = bwin->OpenURI( uri, 0,
1744 nsIBrowserDOMWindow::OPEN_DEFAULTWINDOW,
1745 nsIBrowserDOMWindow::OPEN_EXTERNAL,
1746 getter_AddRefs( container ) );
1747 if ( NS_SUCCEEDED( rv ) )
1748 return NS_OK;
1752 NS_ERROR("failed to hand off external URL to extant window\n");
1753 } while ( PR_FALSE );
1755 // open a new window if caller requested it or if anything above failed
1757 char* argv[] = { 0 };
1758 nsCOMPtr<nsICommandLineRunner> cmdLine
1759 (do_CreateInstance("@mozilla.org/toolkit/command-line;1"));
1760 NS_ENSURE_TRUE(cmdLine, NS_ERROR_FAILURE);
1762 rv = cmdLine->Init(0, argv, nsnull, nsICommandLine::STATE_REMOTE_EXPLICIT);
1763 NS_ENSURE_SUCCESS(rv, rv);
1765 return cmdLine->Run();
1768 // This is a public function called by nsAppRunner.cpp. Its primary
1769 // purpose is to determine if any commandline options require a VIO
1770 // ("console") window. If so and one isn't present, it will restart
1771 // the app in a VIO session. It is intended to be called as early as
1772 // possible during startup and before any other commandline processing.
1773 // It returns TRUE if the current instance should continue running and
1774 // FALSE if it should terminate upon this function's return.
1776 PRBool StartOS2App(int aArgc, char **aArgv)
1778 PRBool rv = PR_TRUE;
1779 PPIB ppib;
1780 PTIB ptib;
1782 DosGetInfoBlocks(&ptib, &ppib);
1784 // if this isn't a PM session, reset the session type to enable use
1785 // of PM functions; if it is PM, see if a VIO session is required
1786 if (ppib->pib_ultype != SSF_TYPE_PM)
1787 ppib->pib_ultype = SSF_TYPE_PM;
1788 else {
1789 for (int i = 1; i < aArgc; i++ ) {
1790 char *arg = aArgv[i];
1791 if (*arg != '-' && *arg != '/')
1792 continue;
1793 arg++;
1794 if (stricmp("?", arg) == 0 ||
1795 stricmp("h", arg) == 0 ||
1796 stricmp("v", arg) == 0 ||
1797 stricmp("help", arg) == 0 ||
1798 stricmp("version", arg) == 0 ||
1799 stricmp("console", arg) == 0) {
1800 rv = PR_FALSE;
1801 break;
1806 // if the session type is OK, increase the number of
1807 // file handles available to the app, then exit
1808 if (rv) {
1809 ULONG ulMaxFH = 0;
1810 LONG ulReqCount = 0;
1812 DosSetRelMaxFH(&ulReqCount, &ulMaxFH);
1813 if (ulMaxFH < 256)
1814 DosSetMaxFH(256);
1816 return rv;
1819 // the app has to be restarted in a VIO session
1820 char szErrObj[64] = "";
1821 STARTDATA x;
1823 memset(&x, 0, sizeof(x));
1824 x.Length = sizeof(x);
1825 x.PgmTitle = (PSZ)gAppData->name;
1826 x.InheritOpt = SSF_INHERTOPT_PARENT;
1827 x.SessionType = SSF_TYPE_WINDOWABLEVIO;
1828 x.PgmControl = SSF_CONTROL_NOAUTOCLOSE;
1829 x.ObjectBuffer = szErrObj;
1830 x.ObjectBuffLen = sizeof(szErrObj);
1832 // the f/q exename is the string preceding ppib->pib_pchcmd;
1833 // the original commandline is the string following it
1834 char * ptr = ppib->pib_pchcmd - 2;
1835 while (*ptr)
1836 ptr--;
1837 x.PgmName = ptr + 1;
1838 x.PgmInputs = strchr(ppib->pib_pchcmd, 0) + 1;
1840 // restart the app; if this session is in the background, trying
1841 // to start in the foreground will produce an error, but the app
1842 // will still start; if DosStartSession has a real failure, forget
1843 // the console and let the current instance keep running
1844 ULONG ulSession;
1845 PID pid;
1846 ULONG rc = DosStartSession(&x, &ulSession, &pid);
1847 if (rc && rc != ERROR_SMG_START_IN_BACKGROUND)
1848 rv = PR_TRUE;
1850 return rv;