source: trunk/third/mozilla/xpfe/bootstrap/nsNativeAppSupportWin.cpp @ 19518

Revision 19518, 98.6 KB checked in by rbasch, 21 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r19517, which included commits to RCS files with non-trunk default branches.
Line 
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/* ***** BEGIN LICENSE BLOCK *****
3 * Version: NPL 1.1/GPL 2.0/LGPL 2.1
4 *
5 * The contents of this file are subject to the Netscape Public License
6 * Version 1.1 (the "License"); you may not use this file except in
7 * compliance with the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/NPL/
9 *
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.
14 *
15 * The Original Code is Mozilla Communicator client code.
16 *
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.
21 *
22 * Contributor(s):
23 *   Bill Law       law@netscape.com
24 *
25 * Alternatively, the contents of this file may be used under the terms of
26 * either the GNU General Public License Version 2 or later (the "GPL"), or
27 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 * in which case the provisions of the GPL or the LGPL are applicable instead
29 * of those above. If you wish to allow use of your version of this file only
30 * under the terms of either the GPL or the LGPL, and not to allow others to
31 * use your version of this file under the terms of the NPL, indicate your
32 * decision by deleting the provisions above and replace them with the notice
33 * and other provisions required by the GPL or the LGPL. If you do not delete
34 * the provisions above, a recipient may use your version of this file under
35 * the terms of any one of the NPL, the GPL or the LGPL.
36 *
37 * ***** END LICENSE BLOCK ***** */
38
39// For server mode systray icon.
40#include "nsIStringBundle.h"
41
42#include "nsNativeAppSupportBase.h"
43#include "nsNativeAppSupportWin.h"
44#include "nsString.h"
45#include "nsICmdLineService.h"
46#include "nsCOMPtr.h"
47#include "nsXPIDLString.h"
48#include "nsIComponentManager.h"
49#include "nsIServiceManager.h"
50#include "nsICmdLineHandler.h"
51#include "nsIDOMWindow.h"
52#include "nsXPCOM.h"
53#include "nsISupportsPrimitives.h"
54#include "nsISupportsArray.h"
55#include "nsIWindowWatcher.h"
56#include "nsIDOMWindowInternal.h"
57#include "nsIScriptGlobalObject.h"
58#include "nsIDocShell.h"
59#include "nsIBaseWindow.h"
60#include "nsIWidget.h"
61#include "nsIAppShellService.h"
62#include "nsIProfileInternal.h"
63#include "nsIXULWindow.h"
64#include "nsIInterfaceRequestor.h"
65#include "nsIInterfaceRequestorUtils.h"
66#include "nsIPref.h"
67#include "nsIWindowsHooks.h"
68#include "nsIPromptService.h"
69#include "nsNetCID.h"
70#include "nsIObserverService.h"
71#include "nsXPCOM.h"
72
73// These are needed to load a URL in a browser window.
74#include "nsIDOMLocation.h"
75#include "nsIJSContextStack.h"
76#include "nsIWindowMediator.h"
77
78#include <windows.h>
79#include <shellapi.h>
80#include <ddeml.h>
81#include <stdlib.h>
82#include <stdio.h>
83#include <io.h>
84#include <fcntl.h>
85
86#define TURBO_NAVIGATOR 1
87#define TURBO_MAIL 2
88#define TURBO_EDITOR 3
89#define TURBO_ADDRESSBOOK 4
90#define TURBO_DISABLE 5
91#define TURBO_EXIT 6
92
93#define MAPI_STARTUP_ARG       "/MAPIStartUp"
94
95static HWND hwndForDOMWindow( nsISupports * );
96
97static
98nsresult
99GetMostRecentWindow(const PRUnichar* aType, nsIDOMWindowInternal** aWindow) {
100    nsresult rv;
101    nsCOMPtr<nsIWindowMediator> med( do_GetService( NS_WINDOWMEDIATOR_CONTRACTID, &rv ) );
102    if ( NS_FAILED( rv ) )
103        return rv;
104
105    if ( med )
106        return med->GetMostRecentWindow( aType, aWindow );
107
108    return NS_ERROR_FAILURE;
109}
110
111static char* GetACPString(const nsString& aStr)
112{
113    int acplen = aStr.Length() * 2 + 1;
114    char * acp = new char[ acplen ];
115    if( acp ) {
116        int outlen = ::WideCharToMultiByte( CP_ACP, 0, aStr.get(), aStr.Length(),
117                                            acp, acplen, NULL, NULL );
118        if ( outlen >= 0)
119            acp[ outlen ] = '\0';  // null terminate
120    }
121    return acp;
122}
123
124static
125void
126activateWindow( nsIDOMWindowInternal *win ) {
127    // Try to get native window handle.
128    HWND hwnd = hwndForDOMWindow( win );
129    if ( hwnd ) {
130        // Restore the window if it is minimized.
131        if ( ::IsIconic( hwnd ) ) {
132            ::ShowWindow( hwnd, SW_RESTORE );
133        }
134        // Use the OS call, if possible.
135        ::SetForegroundWindow( hwnd );
136    } else {
137        // Use internal method.
138        win->Focus();
139    }
140}
141
142
143#ifdef DEBUG_law
144#undef MOZ_DEBUG_DDE
145#define MOZ_DEBUG_DDE 1
146#endif
147
148class nsSplashScreenWin : public nsISplashScreen {
149public:
150    nsSplashScreenWin();
151    ~nsSplashScreenWin();
152
153    NS_IMETHOD Show();
154    NS_IMETHOD Hide();
155
156    // nsISupports methods
157    NS_IMETHOD_(nsrefcnt) AddRef() {
158        mRefCnt++;
159        return mRefCnt;
160    }
161    NS_IMETHOD_(nsrefcnt) Release() {
162        --mRefCnt;
163        if ( !mRefCnt ) {
164            delete this;
165            return 0;
166        }
167        return mRefCnt;
168    }
169    NS_IMETHOD QueryInterface( const nsIID &iid, void**p ) {
170        nsresult rv = NS_OK;
171        if ( p ) {
172            *p = 0;
173            if ( iid.Equals( NS_GET_IID( nsISplashScreen ) ) ) {
174                nsISplashScreen *result = this;
175                *p = result;
176                NS_ADDREF( result );
177            } else if ( iid.Equals( NS_GET_IID( nsISupports ) ) ) {
178                nsISupports *result = NS_STATIC_CAST( nsISupports*, this );
179                *p = result;
180                NS_ADDREF( result );
181            } else {
182                rv = NS_NOINTERFACE;
183            }
184        } else {
185            rv = NS_ERROR_NULL_POINTER;
186        }
187        return rv;
188    }
189
190    void SetDialog( HWND dlg );
191    void LoadBitmap();
192    static nsSplashScreenWin* GetPointer( HWND dlg );
193
194    static BOOL CALLBACK DialogProc( HWND dlg, UINT msg, WPARAM wp, LPARAM lp );
195    static DWORD WINAPI ThreadProc( LPVOID );
196
197    HWND mDlg;
198    HBITMAP mBitmap;
199    nsrefcnt mRefCnt;
200}; // class nsSplashScreenWin
201
202// Simple Win32 mutex wrapper.
203struct Mutex {
204    Mutex( const char *name )
205        : mName( name ),
206          mHandle( 0 ),
207          mState( -1 ) {
208        mHandle = CreateMutex( 0, FALSE, mName.get() );
209#if MOZ_DEBUG_DDE
210        printf( "CreateMutex error = 0x%08X\n", (int)GetLastError() );
211#endif
212    }
213    ~Mutex() {
214        if ( mHandle ) {
215            // Make sure we release it if we own it.
216            Unlock();
217
218            BOOL rc = CloseHandle( mHandle );
219#if MOZ_DEBUG_DDE
220            if ( !rc ) {
221                printf( "CloseHandle error = 0x%08X\n", (int)GetLastError() );
222            }
223#endif
224        }
225    }
226    BOOL Lock( DWORD timeout ) {
227        if ( mHandle ) {
228#if MOZ_DEBUG_DDE
229            printf( "Waiting (%d msec) for DDE mutex...\n", (int)timeout );
230#endif
231            mState = WaitForSingleObject( mHandle, timeout );
232#if MOZ_DEBUG_DDE
233            printf( "...wait complete, result = 0x%08X, GetLastError=0x%08X\n", (int)mState, (int)::GetLastError() );
234#endif
235            return mState == WAIT_OBJECT_0 || mState == WAIT_ABANDONED;
236        } else {
237            return FALSE;
238        }
239    }
240    void Unlock() {
241        if ( mHandle && mState == WAIT_OBJECT_0 ) {
242#if MOZ_DEBUG_DDE
243            printf( "Releasing DDE mutex\n" );
244#endif
245            ReleaseMutex( mHandle );
246            mState = -1;
247        }
248    }
249private:
250    nsCString mName;
251    HANDLE    mHandle;
252    DWORD     mState;
253};
254
255/* DDE Notes
256 *
257 * This section describes the Win32 DDE service implementation for
258 * Mozilla.  DDE is used on Win32 platforms to communicate between
259 * separate instances of mozilla.exe (or other Mozilla-based
260 * executables), or, between the Win32 desktop shell and Mozilla.
261 *
262 * The first instance of Mozilla will become the "server" and
263 * subsequent executables (and the shell) will use DDE to send
264 * requests to that process.  The requests are DDE "execute" requests
265 * that pass the command line arguments.
266 *
267 * Mozilla registers the DDE application "Mozilla" and currently
268 * supports only the "WWW_OpenURL" topic.  This should be reasonably
269 * compatible with applications that interfaced with Netscape
270 * Communicator (and its predecessors?).  Note that even that topic
271 * may not be supported in a compatible fashion as the command-line
272 * options for Mozilla are different than for Communiator.
273 *
274 * It is imperative that at most one instance of Mozilla execute in
275 * "server mode" at any one time.  The "native app support" in Mozilla
276 * on Win32 ensures that only the server process performs XPCOM
277 * initialization (that is not required for subsequent client processes
278 * to communicate with the server process).
279 *
280 * To guarantee that only one server starts up, a Win32 "mutex" is used
281 * to ensure only one process executes the server-detection code.  That
282 * code consists of initializing DDE and doing a DdeConnect to Mozilla's
283 * application/topic.  If that connection succeeds, then a server process
284 * must be running already.
285 *
286 * Otherwise, no server has started.  In that case, the current process
287 * calls DdeNameService to register that application/topic.  Only at that
288 * point does the mutex get released.
289 *
290 * There are a couple of subtleties that one should be aware of:
291 *
292 * 1. It is imperative that DdeInitialize be called only after the mutex
293 *    lock has been obtained.  The reason is that at shutdown, DDE
294 *    notifications go out to all initialized DDE processes.  Thus, if
295 *    the mutex is owned by a terminating intance of Mozilla, then
296 *    calling DdeInitialize and then WaitForSingleObject will cause the
297 *    DdeUninitialize from the terminating process to "hang" until the
298 *    process waiting for the mutex times out (and can then service the
299 *    notification that the DDE server is terminating).  So, don't mess
300 *    with the sequence of things in the startup/shutdown logic.
301 *
302 * 2. All mutex requests are made with a reasonably long timeout value and
303 *    are designed to "fail safe" (i.e., a timeout is treated as failure).
304 *
305 * 3. An attempt has been made to minimize the degree to which the main
306 *    Mozilla application logic needs to be aware of the DDE mechanisms
307 *    implemented herein.  As a result, this module surfaces a very
308 *    large-grained interface, consisting of simple start/stop methods.
309 *    As a consequence, details of certain scenarios can be "lost."
310 *    Particularly, incoming DDE requests can arrive after this module
311 *    initiates the DDE server, but before Mozilla is initialized to the
312 *    point where those requests can be serviced (e.g., open a browser
313 *    window to a particular URL).  Since the client process sends the
314 *    request early on, it may not be prepared to respond to that error.
315 *    Thus, such situations may fail silently.  The design goal is that
316 *    they fail harmlessly.  Refinements on this point will be made as
317 *    details emerge (and time permits).
318 */
319
320/* Update 2001 March
321 *
322 * A significant DDE bug in Windows is causing Mozilla to get wedged at
323 * startup.  This is detailed in Bugzill bug 53952
324 * (http://bugzilla.mozilla.org/show_bug.cgi?id=53952).
325 *
326 * To resolve this, we are using a new strategy:
327 *   o Use a "message window" to detect that Mozilla is already running and
328 *     to pass requests from a second instance back to the first;
329 *   o Run only as a "DDE server" (not as DDE client); this avoids the
330 *     problematic call to DDEConnect().
331 *
332 * We still use the mutex semaphore to protect the code that detects
333 * whether Mozilla is already running.
334 */
335
336class nsNativeAppSupportWin : public nsNativeAppSupportBase {
337public:
338    // Overrides of base implementation.
339    NS_IMETHOD Start( PRBool *aResult );
340    NS_IMETHOD Stop( PRBool *aResult );
341    NS_IMETHOD Quit();
342    NS_IMETHOD StartServerMode();
343    NS_IMETHOD OnLastWindowClosing();
344    NS_IMETHOD SetIsServerMode( PRBool isServerMode );
345    NS_IMETHOD EnsureProfile(nsICmdLineService* args);
346
347    // The "old" Start method (renamed).
348    NS_IMETHOD StartDDE();
349
350    // Utility function to handle a Win32-specific command line
351    // option: "-console", which dynamically creates a Windows
352    // console.
353    void CheckConsole();
354
355private:
356    static HDDEDATA CALLBACK HandleDDENotification( UINT     uType,
357                                                    UINT     uFmt,
358                                                    HCONV    hconv,
359                                                    HSZ      hsz1,
360                                                    HSZ      hsz2,
361                                                    HDDEDATA hdata,
362                                                    ULONG    dwData1,
363                                                    ULONG    dwData2 );
364    static void HandleRequest( LPBYTE request, PRBool newWindow = PR_TRUE );
365    static void ParseDDEArg( HSZ args, int index, nsCString& string);
366    static void ParseDDEArg( const char* args, int index, nsCString& aString);
367    static void ActivateLastWindow();
368    static HDDEDATA CreateDDEData( DWORD value );
369    static HDDEDATA CreateDDEData( LPBYTE value, DWORD len );
370    static PRBool   InitTopicStrings();
371    static int      FindTopic( HSZ topic );
372    static nsresult GetCmdLineArgs( LPBYTE request, nsICmdLineService **aResult );
373    static nsresult OpenWindow( const char *urlstr, const char *args );
374    static nsresult OpenBrowserWindow( const char *args, PRBool newWindow = PR_TRUE );
375    static nsresult ReParent( nsISupports *window, HWND newParent );
376    static nsresult GetStartupURL(nsICmdLineService *args, nsCString& taskURL);
377    static void     SetupSysTrayIcon();
378    static void     RemoveSysTrayIcon();
379
380    static UINT mTrayRestart;
381
382    static int   mConversations;
383    enum {
384        topicOpenURL,
385        topicActivate,
386        topicCancelProgress,
387        topicVersion,
388        topicRegisterViewer,
389        topicUnRegisterViewer,
390        topicGetWindowInfo,
391        // Note: Insert new values above this line!!!!!
392        topicCount // Count of the number of real topics
393    };
394    static NOTIFYICONDATA mIconData;
395    static HMENU          mTrayIconMenu;
396
397    static HSZ   mApplication, mTopics[ topicCount ];
398    static DWORD mInstance;
399    static char *mAppName;
400    static PRBool mInitialWindowActive;
401    static PRBool mForceProfileStartup;
402    static PRBool mSupportingDDEExec;
403    static char mMutexName[];
404    friend struct MessageWindow;
405}; // nsNativeAppSupportWin
406
407nsSplashScreenWin::nsSplashScreenWin()
408    : mDlg( 0 ), mBitmap( 0 ), mRefCnt( 0 ) {
409}
410
411nsSplashScreenWin::~nsSplashScreenWin() {
412#if MOZ_DEBUG_DDE
413    printf( "splash screen dtor called\n" );
414#endif
415    // Make sure dialog is gone.
416    Hide();
417}
418
419NS_IMETHODIMP
420nsSplashScreenWin::Show() {
421    // Spawn new thread to display real splash screen.
422    DWORD threadID = 0;
423    HANDLE handle = CreateThread( 0, 0, (LPTHREAD_START_ROUTINE)ThreadProc, this, 0, &threadID );
424    CloseHandle(handle);
425
426    return NS_OK;
427}
428
429NS_IMETHODIMP
430nsSplashScreenWin::Hide() {
431    if ( mDlg ) {
432        // Fix for bugs:
433        //  http://bugzilla.mozilla.org/show_bug.cgi?id=26581
434        //  http://bugzilla.mozilla.org/show_bug.cgi?id=65974
435        //  http://bugzilla.mozilla.org/show_bug.cgi?id=29172
436        //  http://bugzilla.mozilla.org/show_bug.cgi?id=45805
437        // As the splash-screen is in a separate thread, Windows considers
438        // this the "foreground" thread.  When our main windows on the main
439        // thread are activated, they are treated like windows from a different
440        // application, so Windows 2000 and 98 both leave the window in the background.
441        // Therefore, we post a message to the splash-screen thread that includes
442        // the hwnd of the window we want moved to the foreground.  This thread
443        // can then successfully bring the top-level window to the foreground.
444        nsCOMPtr<nsIDOMWindowInternal> topLevel;
445        GetMostRecentWindow(nsnull, getter_AddRefs( topLevel ) );
446        HWND hWndTopLevel = topLevel ? hwndForDOMWindow(topLevel) : 0;
447        // Dismiss the dialog.
448        ::PostMessage(mDlg, WM_CLOSE, (WPARAM)mBitmap, (LPARAM)hWndTopLevel);
449        mBitmap = 0;
450        mDlg = 0;
451    }
452    return NS_OK;
453}
454
455void
456nsSplashScreenWin::LoadBitmap() {
457    // Check for '<program-name>.bmp" in same directory as executable.
458    char fileName[ _MAX_PATH ];
459    int fileNameLen = ::GetModuleFileName( NULL, fileName, sizeof fileName );
460    if ( fileNameLen >= 3 ) {
461        fileName[ fileNameLen - 3 ] = 0;
462        strcat( fileName, "bmp" );
463        // Try to load bitmap from that file.
464        HBITMAP bitmap = (HBITMAP)::LoadImage( NULL,
465                                               fileName,
466                                               IMAGE_BITMAP,
467                                               0,
468                                               0,
469                                               LR_LOADFROMFILE );
470        if ( bitmap ) {
471            HWND bitmapControl = GetDlgItem( mDlg, IDB_SPLASH );
472            if ( bitmapControl ) {
473                HBITMAP old = (HBITMAP)SendMessage( bitmapControl,
474                                                    STM_SETIMAGE,
475                                                    IMAGE_BITMAP,
476                                                    (LPARAM)bitmap );
477                // Remember bitmap so we can delete it later.
478                mBitmap = bitmap;
479                // Delete old bitmap.
480                if ( old ) {
481                    BOOL ok = DeleteObject( old );
482                }
483            } else {
484                // Delete bitmap since it isn't going to be used.
485                DeleteObject( bitmap );
486            }
487        }
488    }
489}
490
491BOOL CALLBACK
492nsSplashScreenWin::DialogProc( HWND dlg, UINT msg, WPARAM wp, LPARAM lp ) {
493    if ( msg == WM_INITDIALOG ) {
494        // Store dialog handle.
495        nsSplashScreenWin *splashScreen = (nsSplashScreenWin*)lp;
496        if ( lp ) {
497            splashScreen->SetDialog( dlg );
498
499            // Try to load customized bitmap.
500            splashScreen->LoadBitmap();
501        }
502
503        /* Size and center the splash screen correctly. The flags in the
504         * dialog template do not do the right thing if the user's
505         * machine is using large fonts.
506         */
507        HWND bitmapControl = GetDlgItem( dlg, IDB_SPLASH );
508        if ( bitmapControl ) {
509            HBITMAP hbitmap = (HBITMAP)SendMessage( bitmapControl,
510                                                    STM_GETIMAGE,
511                                                    IMAGE_BITMAP,
512                                                    0 );
513            if ( hbitmap ) {
514                BITMAP bitmap;
515                if ( GetObject( hbitmap, sizeof bitmap, &bitmap ) ) {
516                    SetWindowPos( dlg,
517                                  NULL,
518                                  GetSystemMetrics(SM_CXSCREEN)/2 - bitmap.bmWidth/2,
519                                  GetSystemMetrics(SM_CYSCREEN)/2 - bitmap.bmHeight/2,
520                                  bitmap.bmWidth,
521                                  bitmap.bmHeight,
522                                  SWP_NOZORDER );
523                    ShowWindow( dlg, SW_SHOW );
524                }
525            }
526        }
527        return 1;
528    } else if (msg == WM_CLOSE) {
529        // Before killing ourself, set the top-level current.
530        // See comments in nsSplashScreenWin::Hide() above.
531        HWND topLevel = (HWND)lp;
532        if (topLevel)
533            ::SetForegroundWindow(topLevel);
534        // Destroy the dialog
535        ::EndDialog(dlg, 0);
536        // Release custom bitmap (if there is one).
537        HBITMAP bitmap = (HBITMAP)wp;
538        if ( bitmap ) {
539            ::DeleteObject( bitmap );
540        }
541    }
542    return 0;
543}
544
545void nsSplashScreenWin::SetDialog( HWND dlg ) {
546    // Save dialog handle.
547    mDlg = dlg;
548    // Store this pointer in the dialog.
549    SetWindowLong( mDlg, DWL_USER, (LONG)(void*)this );
550}
551
552nsSplashScreenWin *nsSplashScreenWin::GetPointer( HWND dlg ) {
553    // Get result from dialog user data.
554    LONG data = GetWindowLong( dlg, DWL_USER );
555    return (nsSplashScreenWin*)(void*)data;
556}
557
558DWORD WINAPI nsSplashScreenWin::ThreadProc( LPVOID splashScreen ) {
559    DialogBoxParam( GetModuleHandle( 0 ),
560                    MAKEINTRESOURCE( IDD_SPLASH ),
561                    HWND_DESKTOP,
562                    (DLGPROC)DialogProc,
563                    (LPARAM)splashScreen );
564    return 0;
565}
566
567PRBool gAbortServer = PR_FALSE;
568
569void
570nsNativeAppSupportWin::CheckConsole() {
571    for ( int i = 1; i < __argc; i++ ) {
572        if ( strcmp( "-console", __argv[i] ) == 0
573             ||
574             strcmp( "/console", __argv[i] ) == 0 ) {
575            // Users wants to make sure we have a console.
576            // Try to allocate one.
577            BOOL rc = ::AllocConsole();
578            if ( rc ) {
579                // Console allocated.  Fix it up so that output works in
580                // all cases.  See http://support.microsoft.com/support/kb/articles/q105/3/05.asp.
581
582                // stdout
583                int hCrt = ::_open_osfhandle( (long)GetStdHandle( STD_OUTPUT_HANDLE ),
584                                            _O_TEXT );
585                if ( hCrt != -1 ) {
586                    FILE *hf = ::_fdopen( hCrt, "w" );
587                    if ( hf ) {
588                        *stdout = *hf;
589                        ::fprintf( stdout, "stdout directed to dynamic console\n" );
590                    }
591                }
592
593                // stderr
594                hCrt = ::_open_osfhandle( (long)::GetStdHandle( STD_ERROR_HANDLE ),
595                                          _O_TEXT );
596                if ( hCrt != -1 ) {
597                    FILE *hf = ::_fdopen( hCrt, "w" );
598                    if ( hf ) {
599                        *stderr = *hf;
600                        ::fprintf( stderr, "stderr directed to dynamic console\n" );
601                    }
602                }
603
604                // stdin?
605                /* Don't bother for now.
606                hCrt = ::_open_osfhandle( (long)::GetStdHandle( STD_INPUT_HANDLE ),
607                                          _O_TEXT );
608                if ( hCrt != -1 ) {
609                    FILE *hf = ::_fdopen( hCrt, "r" );
610                    if ( hf ) {
611                        *stdin = *hf;
612                    }
613                }
614                */
615            } else {
616                // Failed.  Probably because there already is one.
617                // There's little we can do, in any case.
618            }
619            // Don't bother doing this more than once.
620            break;
621        } else if ( strcmp( "-turbo", __argv[i] ) == 0
622                    ||
623                    strcmp( "/turbo", __argv[i] ) == 0
624                    ||
625                    strcmp( "-server", __argv[i] ) == 0
626                    ||
627                    strcmp( "/server", __argv[i] ) == 0 ) {
628            // Start in server mode (and suppress splash screen).
629            mServerMode = PR_TRUE;
630            mShouldShowUI = PR_FALSE;
631            __argv[i] = "-nosplash"; // Bit of a hack, but it works!
632            // Ignore other args.
633            break;
634        }
635    }
636
637    PRBool checkTurbo = PR_TRUE;
638    for ( int j = 1; j < __argc; j++ ) {
639        if (strcmp("-killAll", __argv[j]) == 0 || strcmp("/killAll", __argv[j]) == 0 ||
640            strcmp("-kill", __argv[j]) == 0 || strcmp("/kill", __argv[j]) == 0) {
641            gAbortServer = PR_TRUE;
642            break;
643        }
644
645        if ( strcmp( "-silent", __argv[j] ) == 0 || strcmp( "/silent", __argv[j] ) == 0 ) {
646            checkTurbo = PR_FALSE;
647        }
648    }
649
650    // check if this is a restart of the browser after quiting from
651    // the servermoded browser instance.
652    if ( checkTurbo && !mServerMode ) {
653        HKEY key;
654        LONG result = ::RegOpenKeyEx( HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Run", 0, KEY_QUERY_VALUE, &key );
655        if ( result == ERROR_SUCCESS ) {
656          BYTE regvalue[_MAX_PATH];
657          DWORD type, len = sizeof(regvalue);
658          result = ::RegQueryValueEx( key, NS_QUICKLAUNCH_RUN_KEY, NULL, &type, regvalue, &len);
659          ::RegCloseKey( key );
660          if ( result == ERROR_SUCCESS && len > 0 ) {
661              // Make sure the filename in the quicklaunch command matches us
662              char fileName[_MAX_PATH];
663              int rv = ::GetModuleFileName( NULL, fileName, sizeof fileName );
664              nsCAutoString regvalueholder;
665              regvalueholder.Assign((char *) regvalue);
666              if ((regvalueholder.Find(fileName, PR_TRUE) != kNotFound) && (regvalueholder.Find("-turbo", PR_TRUE) != kNotFound) ) {
667                  mServerMode = PR_TRUE;
668                  mShouldShowUI = PR_TRUE;
669              }
670          }
671        }
672    }
673
674    return;
675}
676
677
678// Create and return an instance of class nsNativeAppSupportWin.
679nsresult
680NS_CreateNativeAppSupport( nsINativeAppSupport **aResult ) {
681    if ( aResult ) {
682        nsNativeAppSupportWin *pNative = new nsNativeAppSupportWin;
683        if ( pNative ) {
684            *aResult = pNative;
685            NS_ADDREF( *aResult );
686            // Check for dynamic console creation request.
687            pNative->CheckConsole();
688        } else {
689            return NS_ERROR_OUT_OF_MEMORY;
690        }
691    } else {
692        return NS_ERROR_NULL_POINTER;
693    }
694
695    return NS_OK;
696}
697
698// Create instance of Windows splash screen object.
699nsresult
700NS_CreateSplashScreen( nsISplashScreen **aResult ) {
701    if ( aResult ) {
702        *aResult = 0;
703        for ( int i = 1; i < __argc; i++ ) {
704            if ( strcmp( "-quiet", __argv[i] ) == 0
705                 ||
706                 strcmp( "/quiet", __argv[i] ) == 0 ) {
707                // No splash screen, please.
708                return NS_OK;
709            }
710        }
711        *aResult = new nsSplashScreenWin;
712        if ( *aResult ) {
713            NS_ADDREF( *aResult );
714        } else {
715            return NS_ERROR_OUT_OF_MEMORY;
716        }
717    } else {
718        return NS_ERROR_NULL_POINTER;
719    }
720
721    return NS_OK;
722}
723
724// Constants
725#define MOZ_DDE_APPLICATION    "Mozilla"
726#define MOZ_STARTUP_MUTEX_NAME "StartupMutex"
727#define MOZ_DDE_START_TIMEOUT 30000
728#define MOZ_DDE_STOP_TIMEOUT  15000
729#define MOZ_DDE_EXEC_TIMEOUT  15000
730
731// The array entries must match the enum ordering!
732const char * const topicNames[] = { "WWW_OpenURL",
733                                    "WWW_Activate",
734                                    "WWW_CancelProgress",
735                                    "WWW_Version",
736                                    "WWW_RegisterViewer",
737                                    "WWW_UnRegisterViewer",
738                                    "WWW_GetWindowInfo" };
739
740// Static member definitions.
741int   nsNativeAppSupportWin::mConversations = 0;
742HSZ   nsNativeAppSupportWin::mApplication   = 0;
743HSZ   nsNativeAppSupportWin::mTopics[nsNativeAppSupportWin::topicCount] = { 0 };
744DWORD nsNativeAppSupportWin::mInstance      = 0;
745PRBool nsNativeAppSupportWin::mInitialWindowActive = PR_FALSE;
746PRBool nsNativeAppSupportWin::mForceProfileStartup = PR_FALSE;
747PRBool nsNativeAppSupportWin::mSupportingDDEExec   = PR_FALSE;
748
749NOTIFYICONDATA nsNativeAppSupportWin::mIconData = { sizeof(NOTIFYICONDATA),
750                                                    0,
751                                                    1,
752                                                    NIF_ICON | NIF_MESSAGE | NIF_TIP,
753                                                    WM_USER,
754                                                    0,
755                                                    0 };
756HMENU nsNativeAppSupportWin::mTrayIconMenu = 0;
757
758char nsNativeAppSupportWin::mMutexName[ 128 ] = { 0 };
759
760
761// Message window encapsulation.
762struct MessageWindow {
763    // ctor/dtor are simplistic
764    MessageWindow() {
765        // Try to find window.
766        mHandle = ::FindWindow( className(), 0 );
767    }
768
769    // Act like an HWND.
770    operator HWND() {
771        return mHandle;
772    }
773
774    // Class name: appName + "MessageWindow"
775    static const char *className() {
776        static char classNameBuffer[128];
777        static char *mClassName = 0;
778        if ( !mClassName ) {
779            ::_snprintf( classNameBuffer,
780                         sizeof classNameBuffer,
781                         "%s%s",
782                         nsNativeAppSupportWin::mAppName,
783                         "MessageWindow" );
784            mClassName = classNameBuffer;
785        }
786        return mClassName;
787    }
788
789    // Create: Register class and create window.
790    NS_IMETHOD Create() {
791        WNDCLASS classStruct = { 0,                          // style
792                                 &MessageWindow::WindowProc, // lpfnWndProc
793                                 0,                          // cbClsExtra
794                                 0,                          // cbWndExtra
795                                 0,                          // hInstance
796                                 0,                          // hIcon
797                                 0,                          // hCursor
798                                 0,                          // hbrBackground
799                                 0,                          // lpszMenuName
800                                 className() };              // lpszClassName
801
802        // Register the window class.
803        NS_ENSURE_TRUE( ::RegisterClass( &classStruct ), NS_ERROR_FAILURE );
804
805        // Create the window.
806        NS_ENSURE_TRUE( ( mHandle = ::CreateWindow( className(),
807                                                    0,          // title
808                                                    WS_CAPTION, // style
809                                                    0,0,0,0,    // x, y, cx, cy
810                                                    0,          // parent
811                                                    0,          // menu
812                                                    0,          // instance
813                                                    0 ) ),      // create struct
814                        NS_ERROR_FAILURE );
815
816#if MOZ_DEBUG_DDE
817        printf( "Message window = 0x%08X\n", (int)mHandle );
818#endif
819
820        return NS_OK;
821    }
822
823    // Destory:  Get rid of window and reset mHandle.
824    NS_IMETHOD Destroy() {
825        nsresult retval = NS_OK;
826
827        if ( mHandle ) {
828            // DestroyWindow can only destroy windows created from
829            //  the same thread.
830            BOOL desRes = DestroyWindow( mHandle );
831            if ( FALSE != desRes ) {
832                mHandle = NULL;
833            }
834            else {
835                retval = NS_ERROR_FAILURE;
836            }
837        }
838
839        return retval;
840    }
841
842    // SendRequest: Pass string via WM_COPYDATA to message window.
843    NS_IMETHOD SendRequest( const char *cmd ) {
844        COPYDATASTRUCT cds = { 0, ::strlen( cmd ) + 1, (void*)cmd };
845        HWND newWin = (HWND)::SendMessage( mHandle, WM_COPYDATA, 0, (LPARAM)&cds );
846        if ( newWin ) {
847            ::SetForegroundWindow( newWin );
848        }
849        return NS_OK;
850    }
851
852    // Window proc.
853    static long CALLBACK WindowProc( HWND msgWindow, UINT msg, WPARAM wp, LPARAM lp ) {
854        if ( msg == WM_COPYDATA ) {
855            // This is an incoming request.
856            COPYDATASTRUCT *cds = (COPYDATASTRUCT*)lp;
857#if MOZ_DEBUG_DDE
858            printf( "Incoming request: %s\n", (const char*)cds->lpData );
859#endif
860            (void)nsNativeAppSupportWin::HandleRequest( (LPBYTE)cds->lpData );
861
862            // Get current window and return its window handle.
863            nsCOMPtr<nsIDOMWindowInternal> win;
864            GetMostRecentWindow( 0, getter_AddRefs( win ) );
865            return win ? (long)hwndForDOMWindow( win ) : 0;
866 } else if ( msg == WM_USER ) {
867     if ( lp == WM_RBUTTONUP ) {
868         // Show menu with Exit disabled/enabled appropriately.
869         nsCOMPtr<nsIDOMWindowInternal> win;
870         GetMostRecentWindow( 0, getter_AddRefs( win ) );
871         ::EnableMenuItem( nsNativeAppSupportWin::mTrayIconMenu, TURBO_EXIT, win ? MF_GRAYED : MF_ENABLED );
872         POINT pt;
873         GetCursorPos( &pt );
874
875         SetForegroundWindow(msgWindow);
876         int selectedItem = ::TrackPopupMenu( nsNativeAppSupportWin::mTrayIconMenu,
877                                              TPM_NONOTIFY | TPM_RETURNCMD |
878                                              TPM_RIGHTBUTTON,
879                                              pt.x,
880                                              pt.y,
881                                              0,
882                                              msgWindow,
883                                              0 );
884
885         switch (selectedItem) {
886         case TURBO_NAVIGATOR:
887             (void)nsNativeAppSupportWin::HandleRequest( (LPBYTE)"mozilla -browser" );
888             break;
889         case TURBO_MAIL:
890             (void)nsNativeAppSupportWin::HandleRequest( (LPBYTE)"mozilla -mail" );
891              break;
892         case TURBO_EDITOR:
893             (void)nsNativeAppSupportWin::HandleRequest( (LPBYTE)"mozilla -editor" );
894             break;
895         case TURBO_ADDRESSBOOK:
896             (void)nsNativeAppSupportWin::HandleRequest( (LPBYTE)"mozilla -addressbook" );
897             break;
898         case TURBO_EXIT:
899             (void)nsNativeAppSupportWin::HandleRequest( (LPBYTE)"mozilla -kill" );
900             break;
901         case TURBO_DISABLE:
902             nsresult rv;
903             nsCOMPtr<nsIStringBundleService> stringBundleService( do_GetService( NS_STRINGBUNDLE_CONTRACTID ) );
904             nsCOMPtr<nsIStringBundle> turboMenuBundle;
905             nsCOMPtr<nsIStringBundle> brandBundle;
906             if ( stringBundleService ) {
907                 stringBundleService->CreateBundle( "chrome://global/locale/brand.properties", getter_AddRefs( brandBundle ) );
908                 stringBundleService->CreateBundle( "chrome://navigator/locale/turboMenu.properties",
909                                                    getter_AddRefs( turboMenuBundle ) );
910             }
911             nsXPIDLString dialogMsg;
912             nsXPIDLString dialogTitle;
913             nsXPIDLString brandName;
914             if ( brandBundle && turboMenuBundle ) {
915                 brandBundle->GetStringFromName( NS_LITERAL_STRING( "brandShortName" ).get(),
916                                                 getter_Copies( brandName ) );
917                 const PRUnichar *formatStrings[] = { brandName.get() };
918                 turboMenuBundle->FormatStringFromName( NS_LITERAL_STRING( "DisableDlgMsg" ).get(), formatStrings,
919                                                        1, getter_Copies( dialogMsg ) );
920                 turboMenuBundle->FormatStringFromName( NS_LITERAL_STRING( "DisableDlgTitle" ).get(), formatStrings,
921                                                        1, getter_Copies( dialogTitle ) );
922
923             }
924             if ( dialogMsg.get() && dialogTitle.get() && brandName.get() ) {
925                 nsCOMPtr<nsIPromptService> dialog( do_GetService( "@mozilla.org/embedcomp/prompt-service;1" ) );
926                 if ( dialog ) {
927                     PRBool reallyDisable;
928                     nsNativeAppSupportWin::mLastWindowIsConfirmation = PR_TRUE;
929                     dialog->Confirm( nsnull, dialogTitle.get(), dialogMsg.get(), &reallyDisable );
930                     if ( !reallyDisable ) {
931                          break;
932                     }
933                 }
934
935             }
936             nsCOMPtr<nsIWindowsHooks> winHooksService ( do_GetService( NS_IWINDOWSHOOKS_CONTRACTID, &rv ) );
937             if ( NS_SUCCEEDED( rv ) )
938                 winHooksService->StartupRemoveOption("-turbo");
939
940             nsCOMPtr<nsIAppShellService> appShell = do_GetService( "@mozilla.org/appshell/appShellService;1", &rv );
941             if ( NS_SUCCEEDED( rv ) ) {
942                 nsCOMPtr<nsINativeAppSupport> native;
943                 rv = appShell->GetNativeAppSupport( getter_AddRefs( native ) );
944                 if ( NS_SUCCEEDED( rv ) )
945                     native->SetIsServerMode( PR_FALSE );
946                 if ( !win )
947                     appShell->Quit(nsIAppShellService::eAttemptQuit);
948             }
949             break;
950         }
951         PostMessage(msgWindow, WM_NULL, 0, 0);
952     } else if ( lp == WM_LBUTTONDBLCLK ) {
953         // Dbl-click will open nav/mailnews/composer based on prefs
954         // (if no windows are open), or, open nav (if some windows are
955         // already open).  That's done in HandleRequest.
956         (void)nsNativeAppSupportWin::HandleRequest( (LPBYTE)"mozilla" );
957     }
958     return TRUE;
959  } else if ( msg == WM_QUERYENDSESSION ) {
960    // Invoke "-killAll" cmd line handler.  That will close all open windows,
961    // and display dialog asking whether to save/don't save/cancel.  If the
962    // user says cancel, then we pass that indicator along to the system
963    // in order to stop the system shutdown/logoff.
964    nsCOMPtr<nsICmdLineHandler>
965        killAll( do_CreateInstance( "@mozilla.org/commandlinehandler/general-startup;1?type=killAll" ) );
966    if ( killAll ) {
967        nsXPIDLCString unused;
968        // Note: "GetChromeUrlForTask" is a euphemism for
969        //       "ProcessYourCommandLineSwitch".  The interface was written
970        //       presuming a less general-purpose role for command line
971        //       handlers than it ought to have.
972        nsresult rv = killAll->GetChromeUrlForTask( getter_Copies( unused ) );
973        if ( rv == NS_ERROR_ABORT ) {
974            // User cancelled shutdown/logoff.
975            return FALSE;
976        } else {
977            // Shutdown/logoff OK.
978            return TRUE;
979        }
980    }
981  } else if ((nsNativeAppSupportWin::mTrayRestart) && (msg == nsNativeAppSupportWin::mTrayRestart)) {
982     //Re-add the icon. The taskbar must have been destroyed and recreated
983     ::Shell_NotifyIcon( NIM_ADD, &nsNativeAppSupportWin::mIconData );
984  }
985  return DefWindowProc( msgWindow, msg, wp, lp );
986}
987
988private:
989    HWND mHandle;
990}; // struct MessageWindow
991
992UINT nsNativeAppSupportWin::mTrayRestart = 0;
993static char nameBuffer[128] = { 0 };
994char *nsNativeAppSupportWin::mAppName = nameBuffer;
995
996/* Start: Tries to find the "message window" to determine if it
997 *        exists.  If so, then Mozilla is already running.  In that
998 *        case, we use the handle to the "message" window and send
999 *        a request corresponding to this process's command line
1000 *        options.
1001 *
1002 *        If not, then this is the first instance of Mozilla.  In
1003 *        that case, we create and set up the message window.
1004 *
1005 *        The checking for existance of the message window must
1006 *        be protected by use of a mutex semaphore.
1007 */
1008NS_IMETHODIMP
1009nsNativeAppSupportWin::Start( PRBool *aResult ) {
1010    NS_ENSURE_ARG( aResult );
1011    NS_ENSURE_TRUE( mInstance == 0, NS_ERROR_NOT_INITIALIZED );
1012
1013    if (getenv("MOZ_NO_REMOTE"))
1014    {
1015        *aResult = PR_TRUE;
1016        return NS_OK;
1017    }
1018
1019    nsresult rv = NS_ERROR_FAILURE;
1020    *aResult = PR_FALSE;
1021
1022    // Grab mutex first.
1023    int retval;
1024    UINT id = ID_DDE_APPLICATION_NAME;
1025    retval = LoadString( (HINSTANCE) NULL, id, (LPTSTR) nameBuffer, sizeof(nameBuffer) );
1026    if ( retval == 0 ) {
1027        // No app name; just keep running.
1028        *aResult = PR_TRUE;
1029        return NS_OK;
1030    }
1031
1032    // Build mutex name from app name.
1033    ::_snprintf( mMutexName, sizeof mMutexName, "%s%s", nameBuffer, MOZ_STARTUP_MUTEX_NAME );
1034    Mutex startupLock = Mutex( mMutexName );
1035
1036    NS_ENSURE_TRUE( startupLock.Lock( MOZ_DDE_START_TIMEOUT ), NS_ERROR_FAILURE );
1037
1038    // Search for existing message window.
1039    MessageWindow msgWindow;
1040    if ( (HWND)msgWindow ) {
1041        // We are a client process.  Pass request to message window.
1042        LPTSTR cmd = ::GetCommandLine();
1043        rv = msgWindow.SendRequest( cmd );
1044    } else {
1045        // We will be server.
1046        if (!gAbortServer) {
1047            rv = msgWindow.Create();
1048            if ( NS_SUCCEEDED( rv ) ) {
1049                // Start up DDE server.
1050                this->StartDDE();
1051                // Tell caller to spin message loop.
1052                *aResult = PR_TRUE;
1053            }
1054        }
1055    }
1056
1057    startupLock.Unlock();
1058
1059    return rv;
1060}
1061
1062PRBool
1063nsNativeAppSupportWin::InitTopicStrings() {
1064    for ( int i = 0; i < topicCount; i++ ) {
1065        if ( !( mTopics[ i ] = DdeCreateStringHandle( mInstance, topicNames[ i ], CP_WINANSI ) ) ) {
1066            return PR_FALSE;
1067        }
1068    }
1069    return PR_TRUE;
1070}
1071
1072int
1073nsNativeAppSupportWin::FindTopic( HSZ topic ) {
1074    for ( int i = 0; i < topicCount; i++ ) {
1075        if ( DdeCmpStringHandles( topic, mTopics[i] ) == 0 ) {
1076            return i;
1077        }
1078    }
1079    return -1;
1080}
1081
1082// Utility function that determines if we're handling http Internet shortcuts.
1083static PRBool handlingHTTP() {
1084    PRBool result = PR_FALSE; // Answer no if an error occurs.
1085    // See if we're the "default browser" (i.e., handling http Internet shortcuts)       
1086    nsCOMPtr<nsIWindowsHooks> winhooks( do_GetService( NS_IWINDOWSHOOKS_CONTRACTID ) );
1087    if ( winhooks ) {
1088        nsCOMPtr<nsIWindowsHooksSettings> settings;
1089        nsresult rv = winhooks->GetSettings( getter_AddRefs( settings ) );
1090        if ( NS_SUCCEEDED( rv ) ) {
1091            settings->GetIsHandlingHTTP( &result );
1092            if ( result ) {
1093                // The user *said* to handle http.  See if we really
1094                // are doing it.  We need to check *only* the HTTP
1095                // settings.  If we don't mask off all others, we
1096                // may erroneously conclude that we're not handling
1097                // HTTP when really we are (although, a false negative
1098                // is much better than a false positive).  Please note
1099                // that setting these attributes false only affects
1100                // this "Settings" object.  It *does not* change the
1101                // actual user preferences stored in the registry!
1102
1103                // First, turn off all the other protocols.
1104                settings->SetIsHandlingHTTPS( PR_FALSE );
1105                settings->SetIsHandlingFTP( PR_FALSE );
1106                settings->SetIsHandlingCHROME( PR_FALSE );
1107                settings->SetIsHandlingGOPHER( PR_FALSE );
1108
1109                // Next, all the file types.
1110                settings->SetIsHandlingHTML( PR_FALSE );
1111                settings->SetIsHandlingJPEG( PR_FALSE );
1112                settings->SetIsHandlingGIF( PR_FALSE );
1113                settings->SetIsHandlingPNG( PR_FALSE );
1114                settings->SetIsHandlingMNG( PR_FALSE );
1115                settings->SetIsHandlingBMP( PR_FALSE );
1116                settings->SetIsHandlingICO( PR_FALSE );
1117                settings->SetIsHandlingXML( PR_FALSE );
1118                settings->SetIsHandlingXHTML( PR_FALSE );
1119                settings->SetIsHandlingXUL( PR_FALSE );
1120
1121                // Now test the HTTP setting in the registry.
1122                settings->GetRegistryMatches( &result );
1123            }
1124        }
1125    }
1126    return result;
1127}
1128
1129// Utility function to delete a registry subkey.
1130static DWORD deleteKey( HKEY baseKey, const char *keyName ) {
1131    // Make sure input subkey isn't null.
1132    DWORD rc;
1133    if ( keyName && ::strlen(keyName) ) {
1134        // Open subkey.
1135        HKEY key;
1136        rc = ::RegOpenKeyEx( baseKey,
1137                             keyName,
1138                             0,
1139                             KEY_ENUMERATE_SUB_KEYS | DELETE,
1140                             &key );
1141        // Continue till we get an error or are done.
1142        while ( rc == ERROR_SUCCESS ) {
1143            char subkeyName[_MAX_PATH];
1144            DWORD len = sizeof subkeyName;
1145            // Get first subkey name.  Note that we always get the
1146            // first one, then delete it.  So we need to get
1147            // the first one next time, also.
1148            rc = ::RegEnumKeyEx( key,
1149                                 0,
1150                                 subkeyName,
1151                                 &len,
1152                                 0,
1153                                 0,
1154                                 0,
1155                                 0 );
1156            if ( rc == ERROR_NO_MORE_ITEMS ) {
1157                // No more subkeys.  Delete the main one.
1158                rc = ::RegDeleteKey( baseKey, keyName );
1159                break;
1160            } else if ( rc == ERROR_SUCCESS ) {
1161                // Another subkey, delete it, recursively.
1162                rc = deleteKey( key, subkeyName );
1163            }
1164        }
1165        // Close the key we opened.
1166        ::RegCloseKey( key );
1167    } else {
1168        rc = ERROR_BADKEY;
1169    }
1170    return rc;
1171}
1172
1173
1174// Start DDE server.
1175//
1176// This used to be the Start() method when we were using DDE as the
1177// primary IPC mechanism between secondary Mozilla processes and the
1178// initial "server" process.
1179//
1180// Now, it simply initializes the DDE server.  The caller must check
1181// that this process is to be the server, and, must acquire the DDE
1182// startup mutex semaphore prior to calling this routine.  See ::Start(),
1183// above.
1184NS_IMETHODIMP
1185nsNativeAppSupportWin::StartDDE() {
1186    NS_ENSURE_TRUE( mInstance == 0, NS_ERROR_NOT_INITIALIZED );
1187
1188    // Initialize DDE.
1189    NS_ENSURE_TRUE( DMLERR_NO_ERROR == DdeInitialize( &mInstance,
1190                                                      nsNativeAppSupportWin::HandleDDENotification,
1191                                                      APPCLASS_STANDARD,
1192                                                      0 ),
1193                    NS_ERROR_FAILURE );
1194
1195    // Allocate DDE strings.
1196    NS_ENSURE_TRUE( ( mApplication = DdeCreateStringHandle( mInstance, mAppName, CP_WINANSI ) ) && InitTopicStrings(),
1197                    NS_ERROR_FAILURE );
1198
1199    // Next step is to register a DDE service.
1200    NS_ENSURE_TRUE( DdeNameService( mInstance, mApplication, 0, DNS_REGISTER ), NS_ERROR_FAILURE );
1201
1202#if MOZ_DEBUG_DDE
1203    printf( "DDE server started\n" );
1204#endif
1205
1206    return NS_OK;
1207}
1208
1209// If no DDE conversations are pending, terminate DDE.
1210NS_IMETHODIMP
1211nsNativeAppSupportWin::Stop( PRBool *aResult ) {
1212    NS_ENSURE_ARG( aResult );
1213    NS_ENSURE_TRUE( mInstance, NS_ERROR_NOT_INITIALIZED );
1214
1215    nsresult rv = NS_OK;
1216    *aResult = PR_TRUE;
1217
1218    Mutex ddeLock( mMutexName );
1219
1220    if ( ddeLock.Lock( MOZ_DDE_STOP_TIMEOUT ) ) {
1221        if ( mConversations == 0 ) {
1222            this->Quit();
1223        } else {
1224            *aResult = PR_FALSE;
1225        }
1226
1227        ddeLock.Unlock();
1228    }
1229    else {
1230        // No DDE application name specified, but that's OK.  Just
1231        // forge ahead.
1232        *aResult = PR_TRUE;
1233    }
1234
1235    return rv;
1236}
1237
1238// Terminate DDE regardless.
1239NS_IMETHODIMP
1240nsNativeAppSupportWin::Quit() {
1241    // If another process wants to look for the message window, they need
1242    // to wait to hold the lock, in which case they will not find the
1243    // window as we will destroy ours under our lock.
1244    // When the mutex goes off the stack, it is unlocked via destructor.
1245    Mutex mutexLock(mMutexName);
1246    NS_ENSURE_TRUE(mutexLock.Lock(MOZ_DDE_START_TIMEOUT), NS_ERROR_FAILURE );
1247
1248    // If we've got a message window to receive IPC or new window requests,
1249    // get rid of it as we are shutting down.
1250    // Note:  Destroy calls DestroyWindow, which will only work on a window
1251    //  created by the same thread.
1252    MessageWindow mw;
1253    mw.Destroy();
1254   
1255    if ( mInstance ) {
1256        // Undo registry setting if we need to.
1257        if ( mSupportingDDEExec && handlingHTTP() ) {
1258            mSupportingDDEExec = PR_FALSE;
1259#if MOZ_DEBUG_DDE
1260            printf( "Deleting ddexec subkey on exit\n" );
1261#endif
1262            deleteKey( HKEY_CLASSES_ROOT, "http\\shell\\open\\ddeexec" );
1263        }
1264
1265        // Unregister application name.
1266        DdeNameService( mInstance, mApplication, 0, DNS_UNREGISTER );
1267        // Clean up strings.
1268        if ( mApplication ) {
1269            DdeFreeStringHandle( mInstance, mApplication );
1270            mApplication = 0;
1271        }
1272        for ( int i = 0; i < topicCount; i++ ) {
1273            if ( mTopics[i] ) {
1274                DdeFreeStringHandle( mInstance, mTopics[i] );
1275                mTopics[i] = 0;
1276            }
1277        }
1278        DdeUninitialize( mInstance );
1279        mInstance = 0;
1280    }
1281
1282    return NS_OK;
1283}
1284
1285PRBool NS_CanRun()
1286{
1287      return PR_TRUE;
1288}
1289
1290#if MOZ_DEBUG_DDE
1291// Macro to generate case statement for a given XTYP value.
1292#define XTYP_CASE(t) \
1293    case t: result = #t; break
1294
1295static nsCString uTypeDesc( UINT uType ) {
1296    nsCString result;
1297    switch ( uType ) {
1298    XTYP_CASE(XTYP_ADVSTART);
1299    XTYP_CASE(XTYP_CONNECT);
1300    XTYP_CASE(XTYP_ADVREQ);
1301    XTYP_CASE(XTYP_REQUEST);
1302    XTYP_CASE(XTYP_WILDCONNECT);
1303    XTYP_CASE(XTYP_ADVDATA);
1304    XTYP_CASE(XTYP_EXECUTE);
1305    XTYP_CASE(XTYP_POKE);
1306    XTYP_CASE(XTYP_ADVSTOP);
1307    XTYP_CASE(XTYP_CONNECT_CONFIRM);
1308    XTYP_CASE(XTYP_DISCONNECT);
1309    XTYP_CASE(XTYP_ERROR);
1310    XTYP_CASE(XTYP_MONITOR);
1311    XTYP_CASE(XTYP_REGISTER);
1312    XTYP_CASE(XTYP_XACT_COMPLETE);
1313    XTYP_CASE(XTYP_UNREGISTER);
1314    default: result = "XTYP_?????";
1315    }
1316    return result;
1317}
1318
1319static nsCString hszValue( DWORD instance, HSZ hsz ) {
1320    // Extract string from HSZ.
1321    nsCString result("[");
1322    DWORD len = DdeQueryString( instance, hsz, NULL, NULL, CP_WINANSI );
1323    if ( len ) {
1324        char buffer[ 256 ];
1325        DdeQueryString( instance, hsz, buffer, sizeof buffer, CP_WINANSI );
1326        result += buffer;
1327    }
1328    result += "]";
1329    return result;
1330}
1331#else
1332// These are purely a safety measure to avoid the infamous "won't
1333// build non-debug" type Tinderbox flames.
1334static nsCString uTypeDesc( UINT ) {
1335    return nsCString( "?" );
1336}
1337static nsCString hszValue( DWORD, HSZ ) {
1338    return nsCString( "?" );
1339}
1340#endif
1341
1342
1343// Utility function to escape double-quotes within a string.
1344static void escapeQuotes( nsAString &aString ) {
1345    PRInt32 offset = -1;
1346    while( 1 ) {
1347       // Find next '"'.
1348       offset = aString.FindChar( '"', ++offset );
1349       if ( offset == kNotFound ) {
1350           // No more quotes, exit.
1351           break;
1352       } else {
1353           // Insert back-slash ahead of the '"'.
1354           aString.Insert( PRUnichar('\\'), offset );
1355           // Increment offset because we just inserted a slash
1356           offset++;
1357       }
1358    }
1359    return;
1360}
1361
1362HDDEDATA CALLBACK
1363nsNativeAppSupportWin::HandleDDENotification( UINT uType,       // transaction type
1364                                              UINT uFmt,        // clipboard data format
1365                                              HCONV hconv,      // handle to the conversation
1366                                              HSZ hsz1,         // handle to a string
1367                                              HSZ hsz2,         // handle to a string
1368                                              HDDEDATA hdata,   // handle to a global memory object
1369                                              ULONG dwData1,    // transaction-specific data
1370                                              ULONG dwData2 ) { // transaction-specific data
1371
1372#if MOZ_DEBUG_DDE
1373    printf( "DDE: uType  =%s\n",      uTypeDesc( uType ).get() );
1374    printf( "     uFmt   =%u\n",      (unsigned)uFmt );
1375    printf( "     hconv  =%08x\n",    (int)hconv );
1376    printf( "     hsz1   =%08x:%s\n", (int)hsz1, hszValue( mInstance, hsz1 ).get() );
1377    printf( "     hsz2   =%08x:%s\n", (int)hsz2, hszValue( mInstance, hsz2 ).get() );
1378    printf( "     hdata  =%08x\n",    (int)hdata );
1379    printf( "     dwData1=%08x\n",    (int)dwData1 );
1380    printf( "     dwData2=%08x\n",    (int)dwData2 );
1381#endif
1382
1383    HDDEDATA result = 0;
1384    if ( uType & XCLASS_BOOL ) {
1385        switch ( uType ) {
1386            case XTYP_CONNECT:
1387                // Make sure its for our service/topic.
1388                if ( FindTopic( hsz1 ) != -1 ) {
1389                    // We support this connection.
1390                    result = (HDDEDATA)1;
1391                }
1392                break;
1393            case XTYP_CONNECT_CONFIRM:
1394                // We don't care about the conversation handle, at this point.
1395                result = (HDDEDATA)1;
1396                break;
1397        }
1398    } else if ( uType & XCLASS_DATA ) {
1399        if ( uType == XTYP_REQUEST ) {
1400            switch ( FindTopic( hsz1 ) ) {
1401                case topicOpenURL: {
1402                    // Open a given URL...
1403
1404                    // Default is to open in current window.
1405                    PRBool new_window = PR_FALSE;
1406
1407                    // Get the URL from the first argument in the command.
1408                    nsCAutoString url;
1409                    ParseDDEArg(hsz2, 0, url);
1410                    // Read the 3rd argument in the command to determine if a
1411                    // new window is to be used.
1412                    nsCAutoString windowID;
1413                    ParseDDEArg(hsz2, 2, windowID);
1414                    // "0" means to open the URL in a new window.
1415                    if ( windowID.Equals( "0" ) ) {
1416                        new_window = PR_TRUE;
1417                    }
1418
1419                    // Make it look like command line args.
1420                    url.Insert( "mozilla -url ", 0 );
1421#if MOZ_DEBUG_DDE
1422                    printf( "Handling dde XTYP_REQUEST request: [%s]...\n", url.get() );
1423#endif
1424                    // Now handle it.
1425                    HandleRequest( LPBYTE( url.get() ), new_window );
1426                    // Return pseudo window ID.
1427                    result = CreateDDEData( 1 );
1428                    break;
1429                }
1430                case topicGetWindowInfo: {
1431                    // This topic has to get the current URL, get the current
1432                    // page title and then format the output into the DDE
1433                    // return string.  The return value is "URL","Page Title",
1434                    // "Window ID" however the window ID is not used for this
1435                    // command, therefore it is returned as a null string
1436
1437                    // This isn't really a loop.  We just use "break"
1438                    // statements to bypass the remaining steps when
1439                    // something goes wrong.
1440                    do {
1441                        // Get most recently used Nav window.
1442                        nsCOMPtr<nsIDOMWindowInternal> navWin;
1443                        GetMostRecentWindow( NS_LITERAL_STRING( "navigator:browser" ).get(),
1444                                             getter_AddRefs( navWin ) );
1445                        if ( !navWin ) {
1446                            // There is not a window open
1447                            break;
1448                        }
1449                        // Get content window.
1450                        nsCOMPtr<nsIDOMWindow> content;
1451                        navWin->GetContent( getter_AddRefs( content ) );
1452                        if ( !content ) {
1453                            break;
1454                        }
1455                        // Convert that to internal interface.
1456                        nsCOMPtr<nsIDOMWindowInternal> internalContent( do_QueryInterface( content ) );
1457                        if ( !internalContent ) {
1458                            break;
1459                        }
1460                        // Get location.
1461                        nsCOMPtr<nsIDOMLocation> location;
1462                        internalContent->GetLocation( getter_AddRefs( location ) );
1463                        if ( !location ) {
1464                            break;
1465                        }
1466                        // Get href for URL.
1467                        nsAutoString url;
1468                        if ( NS_FAILED( location->GetHref( url ) ) ) {
1469                            break;
1470                        }
1471                        // Escape any double-quotes.
1472                        escapeQuotes( url );
1473
1474                        // Now for the title; first, get the "window" JS object.
1475                        nsCOMPtr<nsIScriptGlobalObject> scrGlobalObj( do_QueryInterface( internalContent ) );
1476                        if ( !scrGlobalObj ) {
1477                            break;
1478                        }
1479                        // Then the doc shell...
1480                        nsCOMPtr<nsIDocShell> docShell;
1481                        scrGlobalObj->GetDocShell( getter_AddRefs( docShell ) );
1482                        if ( !docShell ) {
1483                            break;
1484                        }
1485                        // And from that the base window...
1486                        nsCOMPtr<nsIBaseWindow> baseWindow( do_QueryInterface( docShell ) );
1487                        if ( !baseWindow ) {
1488                            break;
1489                        }
1490                        // And from the base window we can get the title.
1491                        nsXPIDLString title;
1492                        if(!baseWindow) {
1493                            break;
1494                        }
1495                        baseWindow->GetTitle(getter_Copies(title));
1496                        // Escape any double-quotes in the title.
1497                        escapeQuotes( title );
1498
1499                        // Use a string buffer for the output data, first
1500                        // save a quote.
1501                        nsCAutoString   outpt( NS_LITERAL_CSTRING("\"") );
1502                        // Now copy the URL converting the Unicode string
1503                        // to a single-byte ASCII string
1504                        outpt.Append( NS_LossyConvertUCS2toASCII( url ) );
1505                        // Add the "," used to separate the URL and the page
1506                        // title
1507                        outpt.Append( NS_LITERAL_CSTRING("\",\"") );
1508                        // Now copy the current page title to the return string
1509                        outpt.Append( NS_LossyConvertUCS2toASCII( title.get() ));
1510                        // Fill out the return string with the remainin ",""
1511                        outpt.Append( NS_LITERAL_CSTRING( "\",\"\"" ));
1512
1513                        // Create a DDE handle to a char string for the data
1514                        // being returned, this copies and creates a "shared"
1515                        // copy of the DDE response until the calling APP
1516                        // reads it and says it can be freed.
1517                        result = CreateDDEData( (LPBYTE)(const char*)outpt.get(),
1518                                                outpt.Length() + 1 );
1519#if MOZ_DEBUG_DDE
1520                        printf( "WWW_GetWindowInfo->%s\n", outpt.get() );
1521#endif
1522                    } while ( PR_FALSE );
1523                    break;
1524                }
1525                case topicActivate: {
1526                    // Activate a Nav window...
1527                    nsCAutoString windowID;
1528                    ParseDDEArg(hsz2, 0, windowID);
1529                    // 4294967295 is decimal for 0xFFFFFFFF which is also a
1530                    //   correct value to do that Activate last window stuff
1531                    if ( windowID.Equals( "-1" ) ||
1532                         windowID.Equals( "4294967295" ) ) {
1533                        // We only support activating the most recent window (or a new one).
1534                        ActivateLastWindow();
1535                        // Return pseudo window ID.
1536                        result = CreateDDEData( 1 );
1537                    }
1538                    break;
1539                }
1540                case topicVersion: {
1541                    // Return version.  We're restarting at 1.0!
1542                    DWORD version = 1 << 16; // "1.0"
1543                    result = CreateDDEData( version );
1544                    break;
1545                }
1546                case topicRegisterViewer: {
1547                    // Register new viewer (not implemented).
1548                    result = CreateDDEData( PR_FALSE );
1549                    break;
1550                }
1551                case topicUnRegisterViewer: {
1552                    // Unregister new viewer (not implemented).
1553                    result = CreateDDEData( PR_FALSE );
1554                    break;
1555                }
1556                default:
1557                    break;
1558            }
1559        } else if ( uType & XTYP_POKE ) {
1560            switch ( FindTopic( hsz1 ) ) {
1561                case topicCancelProgress: {
1562                    // "Handle" progress cancel (actually, pretty much ignored).
1563                    result = (HDDEDATA)DDE_FACK;
1564                    break;
1565                }
1566                default:
1567                    break;
1568            }
1569        }
1570    } else if ( uType & XCLASS_FLAGS ) {
1571        if ( uType == XTYP_EXECUTE ) {
1572            // Prove that we received the request.
1573            DWORD bytes;
1574            LPBYTE request = DdeAccessData( hdata, &bytes );
1575#if MOZ_DEBUG_DDE
1576            printf( "Handling dde request: [%s]...\n", (char*)request );
1577#endif
1578            // Default is to open in current window.
1579            PRBool new_window = PR_FALSE;
1580
1581            nsCAutoString url;
1582            ParseDDEArg((const char*) request, 0, url);
1583
1584            // Read the 3rd argument in the command to determine if a
1585            // new window is to be used.
1586            nsCAutoString windowID;
1587            ParseDDEArg((const char*) request, 2, windowID);
1588
1589            // "0" means to open the URL in a new window.
1590            if ( windowID.Equals( "0" ) ) {
1591                new_window = PR_TRUE;
1592            }
1593
1594            // Make it look like command line args.
1595            url.Insert( "mozilla -url ", 0 );
1596#if MOZ_DEBUG_DDE
1597            printf( "Handling dde XTYP_REQUEST request: [%s]...\n", url.get() );
1598#endif
1599            // Now handle it.
1600            HandleRequest( LPBYTE( url.get() ), new_window );
1601
1602            // Release the data.
1603            DdeUnaccessData( hdata );
1604            result = (HDDEDATA)DDE_FACK;
1605        } else {
1606            result = (HDDEDATA)DDE_FNOTPROCESSED;
1607        }
1608    } else if ( uType & XCLASS_NOTIFICATION ) {
1609    }
1610#if MOZ_DEBUG_DDE
1611    printf( "DDE result=%d (0x%08X)\n", (int)result, (int)result );
1612#endif
1613    return result;
1614}
1615
1616// Utility function to advance to end of quoted string.
1617// p+offset must point to the comma preceding the arg on entry.
1618// On return, p+result points to the closing '"' (or end of the string
1619// if the closing '"' is missing) if the arg is quoted.  If the arg
1620// is not quoted, then p+result will point to the first character
1621// of the arg.
1622static PRInt32 advanceToEndOfQuotedArg( const char *p, PRInt32 offset, PRInt32 len ) {
1623    // Check whether the current arg is quoted.
1624    if ( p[++offset] == '"' ) {
1625        // Advance past the closing quote.
1626        while ( offset < len && p[++offset] != '"' ) {
1627            // If the current character is a backslash, then the
1628            // next character can't be a *real* '"', so skip it.
1629            if ( p[offset] == '\\' ) {
1630                offset++;
1631            }
1632        }
1633    }
1634    return offset;
1635}
1636
1637void nsNativeAppSupportWin::ParseDDEArg( const char* args, int index, nsCString& aString) {
1638    if ( args ) {
1639        int argLen = strlen(args);
1640        nsDependentCString temp(args, argLen);
1641
1642        // offset points to the comma preceding the desired arg.
1643        PRInt32 offset = -1;
1644        // Skip commas till we get to the arg we want.
1645        while( index-- ) {
1646            // If this arg is quoted, then go to closing quote.
1647            offset = advanceToEndOfQuotedArg( args, offset, argLen);
1648            // Find next comma.
1649            offset = temp.FindChar( ',', offset );
1650            if ( offset == kNotFound ) {
1651                // No more commas, give up.
1652                aString = args;
1653                return;
1654            }
1655        }
1656        // The desired argument starts just past the preceding comma,
1657        // which offset points to, and extends until the following
1658        // comma (or the end of the string).
1659        //
1660        // Since the argument might be enclosed in quotes, we need to
1661        // deal with that before searching for the terminating comma.
1662        // We advance offset so it ends up pointing to the start of
1663        // the argument we want.
1664        PRInt32 end = advanceToEndOfQuotedArg( args, offset++, argLen );
1665        // Find next comma (or end of string).
1666        end = temp.FindChar( ',', end );
1667        if ( end == kNotFound ) {
1668            // Arg is the rest of the string.
1669            end = argLen;
1670        }
1671        // Extract result.
1672        aString.Assign( args + offset, end - offset );
1673    }
1674    return;
1675}
1676
1677// Utility to parse out argument from a DDE item string.
1678void nsNativeAppSupportWin::ParseDDEArg( HSZ args, int index, nsCString& aString) {
1679    DWORD argLen = DdeQueryString( mInstance, args, NULL, NULL, CP_WINANSI );
1680    // there wasn't any string, so return empty string
1681    if ( !argLen ) return;
1682    nsCAutoString temp;
1683    // Ensure result's buffer is sufficiently big.
1684    temp.SetLength( argLen );
1685    // Now get the string contents.
1686    DdeQueryString( mInstance, args, (char*)temp.get(), temp.Length(), CP_WINANSI );
1687    // Parse out the given arg.
1688    ParseDDEArg(temp.get(), index, aString);
1689    return;
1690}
1691
1692void nsNativeAppSupportWin::ActivateLastWindow() {
1693    nsCOMPtr<nsIDOMWindowInternal> navWin;
1694    GetMostRecentWindow( NS_LITERAL_STRING("navigator:browser").get(), getter_AddRefs( navWin ) );
1695    if ( navWin ) {
1696        // Activate that window.
1697        activateWindow( navWin );
1698    } else {
1699        // Need to create a Navigator window, then.
1700        OpenBrowserWindow( "about:blank" );
1701    }
1702}
1703
1704HDDEDATA nsNativeAppSupportWin::CreateDDEData( DWORD value ) {
1705    return CreateDDEData( (LPBYTE)&value, sizeof value );
1706}
1707
1708HDDEDATA nsNativeAppSupportWin::CreateDDEData( LPBYTE value, DWORD len ) {
1709    HDDEDATA result = DdeCreateDataHandle( mInstance,
1710                                           value,
1711                                           len,
1712                                           0,
1713                                           mApplication,
1714                                           CF_TEXT,
1715                                           0 );
1716    return result;
1717}
1718
1719// Handle DDE request.  The argument is the command line received by the
1720// DDE client process.  We convert that string to an nsICmdLineService
1721// object via GetCmdLineArgs.  Then, we look for certain well-known cmd
1722// arguments.  This replicates code elsewhere, to some extent,
1723// unfortunately (if you can fix that, please do).
1724void
1725nsNativeAppSupportWin::HandleRequest( LPBYTE request, PRBool newWindow ) {
1726
1727    // if initial hidden window is still being displayed, we need to ignore requests
1728    // because such requests might not function properly.  See bug 147223 for details
1729
1730    if (mInitialWindowActive) {
1731      return;
1732    }
1733
1734    // Parse command line.
1735
1736    nsCOMPtr<nsICmdLineService> args;
1737    nsresult rv;
1738
1739    rv = GetCmdLineArgs( request, getter_AddRefs( args ) );
1740    if (NS_FAILED(rv)) return;
1741
1742    nsCOMPtr<nsIAppShellService> appShell(do_GetService("@mozilla.org/appshell/appShellService;1", &rv));
1743    if (NS_FAILED(rv)) return;
1744
1745    nsCOMPtr<nsINativeAppSupport> nativeApp;
1746    rv = appShell->GetNativeAppSupport(getter_AddRefs( nativeApp ));
1747    if (NS_FAILED(rv)) return;
1748
1749    // first see if there is a url
1750    nsXPIDLCString arg;
1751    rv = args->GetURLToLoad(getter_Copies(arg));
1752    if (NS_SUCCEEDED(rv) && (const char*)arg ) {
1753      // Launch browser.
1754#if MOZ_DEBUG_DDE
1755      printf( "Launching browser on url [%s]...\n", (const char*)arg );
1756#endif
1757      if (NS_SUCCEEDED(nativeApp->EnsureProfile(args)))
1758        (void)OpenBrowserWindow( arg, newWindow );
1759      return;
1760    }
1761
1762
1763    // ok, let's try the -chrome argument
1764    rv = args->GetCmdLineValue("-chrome", getter_Copies(arg));
1765    if (NS_SUCCEEDED(rv) && (const char*)arg ) {
1766      // Launch chrome.
1767#if MOZ_DEBUG_DDE
1768      printf( "Launching chrome url [%s]...\n", (const char*)arg );
1769#endif
1770      if (NS_SUCCEEDED(nativeApp->EnsureProfile(args)))
1771        (void)OpenWindow( arg, "" );
1772      return;
1773    }
1774
1775    // try for the "-profilemanager" argument, in which case we want the
1776    // profile manager to appear, but only if there are no windows open
1777
1778    rv = args->GetCmdLineValue( "-profilemanager", getter_Copies(arg));
1779    if ( NS_SUCCEEDED(rv) && (const char*)arg ) { // -profilemanager on command line
1780      nsCOMPtr<nsIDOMWindowInternal> window;
1781      GetMostRecentWindow(0, getter_AddRefs(window));
1782      if (!window) { // there are no open windows
1783        mForceProfileStartup = PR_TRUE;
1784      }
1785    }
1786
1787    // try for the "-kill" argument, to shut down the server
1788    rv = args->GetCmdLineValue( "-kill", getter_Copies(arg));
1789    if ( NS_SUCCEEDED(rv) && (const char*)arg ) {
1790      // Turn off server mode.
1791      nsCOMPtr<nsIAppShellService> appShell =
1792        do_GetService( "@mozilla.org/appshell/appShellService;1", &rv);
1793      if (NS_FAILED(rv)) return;
1794
1795      nsCOMPtr<nsINativeAppSupport> native;
1796      rv = appShell->GetNativeAppSupport( getter_AddRefs( native ));
1797      if (NS_SUCCEEDED(rv)) {
1798        native->SetIsServerMode( PR_FALSE );
1799
1800        // close app if there are no more top-level windows.
1801        appShell->Quit(nsIAppShellService::eConsiderQuit);
1802      }
1803
1804      return;
1805    }
1806
1807    // check wheather it is a MAPI request.  If yes, don't open any new
1808    // windows and just return.
1809    rv = args->GetCmdLineValue(MAPI_STARTUP_ARG, getter_Copies(arg));
1810    if (NS_SUCCEEDED(rv) && (const char*)arg) {
1811      nativeApp->EnsureProfile(args);
1812      return;
1813    }
1814
1815    // Try standard startup's command-line handling logic from nsAppRunner.cpp...
1816
1817    // Need profile before opening windows.
1818    rv = nativeApp->EnsureProfile(args);
1819    if (NS_FAILED(rv)) return;
1820
1821    // This will tell us whether the command line processing opened a window.
1822    PRBool windowOpened = PR_FALSE;
1823
1824    // If there are no command line arguments, then we want to open windows
1825    // based on startup prefs (which say to open navigator and/or mailnews
1826    // and/or composer), or, open just a Navigator window.  We do the former
1827    // if there are no open windows (i.e., we're in turbo mode), the latter
1828    // if there are open windows.  Note that we call DoCommandLines in the
1829    // case where there are no command line args but there are windows open
1830    // (i.e., with heedStartupPrefs==PR_FALSE) despite the fact that it may
1831    // not actually do anything in that case.  That way we're covered if the
1832    // logic in DoCommandLines changes.  Note that we cover this case below
1833    // by opening a navigator window if DoCommandLines doesn't open one.  We
1834    // have to cover that case anyway, because DoCommandLines won't open a
1835    // window when given "mozilla -foobar" or the like.
1836    PRBool heedStartupPrefs = PR_FALSE;
1837    PRInt32 argc = 0;
1838    args->GetArgc( &argc );
1839    if ( argc <= 1 ) {
1840        // Use startup prefs iff there are no windows currently open.
1841        nsCOMPtr<nsIDOMWindowInternal> win;
1842        GetMostRecentWindow( 0, getter_AddRefs( win ) );
1843        if ( !win ) {
1844            heedStartupPrefs = PR_TRUE;
1845        }
1846    }
1847
1848    // Process command line options.
1849    rv = DoCommandLines( args, heedStartupPrefs, &windowOpened );
1850
1851    // If a window was opened, then we're done.
1852    // Note that we keep on trying in the unlikely event of an error.
1853    if (rv == NS_ERROR_NOT_AVAILABLE || rv == NS_ERROR_ABORT || windowOpened) {
1854      return;
1855    }
1856
1857    // ok, no idea what the param is.
1858#if MOZ_DEBUG_DDE
1859    printf( "Unknown request [%s]\n", (char*) request );
1860#endif
1861    // if all else fails, open a browser window
1862    const char * const contractID =
1863      "@mozilla.org/commandlinehandler/general-startup;1?type=browser";
1864    nsCOMPtr<nsICmdLineHandler> handler = do_GetService(contractID, &rv);
1865    if (NS_FAILED(rv)) return;
1866
1867    nsXPIDLString defaultArgs;
1868    rv = handler->GetDefaultArgs(getter_Copies(defaultArgs));
1869    if (NS_FAILED(rv) || !defaultArgs) return;
1870
1871    if (defaultArgs) {
1872      nsCAutoString url;
1873      url.AssignWithConversion( defaultArgs );
1874      OpenBrowserWindow(url.get());
1875    } else {
1876      OpenBrowserWindow("about:blank");
1877    }
1878}
1879
1880// Parse command line args according to MS spec
1881// (see "Parsing C++ Command-Line Arguments" at
1882// http://msdn.microsoft.com/library/devprods/vs6/visualc/vclang/_pluslang_parsing_c.2b2b_.command.2d.line_arguments.htm).
1883nsresult
1884nsNativeAppSupportWin::GetCmdLineArgs( LPBYTE request, nsICmdLineService **aResult ) {
1885    nsresult rv = NS_OK;
1886
1887    int justCounting = 1;
1888    char **argv = 0;
1889    // Flags, etc.
1890    int init = 1;
1891    int between, quoted, bSlashCount;
1892    int argc;
1893    char *p;
1894    nsCAutoString arg;
1895    // We loop if we've not finished the second pass through.
1896    while ( 1 ) {
1897        // Initialize if required.
1898        if ( init ) {
1899            p = (char*)request;
1900            between = 1;
1901            argc = quoted = bSlashCount = 0;
1902
1903            init = 0;
1904        }
1905        if ( between ) {
1906            // We are traversing whitespace between args.
1907            // Check for start of next arg.
1908            if (  *p != 0 && !isspace( *p ) ) {
1909                // Start of another arg.
1910                between = 0;
1911                arg = "";
1912                switch ( *p ) {
1913                    case '\\':
1914                        // Count the backslash.
1915                        bSlashCount = 1;
1916                        break;
1917                    case '"':
1918                        // Remember we're inside quotes.
1919                        quoted = 1;
1920                        break;
1921                    default:
1922                        // Add character to arg.
1923                        arg += *p;
1924                        break;
1925                }
1926            } else {
1927                // Another space between args, ignore it.
1928            }
1929        } else {
1930            // We are processing the contents of an argument.
1931            // Check for whitespace or end.
1932            if ( *p == 0 || ( !quoted && isspace( *p ) ) ) {
1933                // Process pending backslashes (interpret them
1934                // literally since they're not followed by a ").
1935                while( bSlashCount ) {
1936                    arg += '\\';
1937                    bSlashCount--;
1938                }
1939                // End current arg.
1940                if ( !justCounting ) {
1941                    argv[argc] = new char[ arg.Length() + 1 ];
1942                    strcpy( argv[argc], arg.get() );
1943                }
1944                argc++;
1945                // We're now between args.
1946                between = 1;
1947            } else {
1948                // Still inside argument, process the character.
1949                switch ( *p ) {
1950                    case '"':
1951                        // First, digest preceding backslashes (if any).
1952                        while ( bSlashCount > 1 ) {
1953                            // Put one backsplash in arg for each pair.
1954                            arg += '\\';
1955                            bSlashCount -= 2;
1956                        }
1957                        if ( bSlashCount ) {
1958                            // Quote is literal.
1959                            arg += '"';
1960                            bSlashCount = 0;
1961                        } else {
1962                            // Quote starts or ends a quoted section.
1963                            if ( quoted ) {
1964                                // Check for special case of consecutive double
1965                                // quotes inside a quoted section.
1966                                if ( *(p+1) == '"' ) {
1967                                    // This implies a literal double-quote.  Fake that
1968                                    // out by causing next double-quote to look as
1969                                    // if it was preceded by a backslash.
1970                                    bSlashCount = 1;
1971                                } else {
1972                                    quoted = 0;
1973                                }
1974                            } else {
1975                                quoted = 1;
1976                            }
1977                        }
1978                        break;
1979                    case '\\':
1980                        // Add to count.
1981                        bSlashCount++;
1982                        break;
1983                    default:
1984                        // Accept any preceding backslashes literally.
1985                        while ( bSlashCount ) {
1986                            arg += '\\';
1987                            bSlashCount--;
1988                        }
1989                        // Just add next char to the current arg.
1990                        arg += *p;
1991                        break;
1992                }
1993            }
1994        }
1995        // Check for end of input.
1996        if ( *p ) {
1997            // Go to next character.
1998            p++;
1999        } else {
2000            // If on first pass, go on to second.
2001            if ( justCounting ) {
2002                // Allocate argv array.
2003                argv = new char*[ argc ];
2004
2005                // Start second pass
2006                justCounting = 0;
2007                init = 1;
2008            } else {
2009                // Quit.
2010                break;
2011            }
2012        }
2013    }
2014
2015    // OK, now create nsICmdLineService object from argc/argv.
2016    static NS_DEFINE_CID( kCmdLineServiceCID,    NS_COMMANDLINE_SERVICE_CID );
2017
2018    nsCOMPtr<nsIComponentManager> compMgr;
2019    NS_GetComponentManager(getter_AddRefs(compMgr));
2020    rv = compMgr->CreateInstance( kCmdLineServiceCID,
2021                                  0,
2022                                  NS_GET_IID( nsICmdLineService ),
2023                                  (void**)aResult );
2024
2025    if ( NS_FAILED( rv ) || NS_FAILED( ( rv = (*aResult)->Initialize( argc, argv ) ) ) ) {
2026#if MOZ_DEBUG_DDE
2027        printf( "Error creating command line service = 0x%08X (argc=%d, argv=0x%08X)\n", (int)rv, (int)argc, (void*)argv );
2028#endif
2029    }
2030
2031    // Cleanup.
2032    while ( argc ) {
2033        delete [] argv[ --argc ];
2034    }
2035    delete [] argv;
2036
2037    return rv;
2038}
2039
2040// Check to see if we have a profile. We will not have a profile
2041// at this point if we were launched invisibly in -turbo mode, and
2042// the profile mgr needed to show UI (to pick from multiple profiles).
2043// At this point, we can show UI, so call DoProfileStartUp().
2044nsresult
2045nsNativeAppSupportWin::EnsureProfile(nsICmdLineService* args)
2046{
2047  static PRBool firstTime = PR_TRUE;
2048  if ( firstTime ) {
2049    firstTime = PR_FALSE;
2050    // Check pref for whether to set ddeexec subkey entries.
2051    nsCOMPtr<nsIPref> prefService( do_GetService( NS_PREF_CONTRACTID ) );
2052    PRBool supportDDEExec = PR_FALSE;
2053    if ( prefService ) {
2054        prefService->GetBoolPref( "advanced.system.supportDDEExec", &supportDDEExec );
2055    }
2056    if ( supportDDEExec && handlingHTTP() ) {
2057#if MOZ_DEBUG_DDE
2058printf( "Setting ddexec subkey entries\n" );
2059#endif
2060      // Set ddeexec default value.
2061      const char ddeexec[] = "\"%1\",,-1,0,,,,";
2062      ::RegSetValue( HKEY_CLASSES_ROOT,
2063                     "http\\shell\\open\\ddeexec",
2064                     REG_SZ,
2065                     ddeexec,
2066                     sizeof ddeexec );
2067
2068      // Set application/topic (while we're running), reset at exit.
2069      ::RegSetValue( HKEY_CLASSES_ROOT,
2070                     "http\\shell\\open\\ddeexec\\application",
2071                     REG_SZ,
2072                     mAppName,
2073                     ::strlen( mAppName ) );
2074
2075      const char topic[] = "WWW_OpenURL";
2076      ::RegSetValue( HKEY_CLASSES_ROOT,
2077                     "http\\shell\\open\\ddeexec\\topic",
2078                     REG_SZ,
2079                     topic,
2080                     sizeof topic );
2081
2082      // Remember we need to undo this.
2083      mSupportingDDEExec = PR_TRUE;
2084    }
2085  }
2086
2087  nsresult rv;
2088
2089  nsCOMPtr<nsIProfileInternal> profileMgr(do_GetService(NS_PROFILE_CONTRACTID, &rv));
2090  if (NS_FAILED(rv)) return rv;
2091  nsCOMPtr<nsIAppShellService> appShell(do_GetService("@mozilla.org/appshell/appShellService;1", &rv));
2092  if (NS_FAILED(rv)) return rv;
2093
2094  // If we have a profile, everything is fine -
2095  // unless mForceProfileStartup is TRUE. This flag is set when the
2096  // last window is closed in -turbo mode. When TRUE, it forces the
2097  // profile UI to come up at the beginning of the next -turbo session
2098  // even if we currently have a profile.
2099  PRBool haveProfile;
2100  rv = profileMgr->IsCurrentProfileAvailable(&haveProfile);
2101  if (!mForceProfileStartup && NS_SUCCEEDED(rv) && haveProfile)
2102      return NS_OK;
2103
2104  // If the profile selection is happening, fail.
2105  PRBool doingProfileStartup;
2106  rv = profileMgr->GetIsStartingUp(&doingProfileStartup);
2107  if (NS_FAILED(rv) || doingProfileStartup) return NS_ERROR_FAILURE;
2108
2109  // See if profile manager is being suppressed via -silent flag.
2110  PRBool canInteract = PR_TRUE;
2111  nsXPIDLCString arg;
2112  if (NS_SUCCEEDED(args->GetCmdLineValue("-silent", getter_Copies(arg))) && (const char*)arg) {
2113    canInteract = PR_FALSE;
2114  }
2115  rv = appShell->DoProfileStartup(args, canInteract);
2116
2117  mForceProfileStartup = PR_FALSE;
2118
2119  return rv;
2120}
2121
2122nsresult
2123nsNativeAppSupportWin::OpenWindow( const char*urlstr, const char *args ) {
2124
2125  nsresult rv = NS_ERROR_FAILURE;
2126
2127  nsCOMPtr<nsIWindowWatcher> wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID));
2128  nsCOMPtr<nsISupportsCString> sarg(do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID));
2129  if (sarg)
2130    sarg->SetData(nsDependentCString(args));
2131
2132  if (wwatch && sarg) {
2133    nsCOMPtr<nsIDOMWindow> newWindow;
2134    rv = wwatch->OpenWindow(0, urlstr, "_blank", "chrome,dialog=no,all",
2135                   sarg, getter_AddRefs(newWindow));
2136#if MOZ_DEBUG_DDE
2137  } else {
2138      printf("Get WindowWatcher (or create string) failed\n");
2139#endif
2140  }
2141  return rv;
2142}
2143
2144static char procPropertyName[] = "MozillaProcProperty";
2145
2146// Subclass procedure used to filter out WM_SETFOCUS messages while reparenting.
2147static LRESULT CALLBACK focusFilterProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) {
2148    if ( uMsg == WM_SETFOCUS ) {
2149        // Don't let Mozilla's window procedure see this.
2150        return 0;
2151    } else {
2152        // Pass on all other messages to Mozilla's window proc.
2153        HANDLE oldProc = ::GetProp( hwnd, procPropertyName );
2154        if ( oldProc ) {
2155            return ::CallWindowProc( (WNDPROC)oldProc, hwnd, uMsg, wParam, lParam );
2156        } else {
2157            // Last resort is the default window proc.
2158            return ::DefWindowProc( hwnd, uMsg, wParam, lParam );
2159        }
2160    }
2161}
2162
2163HWND hwndForDOMWindow( nsISupports *window ) {
2164    nsCOMPtr<nsIScriptGlobalObject> ppScriptGlobalObj( do_QueryInterface(window) );
2165    if ( !ppScriptGlobalObj ) {
2166        return 0;
2167    }
2168    nsCOMPtr<nsIDocShell> ppDocShell;
2169    ppScriptGlobalObj->GetDocShell( getter_AddRefs( ppDocShell ) );
2170    if ( !ppDocShell ) {
2171        return 0;
2172    }
2173    nsCOMPtr<nsIBaseWindow> ppBaseWindow( do_QueryInterface( ppDocShell ) );
2174    if ( !ppBaseWindow ) {
2175        return 0;
2176    }
2177
2178    nsCOMPtr<nsIWidget> ppWidget;
2179    ppBaseWindow->GetMainWidget( getter_AddRefs( ppWidget ) );
2180
2181    return (HWND)( ppWidget->GetNativeData( NS_NATIVE_WIDGET ) );
2182}
2183
2184nsresult
2185nsNativeAppSupportWin::ReParent( nsISupports *window, HWND newParent ) {
2186    HWND hMainFrame = hwndForDOMWindow( window );
2187    if ( !hMainFrame ) {
2188        return NS_ERROR_FAILURE;
2189    }
2190
2191    // Filter out WM_SETFOCUS messages while reparenting to
2192    // other than the desktop.
2193    //
2194    // For some reason, Windows generates one and it causes
2195    // our focus/activation code to assert.
2196    LONG oldProc = 0;
2197    if ( newParent ) {
2198        // Subclass the window.
2199        oldProc = ::SetWindowLong( hMainFrame,
2200                                   GWL_WNDPROC,
2201                                   (LONG)(WNDPROC)focusFilterProc );
2202
2203        // Store old procedure in window so it is available within
2204        // focusFilterProc.
2205        ::SetProp( hMainFrame, procPropertyName, (HANDLE)oldProc );
2206    }
2207
2208    // Reset the parent.
2209    ::SetParent( hMainFrame, newParent );
2210
2211    // Restore old procedure.
2212    if ( newParent ) {
2213        ::SetWindowLong( hMainFrame, GWL_WNDPROC, oldProc );
2214        ::RemoveProp( hMainFrame, procPropertyName );
2215    }
2216
2217    return NS_OK;
2218}
2219
2220static const char sJSStackContractID[] = "@mozilla.org/js/xpc/ContextStack;1";
2221
2222class SafeJSContext {
2223public:
2224  SafeJSContext();
2225  ~SafeJSContext();
2226
2227  nsresult   Push();
2228  JSContext *get() { return mContext; }
2229
2230protected:
2231  nsCOMPtr<nsIThreadJSContextStack>  mService;
2232  JSContext                         *mContext;
2233};
2234
2235SafeJSContext::SafeJSContext() : mContext(nsnull) {
2236}
2237
2238SafeJSContext::~SafeJSContext() {
2239  JSContext *cx;
2240  nsresult   rv;
2241
2242  if(mContext) {
2243    rv = mService->Pop(&cx);
2244    NS_ASSERTION(NS_SUCCEEDED(rv) && cx == mContext, "JSContext push/pop mismatch");
2245  }
2246}
2247
2248nsresult SafeJSContext::Push() {
2249  nsresult rv;
2250
2251  if (mContext) // only once
2252    return NS_ERROR_FAILURE;
2253
2254  mService = do_GetService(sJSStackContractID);
2255  if(mService) {
2256    rv = mService->GetSafeJSContext(&mContext);
2257    if (NS_SUCCEEDED(rv) && mContext) {
2258      rv = mService->Push(mContext);
2259      if (NS_FAILED(rv))
2260        mContext = 0;
2261    }
2262  }
2263  return mContext ? NS_OK : NS_ERROR_FAILURE;
2264}
2265
2266
2267nsresult
2268nsNativeAppSupportWin::OpenBrowserWindow( const char *args, PRBool newWindow ) {
2269    nsresult rv = NS_OK;
2270    // Open the argument URL in the most recently used Navigator window.
2271    // If there is no Nav window, open a new one.
2272
2273    // Get most recently used Nav window.
2274    nsCOMPtr<nsIDOMWindowInternal> navWin;
2275    GetMostRecentWindow( NS_LITERAL_STRING( "navigator:browser" ).get(), getter_AddRefs( navWin ) );
2276
2277    // This isn't really a loop.  We just use "break" statements to fall
2278    // out to the OpenWindow call when things go awry.
2279    do {
2280        // If caller requires a new window, then don't use an existing one.
2281        if ( newWindow ) {
2282            break;
2283        }
2284        if ( !navWin ) {
2285            // Have to open a new one.
2286            break;
2287        }
2288        // Get content window.
2289        nsCOMPtr<nsIDOMWindow> content;
2290        navWin->GetContent( getter_AddRefs( content ) );
2291        if ( !content ) {
2292            break;
2293        }
2294        // Convert that to internal interface.
2295        nsCOMPtr<nsIDOMWindowInternal> internalContent( do_QueryInterface( content ) );
2296        if ( !internalContent ) {
2297            break;
2298        }
2299        // Get location.
2300        nsCOMPtr<nsIDOMLocation> location;
2301        internalContent->GetLocation( getter_AddRefs( location ) );
2302        if ( !location ) {
2303            break;
2304        }
2305        // Set up environment.
2306        SafeJSContext context;
2307        if ( NS_FAILED( context.Push() ) ) {
2308            break;
2309        }
2310        // Set href.
2311        nsAutoString url; url.AssignWithConversion( args );
2312        if ( NS_FAILED( location->SetHref( url ) ) ) {
2313            break;
2314        }
2315        // Finally, if we get here, we're done.
2316        return NS_OK;
2317    } while ( PR_FALSE );
2318
2319    nsCOMPtr<nsICmdLineHandler> handler(do_GetService("@mozilla.org/commandlinehandler/general-startup;1?type=browser", &rv));
2320    if (NS_FAILED(rv)) return rv;
2321
2322    nsXPIDLCString chromeUrlForTask;
2323    rv = handler->GetChromeUrlForTask(getter_Copies(chromeUrlForTask));
2324    if (NS_FAILED(rv)) return rv;
2325
2326    // Last resort is to open a brand new window.
2327    return OpenWindow( chromeUrlForTask, args );
2328}
2329
2330void AppendMenuItem( HMENU& menu, PRInt32 aIdentifier, const nsString& aText ) {
2331  char* ACPText = GetACPString( aText );
2332  if ( ACPText ) {
2333    ::AppendMenu( menu, MF_STRING, aIdentifier, ACPText );
2334    delete [] ACPText;
2335  }
2336}
2337
2338// Utility function that sets up system tray icon.
2339
2340void
2341nsNativeAppSupportWin::SetupSysTrayIcon() {
2342    // Messages go to the hidden window.
2343    mIconData.hWnd  = (HWND)MessageWindow();
2344
2345    // Icon is our default application icon.
2346    mIconData.hIcon =  (HICON)::LoadImage( ::GetModuleHandle( NULL ),
2347                                           IDI_APPLICATION,
2348                                           IMAGE_ICON,
2349                                           ::GetSystemMetrics( SM_CXSMICON ),
2350                                           ::GetSystemMetrics( SM_CYSMICON ),
2351                                           NULL );
2352
2353    // Tooltip is the brand short name.
2354    mIconData.szTip[0] = 0;
2355    nsCOMPtr<nsIStringBundleService> svc( do_GetService( NS_STRINGBUNDLE_CONTRACTID ) );
2356    if ( svc ) {
2357        nsCOMPtr<nsIStringBundle> brandBundle;
2358        nsXPIDLString tooltip;
2359        svc->CreateBundle( "chrome://global/locale/brand.properties", getter_AddRefs( brandBundle ) );
2360        if ( brandBundle ) {
2361            brandBundle->GetStringFromName( NS_LITERAL_STRING( "brandShortName" ).get(),
2362                                            getter_Copies( tooltip ) );
2363            ::strncpy( mIconData.szTip,
2364                       NS_LossyConvertUCS2toASCII(tooltip).get(),
2365                       sizeof mIconData.szTip - 1 );
2366        }
2367        // Build menu.
2368        nsCOMPtr<nsIStringBundle> turboBundle;
2369        nsCOMPtr<nsIStringBundle> mailBundle;
2370        svc->CreateBundle( "chrome://navigator/locale/turboMenu.properties",
2371                           getter_AddRefs( turboBundle ) );
2372        nsresult rv = svc->CreateBundle( "chrome://messenger/locale/mailTurboMenu.properties",
2373                                         getter_AddRefs( mailBundle ) );
2374        PRBool isMail = NS_SUCCEEDED(rv) && mailBundle;
2375        nsAutoString exitText;
2376        nsAutoString disableText;
2377        nsAutoString navigatorText;
2378        nsAutoString editorText;
2379        nsAutoString mailText;
2380        nsAutoString addressbookText;
2381        nsXPIDLString text;
2382        if ( turboBundle ) {
2383            if ( brandBundle ) {
2384                const PRUnichar* formatStrings[] = { tooltip.get() };
2385                turboBundle->FormatStringFromName( NS_LITERAL_STRING( "Exit" ).get(), formatStrings, 1,
2386                                                   getter_Copies( text ) );
2387                exitText = (const PRUnichar*)text;
2388            }
2389            turboBundle->GetStringFromName( NS_LITERAL_STRING( "Disable" ).get(),
2390                                            getter_Copies( text ) );
2391            disableText = (const PRUnichar*)text;
2392            turboBundle->GetStringFromName( NS_LITERAL_STRING( "Navigator" ).get(),
2393                                            getter_Copies( text ) );
2394            navigatorText = (const PRUnichar*)text;
2395            turboBundle->GetStringFromName( NS_LITERAL_STRING( "Editor" ).get(),
2396                                            getter_Copies( text ) );
2397            editorText = (const PRUnichar*)text;
2398        }
2399        if (isMail) {
2400            mailBundle->GetStringFromName( NS_LITERAL_STRING( "MailNews" ).get(),
2401                                           getter_Copies( text ) );
2402            mailText = (const PRUnichar*)text;
2403            mailBundle->GetStringFromName( NS_LITERAL_STRING( "Addressbook" ).get(),
2404                                           getter_Copies( text ) );
2405            addressbookText = (const PRUnichar*)text;
2406        }
2407
2408        if ( exitText.IsEmpty() )
2409            exitText = NS_LITERAL_STRING( "E&xit Mozilla" );
2410
2411        if ( disableText.IsEmpty() )
2412            disableText = NS_LITERAL_STRING( "&Disable Quick Launch" );
2413
2414        if ( navigatorText.IsEmpty() )
2415            navigatorText = NS_LITERAL_STRING( "&Navigator" );
2416
2417        if ( editorText.IsEmpty() )
2418            editorText = NS_LITERAL_STRING( "&Composer" );
2419
2420        if ( isMail ) {
2421            if ( mailText.IsEmpty() )
2422              mailText = NS_LITERAL_STRING( "&Mail && Newsgroups" );
2423            if ( addressbookText.IsEmpty() )
2424              addressbookText = NS_LITERAL_STRING( "&Address Book" );
2425        }
2426        // Create menu and add item.
2427        mTrayIconMenu = ::CreatePopupMenu();
2428        ::AppendMenuW( mTrayIconMenu, MF_STRING, TURBO_NAVIGATOR, navigatorText.get() );
2429        if ( ::GetLastError() == ERROR_CALL_NOT_IMPLEMENTED ) {
2430            AppendMenuItem( mTrayIconMenu, TURBO_NAVIGATOR, navigatorText );
2431            if ( isMail )
2432                AppendMenuItem( mTrayIconMenu, TURBO_MAIL, mailText );
2433            AppendMenuItem( mTrayIconMenu, TURBO_EDITOR, editorText );
2434            if ( isMail )
2435                AppendMenuItem( mTrayIconMenu, TURBO_ADDRESSBOOK, addressbookText );
2436            ::AppendMenu( mTrayIconMenu, MF_SEPARATOR, NULL, NULL );
2437            AppendMenuItem( mTrayIconMenu, TURBO_DISABLE, disableText );
2438            AppendMenuItem( mTrayIconMenu, TURBO_EXIT, exitText );
2439        }
2440        else {
2441            if (isMail)
2442                ::AppendMenuW( mTrayIconMenu, MF_STRING, TURBO_MAIL, mailText.get() );
2443            ::AppendMenuW( mTrayIconMenu, MF_STRING, TURBO_EDITOR, editorText.get() );
2444            if (isMail)
2445                ::AppendMenuW( mTrayIconMenu, MF_STRING, TURBO_ADDRESSBOOK, addressbookText.get() );
2446            ::AppendMenuW( mTrayIconMenu, MF_SEPARATOR, NULL, NULL );
2447            ::AppendMenuW( mTrayIconMenu, MF_STRING, TURBO_DISABLE, disableText.get() );
2448            ::AppendMenuW( mTrayIconMenu, MF_STRING, TURBO_EXIT, exitText.get() );
2449        }
2450    }
2451
2452    // Add the tray icon.
2453
2454    /* The tray icon will be removed if explorer restarts. Therefore, we are registering
2455    the following window message so we know when the taskbar is created. Explorer will send
2456    this message when explorer restarts.*/
2457    mTrayRestart = ::RegisterWindowMessage(TEXT("TaskbarCreated"));
2458    ::Shell_NotifyIcon( NIM_ADD, &mIconData );
2459}
2460
2461// Utility function to remove the system tray icon.
2462void
2463nsNativeAppSupportWin::RemoveSysTrayIcon() {
2464    // Remove the tray icon.
2465    mTrayRestart = 0;
2466    ::Shell_NotifyIcon( NIM_DELETE, &mIconData );
2467    // Delete the menu.
2468    ::DestroyMenu( mTrayIconMenu );
2469}
2470
2471
2472
2473//   This opens a special browser window for purposes of priming the pump for
2474//   server mode (getting stuff into the caching, loading .dlls, etc.).  The
2475//   window will have these attributes:
2476//     - Load about:blank (no home page)
2477//     - No toolbar (so there's no sidebar panels loaded, either)
2478//     - Pass magic arg to cause window to close in onload handler.
2479NS_IMETHODIMP
2480nsNativeAppSupportWin::StartServerMode() {
2481
2482    // Turn on system tray icon.
2483    SetupSysTrayIcon();
2484
2485    if (mShouldShowUI) {
2486        // We dont have to anything anymore. The native UI
2487        // will create the window
2488        return NS_OK;
2489    } else {
2490        // Sometimes a window will have been opened even though mShouldShowUI is false
2491        // (e.g., mozilla -mail -turbo).  Detect that by testing whether there's a
2492        // window already open.
2493        nsCOMPtr<nsIDOMWindowInternal> win;
2494        GetMostRecentWindow( 0, getter_AddRefs( win ) );
2495        if ( win ) {
2496            // Window already opened, don't need this special Nav window.
2497            return NS_OK;
2498        }
2499    }
2500
2501    // Since native UI wont create any window, we create a hidden window
2502    // so thing work alright.
2503
2504    // Create some of the objects we'll need.
2505    nsCOMPtr<nsIWindowWatcher>   ww(do_GetService(NS_WINDOWWATCHER_CONTRACTID));
2506    nsCOMPtr<nsISupportsString> arg1(do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID));
2507    nsCOMPtr<nsISupportsString> arg2(do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID));
2508    if ( !ww || !arg1 || !arg2 ) {
2509        return NS_OK;
2510    }
2511
2512    // Create the array for the arguments.
2513    nsCOMPtr<nsISupportsArray> argArray = do_CreateInstance(NS_SUPPORTSARRAY_CONTRACTID);
2514    if ( !argArray ) {
2515        return NS_OK;
2516    }
2517
2518    // arg1 is the url to load.
2519    // arg2 is the string that tells navigator.js to auto-close.
2520    arg1->SetData( NS_LITERAL_STRING( "about:blank" ) );
2521    arg2->SetData( NS_LITERAL_STRING( "turbo=yes" ) );
2522
2523    // Put args into array.
2524    if ( NS_FAILED( argArray->AppendElement( arg1 ) ) ||
2525        NS_FAILED( argArray->AppendElement( arg2 ) ) ) {
2526        return NS_OK;
2527    }
2528
2529    // Now open the window.
2530    nsCOMPtr<nsIDOMWindow> newWindow;
2531    ww->OpenWindow( 0,
2532        "chrome://navigator/content",
2533        "_blank",
2534        "chrome,dialog=no,toolbar=no",
2535        argArray,
2536        getter_AddRefs( newWindow ) );
2537
2538    if ( !newWindow ) {
2539        return NS_OK;
2540    }
2541    mInitialWindowActive = PR_TRUE;
2542
2543    // Hide this window by re-parenting it (to ensure it doesn't appear).
2544    ReParent( newWindow, (HWND)MessageWindow() );
2545
2546    return NS_OK;
2547}
2548
2549NS_IMETHODIMP
2550nsNativeAppSupportWin::SetIsServerMode( PRBool isServerMode ) {
2551    // If it is being turned off, remove systray icon.
2552    if ( mServerMode && !isServerMode ) {
2553        RemoveSysTrayIcon();
2554    }
2555    else if ( !mServerMode && isServerMode) {
2556        SetupSysTrayIcon();
2557    }
2558    return nsNativeAppSupportBase::SetIsServerMode( isServerMode );
2559}
2560
2561NS_IMETHODIMP
2562nsNativeAppSupportWin::OnLastWindowClosing() {
2563
2564    if ( !mServerMode )
2565        return NS_OK;
2566
2567    // If the last window closed is our special "turbo" window made
2568    // in StartServerMode(), don't do anything.
2569    if ( mInitialWindowActive ) {
2570        mInitialWindowActive = PR_FALSE;
2571        return NS_OK;
2572    }
2573
2574    // If the last window closed is our confirmation dialog,
2575    // don't do anything.
2576    if ( mLastWindowIsConfirmation ) {
2577        mLastWindowIsConfirmation = PR_FALSE;
2578        return NS_OK;
2579    }
2580
2581
2582    nsresult rv;
2583
2584    // If activated by the browser.turbo.singleProfileOnly pref,
2585    // check for multi-profile situation and turn off turbo mode
2586    // if there are multiple profiles.
2587    PRBool singleProfileOnly = PR_FALSE;
2588    nsCOMPtr<nsIPref> prefService( do_GetService( NS_PREF_CONTRACTID, &rv ) );
2589    if ( NS_SUCCEEDED( rv ) ) {
2590        prefService->GetBoolPref( "browser.turbo.singleProfileOnly", &singleProfileOnly );
2591    }
2592    if ( singleProfileOnly ) {
2593        nsCOMPtr<nsIProfile> profileMgr( do_GetService( NS_PROFILE_CONTRACTID, &rv ) );
2594        if ( NS_SUCCEEDED( rv ) ) {
2595            PRInt32 profileCount = 0;
2596            if ( NS_SUCCEEDED( profileMgr->GetProfileCount( &profileCount ) ) &&
2597                 profileCount > 1 ) {
2598                // Turn off turbo mode and quit the application.
2599                SetIsServerMode( PR_FALSE );
2600                nsCOMPtr<nsIAppShellService> appShell =
2601                    do_GetService( "@mozilla.org/appshell/appShellService;1", &rv);
2602                if ( NS_SUCCEEDED( rv ) ) {
2603                    appShell->Quit(nsIAppShellService::eAttemptQuit);
2604                }
2605                return NS_OK;
2606            }
2607        }
2608    }
2609
2610    if ( !mShownTurboDialog ) {
2611        PRBool showDialog = PR_TRUE;
2612        if ( NS_SUCCEEDED( rv ) )
2613            prefService->GetBoolPref( "browser.turbo.showDialog", &showDialog );
2614
2615        if ( showDialog ) {
2616          /* show turbo dialog, unparented. at this point in the application
2617             shutdown process the last window is largely torn down and
2618             unsuitable for parenthood.
2619          */
2620          nsCOMPtr<nsIWindowWatcher> wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID));
2621          if ( wwatch ) {
2622              nsCOMPtr<nsIDOMWindow> newWindow;
2623              mShownTurboDialog = PR_TRUE;
2624              mLastWindowIsConfirmation = PR_TRUE;
2625              rv = wwatch->OpenWindow(0, "chrome://navigator/content/turboDialog.xul",
2626                                      "_blank", "chrome,modal,titlebar,centerscreen,dialog",
2627                                      0, getter_AddRefs(newWindow));
2628          }
2629        }
2630    }
2631
2632    nsCOMPtr<nsIAppShellService> appShell =
2633        do_GetService( "@mozilla.org/appshell/appShellService;1", &rv);
2634    if ( NS_SUCCEEDED( rv ) ) {
2635        // Instead of staying alive, launch a new instance of the application and then
2636        // terminate for real.  We take steps to ensure that the new instance will run
2637        // as a "server process" and not try to pawn off its request back on this
2638        // instance.
2639
2640        // Grab mutex.  Process termination will release it.
2641        Mutex mutexLock = Mutex(mMutexName);
2642        NS_ENSURE_TRUE(mutexLock.Lock(MOZ_DDE_START_TIMEOUT), NS_ERROR_FAILURE );
2643
2644        // Turn off MessageWindow so the other process can't see us.
2645        MessageWindow mw;
2646        mw.Destroy();
2647   
2648        // Launch another instance.
2649        char buffer[ _MAX_PATH ];
2650        // Same application as this one.
2651        ::GetModuleFileName( 0, buffer, sizeof buffer );
2652        // Clean up name so we don't have to worry about enclosing it in quotes.
2653        ::GetShortPathName( buffer, buffer, sizeof buffer );
2654        nsCAutoString cmdLine( buffer );
2655        // The new process must run in turbo mode (no splash screen, no window, etc.).
2656        cmdLine.Append( " -turbo" );
2657   
2658        // Now do the Win32 stuff...
2659        STARTUPINFO startupInfo;
2660        ::GetStartupInfo( &startupInfo );
2661        PROCESS_INFORMATION processInfo;
2662        DWORD rc = ::CreateProcess( 0,
2663                              (LPTSTR)cmdLine.get(),
2664                              0,
2665                              0,
2666                              0,
2667                              0,
2668                              0,
2669                              0,
2670                              &startupInfo,
2671                              &processInfo );
2672   
2673        // Turn off turbo mode and quit the application.
2674        SetIsServerMode( PR_FALSE );
2675        appShell->Quit(nsIAppShellService::eAttemptQuit);
2676
2677        // Done.  This app will now commence shutdown.
2678    }
2679    return NS_OK;
2680}
Note: See TracBrowser for help on using the repository browser.