Bug 575870 - Enable the firefox button on xp themed, classic, and aero basic. r=dao...
[mozilla-central.git] / toolkit / xre / nsNativeAppSupportOS2.cpp
blob77ecfc38cb6917ad648f6eb4520d1f981f504dce
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 existence 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 = mozilla::services::GetObserverService();
1016 if (obs) {
1017 obs->AddObserver(this, "quit-application", PR_FALSE);
1018 } else {
1019 NS_ERROR("No observer service?");
1022 return NS_OK;
1025 #if MOZ_DEBUG_DDE
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 ) {
1031 nsCString result;
1032 switch ( 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_?????";
1051 return result;
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 );
1058 if ( len ) {
1059 char buffer[ 256 ];
1060 WinDdeQueryString( hsz, buffer, sizeof buffer, CP_WINANSI );
1061 result += buffer;
1063 result += "]";
1064 return result;
1066 #endif
1069 // Utility function to escape double-quotes within a string.
1070 static void escapeQuotes( nsAString &aString ) {
1071 PRInt32 offset = -1;
1072 while( 1 ) {
1073 // Find next '"'.
1074 offset = aString.FindChar( '"', ++offset );
1075 if ( offset == kNotFound ) {
1076 // No more quotes, exit.
1077 break;
1078 } else {
1079 // Insert back-slash ahead of the '"'.
1080 aString.Insert( PRUnichar('\\'), offset );
1081 // Increment offset because we just inserted a slash
1082 offset++;
1085 return;
1089 HDDEDATA APIENTRY
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
1100 #if MOZ_DEBUG_DDE
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 );
1109 #endif
1111 HDDEDATA result = 0;
1112 if ( uType & XCLASS_BOOL ) {
1113 switch ( uType ) {
1114 case XTYP_CONNECT:
1115 // Make sure its for our service/topic.
1116 if ( FindTopic( hsz1 ) != -1 ) {
1117 // We support this connection.
1118 result = (HDDEDATA)1;
1120 break;
1121 case XTYP_CONNECT_CONFIRM:
1122 // We don't care about the conversation handle, at this point.
1123 result = (HDDEDATA)1;
1124 break;
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.
1133 nsCAutoString url;
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);
1145 else
1146 url.Insert("mozilla -url ", 0);
1148 #if MOZ_DEBUG_DDE
1149 printf( "Handling dde XTYP_REQUEST request: [%s]...\n", url.get() );
1150 #endif
1151 // Now handle it.
1152 HandleCommandLine(url.get(), nsnull, nsICommandLine::STATE_REMOTE_EXPLICIT);
1153 // Return pseudo window ID.
1154 result = CreateDDEData( 1 );
1155 break;
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.
1167 do {
1168 // Get most recently used Nav window.
1169 nsCOMPtr<nsIDOMWindowInternal> navWin;
1170 GetMostRecentWindow( NS_LITERAL_STRING( "navigator:browser" ).get(),
1171 getter_AddRefs( navWin ) );
1172 if ( !navWin ) {
1173 // There is not a window open
1174 break;
1176 // Get content window.
1177 nsCOMPtr<nsIDOMWindow> content;
1178 navWin->GetContent( getter_AddRefs( content ) );
1179 if ( !content ) {
1180 break;
1182 // Convert that to internal interface.
1183 nsCOMPtr<nsPIDOMWindow> internalContent( do_QueryInterface( content ) );
1184 if ( !internalContent ) {
1185 break;
1187 // Get location.
1188 nsCOMPtr<nsIDOMLocation> location;
1189 internalContent->GetLocation( getter_AddRefs( location ) );
1190 if ( !location ) {
1191 break;
1193 // Get href for URL.
1194 nsAutoString url;
1195 if ( NS_FAILED( location->GetHref( url ) ) ) {
1196 break;
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 ) {
1207 break;
1209 // And from the base window we can get the title.
1210 nsXPIDLString title;
1211 if(!baseWindow) {
1212 break;
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
1219 // save a quote.
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
1225 // title
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 );
1238 #if MOZ_DEBUG_DDE
1239 printf( "WWW_GetWindowInfo->%s\n", outpt.get() );
1240 #endif
1241 } while ( PR_FALSE );
1242 break;
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 );
1257 break;
1259 case topicVersion: {
1260 // Return version. We're restarting at 1.0!
1261 DWORD version = 1 << 16; // "1.0"
1262 result = CreateDDEData( version );
1263 break;
1265 case topicRegisterViewer: {
1266 // Register new viewer (not implemented).
1267 result = CreateDDEData( PR_FALSE );
1268 break;
1270 case topicUnRegisterViewer: {
1271 // Unregister new viewer (not implemented).
1272 result = CreateDDEData( PR_FALSE );
1273 break;
1275 default:
1276 break;
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;
1283 break;
1285 default:
1286 break;
1289 } else if ( uType & XCLASS_FLAGS ) {
1290 if ( uType == XTYP_EXECUTE ) {
1291 // Prove that we received the request.
1292 DWORD bytes;
1293 LPBYTE request = (LPBYTE)WinDdeAccessData( hdata, &bytes );
1294 #if MOZ_DEBUG_DDE
1295 printf( "Handling dde request: [%s]...\n", (char*)request );
1296 #endif
1297 nsCAutoString url;
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);
1310 else
1311 url.Insert("mozilla -url ", 0);
1313 #if MOZ_DEBUG_DDE
1314 printf( "Handling dde XTYP_REQUEST request: [%s]...\n", url.get() );
1315 #endif
1316 // Now handle it.
1317 HandleCommandLine(url.get(), nsnull, nsICommandLine::STATE_REMOTE_EXPLICIT);
1319 // Release the data.
1320 // DdeUnaccessData( hdata );
1321 result = (HDDEDATA)DDE_FACK;
1322 } else {
1323 result = (HDDEDATA)DDE_NOTPROCESSED;
1325 } else if ( uType & XCLASS_NOTIFICATION ) {
1327 #if MOZ_DEBUG_DDE
1328 printf( "DDE result=%d (0x%08X)\n", (int)result, (int)result );
1329 #endif
1330 return 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
1339 // of the arg.
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] == '\\' ) {
1348 offset++;
1352 return offset;
1355 void nsNativeAppSupportOS2::ParseDDEArg( const char* args, int index, nsCString& aString) {
1356 if ( args ) {
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.
1363 while( index-- ) {
1364 // If this arg is quoted, then go to closing quote.
1365 offset = advanceToEndOfQuotedArg( args, offset, argLen);
1366 // Find next comma.
1367 offset = temp.FindChar( ',', offset );
1368 if ( offset == kNotFound ) {
1369 // No more commas, give up.
1370 aString = args;
1371 return;
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.
1387 end = argLen;
1389 // Extract result.
1390 aString.Assign( args + offset, end - offset );
1392 return;
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;
1400 nsCAutoString temp;
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);
1407 return;
1410 void nsNativeAppSupportOS2::ActivateLastWindow() {
1411 nsCOMPtr<nsIDOMWindowInternal> navWin;
1412 GetMostRecentWindow( NS_LITERAL_STRING("navigator:browser").get(), getter_AddRefs( navWin ) );
1413 if ( navWin )
1414 // Activate that window.
1415 activateWindow( navWin );
1416 else
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,
1427 len,
1429 mApplication,
1430 CF_TEXT,
1431 0 );
1432 return result;
1436 void
1437 nsNativeAppSupportOS2::HandleCommandLine(const char* aCmdLineString,
1438 nsIFile* aWorkingDir,
1439 PRUint32 aState)
1441 nsresult rv;
1443 int justCounting = 1;
1444 char **argv = 0;
1445 // Flags, etc.
1446 int init = 1;
1447 int between, quoted, bSlashCount;
1448 int argc;
1449 const char *p;
1450 nsCAutoString arg;
1452 nsCOMPtr<nsICommandLineRunner> cmdLine
1453 (do_CreateInstance("@mozilla.org/toolkit/command-line;1"));
1454 if (!cmdLine) {
1455 NS_ERROR("Couldn't create command line!");
1456 return;
1459 // We loop if we've not finished the second pass through.
1460 while ( 1 ) {
1461 // Initialize if required.
1462 if ( init ) {
1463 p = aCmdLineString;
1464 between = 1;
1465 argc = quoted = bSlashCount = 0;
1467 init = 0;
1469 if ( between ) {
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.
1474 between = 0;
1475 arg = "";
1476 switch ( *p ) {
1477 case '\\':
1478 // Count the backslash.
1479 bSlashCount = 1;
1480 break;
1481 case '"':
1482 // Remember we're inside quotes.
1483 quoted = 1;
1484 break;
1485 default:
1486 // Add character to arg.
1487 arg += *p;
1488 break;
1490 } else {
1491 // Another space between args, ignore it.
1493 } else {
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 ) {
1506 arg += '\\';
1507 bSlashCount--;
1509 // End current arg.
1510 if ( !justCounting ) {
1511 argv[argc] = new char[ arg.Length() + 1 ];
1512 strcpy( argv[argc], arg.get() );
1514 argc++;
1515 // We're now between args.
1516 between = 1;
1517 } else {
1518 // Still inside argument, process the character.
1519 switch ( *p ) {
1520 case '"':
1521 // First, digest preceding backslashes (if any).
1522 while ( bSlashCount > 1 ) {
1523 // Put one backsplash in arg for each pair.
1524 arg += '\\';
1525 bSlashCount -= 2;
1527 if ( bSlashCount ) {
1528 // Quote is literal.
1529 arg += '"';
1530 bSlashCount = 0;
1531 } else {
1532 // Quote starts or ends a quoted section.
1533 if ( quoted ) {
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.
1540 bSlashCount = 1;
1541 } else {
1542 quoted = 0;
1544 } else {
1545 quoted = 1;
1548 break;
1549 case '\\':
1550 // Add to count.
1551 bSlashCount++;
1552 break;
1553 default:
1554 // Accept any preceding backslashes literally.
1555 while ( bSlashCount ) {
1556 arg += '\\';
1557 bSlashCount--;
1559 // Just add next char to the current arg.
1560 arg += *p;
1561 break;
1565 // Check for end of input.
1566 if ( *p ) {
1567 // Go to next character.
1568 p++;
1569 } else {
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
1576 justCounting = 0;
1577 init = 1;
1578 } else {
1579 // Quit.
1580 break;
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
1589 PRBool found;
1590 cmdLine->HandleFlag(NS_LITERAL_STRING("console"), PR_FALSE, &found);
1591 cmdLine->HandleFlag(NS_LITERAL_STRING("dde"), PR_FALSE, &found);
1593 // Cleanup.
1594 while ( argc ) {
1595 delete [] argv[ --argc ];
1597 delete [] argv;
1599 if (NS_FAILED(rv)) {
1600 NS_ERROR("Error initializing command line.");
1601 return;
1604 cmdLine->Run();
1608 nsresult
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));
1615 if (sarg)
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));
1622 #if MOZ_DEBUG_DDE
1623 } else {
1624 printf("Get WindowWatcher (or create string) failed\n");
1625 #endif
1627 return rv;
1630 HWND hwndForDOMWindow( nsISupports *window ) {
1631 nsCOMPtr<nsPIDOMWindow> pidomwindow( do_QueryInterface(window) );
1632 if ( !pidomwindow ) {
1633 return 0;
1636 nsCOMPtr<nsIBaseWindow> ppBaseWindow =
1637 do_QueryInterface( pidomwindow->GetDocShell() );
1638 if ( !ppBaseWindow ) {
1639 return 0;
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 {
1651 public:
1652 SafeJSContext();
1653 ~SafeJSContext();
1655 nsresult Push();
1656 JSContext *get() { return mContext; }
1658 protected:
1659 nsCOMPtr<nsIThreadJSContextStack> mService;
1660 JSContext *mContext;
1663 SafeJSContext::SafeJSContext() : mContext(nsnull) {
1666 SafeJSContext::~SafeJSContext() {
1667 JSContext *cx;
1668 nsresult rv;
1670 if(mContext) {
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);
1681 if(mService) {
1682 JSContext *cx;
1683 if (NS_SUCCEEDED(mService->GetSafeJSContext(&cx)) &&
1684 cx &&
1685 NS_SUCCEEDED(mService->Push(cx))) {
1686 // Save cx in mContext to indicate need to pop.
1687 mContext = cx;
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.
1701 nsresult
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
1710 // browser window.
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.
1717 do {
1718 // If caller requires a new window, then don't use an existing one.
1719 if ( !navWin ) {
1720 // Have to open a new one.
1721 break;
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 ) );
1728 if ( navItem ) {
1729 nsCOMPtr<nsIDocShellTreeItem> rootItem;
1730 navItem->GetRootTreeItem( getter_AddRefs( rootItem ) );
1731 nsCOMPtr<nsIDOMWindow> rootWin( do_GetInterface( rootItem ) );
1732 nsCOMPtr<nsIDOMChromeWindow> chromeWin(do_QueryInterface(rootWin));
1733 if ( chromeWin )
1734 chromeWin->GetBrowserDOMWindow( getter_AddRefs ( bwin ) );
1737 if ( bwin ) {
1738 nsCOMPtr<nsIURI> uri;
1739 NS_NewURI( getter_AddRefs( uri ), NS_LITERAL_CSTRING("about:blank"), 0, 0 );
1740 if ( uri ) {
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 ) )
1747 return NS_OK;
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;
1778 PPIB ppib;
1779 PTIB ptib;
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;
1787 else {
1788 for (int i = 1; i < aArgc; i++ ) {
1789 char *arg = aArgv[i];
1790 if (*arg != '-' && *arg != '/')
1791 continue;
1792 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) {
1799 rv = PR_FALSE;
1800 break;
1805 // if the session type is OK, increase the number of
1806 // file handles available to the app, then exit
1807 if (rv) {
1808 ULONG ulMaxFH = 0;
1809 LONG ulReqCount = 0;
1811 DosSetRelMaxFH(&ulReqCount, &ulMaxFH);
1812 if (ulMaxFH < 256)
1813 DosSetMaxFH(256);
1815 return rv;
1818 // the app has to be restarted in a VIO session
1819 char szErrObj[64] = "";
1820 STARTDATA x;
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;
1834 while (*ptr)
1835 ptr--;
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
1843 ULONG ulSession;
1844 PID pid;
1845 ULONG rc = DosStartSession(&x, &ulSession, &pid);
1846 if (rc && rc != ERROR_SMG_START_IN_BACKGROUND)
1847 rv = PR_TRUE;
1849 return rv;