Back to index

lightning-sunbird  0.9+nobinonly
nsNativeAppSupportWin.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is Mozilla Communicator client code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Bill Law <law@netscape.com>
00024  *   Masayuki Nakano <masayuki@d-toybox.com>
00025  *
00026  * Alternatively, the contents of this file may be used under the terms of
00027  * either of the GNU General Public License Version 2 or later (the "GPL"),
00028  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00029  * in which case the provisions of the GPL or the LGPL are applicable instead
00030  * of those above. If you wish to allow use of your version of this file only
00031  * under the terms of either the GPL or the LGPL, and not to allow others to
00032  * use your version of this file under the terms of the MPL, indicate your
00033  * decision by deleting the provisions above and replace them with the notice
00034  * and other provisions required by the GPL or the LGPL. If you do not delete
00035  * the provisions above, a recipient may use your version of this file under
00036  * the terms of any one of the MPL, the GPL or the LGPL.
00037  *
00038  * ***** END LICENSE BLOCK ***** */
00039 
00040 #include "nsStringSupport.h"
00041 
00042 // For server mode systray icon.
00043 #include "nsIStringBundle.h"
00044 
00045 #include "nsNativeAppSupportBase.h"
00046 #include "nsNativeAppSupportWin.h"
00047 #include "nsICmdLineService.h"
00048 #include "nsCOMPtr.h"
00049 #include "nsIComponentManager.h"
00050 #include "nsComponentManagerUtils.h"
00051 #include "nsIServiceManager.h"
00052 #include "nsServiceManagerUtils.h"
00053 #include "nsICmdLineHandler.h"
00054 #include "nsIDOMWindow.h"
00055 #include "nsXPCOM.h"
00056 #include "nsISupportsPrimitives.h"
00057 #include "nsISupportsArray.h"
00058 #include "nsIWindowWatcher.h"
00059 #include "nsIDOMWindowInternal.h"
00060 #include "nsIDOMChromeWindow.h"
00061 #include "nsIBrowserDOMWindow.h"
00062 #include "nsIScriptGlobalObject.h"
00063 #include "nsIDocShell.h"
00064 #include "nsIBaseWindow.h"
00065 #include "nsIWidget.h"
00066 #include "nsIAppStartup.h"
00067 #include "nsIProfileInternal.h"
00068 #include "nsIXULWindow.h"
00069 #include "nsIInterfaceRequestor.h"
00070 #include "nsIInterfaceRequestorUtils.h"
00071 #include "nsIPrefService.h"
00072 #include "nsIPrefBranch.h"
00073 #include "nsIWindowsHooks.h"
00074 #include "nsIPromptService.h"
00075 #include "nsNetCID.h"
00076 #include "nsIIOService.h"
00077 #include "nsIURI.h"
00078 #include "nsIObserverService.h"
00079 #include "nsXPCOM.h"
00080 #include "nsXPFEComponentsCID.h"
00081 #include "nsEmbedCID.h"
00082 #include "nsIURIFixup.h"
00083 #include "nsCDefaultURIFixup.h"
00084 
00085 struct JSContext;
00086 
00087 // These are needed to load a URL in a browser window.
00088 #include "nsIDOMLocation.h"
00089 #include "nsIJSContextStack.h"
00090 #include "nsIWindowMediator.h"
00091 
00092 #include <windows.h>
00093 #include <shellapi.h>
00094 #include <ddeml.h>
00095 #include <stdlib.h>
00096 #include <stdio.h>
00097 #include <io.h>
00098 #include <fcntl.h>
00099 #include <ctype.h>
00100 
00101 #define TURBO_NAVIGATOR 1
00102 #define TURBO_MAIL 2
00103 #define TURBO_EDITOR 3
00104 #define TURBO_ADDRESSBOOK 4
00105 #define TURBO_DISABLE 5
00106 #define TURBO_EXIT 6
00107 
00108 #define MAPI_STARTUP_ARG       "/MAPIStartUp"
00109 
00110 #ifndef LWA_ALPHA
00111 #define LWA_ALPHA 2
00112 #endif
00113 
00114 #ifndef WS_EX_LAYERED
00115 #define WS_EX_LAYERED 0x80000
00116 #endif
00117 
00118 #ifndef SM_REMOTESESSION
00119 #define SM_REMOTESESSION 0x1000
00120 #endif
00121 
00122 #define REG_SUCCEEDED(val) (val == ERROR_SUCCESS)
00123 
00124 static HWND hwndForDOMWindow( nsISupports * );
00125 
00126 static
00127 nsresult
00128 GetMostRecentWindow(const PRUnichar* aType, nsIDOMWindowInternal** aWindow) {
00129     nsresult rv;
00130     nsCOMPtr<nsIWindowMediator> med( do_GetService( NS_WINDOWMEDIATOR_CONTRACTID, &rv ) );
00131     if ( NS_FAILED( rv ) )
00132         return rv;
00133 
00134     if ( med )
00135         return med->GetMostRecentWindow( aType, aWindow );
00136 
00137     return NS_ERROR_FAILURE;
00138 }
00139 
00140 static char* GetACPString(const nsString& aStr)
00141 {
00142     int acplen = aStr.Length() * 2 + 1;
00143     char * acp = new char[ acplen ];
00144     if( acp ) {
00145         int outlen = ::WideCharToMultiByte( CP_ACP, 0, aStr.get(), aStr.Length(),
00146                                             acp, acplen-1, NULL, NULL );
00147         acp[ outlen ] = '\0';  // null terminate
00148     }
00149     return acp;
00150 }
00151 
00152 static
00153 void
00154 activateWindow( nsIDOMWindowInternal *win ) {
00155     // Try to get native window handle.
00156     HWND hwnd = hwndForDOMWindow( win );
00157     if ( hwnd ) {
00158         // Restore the window if it is minimized.
00159         if ( ::IsIconic( hwnd ) ) {
00160             ::ShowWindow( hwnd, SW_RESTORE );
00161         }
00162         // Use the OS call, if possible.
00163         ::SetForegroundWindow( hwnd );
00164     } else {
00165         // Use internal method.
00166         win->Focus();
00167     }
00168 }
00169 
00170 
00171 #ifdef DEBUG_law
00172 #undef MOZ_DEBUG_DDE
00173 #define MOZ_DEBUG_DDE 1
00174 #endif
00175 
00176 typedef BOOL (WINAPI *MOZ_SetLayeredWindowAttributesProc)(HWND, COLORREF, BYTE, DWORD);
00177 
00178 class nsSplashScreenWin : public nsISplashScreen {
00179 public:
00180     nsSplashScreenWin();
00181     ~nsSplashScreenWin();
00182 
00183     NS_IMETHOD Show();
00184     NS_IMETHOD Hide();
00185 
00186     // nsISupports methods
00187     NS_IMETHOD_(nsrefcnt) AddRef() {
00188         mRefCnt++;
00189         return mRefCnt;
00190     }
00191     NS_IMETHOD_(nsrefcnt) Release() {
00192         --mRefCnt;
00193         if ( !mRefCnt ) {
00194             delete this;
00195             return 0;
00196         }
00197         return mRefCnt;
00198     }
00199     NS_IMETHOD QueryInterface( const nsIID &iid, void**p ) {
00200         nsresult rv = NS_OK;
00201         if ( p ) {
00202             *p = 0;
00203             if ( iid.Equals( NS_GET_IID( nsISplashScreen ) ) ) {
00204                 nsISplashScreen *result = this;
00205                 *p = result;
00206                 NS_ADDREF( result );
00207             } else if ( iid.Equals( NS_GET_IID( nsISupports ) ) ) {
00208                 nsISupports *result = NS_STATIC_CAST( nsISupports*, this );
00209                 *p = result;
00210                 NS_ADDREF( result );
00211             } else {
00212                 rv = NS_NOINTERFACE;
00213             }
00214         } else {
00215             rv = NS_ERROR_NULL_POINTER;
00216         }
00217         return rv;
00218     }
00219 
00220     void SetDialog( HWND dlg );
00221     void LoadBitmap();
00222     static nsSplashScreenWin* GetPointer( HWND dlg );
00223 
00224     static BOOL CALLBACK DialogProc( HWND dlg, UINT msg, WPARAM wp, LPARAM lp );
00225     static DWORD WINAPI ThreadProc( LPVOID );
00226     static VOID CALLBACK TimerProc(HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime);
00227 
00228     HWND mDlg;
00229     HBITMAP mBitmap;
00230     nsrefcnt mRefCnt;
00231 
00232     static int mOpacity;
00233     static MOZ_SetLayeredWindowAttributesProc mSetLayeredWindowAttributesProc;
00234 
00235 }; // class nsSplashScreenWin
00236 
00237 // Simple Win32 mutex wrapper.
00238 struct Mutex {
00239     Mutex( const char *name )
00240         : mName( name ),
00241           mHandle( 0 ),
00242           mState( -1 ) {
00243         mHandle = CreateMutex( 0, FALSE, mName.get() );
00244 #if MOZ_DEBUG_DDE
00245         printf( "CreateMutex error = 0x%08X\n", (int)GetLastError() );
00246 #endif
00247     }
00248     ~Mutex() {
00249         if ( mHandle ) {
00250             // Make sure we release it if we own it.
00251             Unlock();
00252 
00253             BOOL rc = CloseHandle( mHandle );
00254 #if MOZ_DEBUG_DDE
00255             if ( !rc ) {
00256                 printf( "CloseHandle error = 0x%08X\n", (int)GetLastError() );
00257             }
00258 #endif
00259         }
00260     }
00261     BOOL Lock( DWORD timeout ) {
00262         if ( mHandle ) {
00263 #if MOZ_DEBUG_DDE
00264             printf( "Waiting (%d msec) for DDE mutex...\n", (int)timeout );
00265 #endif
00266             mState = WaitForSingleObject( mHandle, timeout );
00267 #if MOZ_DEBUG_DDE
00268             printf( "...wait complete, result = 0x%08X, GetLastError=0x%08X\n", (int)mState, (int)::GetLastError() );
00269 #endif
00270             return mState == WAIT_OBJECT_0 || mState == WAIT_ABANDONED;
00271         } else {
00272             return FALSE;
00273         }
00274     }
00275     void Unlock() {
00276         if ( mHandle && mState == WAIT_OBJECT_0 ) {
00277 #if MOZ_DEBUG_DDE
00278             printf( "Releasing DDE mutex\n" );
00279 #endif
00280             ReleaseMutex( mHandle );
00281             mState = -1;
00282         }
00283     }
00284 private:
00285     nsCString mName;
00286     HANDLE    mHandle;
00287     DWORD     mState;
00288 };
00289 
00290 /* DDE Notes
00291  *
00292  * This section describes the Win32 DDE service implementation for
00293  * SeaMonkey.  DDE is used on Win32 platforms to communicate between
00294  * separate instances of seamonkey.exe (or other Mozilla-based
00295  * executables), or, between the Win32 desktop shell and SeaMonkey.
00296  *
00297  * The first instance of SeaMonkey will become the "server" and
00298  * subsequent executables (and the shell) will use DDE to send
00299  * requests to that process.  The requests are DDE "execute" requests
00300  * that pass the command line arguments.
00301  *
00302  * SeaMonkey registers the DDE application "SeaMonkey" and currently
00303  * supports only the "WWW_OpenURL" topic.  This should be reasonably
00304  * compatible with applications that interfaced with Netscape
00305  * Communicator (and its predecessors?).  Note that even that topic
00306  * may not be supported in a compatible fashion as the command-line
00307  * options for SeaMonkey are different than for Communiator.
00308  *
00309  * Note: The DDE application name is set via splash.rc
00310  *
00311  * It is imperative that at most one instance of SeaMonkey execute in
00312  * "server mode" at any one time.  The "native app support" in SeaMonkey
00313  * on Win32 ensures that only the server process performs XPCOM
00314  * initialization (that is not required for subsequent client processes
00315  * to communicate with the server process).
00316  *
00317  * To guarantee that only one server starts up, a Win32 "mutex" is used
00318  * to ensure only one process executes the server-detection code.  That
00319  * code consists of initializing DDE and doing a DdeConnect to SeaMonkey's
00320  * application/topic.  If that connection succeeds, then a server process
00321  * must be running already.
00322  *
00323  * Otherwise, no server has started.  In that case, the current process
00324  * calls DdeNameService to register that application/topic.  Only at that
00325  * point does the mutex get released.
00326  *
00327  * There are a couple of subtleties that one should be aware of:
00328  *
00329  * 1. It is imperative that DdeInitialize be called only after the mutex
00330  *    lock has been obtained.  The reason is that at shutdown, DDE
00331  *    notifications go out to all initialized DDE processes.  Thus, if
00332  *    the mutex is owned by a terminating intance of SeaMonkey, then
00333  *    calling DdeInitialize and then WaitForSingleObject will cause the
00334  *    DdeUninitialize from the terminating process to "hang" until the
00335  *    process waiting for the mutex times out (and can then service the
00336  *    notification that the DDE server is terminating).  So, don't mess
00337  *    with the sequence of things in the startup/shutdown logic.
00338  *
00339  * 2. All mutex requests are made with a reasonably long timeout value and
00340  *    are designed to "fail safe" (i.e., a timeout is treated as failure).
00341  *
00342  * 3. An attempt has been made to minimize the degree to which the main
00343  *    SeaMonkey application logic needs to be aware of the DDE mechanisms
00344  *    implemented herein.  As a result, this module surfaces a very
00345  *    large-grained interface, consisting of simple start/stop methods.
00346  *    As a consequence, details of certain scenarios can be "lost."
00347  *    Particularly, incoming DDE requests can arrive after this module
00348  *    initiates the DDE server, but before SeaMonkey is initialized to the
00349  *    point where those requests can be serviced (e.g., open a browser
00350  *    window to a particular URL).  Since the client process sends the
00351  *    request early on, it may not be prepared to respond to that error.
00352  *    Thus, such situations may fail silently.  The design goal is that
00353  *    they fail harmlessly.  Refinements on this point will be made as
00354  *    details emerge (and time permits).
00355  */
00356 
00357 /* Update 2001 March
00358  *
00359  * A significant DDE bug in Windows is causing SeaMonkey to get wedged at
00360  * startup.  This is detailed in Bugzill bug 53952
00361  * (http://bugzilla.mozilla.org/show_bug.cgi?id=53952).
00362  *
00363  * To resolve this, we are using a new strategy:
00364  *   o Use a "message window" to detect that SeaMonkey is already running and
00365  *     to pass requests from a second instance back to the first;
00366  *   o Run only as a "DDE server" (not as DDE client); this avoids the
00367  *     problematic call to DDEConnect().
00368  *
00369  * We still use the mutex semaphore to protect the code that detects
00370  * whether SeaMonkey is already running.
00371  */
00372 
00373 class nsNativeAppSupportWin : public nsNativeAppSupportBase {
00374 public:
00375     // Overrides of base implementation.
00376     NS_IMETHOD Start( PRBool *aResult );
00377     NS_IMETHOD Stop( PRBool *aResult );
00378     NS_IMETHOD Quit();
00379     NS_IMETHOD StartServerMode();
00380     NS_IMETHOD OnLastWindowClosing();
00381     NS_IMETHOD SetIsServerMode( PRBool isServerMode );
00382     NS_IMETHOD EnsureProfile(nsICmdLineService* args);
00383 
00384     // The "old" Start method (renamed).
00385     NS_IMETHOD StartDDE();
00386 
00387     // Utility function to handle a Win32-specific command line
00388     // option: "-console", which dynamically creates a Windows
00389     // console.
00390     void CheckConsole();
00391 
00392 private:
00393     static HDDEDATA CALLBACK HandleDDENotification( UINT     uType,
00394                                                     UINT     uFmt,
00395                                                     HCONV    hconv,
00396                                                     HSZ      hsz1,
00397                                                     HSZ      hsz2,
00398                                                     HDDEDATA hdata,
00399                                                     ULONG    dwData1,
00400                                                     ULONG    dwData2 );
00401     static nsresult HandleRequest( LPBYTE request, PRBool newWindow, nsIDOMWindow **aResult );
00402     static void ParseDDEArg( HSZ args, int index, nsCString& string);
00403     static void ParseDDEArg( const char* args, int index, nsCString& aString);
00404     static void ActivateLastWindow();
00405     static HDDEDATA CreateDDEData( DWORD value );
00406     static HDDEDATA CreateDDEData( LPBYTE value, DWORD len );
00407     static PRBool   InitTopicStrings();
00408     static int      FindTopic( HSZ topic );
00409     static nsresult GetCmdLineArgs( LPBYTE request, nsICmdLineService **aResult );
00410     static nsresult OpenWindow( const char *urlstr,
00411                                 const nsAString& aArgs,
00412                                 nsIDOMWindow **aResult );
00413     static nsresult OpenBrowserWindow( const nsAString& aArgs,
00414                                        PRBool newWindow,
00415                                        nsIDOMWindow **aResult );
00416     static nsresult ReParent( nsISupports *window, HWND newParent );
00417     static nsresult GetStartupURL(nsICmdLineService *args, nsCString& taskURL);
00418     static void     SetupSysTrayIcon();
00419     static void     RemoveSysTrayIcon();
00420 
00421     static UINT mTrayRestart;
00422 
00423     static int   mConversations;
00424     enum {
00425         topicOpenURL,
00426         topicActivate,
00427         topicCancelProgress,
00428         topicVersion,
00429         topicRegisterViewer,
00430         topicUnRegisterViewer,
00431         topicGetWindowInfo,
00432         // Note: Insert new values above this line!!!!!
00433         topicCount // Count of the number of real topics
00434     };
00435     static NOTIFYICONDATA mIconData;
00436     static HMENU          mTrayIconMenu;
00437 
00438     static HSZ   mApplication, mTopics[ topicCount ];
00439     static DWORD mInstance;
00440     static char *mAppName;
00441     static PRBool mInitialWindowActive;
00442     static PRBool mForceProfileStartup;
00443     static PRBool mSupportingDDEExec;
00444     static char mMutexName[];
00445     friend struct MessageWindow;
00446 }; // nsNativeAppSupportWin
00447 
00448 nsSplashScreenWin::nsSplashScreenWin()
00449     : mDlg( 0 ), mBitmap( 0 ), mRefCnt( 0 ) {
00450 }
00451 
00452 nsSplashScreenWin::~nsSplashScreenWin() {
00453 #if MOZ_DEBUG_DDE
00454     printf( "splash screen dtor called\n" );
00455 #endif
00456     KillTimer(mDlg, 0);
00457     // Make sure dialog is gone.
00458     Hide();
00459 }
00460 
00461 NS_IMETHODIMP
00462 nsSplashScreenWin::Show() {
00463     // Spawn new thread to display real splash screen.
00464     DWORD threadID = 0;
00465     HANDLE handle = CreateThread( 0, 0, (LPTHREAD_START_ROUTINE)ThreadProc, this, 0, &threadID );
00466     CloseHandle(handle);
00467 
00468     return NS_OK;
00469 }
00470 
00471 NS_IMETHODIMP
00472 nsSplashScreenWin::Hide() {
00473     if ( mDlg ) {
00474         // Fix for bugs:
00475         //  http://bugzilla.mozilla.org/show_bug.cgi?id=26581
00476         //  http://bugzilla.mozilla.org/show_bug.cgi?id=65974
00477         //  http://bugzilla.mozilla.org/show_bug.cgi?id=29172
00478         //  http://bugzilla.mozilla.org/show_bug.cgi?id=45805
00479         // As the splash-screen is in a separate thread, Windows considers
00480         // this the "foreground" thread.  When our main windows on the main
00481         // thread are activated, they are treated like windows from a different
00482         // application, so Windows 2000 and 98 both leave the window in the background.
00483         // Therefore, we post a message to the splash-screen thread that includes
00484         // the hwnd of the window we want moved to the foreground.  This thread
00485         // can then successfully bring the top-level window to the foreground.
00486         nsCOMPtr<nsIDOMWindowInternal> topLevel;
00487         GetMostRecentWindow(nsnull, getter_AddRefs( topLevel ) );
00488         HWND hWndTopLevel = topLevel ? hwndForDOMWindow(topLevel) : 0;
00489         // Dismiss the dialog.
00490         ::PostMessage(mDlg, WM_CLOSE, (WPARAM)mBitmap, (LPARAM)hWndTopLevel);
00491         mBitmap = 0;
00492         mDlg = 0;
00493     }
00494     return NS_OK;
00495 }
00496 
00497 void
00498 nsSplashScreenWin::LoadBitmap() {
00499     // Check for '<program-name>.bmp" in same directory as executable.
00500     char fileName[ _MAX_PATH ];
00501     int fileNameLen = ::GetModuleFileName( NULL, fileName, sizeof fileName );
00502     if ( fileNameLen >= 3 ) {
00503         fileName[ fileNameLen - 3 ] = 0;
00504         strcat( fileName, "bmp" );
00505         // Try to load bitmap from that file.
00506         HBITMAP bitmap = (HBITMAP)::LoadImage( NULL,
00507                                                fileName,
00508                                                IMAGE_BITMAP,
00509                                                0,
00510                                                0,
00511                                                LR_LOADFROMFILE );
00512         if ( bitmap ) {
00513             HWND bitmapControl = GetDlgItem( mDlg, IDB_SPLASH );
00514             if ( bitmapControl ) {
00515                 HBITMAP old = (HBITMAP)SendMessage( bitmapControl,
00516                                                     STM_SETIMAGE,
00517                                                     IMAGE_BITMAP,
00518                                                     (LPARAM)bitmap );
00519                 // Remember bitmap so we can delete it later.
00520                 mBitmap = bitmap;
00521                 // Delete old bitmap.
00522                 if ( old ) {
00523                     BOOL ok = DeleteObject( old );
00524                 }
00525             } else {
00526                 // Delete bitmap since it isn't going to be used.
00527                 DeleteObject( bitmap );
00528             }
00529         }
00530     }
00531 }
00532 
00533 int nsSplashScreenWin::mOpacity = 55;
00534 MOZ_SetLayeredWindowAttributesProc nsSplashScreenWin::mSetLayeredWindowAttributesProc = 0;
00535 
00536 VOID CALLBACK
00537 nsSplashScreenWin::TimerProc(HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime) {
00538     mOpacity += 20;
00539     if (mOpacity >= 255) { // >= because we still want to kill the timer for 255
00540         mOpacity = 255;
00541         KillTimer(hwnd, 0);
00542     }
00543     // no need to null check - this code can't be reached if SetLayeredWindowAttributes isn't available
00544     mSetLayeredWindowAttributesProc(hwnd, 0, mOpacity, LWA_ALPHA);
00545 }
00546 
00547 BOOL CALLBACK
00548 nsSplashScreenWin::DialogProc( HWND dlg, UINT msg, WPARAM wp, LPARAM lp ) {
00549     if ( msg == WM_INITDIALOG ) {
00550         // Store dialog handle.
00551         nsSplashScreenWin *splashScreen = (nsSplashScreenWin*)lp;
00552         if ( lp ) {
00553             splashScreen->SetDialog( dlg );
00554 
00555             HMODULE user32lib = GetModuleHandle("user32.dll");
00556             mSetLayeredWindowAttributesProc = (MOZ_SetLayeredWindowAttributesProc) GetProcAddress(user32lib, "SetLayeredWindowAttributes");
00557 
00558             // only do fade if it's supported and the user isn't using remote desktop
00559             if (mSetLayeredWindowAttributesProc && !GetSystemMetrics(SM_REMOTESESSION)) {
00560                 SetWindowLong(dlg, GWL_EXSTYLE,
00561                                  GetWindowLong(dlg, GWL_EXSTYLE) | WS_EX_LAYERED);
00562                 mSetLayeredWindowAttributesProc(dlg, 0,
00563                                                   mOpacity, LWA_ALPHA);
00564                 SetTimer(dlg, 0, 10, TimerProc);
00565             }
00566 
00567             // Try to load customized bitmap.
00568             splashScreen->LoadBitmap();
00569         }
00570 
00571         /* Size and center the splash screen correctly. The flags in the
00572          * dialog template do not do the right thing if the user's
00573          * machine is using large fonts.
00574          */
00575         HWND bitmapControl = GetDlgItem( dlg, IDB_SPLASH );
00576         if ( bitmapControl ) {
00577             HBITMAP hbitmap = (HBITMAP)SendMessage( bitmapControl,
00578                                                     STM_GETIMAGE,
00579                                                     IMAGE_BITMAP,
00580                                                     0 );
00581             if ( hbitmap ) {
00582                 BITMAP bitmap;
00583                 if ( GetObject( hbitmap, sizeof bitmap, &bitmap ) ) {
00584                     SetWindowPos( dlg,
00585                                   NULL,
00586                                   GetSystemMetrics(SM_CXSCREEN)/2 - bitmap.bmWidth/2,
00587                                   GetSystemMetrics(SM_CYSCREEN)/2 - bitmap.bmHeight/2,
00588                                   bitmap.bmWidth,
00589                                   bitmap.bmHeight,
00590                                   SWP_NOZORDER );
00591                     ShowWindow( dlg, SW_SHOW );
00592                 }
00593             }
00594         }
00595         return 1;
00596     } else if (msg == WM_CLOSE) {
00597         // Before killing ourself, set the top-level current.
00598         // See comments in nsSplashScreenWin::Hide() above.
00599         HWND topLevel = (HWND)lp;
00600         if (topLevel)
00601             ::SetForegroundWindow(topLevel);
00602         // Destroy the dialog
00603         ::EndDialog(dlg, 0);
00604         // Release custom bitmap (if there is one).
00605         HBITMAP bitmap = (HBITMAP)wp;
00606         if ( bitmap ) {
00607             ::DeleteObject( bitmap );
00608         }
00609     }
00610     return 0;
00611 }
00612 
00613 void nsSplashScreenWin::SetDialog( HWND dlg ) {
00614     // Save dialog handle.
00615     mDlg = dlg;
00616     // Store this pointer in the dialog.
00617     SetWindowLong( mDlg, DWL_USER, (LONG)(void*)this );
00618 }
00619 
00620 nsSplashScreenWin *nsSplashScreenWin::GetPointer( HWND dlg ) {
00621     // Get result from dialog user data.
00622     LONG data = GetWindowLong( dlg, DWL_USER );
00623     return (nsSplashScreenWin*)(void*)data;
00624 }
00625 
00626 DWORD WINAPI nsSplashScreenWin::ThreadProc( LPVOID splashScreen ) {
00627     DialogBoxParam( GetModuleHandle( 0 ),
00628                     MAKEINTRESOURCE( IDD_SPLASH ),
00629                     HWND_DESKTOP,
00630                     (DLGPROC)DialogProc,
00631                     (LPARAM)splashScreen );
00632     return 0;
00633 }
00634 
00635 PRBool gAbortServer = PR_FALSE;
00636 
00637 void
00638 nsNativeAppSupportWin::CheckConsole() {
00639     for ( int i = 1; i < __argc; i++ ) {
00640         if ( strcmp( "-console", __argv[i] ) == 0
00641              ||
00642              strcmp( "/console", __argv[i] ) == 0 ) {
00643             // Users wants to make sure we have a console.
00644             // Try to allocate one.
00645             BOOL rc = ::AllocConsole();
00646             if ( rc ) {
00647                 // Console allocated.  Fix it up so that output works in
00648                 // all cases.  See http://support.microsoft.com/support/kb/articles/q105/3/05.asp.
00649 
00650                 // stdout
00651                 int hCrt = ::_open_osfhandle( (long)GetStdHandle( STD_OUTPUT_HANDLE ),
00652                                             _O_TEXT );
00653                 if ( hCrt != -1 ) {
00654                     FILE *hf = ::_fdopen( hCrt, "w" );
00655                     if ( hf ) {
00656                         *stdout = *hf;
00657 #ifdef DEBUG
00658                         ::fprintf( stdout, "stdout directed to dynamic console\n" );
00659 #endif
00660                     }
00661                 }
00662 
00663                 // stderr
00664                 hCrt = ::_open_osfhandle( (long)::GetStdHandle( STD_ERROR_HANDLE ),
00665                                           _O_TEXT );
00666                 if ( hCrt != -1 ) {
00667                     FILE *hf = ::_fdopen( hCrt, "w" );
00668                     if ( hf ) {
00669                         *stderr = *hf;
00670 #ifdef DEBUG
00671                         ::fprintf( stderr, "stderr directed to dynamic console\n" );
00672 #endif
00673                     }
00674                 }
00675 
00676                 // stdin?
00677                 /* Don't bother for now.
00678                 hCrt = ::_open_osfhandle( (long)::GetStdHandle( STD_INPUT_HANDLE ),
00679                                           _O_TEXT );
00680                 if ( hCrt != -1 ) {
00681                     FILE *hf = ::_fdopen( hCrt, "r" );
00682                     if ( hf ) {
00683                         *stdin = *hf;
00684                     }
00685                 }
00686                 */
00687             } else {
00688                 // Failed.  Probably because there already is one.
00689                 // There's little we can do, in any case.
00690             }
00691             // Don't bother doing this more than once.
00692             break;
00693         } else if ( strcmp( "-turbo", __argv[i] ) == 0
00694                     ||
00695                     strcmp( "/turbo", __argv[i] ) == 0
00696                     ||
00697                     strcmp( "-server", __argv[i] ) == 0
00698                     ||
00699                     strcmp( "/server", __argv[i] ) == 0 ) {
00700             // Start in server mode (and suppress splash screen).
00701             mServerMode = PR_TRUE;
00702             mShouldShowUI = PR_FALSE;
00703             __argv[i] = "-nosplash"; // Bit of a hack, but it works!
00704             // Ignore other args.
00705             break;
00706         }
00707     }
00708 
00709     PRBool checkTurbo = PR_TRUE;
00710     for ( int j = 1; j < __argc; j++ ) {
00711         if (strcmp("-killAll", __argv[j]) == 0 || strcmp("/killAll", __argv[j]) == 0 ||
00712             strcmp("-kill", __argv[j]) == 0 || strcmp("/kill", __argv[j]) == 0) {
00713             gAbortServer = PR_TRUE;
00714             break;
00715         }
00716 
00717         if ( strcmp( "-silent", __argv[j] ) == 0 || strcmp( "/silent", __argv[j] ) == 0 ) {
00718             checkTurbo = PR_FALSE;
00719         }
00720     }
00721 
00722     // check if this is a restart of the browser after quiting from
00723     // the servermoded browser instance.
00724     if ( checkTurbo && !mServerMode ) {
00725         HKEY key;
00726         LONG result = ::RegOpenKeyEx( HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Run", 0, KEY_QUERY_VALUE, &key );
00727         if ( result == ERROR_SUCCESS ) {
00728           BYTE regvalue[_MAX_PATH];
00729           DWORD type, len = sizeof(regvalue);
00730           result = ::RegQueryValueEx( key, NS_QUICKLAUNCH_RUN_KEY, NULL, &type, regvalue, &len);
00731           ::RegCloseKey( key );
00732           if ( result == ERROR_SUCCESS && len > 0 ) {
00733               // Make sure the filename in the quicklaunch command matches us
00734               char fileName[_MAX_PATH];
00735               int rv = ::GetModuleFileName( NULL, fileName, sizeof fileName );
00736               nsCAutoString regvalueholder;
00737               regvalueholder.Assign((char *) regvalue);
00738               if ((FindInString(regvalueholder, fileName, PR_TRUE) != kNotFound) &&
00739                   (FindInString(regvalueholder, "-turbo", PR_TRUE) != kNotFound) ) {
00740                   mServerMode = PR_TRUE;
00741                   mShouldShowUI = PR_TRUE;
00742               }
00743           }
00744         }
00745     }
00746 
00747     return;
00748 }
00749 
00750 
00751 // Create and return an instance of class nsNativeAppSupportWin.
00752 nsresult
00753 NS_CreateNativeAppSupport( nsINativeAppSupport **aResult ) {
00754     if ( aResult ) {
00755         nsNativeAppSupportWin *pNative = new nsNativeAppSupportWin;
00756         if ( pNative ) {
00757             *aResult = pNative;
00758             NS_ADDREF( *aResult );
00759             // Check for dynamic console creation request.
00760             pNative->CheckConsole();
00761         } else {
00762             return NS_ERROR_OUT_OF_MEMORY;
00763         }
00764     } else {
00765         return NS_ERROR_NULL_POINTER;
00766     }
00767 
00768     return NS_OK;
00769 }
00770 
00771 // Create instance of Windows splash screen object.
00772 nsresult
00773 NS_CreateSplashScreen( nsISplashScreen **aResult ) {
00774     if ( aResult ) {
00775         *aResult = 0;
00776         for ( int i = 1; i < __argc; i++ ) {
00777             if ( strcmp( "-quiet", __argv[i] ) == 0
00778                  ||
00779                  strcmp( "/quiet", __argv[i] ) == 0 ) {
00780                 // No splash screen, please.
00781                 return NS_OK;
00782             }
00783         }
00784         *aResult = new nsSplashScreenWin;
00785         if ( *aResult ) {
00786             NS_ADDREF( *aResult );
00787         } else {
00788             return NS_ERROR_OUT_OF_MEMORY;
00789         }
00790     } else {
00791         return NS_ERROR_NULL_POINTER;
00792     }
00793 
00794     return NS_OK;
00795 }
00796 
00797 // Constants
00798 #define MOZ_STARTUP_MUTEX_NAME "StartupMutex"
00799 #define MOZ_DDE_START_TIMEOUT 30000
00800 #define MOZ_DDE_STOP_TIMEOUT  15000
00801 #define MOZ_DDE_EXEC_TIMEOUT  15000
00802 
00803 // The array entries must match the enum ordering!
00804 const char * const topicNames[] = { "WWW_OpenURL",
00805                                     "WWW_Activate",
00806                                     "WWW_CancelProgress",
00807                                     "WWW_Version",
00808                                     "WWW_RegisterViewer",
00809                                     "WWW_UnRegisterViewer",
00810                                     "WWW_GetWindowInfo" };
00811 
00812 // Static member definitions.
00813 int   nsNativeAppSupportWin::mConversations = 0;
00814 HSZ   nsNativeAppSupportWin::mApplication   = 0;
00815 HSZ   nsNativeAppSupportWin::mTopics[nsNativeAppSupportWin::topicCount] = { 0 };
00816 DWORD nsNativeAppSupportWin::mInstance      = 0;
00817 PRBool nsNativeAppSupportWin::mInitialWindowActive = PR_FALSE;
00818 PRBool nsNativeAppSupportWin::mForceProfileStartup = PR_FALSE;
00819 PRBool nsNativeAppSupportWin::mSupportingDDEExec   = PR_FALSE;
00820 
00821 NOTIFYICONDATA nsNativeAppSupportWin::mIconData = { sizeof(NOTIFYICONDATA),
00822                                                     0,
00823                                                     1,
00824                                                     NIF_ICON | NIF_MESSAGE | NIF_TIP,
00825                                                     WM_USER,
00826                                                     0,
00827                                                     0 };
00828 HMENU nsNativeAppSupportWin::mTrayIconMenu = 0;
00829 
00830 char nsNativeAppSupportWin::mMutexName[ 128 ] = { 0 };
00831 
00832 
00833 // Message window encapsulation.
00834 struct MessageWindow {
00835     // ctor/dtor are simplistic
00836     MessageWindow() {
00837         // Try to find window.
00838         mHandle = ::FindWindow( className(), 0 );
00839     }
00840 
00841     // Act like an HWND.
00842     operator HWND() {
00843         return mHandle;
00844     }
00845 
00846     // Class name: appName + "MessageWindow"
00847     static const char *className() {
00848         static char classNameBuffer[128];
00849         static char *mClassName = 0;
00850         if ( !mClassName ) {
00851             ::_snprintf( classNameBuffer,
00852                          sizeof classNameBuffer,
00853                          "%s%s",
00854                          nsNativeAppSupportWin::mAppName,
00855                          "MessageWindow" );
00856             mClassName = classNameBuffer;
00857         }
00858         return mClassName;
00859     }
00860 
00861     // Create: Register class and create window.
00862     NS_IMETHOD Create() {
00863         WNDCLASS classStruct = { 0,                          // style
00864                                  &MessageWindow::WindowProc, // lpfnWndProc
00865                                  0,                          // cbClsExtra
00866                                  0,                          // cbWndExtra
00867                                  0,                          // hInstance
00868                                  0,                          // hIcon
00869                                  0,                          // hCursor
00870                                  0,                          // hbrBackground
00871                                  0,                          // lpszMenuName
00872                                  className() };              // lpszClassName
00873 
00874         // Register the window class.
00875         NS_ENSURE_TRUE( ::RegisterClass( &classStruct ), NS_ERROR_FAILURE );
00876 
00877         // Create the window.
00878         NS_ENSURE_TRUE( ( mHandle = ::CreateWindow( className(),
00879                                                     0,          // title
00880                                                     WS_CAPTION, // style
00881                                                     0,0,0,0,    // x, y, cx, cy
00882                                                     0,          // parent
00883                                                     0,          // menu
00884                                                     0,          // instance
00885                                                     0 ) ),      // create struct
00886                         NS_ERROR_FAILURE );
00887 
00888 #if MOZ_DEBUG_DDE
00889         printf( "Message window = 0x%08X\n", (int)mHandle );
00890 #endif
00891 
00892         return NS_OK;
00893     }
00894 
00895     // Destory:  Get rid of window and reset mHandle.
00896     NS_IMETHOD Destroy() {
00897         nsresult retval = NS_OK;
00898 
00899         if ( mHandle ) {
00900             // DestroyWindow can only destroy windows created from
00901             //  the same thread.
00902             BOOL desRes = DestroyWindow( mHandle );
00903             if ( FALSE != desRes ) {
00904                 mHandle = NULL;
00905             }
00906             else {
00907                 retval = NS_ERROR_FAILURE;
00908             }
00909         }
00910 
00911         return retval;
00912     }
00913 
00914     // SendRequest: Pass string via WM_COPYDATA to message window.
00915     NS_IMETHOD SendRequest( const char *cmd ) {
00916         COPYDATASTRUCT cds = { 0, ::strlen( cmd ) + 1, (void*)cmd };
00917         // Bring the already running Mozilla process to the foreground.
00918         // nsWindow will restore the window (if minimized) and raise it.
00919         ::SetForegroundWindow( mHandle );
00920         ::SendMessage( mHandle, WM_COPYDATA, 0, (LPARAM)&cds );
00921         return NS_OK;
00922     }
00923 
00924     // Window proc.
00925     static long CALLBACK WindowProc( HWND msgWindow, UINT msg, WPARAM wp, LPARAM lp ) {
00926         if ( msg == WM_COPYDATA ) {
00927             // This is an incoming request.
00928             COPYDATASTRUCT *cds = (COPYDATASTRUCT*)lp;
00929 #if MOZ_DEBUG_DDE
00930             printf( "Incoming request: %s\n", (const char*)cds->lpData );
00931 #endif
00932             // Get current window and return its window handle.
00933             nsCOMPtr<nsIDOMWindow> win;
00934             (void)nsNativeAppSupportWin::HandleRequest( (LPBYTE)cds->lpData, PR_FALSE, getter_AddRefs( win ) );
00935             return win ? (long)hwndForDOMWindow( win ) : 0;
00936 #ifndef MOZ_PHOENIX
00937  } else if ( msg == WM_USER ) {
00938      if ( lp == WM_RBUTTONUP ) {
00939          // Show menu with Exit disabled/enabled appropriately.
00940          nsCOMPtr<nsIDOMWindowInternal> win;
00941          GetMostRecentWindow( 0, getter_AddRefs( win ) );
00942          ::EnableMenuItem( nsNativeAppSupportWin::mTrayIconMenu, TURBO_EXIT, win ? MF_GRAYED : MF_ENABLED );
00943          POINT pt;
00944          GetCursorPos( &pt );
00945 
00946          SetForegroundWindow(msgWindow);
00947          int selectedItem = ::TrackPopupMenu( nsNativeAppSupportWin::mTrayIconMenu,
00948                                               TPM_NONOTIFY | TPM_RETURNCMD |
00949                                               TPM_RIGHTBUTTON,
00950                                               pt.x,
00951                                               pt.y,
00952                                               0,
00953                                               msgWindow,
00954                                               0 );
00955 
00956          nsCOMPtr<nsIDOMWindow> newWin;
00957          switch (selectedItem) {
00958          case TURBO_NAVIGATOR:
00959              (void)nsNativeAppSupportWin::HandleRequest( (LPBYTE)(NS_STRINGIFY(MOZ_APP_NAME) " -browser"), PR_TRUE, getter_AddRefs( newWin ) );
00960              break;
00961          case TURBO_MAIL:
00962              (void)nsNativeAppSupportWin::HandleRequest( (LPBYTE)(NS_STRINGIFY(MOZ_APP_NAME) " -mail"), PR_TRUE, getter_AddRefs( newWin ) );
00963               break;
00964          case TURBO_EDITOR:
00965              (void)nsNativeAppSupportWin::HandleRequest( (LPBYTE)(NS_STRINGIFY(MOZ_APP_NAME) " -editor"), PR_TRUE, getter_AddRefs( newWin ) );
00966              break;
00967          case TURBO_ADDRESSBOOK:
00968              (void)nsNativeAppSupportWin::HandleRequest( (LPBYTE)(NS_STRINGIFY(MOZ_APP_NAME) " -addressbook"), PR_TRUE, getter_AddRefs( newWin ) );
00969              break;
00970          case TURBO_EXIT:
00971              (void)nsNativeAppSupportWin::HandleRequest( (LPBYTE)(NS_STRINGIFY(MOZ_APP_NAME) " -kill"), PR_TRUE, getter_AddRefs( newWin ) );
00972              break;
00973          case TURBO_DISABLE:
00974              nsresult rv;
00975              nsCOMPtr<nsIStringBundleService> stringBundleService( do_GetService( NS_STRINGBUNDLE_CONTRACTID ) );
00976              nsCOMPtr<nsIStringBundle> turboMenuBundle;
00977              nsCOMPtr<nsIStringBundle> brandBundle;
00978              if ( stringBundleService ) {
00979                  stringBundleService->CreateBundle( "chrome://branding/locale/brand.properties", getter_AddRefs( brandBundle ) );
00980                  stringBundleService->CreateBundle( "chrome://navigator/locale/turboMenu.properties",
00981                                                     getter_AddRefs( turboMenuBundle ) );
00982              }
00983              nsXPIDLString dialogMsg;
00984              nsXPIDLString dialogTitle;
00985              nsXPIDLString brandName;
00986              if ( brandBundle && turboMenuBundle ) {
00987                  brandBundle->GetStringFromName( NS_LITERAL_STRING( "brandShortName" ).get(),
00988                                                  getter_Copies( brandName ) );
00989                  const PRUnichar *formatStrings[] = { brandName.get() };
00990                  turboMenuBundle->FormatStringFromName( NS_LITERAL_STRING( "DisableDlgMsg" ).get(), formatStrings,
00991                                                         1, getter_Copies( dialogMsg ) );
00992                  turboMenuBundle->FormatStringFromName( NS_LITERAL_STRING( "DisableDlgTitle" ).get(), formatStrings,
00993                                                         1, getter_Copies( dialogTitle ) );
00994 
00995              }
00996              if ( !dialogMsg.IsEmpty() && !dialogTitle.IsEmpty() && !brandName.IsEmpty() ) {
00997                  nsCOMPtr<nsIPromptService> dialog( do_GetService( NS_PROMPTSERVICE_CONTRACTID ) );
00998                  if ( dialog ) {
00999                      PRBool reallyDisable;
01000                      nsNativeAppSupportWin::mLastWindowIsConfirmation = PR_TRUE;
01001                      dialog->Confirm( nsnull, dialogTitle.get(), dialogMsg.get(), &reallyDisable );
01002                      if ( !reallyDisable ) {
01003                           break;
01004                      }
01005                  }
01006 
01007              }
01008              nsCOMPtr<nsIWindowsHooks> winHooksService ( do_GetService( NS_IWINDOWSHOOKS_CONTRACTID, &rv ) );
01009              if ( NS_SUCCEEDED( rv ) )
01010                  winHooksService->StartupRemoveOption("-turbo");
01011 
01012              nsCOMPtr<nsIAppStartup> appStartup
01013                  (do_GetService(NS_APPSTARTUP_CONTRACTID, &rv));
01014              if ( NS_SUCCEEDED( rv ) ) {
01015                  nsCOMPtr<nsINativeAppSupport> native;
01016                  rv = appStartup->GetNativeAppSupport( getter_AddRefs( native ) );
01017                  if ( NS_SUCCEEDED( rv ) )
01018                      native->SetIsServerMode( PR_FALSE );
01019                  if ( !win )
01020                      appStartup->Quit(nsIAppStartup::eAttemptQuit);
01021              }
01022              break;
01023          }
01024          PostMessage(msgWindow, WM_NULL, 0, 0);
01025      } else if ( lp == WM_LBUTTONDBLCLK ) {
01026          // Dbl-click will open nav/mailnews/composer based on prefs
01027          // (if no windows are open), or, open nav (if some windows are
01028          // already open).  That's done in HandleRequest.
01029          nsCOMPtr<nsIDOMWindow> win;
01030          (void)nsNativeAppSupportWin::HandleRequest( (LPBYTE)NS_STRINGIFY(MOZ_APP_NAME), PR_TRUE, getter_AddRefs( win ) );
01031      }
01032      return TRUE;
01033 #endif
01034   } else if ( msg == WM_QUERYENDSESSION ) {
01035     // Invoke "-killAll" cmd line handler.  That will close all open windows,
01036     // and display dialog asking whether to save/don't save/cancel.  If the
01037     // user says cancel, then we pass that indicator along to the system
01038     // in order to stop the system shutdown/logoff.
01039     nsCOMPtr<nsICmdLineHandler>
01040         killAll( do_CreateInstance( "@mozilla.org/commandlinehandler/general-startup;1?type=killAll" ) );
01041     if ( killAll ) {
01042         nsXPIDLCString unused;
01043         // Note: "GetChromeUrlForTask" is a euphemism for
01044         //       "ProcessYourCommandLineSwitch".  The interface was written
01045         //       presuming a less general-purpose role for command line
01046         //       handlers than it ought to have.
01047         nsresult rv = killAll->GetChromeUrlForTask( getter_Copies( unused ) );
01048         if ( rv == NS_ERROR_ABORT ) {
01049             // User cancelled shutdown/logoff.
01050             return FALSE;
01051         } else {
01052             // Shutdown/logoff OK.
01053             return TRUE;
01054         }
01055     }
01056   } else if ((nsNativeAppSupportWin::mTrayRestart) && (msg == nsNativeAppSupportWin::mTrayRestart)) {
01057      //Re-add the icon. The taskbar must have been destroyed and recreated
01058      ::Shell_NotifyIcon( NIM_ADD, &nsNativeAppSupportWin::mIconData );
01059   }
01060   return DefWindowProc( msgWindow, msg, wp, lp );
01061 }
01062 
01063 private:
01064     HWND mHandle;
01065 }; // struct MessageWindow
01066 
01067 UINT nsNativeAppSupportWin::mTrayRestart = 0;
01068 static char nameBuffer[128] = { 0 };
01069 char *nsNativeAppSupportWin::mAppName = nameBuffer;
01070 
01071 /* Start: Tries to find the "message window" to determine if it
01072  *        exists.  If so, then SeaMonkey is already running.  In that
01073  *        case, we use the handle to the "message" window and send
01074  *        a request corresponding to this process's command line
01075  *        options.
01076  *
01077  *        If not, then this is the first instance of SeaMonkey.  In
01078  *        that case, we create and set up the message window.
01079  *
01080  *        The checking for existance of the message window must
01081  *        be protected by use of a mutex semaphore.
01082  */
01083 NS_IMETHODIMP
01084 nsNativeAppSupportWin::Start( PRBool *aResult ) {
01085     NS_ENSURE_ARG( aResult );
01086     NS_ENSURE_TRUE( mInstance == 0, NS_ERROR_NOT_INITIALIZED );
01087 
01088     if (getenv("MOZ_NO_REMOTE"))
01089     {
01090         *aResult = PR_TRUE;
01091         return NS_OK;
01092     }
01093 
01094     nsresult rv = NS_ERROR_FAILURE;
01095     *aResult = PR_FALSE;
01096 
01097     // Grab mutex first.
01098     int retval;
01099     UINT id = ID_DDE_APPLICATION_NAME;
01100     retval = LoadString( (HINSTANCE) NULL, id, (LPTSTR) nameBuffer, sizeof(nameBuffer) );
01101     if ( retval == 0 ) {
01102         // No app name; just keep running.
01103         *aResult = PR_TRUE;
01104         return NS_OK;
01105     }
01106 
01107     // Build mutex name from app name.
01108     ::_snprintf( mMutexName, sizeof mMutexName, "%s%s", nameBuffer, MOZ_STARTUP_MUTEX_NAME );
01109     Mutex startupLock = Mutex( mMutexName );
01110 
01111     NS_ENSURE_TRUE( startupLock.Lock( MOZ_DDE_START_TIMEOUT ), NS_ERROR_FAILURE );
01112 
01113     // Search for existing message window.
01114     MessageWindow msgWindow;
01115     if ( (HWND)msgWindow ) {
01116         // We are a client process.  Pass request to message window.
01117         LPTSTR cmd = ::GetCommandLine();
01118         rv = msgWindow.SendRequest( cmd );
01119     } else {
01120         // We will be server.
01121         if (!gAbortServer) {
01122             rv = msgWindow.Create();
01123             if ( NS_SUCCEEDED( rv ) ) {
01124                 // Start up DDE server.
01125                 this->StartDDE();
01126                 // Tell caller to spin message loop.
01127                 *aResult = PR_TRUE;
01128             }
01129         }
01130     }
01131 
01132     startupLock.Unlock();
01133 
01134     return rv;
01135 }
01136 
01137 PRBool
01138 nsNativeAppSupportWin::InitTopicStrings() {
01139     for ( int i = 0; i < topicCount; i++ ) {
01140         if ( !( mTopics[ i ] = DdeCreateStringHandle( mInstance, NS_CONST_CAST(char *,topicNames[ i ]), CP_WINANSI ) ) ) {
01141             return PR_FALSE;
01142         }
01143     }
01144     return PR_TRUE;
01145 }
01146 
01147 int
01148 nsNativeAppSupportWin::FindTopic( HSZ topic ) {
01149     for ( int i = 0; i < topicCount; i++ ) {
01150         if ( DdeCmpStringHandles( topic, mTopics[i] ) == 0 ) {
01151             return i;
01152         }
01153     }
01154     return -1;
01155 }
01156 
01157 // Utility function that determines if we're handling http Internet shortcuts.
01158 static PRBool handlingHTTP() {
01159     PRBool result = PR_FALSE; // Answer no if an error occurs.
01160     // See if we're the "default browser" (i.e., handling http Internet shortcuts)
01161     nsCOMPtr<nsIWindowsHooks> winhooks( do_GetService( NS_IWINDOWSHOOKS_CONTRACTID ) );
01162     if ( winhooks ) {
01163         nsCOMPtr<nsIWindowsHooksSettings> settings;
01164         nsresult rv = winhooks->GetSettings( getter_AddRefs( settings ) );
01165         if ( NS_SUCCEEDED( rv ) ) {
01166             settings->GetIsHandlingHTTP( &result );
01167             if ( result ) {
01168                 // The user *said* to handle http.  See if we really
01169                 // are doing it.  We need to check *only* the HTTP
01170                 // settings.  If we don't mask off all others, we
01171                 // may erroneously conclude that we're not handling
01172                 // HTTP when really we are (although, a false negative
01173                 // is much better than a false positive).  Please note
01174                 // that setting these attributes false only affects
01175                 // this "Settings" object.  It *does not* change the
01176                 // actual user preferences stored in the registry!
01177 
01178                 // First, turn off all the other protocols.
01179                 settings->SetIsHandlingHTTPS( PR_FALSE );
01180 #ifndef MOZ_PHOENIX
01181                 settings->SetIsHandlingFTP( PR_FALSE );
01182                 settings->SetIsHandlingCHROME( PR_FALSE );
01183                 settings->SetIsHandlingGOPHER( PR_FALSE );
01184 #endif
01185                 // Next, all the file types.
01186                 settings->SetIsHandlingHTML( PR_FALSE );
01187                 settings->SetIsHandlingXHTML( PR_FALSE );
01188 #ifndef MOZ_PHOENIX
01189                 settings->SetIsHandlingJPEG( PR_FALSE );
01190                 settings->SetIsHandlingGIF( PR_FALSE );
01191                 settings->SetIsHandlingPNG( PR_FALSE );
01192                 settings->SetIsHandlingMNG( PR_FALSE );
01193                 settings->SetIsHandlingBMP( PR_FALSE );
01194                 settings->SetIsHandlingICO( PR_FALSE );
01195                 settings->SetIsHandlingXML( PR_FALSE );
01196                 settings->SetIsHandlingXUL( PR_FALSE );
01197 #endif
01198                 // Now test the HTTP setting in the registry.
01199                 settings->GetRegistryMatches( &result );
01200             }
01201         }
01202     }
01203     return result;
01204 }
01205 
01206 // Utility function to delete a registry subkey.
01207 static DWORD deleteKey( HKEY baseKey, const char *keyName ) {
01208     // Make sure input subkey isn't null.
01209     DWORD rc;
01210     if ( keyName && ::strlen(keyName) ) {
01211         // Open subkey.
01212         HKEY key;
01213         rc = ::RegOpenKeyEx( baseKey,
01214                              keyName,
01215                              0,
01216                              KEY_ENUMERATE_SUB_KEYS | DELETE,
01217                              &key );
01218         // Continue till we get an error or are done.
01219         while ( rc == ERROR_SUCCESS ) {
01220             char subkeyName[_MAX_PATH];
01221             DWORD len = sizeof subkeyName;
01222             // Get first subkey name.  Note that we always get the
01223             // first one, then delete it.  So we need to get
01224             // the first one next time, also.
01225             rc = ::RegEnumKeyEx( key,
01226                                  0,
01227                                  subkeyName,
01228                                  &len,
01229                                  0,
01230                                  0,
01231                                  0,
01232                                  0 );
01233             if ( rc == ERROR_NO_MORE_ITEMS ) {
01234                 // No more subkeys.  Delete the main one.
01235                 rc = ::RegDeleteKey( baseKey, keyName );
01236                 break;
01237             } else if ( rc == ERROR_SUCCESS ) {
01238                 // Another subkey, delete it, recursively.
01239                 rc = deleteKey( key, subkeyName );
01240             }
01241         }
01242         // Close the key we opened.
01243         ::RegCloseKey( key );
01244     } else {
01245         rc = ERROR_BADKEY;
01246     }
01247     return rc;
01248 }
01249 
01250 
01251 // Start DDE server.
01252 //
01253 // This used to be the Start() method when we were using DDE as the
01254 // primary IPC mechanism between secondary SeaMonkey processes and the
01255 // initial "server" process.
01256 //
01257 // Now, it simply initializes the DDE server.  The caller must check
01258 // that this process is to be the server, and, must acquire the DDE
01259 // startup mutex semaphore prior to calling this routine.  See ::Start(),
01260 // above.
01261 NS_IMETHODIMP
01262 nsNativeAppSupportWin::StartDDE() {
01263     NS_ENSURE_TRUE( mInstance == 0, NS_ERROR_NOT_INITIALIZED );
01264 
01265     // Initialize DDE.
01266     NS_ENSURE_TRUE( DMLERR_NO_ERROR == DdeInitialize( &mInstance,
01267                                                       nsNativeAppSupportWin::HandleDDENotification,
01268                                                       APPCLASS_STANDARD,
01269                                                       0 ),
01270                     NS_ERROR_FAILURE );
01271 
01272     // Allocate DDE strings.
01273     NS_ENSURE_TRUE( ( mApplication = DdeCreateStringHandle( mInstance, mAppName, CP_WINANSI ) ) && InitTopicStrings(),
01274                     NS_ERROR_FAILURE );
01275 
01276     // Next step is to register a DDE service.
01277     NS_ENSURE_TRUE( DdeNameService( mInstance, mApplication, 0, DNS_REGISTER ), NS_ERROR_FAILURE );
01278 
01279 #if MOZ_DEBUG_DDE
01280     printf( "DDE server started\n" );
01281 #endif
01282 
01283     return NS_OK;
01284 }
01285 
01286 // If no DDE conversations are pending, terminate DDE.
01287 NS_IMETHODIMP
01288 nsNativeAppSupportWin::Stop( PRBool *aResult ) {
01289     NS_ENSURE_ARG( aResult );
01290     NS_ENSURE_TRUE( mInstance, NS_ERROR_NOT_INITIALIZED );
01291 
01292     nsresult rv = NS_OK;
01293     *aResult = PR_TRUE;
01294 
01295     Mutex ddeLock( mMutexName );
01296 
01297     if ( ddeLock.Lock( MOZ_DDE_STOP_TIMEOUT ) ) {
01298         if ( mConversations == 0 ) {
01299             this->Quit();
01300         } else {
01301             *aResult = PR_FALSE;
01302         }
01303 
01304         ddeLock.Unlock();
01305     }
01306     else {
01307         // No DDE application name specified, but that's OK.  Just
01308         // forge ahead.
01309         *aResult = PR_TRUE;
01310     }
01311 
01312     return rv;
01313 }
01314 
01315 // Terminate DDE regardless.
01316 NS_IMETHODIMP
01317 nsNativeAppSupportWin::Quit() {
01318     // If another process wants to look for the message window, they need
01319     // to wait to hold the lock, in which case they will not find the
01320     // window as we will destroy ours under our lock.
01321     // When the mutex goes off the stack, it is unlocked via destructor.
01322     Mutex mutexLock(mMutexName);
01323     NS_ENSURE_TRUE(mutexLock.Lock(MOZ_DDE_START_TIMEOUT), NS_ERROR_FAILURE );
01324 
01325     // If we've got a message window to receive IPC or new window requests,
01326     // get rid of it as we are shutting down.
01327     // Note:  Destroy calls DestroyWindow, which will only work on a window
01328     //  created by the same thread.
01329     MessageWindow mw;
01330     mw.Destroy();
01331 
01332     if ( mInstance ) {
01333         // Undo registry setting if we need to.
01334         if ( mSupportingDDEExec && handlingHTTP() ) {
01335             mSupportingDDEExec = PR_FALSE;
01336 #if MOZ_DEBUG_DDE
01337             printf( "Deleting ddexec subkey on exit\n" );
01338 #endif
01339             deleteKey( HKEY_CLASSES_ROOT, "http\\shell\\open\\ddeexec" );
01340         }
01341 
01342         // Unregister application name.
01343         DdeNameService( mInstance, mApplication, 0, DNS_UNREGISTER );
01344         // Clean up strings.
01345         if ( mApplication ) {
01346             DdeFreeStringHandle( mInstance, mApplication );
01347             mApplication = 0;
01348         }
01349         for ( int i = 0; i < topicCount; i++ ) {
01350             if ( mTopics[i] ) {
01351                 DdeFreeStringHandle( mInstance, mTopics[i] );
01352                 mTopics[i] = 0;
01353             }
01354         }
01355         DdeUninitialize( mInstance );
01356         mInstance = 0;
01357     }
01358 
01359     return NS_OK;
01360 }
01361 
01362 PRBool NS_CanRun()
01363 {
01364       return PR_TRUE;
01365 }
01366 
01367 #if MOZ_DEBUG_DDE
01368 // Macro to generate case statement for a given XTYP value.
01369 #define XTYP_CASE(t) \
01370     case t: result = #t; break
01371 
01372 static nsCString uTypeDesc( UINT uType ) {
01373     nsCString result;
01374     switch ( uType ) {
01375     XTYP_CASE(XTYP_ADVSTART);
01376     XTYP_CASE(XTYP_CONNECT);
01377     XTYP_CASE(XTYP_ADVREQ);
01378     XTYP_CASE(XTYP_REQUEST);
01379     XTYP_CASE(XTYP_WILDCONNECT);
01380     XTYP_CASE(XTYP_ADVDATA);
01381     XTYP_CASE(XTYP_EXECUTE);
01382     XTYP_CASE(XTYP_POKE);
01383     XTYP_CASE(XTYP_ADVSTOP);
01384     XTYP_CASE(XTYP_CONNECT_CONFIRM);
01385     XTYP_CASE(XTYP_DISCONNECT);
01386     XTYP_CASE(XTYP_ERROR);
01387     XTYP_CASE(XTYP_MONITOR);
01388     XTYP_CASE(XTYP_REGISTER);
01389     XTYP_CASE(XTYP_XACT_COMPLETE);
01390     XTYP_CASE(XTYP_UNREGISTER);
01391     default: result = "XTYP_?????";
01392     }
01393     return result;
01394 }
01395 
01396 static nsCString hszValue( DWORD instance, HSZ hsz ) {
01397     // Extract string from HSZ.
01398     nsCString result("[");
01399     DWORD len = DdeQueryString( instance, hsz, NULL, NULL, CP_WINANSI );
01400     if ( len ) {
01401         char buffer[ 256 ];
01402         DdeQueryString( instance, hsz, buffer, sizeof buffer, CP_WINANSI );
01403         result += buffer;
01404     }
01405     result += "]";
01406     return result;
01407 }
01408 #else
01409 // These are purely a safety measure to avoid the infamous "won't
01410 // build non-debug" type Tinderbox flames.
01411 static nsCString uTypeDesc( UINT ) {
01412     return nsCString( "?" );
01413 }
01414 static nsCString hszValue( DWORD, HSZ ) {
01415     return nsCString( "?" );
01416 }
01417 #endif
01418 
01419 
01420 // Utility function to escape double-quotes within a string.
01421 static void escapeQuotes( nsString &aString ) {
01422     PRInt32 offset = -1;
01423     while( 1 ) {
01424        // Find next '"'.
01425        offset = FindCharInString(aString, '"', ++offset );
01426        if ( offset == kNotFound ) {
01427            // No more quotes, exit.
01428            break;
01429        } else {
01430            // Insert back-slash ahead of the '"'.
01431            aString.Insert( PRUnichar('\\'), offset );
01432            // Increment offset because we just inserted a slash
01433            offset++;
01434        }
01435     }
01436     return;
01437 }
01438 
01439 HDDEDATA CALLBACK
01440 nsNativeAppSupportWin::HandleDDENotification( UINT uType,       // transaction type
01441                                               UINT uFmt,        // clipboard data format
01442                                               HCONV hconv,      // handle to the conversation
01443                                               HSZ hsz1,         // handle to a string
01444                                               HSZ hsz2,         // handle to a string
01445                                               HDDEDATA hdata,   // handle to a global memory object
01446                                               ULONG dwData1,    // transaction-specific data
01447                                               ULONG dwData2 ) { // transaction-specific data
01448 
01449 #if MOZ_DEBUG_DDE
01450     printf( "DDE: uType  =%s\n",      uTypeDesc( uType ).get() );
01451     printf( "     uFmt   =%u\n",      (unsigned)uFmt );
01452     printf( "     hconv  =%08x\n",    (int)hconv );
01453     printf( "     hsz1   =%08x:%s\n", (int)hsz1, hszValue( mInstance, hsz1 ).get() );
01454     printf( "     hsz2   =%08x:%s\n", (int)hsz2, hszValue( mInstance, hsz2 ).get() );
01455     printf( "     hdata  =%08x\n",    (int)hdata );
01456     printf( "     dwData1=%08x\n",    (int)dwData1 );
01457     printf( "     dwData2=%08x\n",    (int)dwData2 );
01458 #endif
01459 
01460     HDDEDATA result = 0;
01461     if ( uType & XCLASS_BOOL ) {
01462         switch ( uType ) {
01463             case XTYP_CONNECT:
01464                 // Make sure its for our service/topic.
01465                 if ( FindTopic( hsz1 ) != -1 ) {
01466                     // We support this connection.
01467                     result = (HDDEDATA)1;
01468                 }
01469                 break;
01470             case XTYP_CONNECT_CONFIRM:
01471                 // We don't care about the conversation handle, at this point.
01472                 result = (HDDEDATA)1;
01473                 break;
01474         }
01475     } else if ( uType & XCLASS_DATA ) {
01476         if ( uType == XTYP_REQUEST ) {
01477             switch ( FindTopic( hsz1 ) ) {
01478                 case topicOpenURL: {
01479                     // Open a given URL...
01480 
01481                     // Default is to open in current window.
01482                     PRBool new_window = PR_FALSE;
01483 
01484                     // Get the URL from the first argument in the command.
01485                     nsCAutoString url;
01486                     ParseDDEArg(hsz2, 0, url);
01487                     // Read the 3rd argument in the command to determine if a
01488                     // new window is to be used.
01489                     nsCAutoString windowID;
01490                     ParseDDEArg(hsz2, 2, windowID);
01491                     // "0" means to open the URL in a new window.
01492                     if ( strcmp(windowID.get(), "0" ) == 0 ) {
01493                         new_window = PR_TRUE;
01494                     }
01495 
01496                     // Make it look like command line args.
01497                     url.Insert( NS_STRINGIFY(MOZ_APP_NAME) " -url ", 0 );
01498 #if MOZ_DEBUG_DDE
01499                     printf( "Handling dde XTYP_REQUEST request: [%s]...\n", url.get() );
01500 #endif
01501                     // Now handle it.
01502                     nsCOMPtr<nsIDOMWindow> win;
01503                     HandleRequest( LPBYTE( url.get() ), new_window, getter_AddRefs( win ) );
01504                     // Return pseudo window ID.
01505                     result = CreateDDEData( 1 );
01506                     break;
01507                 }
01508                 case topicGetWindowInfo: {
01509                     // This topic has to get the current URL, get the current
01510                     // page title and then format the output into the DDE
01511                     // return string.  The return value is "URL","Page Title",
01512                     // "Window ID" however the window ID is not used for this
01513                     // command, therefore it is returned as a null string
01514 
01515                     // This isn't really a loop.  We just use "break"
01516                     // statements to bypass the remaining steps when
01517                     // something goes wrong.
01518                     do {
01519                         // Get most recently used Nav window.
01520                         nsCOMPtr<nsIDOMWindowInternal> navWin;
01521                         GetMostRecentWindow( NS_LITERAL_STRING( "navigator:browser" ).get(),
01522                                              getter_AddRefs( navWin ) );
01523                         if ( !navWin ) {
01524                             // There is not a window open
01525                             break;
01526                         }
01527                         // Get content window.
01528                         nsCOMPtr<nsIDOMWindow> content;
01529                         navWin->GetContent( getter_AddRefs( content ) );
01530                         if ( !content ) {
01531                             break;
01532                         }
01533                         // Convert that to internal interface.
01534                         nsCOMPtr<nsIDOMWindowInternal> internalContent( do_QueryInterface( content ) );
01535                         if ( !internalContent ) {
01536                             break;
01537                         }
01538                         // Get location.
01539                         nsCOMPtr<nsIDOMLocation> location;
01540                         internalContent->GetLocation( getter_AddRefs( location ) );
01541                         if ( !location ) {
01542                             break;
01543                         }
01544                         // Get href for URL.
01545                         nsAutoString url;
01546                         if ( NS_FAILED( location->GetHref( url ) ) ) {
01547                             break;
01548                         }
01549                         // Escape any double-quotes.
01550                         escapeQuotes( url );
01551 
01552                         // Now for the title; first, get the "window" script global object.
01553                         nsCOMPtr<nsIScriptGlobalObject> scrGlobalObj( do_QueryInterface( internalContent ) );
01554                         if ( !scrGlobalObj ) {
01555                             break;
01556                         }
01557                         // Then from its doc shell get the base window...
01558                         nsCOMPtr<nsIBaseWindow> baseWindow =
01559                             do_QueryInterface( scrGlobalObj->GetDocShell() );
01560                         if ( !baseWindow ) {
01561                             break;
01562                         }
01563                         // And from the base window we can get the title.
01564                         nsXPIDLString title;
01565                         if(!baseWindow) {
01566                             break;
01567                         }
01568                         baseWindow->GetTitle(getter_Copies(title));
01569                         // Escape any double-quotes in the title.
01570                         escapeQuotes( title );
01571 
01572                         // Use a string buffer for the output data, first
01573                         // save a quote.
01574                         nsCAutoString   outpt( NS_LITERAL_CSTRING("\"") );
01575                         // Now copy the URL converting the Unicode string
01576                         // to a single-byte ASCII string
01577                         nsCAutoString tmpNativeStr;
01578                         NS_CopyUnicodeToNative( url, tmpNativeStr );
01579                         outpt.Append( tmpNativeStr );
01580                         // Add the "," used to separate the URL and the page
01581                         // title
01582                         outpt.Append( NS_LITERAL_CSTRING("\",\"") );
01583                         // Now copy the current page title to the return string
01584                         NS_CopyUnicodeToNative( title, tmpNativeStr );
01585                         outpt.Append( tmpNativeStr );
01586                         // Fill out the return string with the remainin ",""
01587                         outpt.Append( NS_LITERAL_CSTRING( "\",\"\"" ));
01588 
01589                         // Create a DDE handle to a char string for the data
01590                         // being returned, this copies and creates a "shared"
01591                         // copy of the DDE response until the calling APP
01592                         // reads it and says it can be freed.
01593                         result = CreateDDEData( (LPBYTE)(const char*)outpt.get(),
01594                                                 outpt.Length() + 1 );
01595 #if MOZ_DEBUG_DDE
01596                         printf( "WWW_GetWindowInfo->%s\n", outpt.get() );
01597 #endif
01598                     } while ( PR_FALSE );
01599                     break;
01600                 }
01601                 case topicActivate: {
01602                     // Activate a Nav window...
01603                     nsCAutoString windowID;
01604                     ParseDDEArg(hsz2, 0, windowID);
01605                     // 4294967295 is decimal for 0xFFFFFFFF which is also a
01606                     //   correct value to do that Activate last window stuff
01607                     const char *wid = windowID.get();
01608                     if ( strcmp(wid, "-1" ) == 0 ||
01609                          strcmp(wid, "4294967295" ) == 0 ) {
01610                         // We only support activating the most recent window (or a new one).
01611                         ActivateLastWindow();
01612                         // Return pseudo window ID.
01613                         result = CreateDDEData( 1 );
01614                     }
01615                     break;
01616                 }
01617                 case topicVersion: {
01618                     // Return version.  We're restarting at 1.0!
01619                     DWORD version = 1 << 16; // "1.0"
01620                     result = CreateDDEData( version );
01621                     break;
01622                 }
01623                 case topicRegisterViewer: {
01624                     // Register new viewer (not implemented).
01625                     result = CreateDDEData( PR_FALSE );
01626                     break;
01627                 }
01628                 case topicUnRegisterViewer: {
01629                     // Unregister new viewer (not implemented).
01630                     result = CreateDDEData( PR_FALSE );
01631                     break;
01632                 }
01633                 default:
01634                     break;
01635             }
01636         } else if ( uType & XTYP_POKE ) {
01637             switch ( FindTopic( hsz1 ) ) {
01638                 case topicCancelProgress: {
01639                     // "Handle" progress cancel (actually, pretty much ignored).
01640                     result = (HDDEDATA)DDE_FACK;
01641                     break;
01642                 }
01643                 default:
01644                     break;
01645             }
01646         }
01647     } else if ( uType & XCLASS_FLAGS ) {
01648         if ( uType == XTYP_EXECUTE ) {
01649             // Prove that we received the request.
01650             DWORD bytes;
01651             LPBYTE request = DdeAccessData( hdata, &bytes );
01652 #if MOZ_DEBUG_DDE
01653             printf( "Handling dde request: [%s]...\n", (char*)request );
01654 #endif
01655             // Default is to open in current window.
01656             PRBool new_window = PR_FALSE;
01657 
01658             nsCAutoString url;
01659             ParseDDEArg((const char*) request, 0, url);
01660 
01661             // Read the 3rd argument in the command to determine if a
01662             // new window is to be used.
01663             nsCAutoString windowID;
01664             ParseDDEArg((const char*) request, 2, windowID);
01665 
01666             // "0" means to open the URL in a new window.
01667             if ( strcmp(windowID.get(), "0" ) == 0 ) {
01668                 new_window = PR_TRUE;
01669             }
01670 
01671             // Make it look like command line args.
01672             url.Insert( NS_STRINGIFY(MOZ_APP_NAME) " -url ", 0 );
01673 #if MOZ_DEBUG_DDE
01674             printf( "Handling dde XTYP_REQUEST request: [%s]...\n", url.get() );
01675 #endif
01676             // Now handle it.
01677             nsCOMPtr<nsIDOMWindow> win;
01678             HandleRequest( LPBYTE( url.get() ), new_window, getter_AddRefs( win ) );
01679 
01680             // Release the data.
01681             DdeUnaccessData( hdata );
01682             result = (HDDEDATA)DDE_FACK;
01683         } else {
01684             result = (HDDEDATA)DDE_FNOTPROCESSED;
01685         }
01686     } else if ( uType & XCLASS_NOTIFICATION ) {
01687     }
01688 #if MOZ_DEBUG_DDE
01689     printf( "DDE result=%d (0x%08X)\n", (int)result, (int)result );
01690 #endif
01691     return result;
01692 }
01693 
01694 // Utility function to advance to end of quoted string.
01695 // p+offset must point to the comma preceding the arg on entry.
01696 // On return, p+result points to the closing '"' (or end of the string
01697 // if the closing '"' is missing) if the arg is quoted.  If the arg
01698 // is not quoted, then p+result will point to the first character
01699 // of the arg.
01700 static PRInt32 advanceToEndOfQuotedArg( const char *p, PRInt32 offset, PRInt32 len ) {
01701     // Check whether the current arg is quoted.
01702     if ( p[++offset] == '"' ) {
01703         // Advance past the closing quote.
01704         while ( offset < len && p[++offset] != '"' ) {
01705             // If the current character is a backslash, then the
01706             // next character can't be a *real* '"', so skip it.
01707             if ( p[offset] == '\\' ) {
01708                 offset++;
01709             }
01710         }
01711     }
01712     return offset;
01713 }
01714 
01715 void nsNativeAppSupportWin::ParseDDEArg( const char* args, int index, nsCString& aString) {
01716     if ( args ) {
01717         int argLen = strlen(args);
01718         nsDependentCString temp(args, argLen);
01719 
01720         // offset points to the comma preceding the desired arg.
01721         PRInt32 offset = -1;
01722         // Skip commas till we get to the arg we want.
01723         while( index-- ) {
01724             // If this arg is quoted, then go to closing quote.
01725             offset = advanceToEndOfQuotedArg( args, offset, argLen);
01726             // Find next comma.
01727             offset = FindCharInString(temp, ',', offset );
01728             if ( offset == kNotFound ) {
01729                 // No more commas, give up.
01730                 aString = args;
01731                 return;
01732             }
01733         }
01734         // The desired argument starts just past the preceding comma,
01735         // which offset points to, and extends until the following
01736         // comma (or the end of the string).
01737         //
01738         // Since the argument might be enclosed in quotes, we need to
01739         // deal with that before searching for the terminating comma.
01740         // We advance offset so it ends up pointing to the start of
01741         // the argument we want.
01742         PRInt32 end = advanceToEndOfQuotedArg( args, offset++, argLen );
01743         // Find next comma (or end of string).
01744         end = FindCharInString(temp, ',', end );
01745         if ( end == kNotFound ) {
01746             // Arg is the rest of the string.
01747             end = argLen;
01748         }
01749         // Extract result.
01750         aString.Assign( args + offset, end - offset );
01751     }
01752     return;
01753 }
01754 
01755 // Utility to parse out argument from a DDE item string.
01756 void nsNativeAppSupportWin::ParseDDEArg( HSZ args, int index, nsCString& aString) {
01757     DWORD argLen = DdeQueryString( mInstance, args, NULL, NULL, CP_WINANSI );
01758     // there wasn't any string, so return empty string
01759     if ( !argLen ) return;
01760     // Ensure result's buffer is sufficiently big.
01761     char *temp = (char *) malloc(argLen + 1);
01762     if ( !temp ) return;
01763     // Now get the string contents.
01764     DdeQueryString( mInstance, args, temp, argLen + 1, CP_WINANSI );
01765     // Parse out the given arg.
01766     ParseDDEArg(temp, index, aString);
01767     free(temp);
01768 }
01769 
01770 void nsNativeAppSupportWin::ActivateLastWindow() {
01771     nsCOMPtr<nsIDOMWindowInternal> navWin;
01772     GetMostRecentWindow( NS_LITERAL_STRING("navigator:browser").get(), getter_AddRefs( navWin ) );
01773     if ( navWin ) {
01774         // Activate that window.
01775         activateWindow( navWin );
01776     } else {
01777         // Need to create a Navigator window, then.
01778         nsCOMPtr<nsIDOMWindow> newWin;
01779         OpenBrowserWindow( NS_LITERAL_STRING( "about:blank" ),
01780                            PR_TRUE, getter_AddRefs( newWin ) );
01781     }
01782 }
01783 
01784 HDDEDATA nsNativeAppSupportWin::CreateDDEData( DWORD value ) {
01785     return CreateDDEData( (LPBYTE)&value, sizeof value );
01786 }
01787 
01788 HDDEDATA nsNativeAppSupportWin::CreateDDEData( LPBYTE value, DWORD len ) {
01789     HDDEDATA result = DdeCreateDataHandle( mInstance,
01790                                            value,
01791                                            len,
01792                                            0,
01793                                            mApplication,
01794                                            CF_TEXT,
01795                                            0 );
01796     return result;
01797 }
01798 
01799 // Handle external request.  The first argument is the command line
01800 // received from the external client.  We convert that string to an
01801 // nsICmdLineService object via GetCmdLineArgs.  Then, we look for certain
01802 // well-known arguments.  This replicates code elsewhere, to some extent,
01803 // unfortunately (if you can fix that, please do).  The second argument may
01804 // be set to PR_TRUE to override the user's external link preference.
01805 nsresult
01806 nsNativeAppSupportWin::HandleRequest( LPBYTE request, PRBool newWindow, nsIDOMWindow **aResult ) {
01807 
01808     // if initial hidden window is still being displayed, we need to ignore requests
01809     // because such requests might not function properly.  See bug 147223 for details
01810 
01811     if (mInitialWindowActive) {
01812       return NS_ERROR_FAILURE;
01813     }
01814 
01815     // Parse command line.
01816 
01817     nsCOMPtr<nsICmdLineService> args;
01818     nsresult rv;
01819 
01820     rv = GetCmdLineArgs( request, getter_AddRefs( args ) );
01821     if (NS_FAILED(rv)) return rv;
01822 
01823     nsCOMPtr<nsIAppStartup> appStartup (do_GetService(NS_APPSTARTUP_CONTRACTID, &rv));
01824     if (NS_FAILED(rv)) return rv;
01825 
01826     nsCOMPtr<nsINativeAppSupport> nativeApp;
01827     rv = appStartup->GetNativeAppSupport(getter_AddRefs( nativeApp ));
01828     if (NS_FAILED(rv)) return rv;
01829 
01830     // first see if there is a url
01831     nsXPIDLCString arg;
01832     rv = args->GetURLToLoad(getter_Copies(arg));
01833     if (NS_FAILED(rv)) return rv;
01834 
01835     if (!arg.IsEmpty() ) {
01836       // Launch browser.
01837 #if MOZ_DEBUG_DDE
01838       printf( "Launching browser on url [%s]...\n", arg.get() );
01839 #endif
01840       rv = nativeApp->EnsureProfile(args);
01841       if (NS_SUCCEEDED(rv)) {
01842         nsAutoString tmpArg;
01843         NS_CopyNativeToUnicode( arg, tmpArg );
01844         rv = OpenBrowserWindow( tmpArg, newWindow, aResult );
01845       }
01846       return rv;
01847     }
01848 
01849 
01850     // ok, let's try the -chrome argument
01851     rv = args->GetCmdLineValue("-chrome", getter_Copies(arg));
01852     if (NS_SUCCEEDED(rv) && !arg.IsEmpty() ) {
01853       // Launch chrome.
01854 #if MOZ_DEBUG_DDE
01855       printf( "Launching chrome url [%s]...\n", arg.get() );
01856 #endif
01857       rv = nativeApp->EnsureProfile(args);
01858       if (NS_SUCCEEDED(rv))
01859         rv = OpenWindow( arg.get(), EmptyString(), aResult );
01860       return rv;
01861     }
01862 
01863     // try for the "-profilemanager" argument, in which case we want the
01864     // profile manager to appear, but only if there are no windows open
01865 
01866     rv = args->GetCmdLineValue( "-profilemanager", getter_Copies(arg));
01867     if ( NS_SUCCEEDED(rv) && !arg.IsEmpty() ) { // -profilemanager on command line
01868       nsCOMPtr<nsIDOMWindowInternal> window;
01869       GetMostRecentWindow(0, getter_AddRefs(window));
01870       if (!window) { // there are no open windows
01871         mForceProfileStartup = PR_TRUE;
01872       }
01873     }
01874 
01875     // try for the "-kill" argument, to shut down the server
01876     rv = args->GetCmdLineValue( "-kill", getter_Copies(arg));
01877     if ( NS_SUCCEEDED(rv) && !arg.IsEmpty() ) {
01878       // Turn off server mode.
01879       nsCOMPtr<nsIAppStartup> appStartup
01880         (do_GetService(NS_APPSTARTUP_CONTRACTID, &rv));
01881       if (NS_FAILED(rv)) return rv;
01882 
01883       nsCOMPtr<nsINativeAppSupport> native;
01884       rv = appStartup->GetNativeAppSupport( getter_AddRefs( native ));
01885       if (NS_SUCCEEDED(rv)) {
01886         native->SetIsServerMode( PR_FALSE );
01887 
01888         // close app if there are no more top-level windows.
01889         rv = appStartup->Quit(nsIAppStartup::eConsiderQuit);
01890       }
01891 
01892       return rv;
01893     }
01894 
01895     // check wheather it is a MAPI request.  If yes, don't open any new
01896     // windows and just return.
01897     rv = args->GetCmdLineValue(MAPI_STARTUP_ARG, getter_Copies(arg));
01898     if (NS_SUCCEEDED(rv) && !arg.IsEmpty()) {
01899       return nativeApp->EnsureProfile(args);
01900     }
01901 
01902     // Try standard startup's command-line handling logic from nsAppRunner.cpp...
01903 
01904     // Need profile before opening windows.
01905     rv = nativeApp->EnsureProfile(args);
01906     if (NS_FAILED(rv)) return rv;
01907 
01908     // This will tell us whether the command line processing opened a window.
01909     PRBool windowOpened = PR_FALSE;
01910 
01911     // Process command line options.
01912     rv = DoCommandLines( args, &windowOpened );
01913 
01914     // If a window was opened, then we're done.
01915     // Note that we keep on trying in the unlikely event of an error.
01916     if (rv == NS_ERROR_NOT_AVAILABLE || rv == NS_ERROR_ABORT || windowOpened) {
01917       return NS_OK;
01918     }
01919 
01920     // ok, no idea what the param is.
01921 #if MOZ_DEBUG_DDE
01922     printf( "Unknown request [%s]\n", (char*) request );
01923 #endif
01924     // if all else fails, open a browser window
01925     const char * const contractID =
01926       "@mozilla.org/commandlinehandler/general-startup;1?type=browser";
01927     nsCOMPtr<nsICmdLineHandler> handler = do_GetService(contractID, &rv);
01928     if (NS_FAILED(rv)) return rv;
01929 
01930     nsXPIDLString defaultArgs;
01931     rv = handler->GetDefaultArgs(getter_Copies(defaultArgs));
01932     if (NS_FAILED(rv) || defaultArgs.IsEmpty()) return rv;
01933 
01934     // force a new window for a home page group
01935     if (FindCharInString(defaultArgs, '\n') != kNotFound)
01936         newWindow = PR_TRUE;
01937 
01938     return OpenBrowserWindow(defaultArgs, newWindow, aResult);
01939 }
01940 
01941 // Parse command line args according to MS spec
01942 // (see "Parsing C++ Command-Line Arguments" at
01943 // http://msdn.microsoft.com/library/devprods/vs6/visualc/vclang/_pluslang_parsing_c.2b2b_.command.2d.line_arguments.htm).
01944 nsresult
01945 nsNativeAppSupportWin::GetCmdLineArgs( LPBYTE request, nsICmdLineService **aResult ) {
01946     nsresult rv = NS_OK;
01947 
01948     int justCounting = 1;
01949     char **argv = 0;
01950     // Flags, etc.
01951     int init = 1;
01952     int between, quoted, bSlashCount;
01953     int argc;
01954     char *p;
01955     nsCAutoString arg;
01956     // We loop if we've not finished the second pass through.
01957     while ( 1 ) {
01958         // Initialize if required.
01959         if ( init ) {
01960             p = (char*)request;
01961             between = 1;
01962             argc = quoted = bSlashCount = 0;
01963 
01964             init = 0;
01965         }
01966         if ( between ) {
01967             // We are traversing whitespace between args.
01968             // Check for start of next arg.
01969             if (  *p != 0 && !isspace( *p ) ) {
01970                 // Start of another arg.
01971                 between = 0;
01972                 arg = "";
01973                 switch ( *p ) {
01974                     case '\\':
01975                         // Count the backslash.
01976                         bSlashCount = 1;
01977                         break;
01978                     case '"':
01979                         // Remember we're inside quotes.
01980                         quoted = 1;
01981                         break;
01982                     default:
01983                         // Add character to arg.
01984                         arg += *p;
01985                         break;
01986                 }
01987             } else {
01988                 // Another space between args, ignore it.
01989             }
01990         } else {
01991             // We are processing the contents of an argument.
01992             // Check for whitespace or end.
01993             if ( *p == 0 || ( !quoted && isspace( *p ) ) ) {
01994                 // Process pending backslashes (interpret them
01995                 // literally since they're not followed by a ").
01996                 while( bSlashCount ) {
01997                     arg += '\\';
01998                     bSlashCount--;
01999                 }
02000                 // End current arg.
02001                 if ( !justCounting ) {
02002                     argv[argc] = new char[ arg.Length() + 1 ];
02003                     if (!argv[argc]) {
02004                         rv = NS_ERROR_OUT_OF_MEMORY;
02005                         break;
02006                     }
02007                     strcpy( argv[argc], arg.get() );
02008                 }
02009                 argc++;
02010                 // We're now between args.
02011                 between = 1;
02012             } else {
02013                 // Still inside argument, process the character.
02014                 switch ( *p ) {
02015                     case '"':
02016                         // First, digest preceding backslashes (if any).
02017                         while ( bSlashCount > 1 ) {
02018                             // Put one backsplash in arg for each pair.
02019                             arg += '\\';
02020                             bSlashCount -= 2;
02021                         }
02022                         if ( bSlashCount ) {
02023                             // Quote is literal.
02024                             arg += '"';
02025                             bSlashCount = 0;
02026                         } else {
02027                             // Quote starts or ends a quoted section.
02028                             if ( quoted ) {
02029                                 // Check for special case of consecutive double
02030                                 // quotes inside a quoted section.
02031                                 if ( *(p+1) == '"' ) {
02032                                     // This implies a literal double-quote.  Fake that
02033                                     // out by causing next double-quote to look as
02034                                     // if it was preceded by a backslash.
02035                                     bSlashCount = 1;
02036                                 } else {
02037                                     quoted = 0;
02038                                 }
02039                             } else {
02040                                 quoted = 1;
02041                             }
02042                         }
02043                         break;
02044                     case '\\':
02045                         // Add to count.
02046                         bSlashCount++;
02047                         break;
02048                     default:
02049                         // Accept any preceding backslashes literally.
02050                         while ( bSlashCount ) {
02051                             arg += '\\';
02052                             bSlashCount--;
02053                         }
02054                         // Just add next char to the current arg.
02055                         arg += *p;
02056                         break;
02057                 }
02058             }
02059         }
02060         // Check for end of input.
02061         if ( *p ) {
02062             // Go to next character.
02063             p++;
02064         } else {
02065             // If on first pass, go on to second.
02066             if ( justCounting ) {
02067                 // Allocate argv array.
02068                 argv = new char*[ argc ];
02069                 if (!argv) {
02070                     rv = NS_ERROR_OUT_OF_MEMORY;
02071                     break;
02072                 }
02073                 // Start second pass
02074                 justCounting = 0;
02075                 init = 1;
02076             } else {
02077                 // Quit.
02078                 break;
02079             }
02080         }
02081     }
02082 
02083     nsCOMPtr<nsIComponentManager> compMgr;
02084     NS_GetComponentManager(getter_AddRefs(compMgr));
02085     
02086     rv = compMgr->CreateInstanceByContractID(
02087                     NS_COMMANDLINESERVICE_CONTRACTID,
02088                     nsnull, NS_GET_IID(nsICmdLineService),
02089                     (void**) aResult);
02090 
02091     if ( NS_FAILED( rv ) || NS_FAILED( ( rv = (*aResult)->Initialize( argc, argv ) ) ) ) {
02092 #if MOZ_DEBUG_DDE
02093         printf( "Error creating command line service = 0x%08X (argc=%d, argv=0x%08X)\n", (int)rv, (int)argc, (void*)argv );
02094 #endif
02095     }
02096 
02097     // Cleanup.
02098     while ( argc ) {
02099         delete [] argv[ --argc ];
02100     }
02101     delete [] argv;
02102 
02103     return rv;
02104 }
02105 
02106 // Check to see if we have a profile. We will not have a profile
02107 // at this point if we were launched invisibly in -turbo mode, and
02108 // the profile mgr needed to show UI (to pick from multiple profiles).
02109 // At this point, we can show UI, so call DoProfileStartUp().
02110 nsresult
02111 nsNativeAppSupportWin::EnsureProfile(nsICmdLineService* args)
02112 {
02113   static PRBool firstTime = PR_TRUE;
02114   if ( firstTime ) {
02115     firstTime = PR_FALSE;
02116     // Check pref for whether to set ddeexec subkey entries.
02117     nsCOMPtr<nsIPrefBranch> prefService( do_GetService( NS_PREFSERVICE_CONTRACTID ) );
02118     PRBool supportDDEExec = PR_FALSE;
02119     if ( prefService ) {
02120         prefService->GetBoolPref( "advanced.system.supportDDEExec", &supportDDEExec );
02121     }
02122     if ( supportDDEExec && handlingHTTP() ) {
02123 #if MOZ_DEBUG_DDE
02124 printf( "Setting ddexec subkey entries\n" );
02125 #endif
02126 
02127       DWORD dwDisp;
02128       HKEY hKey;
02129       DWORD rc;
02130 
02131       rc = ::RegCreateKeyEx( HKEY_CLASSES_ROOT,
02132                              "http\\shell\\open\\ddeexec", 0,
02133                              NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL,
02134                              &hKey, &dwDisp );
02135 
02136       if ( REG_SUCCEEDED( rc ) ) {
02137         // Set ddeexec default value.
02138         const BYTE ddeexec[] = "\"%1\",,-1,0,,,,";
02139         ::RegSetValueEx( hKey, "", 0, REG_SZ, ddeexec, sizeof ddeexec );
02140         ::RegCloseKey( hKey );
02141       }
02142 
02143       // Set application/topic (while we're running), reset at exit.
02144       rc = ::RegCreateKeyEx( HKEY_CLASSES_ROOT,
02145                              "http\\shell\\open\\ddeexec\\application", 0,
02146                              NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL,
02147                              &hKey, &dwDisp );
02148 
02149       if ( REG_SUCCEEDED( rc ) ) {
02150         ::RegSetValueEx( hKey, "", 0, REG_SZ, (BYTE *) mAppName,
02151                          ::strlen( mAppName ) + 1 );
02152         ::RegCloseKey( hKey );
02153       }
02154 
02155       rc = ::RegCreateKeyEx( HKEY_CLASSES_ROOT,
02156                              "http\\shell\\open\\ddeexec\\topic", 0,
02157                              NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL,
02158                              &hKey, &dwDisp );
02159 
02160       if ( REG_SUCCEEDED( rc ) ) {
02161         const BYTE topic[] = "WWW_OpenURL";
02162         ::RegSetValueEx( hKey, "", 0, REG_SZ, topic, sizeof topic );
02163         ::RegCloseKey( hKey );
02164       }
02165 
02166       // Remember we need to undo this.
02167       mSupportingDDEExec = PR_TRUE;
02168     }
02169   }
02170 
02171   nsresult rv;
02172 
02173   nsCOMPtr<nsIProfileInternal> profileMgr(do_GetService(NS_PROFILE_CONTRACTID, &rv));
02174   if (NS_FAILED(rv)) return rv;
02175   nsCOMPtr<nsIAppStartup> appStartup (do_GetService(NS_APPSTARTUP_CONTRACTID, &rv));
02176   if (NS_FAILED(rv)) return rv;
02177 
02178   // If we have a profile, everything is fine -
02179   // unless mForceProfileStartup is TRUE. This flag is set when the
02180   // last window is closed in -turbo mode. When TRUE, it forces the
02181   // profile UI to come up at the beginning of the next -turbo session
02182   // even if we currently have a profile.
02183   PRBool haveProfile;
02184   rv = profileMgr->IsCurrentProfileAvailable(&haveProfile);
02185   if (!mForceProfileStartup && NS_SUCCEEDED(rv) && haveProfile)
02186       return NS_OK;
02187 
02188   // If the profile selection is happening, fail.
02189   PRBool doingProfileStartup;
02190   rv = profileMgr->GetIsStartingUp(&doingProfileStartup);
02191   if (NS_FAILED(rv) || doingProfileStartup) return NS_ERROR_FAILURE;
02192 
02193   // See if profile manager is being suppressed via -silent flag.
02194   PRBool canInteract = PR_TRUE;
02195   nsXPIDLCString arg;
02196   if (NS_SUCCEEDED(args->GetCmdLineValue("-silent", getter_Copies(arg)))) {
02197     if (!arg.IsEmpty()) {
02198       canInteract = PR_FALSE;
02199     }
02200   }
02201   rv = appStartup->DoProfileStartup(args, canInteract);
02202 
02203   mForceProfileStartup = PR_FALSE;
02204 
02205   return rv;
02206 }
02207 
02208 nsresult
02209 nsNativeAppSupportWin::OpenWindow( const char *urlstr,
02210                                    const nsAString& aArgs,
02211                                    nsIDOMWindow **aResult )
02212 {
02213 
02214   nsresult rv = NS_ERROR_FAILURE;
02215 
02216   nsCOMPtr<nsIWindowWatcher> wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID));
02217   nsCOMPtr<nsISupportsString>
02218     sarg(do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID));
02219   if (sarg)
02220     sarg->SetData(aArgs);
02221 
02222   if (wwatch && sarg) {
02223     rv = wwatch->OpenWindow(0, urlstr, "_blank", "chrome,dialog=no,all",
02224                    sarg, aResult);
02225 #if MOZ_DEBUG_DDE
02226   } else {
02227       printf("Get WindowWatcher (or create string) failed\n");
02228 #endif
02229   }
02230   return rv;
02231 }
02232 
02233 static char procPropertyName[] = "MozillaProcProperty";
02234 
02235 // Subclass procedure used to filter out WM_SETFOCUS messages while reparenting.
02236 static LRESULT CALLBACK focusFilterProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) {
02237     if ( uMsg == WM_SETFOCUS ) {
02238         // Don't let SeaMonkey's window procedure see this.
02239         return 0;
02240     } else {
02241         // Pass on all other messages to SeaMonkey's window proc.
02242         HANDLE oldProc = ::GetProp( hwnd, procPropertyName );
02243         if ( oldProc ) {
02244             return ::CallWindowProc( (WNDPROC)oldProc, hwnd, uMsg, wParam, lParam );
02245         } else {
02246             // Last resort is the default window proc.
02247             return ::DefWindowProc( hwnd, uMsg, wParam, lParam );
02248         }
02249     }
02250 }
02251 
02252 HWND hwndForDOMWindow( nsISupports *window ) {
02253     nsCOMPtr<nsIScriptGlobalObject> ppScriptGlobalObj( do_QueryInterface(window) );
02254     if ( !ppScriptGlobalObj ) {
02255         return 0;
02256     }
02257 
02258     nsCOMPtr<nsIBaseWindow> ppBaseWindow =
02259         do_QueryInterface( ppScriptGlobalObj->GetDocShell() );
02260     if ( !ppBaseWindow ) {
02261         return 0;
02262     }
02263 
02264     nsCOMPtr<nsIWidget> ppWidget;
02265     ppBaseWindow->GetMainWidget( getter_AddRefs( ppWidget ) );
02266 
02267     return (HWND)( ppWidget->GetNativeData( NS_NATIVE_WIDGET ) );
02268 }
02269 
02270 nsresult
02271 nsNativeAppSupportWin::ReParent( nsISupports *window, HWND newParent ) {
02272     HWND hMainFrame = hwndForDOMWindow( window );
02273     if ( !hMainFrame ) {
02274         return NS_ERROR_FAILURE;
02275     }
02276 
02277     // Filter out WM_SETFOCUS messages while reparenting to
02278     // other than the desktop.
02279     //
02280     // For some reason, Windows generates one and it causes
02281     // our focus/activation code to assert.
02282     LONG oldProc = 0;
02283     if ( newParent ) {
02284         // Subclass the window.
02285         oldProc = ::SetWindowLong( hMainFrame,
02286                                    GWL_WNDPROC,
02287                                    (LONG)(WNDPROC)focusFilterProc );
02288 
02289         // Store old procedure in window so it is available within
02290         // focusFilterProc.
02291         ::SetProp( hMainFrame, procPropertyName, (HANDLE)oldProc );
02292     }
02293 
02294     // Reset the parent.
02295     ::SetParent( hMainFrame, newParent );
02296 
02297     // Restore old procedure.
02298     if ( newParent ) {
02299         ::SetWindowLong( hMainFrame, GWL_WNDPROC, oldProc );
02300         ::RemoveProp( hMainFrame, procPropertyName );
02301     }
02302 
02303     return NS_OK;
02304 }
02305 
02306 static const char sJSStackContractID[] = "@mozilla.org/js/xpc/ContextStack;1";
02307 
02308 class SafeJSContext {
02309 public:
02310   SafeJSContext();
02311   ~SafeJSContext();
02312 
02313   nsresult   Push();
02314   JSContext *get() { return mContext; }
02315 
02316 protected:
02317   nsCOMPtr<nsIThreadJSContextStack>  mService;
02318   JSContext                         *mContext;
02319 };
02320 
02321 SafeJSContext::SafeJSContext() : mContext(nsnull) {
02322 }
02323 
02324 SafeJSContext::~SafeJSContext() {
02325   JSContext *cx;
02326   nsresult   rv;
02327 
02328   if(mContext) {
02329     rv = mService->Pop(&cx);
02330     NS_ASSERTION(NS_SUCCEEDED(rv) && cx == mContext, "JSContext push/pop mismatch");
02331   }
02332 }
02333 
02334 nsresult SafeJSContext::Push() {
02335   if (mContext) // only once
02336     return NS_ERROR_FAILURE;
02337 
02338   mService = do_GetService(sJSStackContractID);
02339   if(mService) {
02340     JSContext *cx;
02341     if (NS_SUCCEEDED(mService->GetSafeJSContext(&cx)) &&
02342         cx &&
02343         NS_SUCCEEDED(mService->Push(cx))) {
02344       // Save cx in mContext to indicate need to pop.
02345       mContext = cx;
02346     }
02347   }
02348   return mContext ? NS_OK : NS_ERROR_FAILURE;
02349 }
02350 
02351 
02352 nsresult
02353 nsNativeAppSupportWin::OpenBrowserWindow( const nsAString& aArgs,
02354                                           PRBool newWindow,
02355                                           nsIDOMWindow **aResult )
02356 {
02357     nsresult rv = NS_OK;
02358     // Open the argument URL according to the external link preference.
02359     // If there is no Nav window, or newWindow is PR_TRUE, open a new one.
02360 
02361     // Get most recently used Nav window.
02362     nsCOMPtr<nsIDOMWindowInternal> navWin;
02363     GetMostRecentWindow( NS_LITERAL_STRING( "navigator:browser" ).get(), getter_AddRefs( navWin ) );
02364 
02365     // This isn't really a loop.  We just use "break" statements to fall
02366     // out to the OpenWindow call when things go awry.
02367     do {
02368         // If caller requires a new window, then don't use an existing one.
02369         if ( newWindow ) {
02370             break;
02371         }
02372         if ( !navWin ) {
02373             // Have to open a new one.
02374             break;
02375         }
02376         nsCOMPtr<nsIDOMChromeWindow> chromeWin( do_QueryInterface( navWin ) );
02377         if ( !chromeWin ) {
02378             break;
02379         }
02380         nsCOMPtr<nsIBrowserDOMWindow> bwin;
02381         chromeWin->GetBrowserDOMWindow( getter_AddRefs( bwin ) );
02382         if ( !bwin ) {
02383             break;
02384         }
02385         nsCOMPtr<nsIURIFixup> fixup( do_GetService( NS_URIFIXUP_CONTRACTID ) );
02386         if ( !fixup ) {
02387             break;
02388         }
02389         nsCOMPtr<nsIURI> uri;
02390         rv = fixup->CreateFixupURI( NS_ConvertUTF16toUTF8( aArgs ),
02391                                     nsIURIFixup::FIXUP_FLAG_NONE,
02392                                     getter_AddRefs( uri ) );
02393         if ( NS_FAILED(rv) || !uri ) {
02394             break;
02395         }
02396         return bwin->OpenURI( uri, nsnull, nsIBrowserDOMWindow::OPEN_DEFAULTWINDOW, nsIBrowserDOMWindow::OPEN_EXTERNAL, aResult );
02397     } while ( PR_FALSE );
02398 
02399     nsCOMPtr<nsICmdLineHandler> handler(do_GetService("@mozilla.org/commandlinehandler/general-startup;1?type=browser", &rv));
02400     if (NS_FAILED(rv)) return rv;
02401 
02402     nsXPIDLCString chromeUrlForTask;
02403     rv = handler->GetChromeUrlForTask(getter_Copies(chromeUrlForTask));
02404     if (NS_FAILED(rv)) return rv;
02405 
02406     // Last resort is to open a brand new window.
02407     return OpenWindow( chromeUrlForTask.get(), aArgs, aResult );
02408 }
02409 
02410 void AppendMenuItem( HMENU& menu, PRInt32 aIdentifier, const nsString& aText ) {
02411   char* ACPText = GetACPString( aText );
02412   if ( ACPText ) {
02413     ::AppendMenu( menu, MF_STRING, aIdentifier, ACPText );
02414     delete [] ACPText;
02415   }
02416 }
02417 
02418 // Utility function that sets up system tray icon.
02419 
02420 void
02421 nsNativeAppSupportWin::SetupSysTrayIcon() {
02422     // Messages go to the hidden window.
02423     mIconData.hWnd  = (HWND)MessageWindow();
02424 
02425     // Icon is our default application icon.
02426     mIconData.hIcon =  (HICON)::LoadImage( ::GetModuleHandle( NULL ),
02427                                            IDI_APPLICATION,
02428                                            IMAGE_ICON,
02429                                            ::GetSystemMetrics( SM_CXSMICON ),
02430                                            ::GetSystemMetrics( SM_CYSMICON ),
02431                                            NULL );
02432 
02433     // Tooltip is the brand short name.
02434     mIconData.szTip[0] = 0;
02435     nsCOMPtr<nsIStringBundleService> svc( do_GetService( NS_STRINGBUNDLE_CONTRACTID ) );
02436     if ( svc ) {
02437         nsCOMPtr<nsIStringBundle> brandBundle;
02438         nsXPIDLString tooltip;
02439         svc->CreateBundle( "chrome://branding/locale/brand.properties", getter_AddRefs( brandBundle ) );
02440         if ( brandBundle ) {
02441             brandBundle->GetStringFromName( NS_LITERAL_STRING( "brandShortName" ).get(),
02442                                             getter_Copies( tooltip ) );
02443             ::strncpy( mIconData.szTip,
02444                        NS_LossyConvertUTF16toASCII(tooltip).get(),
02445                        sizeof mIconData.szTip - 1 );
02446         }
02447         // Build menu.
02448         nsCOMPtr<nsIStringBundle> turboBundle;
02449         nsCOMPtr<nsIStringBundle> mailBundle;
02450         svc->CreateBundle( "chrome://navigator/locale/turboMenu.properties",
02451                            getter_AddRefs( turboBundle ) );
02452         nsresult rv = svc->CreateBundle( "chrome://messenger/locale/mailTurboMenu.properties",
02453                                          getter_AddRefs( mailBundle ) );
02454         PRBool isMail = NS_SUCCEEDED(rv) && mailBundle;
02455         nsAutoString exitText;
02456         nsAutoString disableText;
02457         nsAutoString navigatorText;
02458         nsAutoString editorText;
02459         nsAutoString mailText;
02460         nsAutoString addressbookText;
02461         nsXPIDLString text;
02462         if ( turboBundle ) {
02463             if ( brandBundle ) {
02464                 const PRUnichar* formatStrings[] = { tooltip.get() };
02465                 turboBundle->FormatStringFromName( NS_LITERAL_STRING( "Exit" ).get(), formatStrings, 1,
02466                                                    getter_Copies( text ) );
02467                 exitText = text;
02468             }
02469             turboBundle->GetStringFromName( NS_LITERAL_STRING( "Disable" ).get(),
02470                                             getter_Copies( text ) );
02471             disableText = text;
02472             turboBundle->GetStringFromName( NS_LITERAL_STRING( "Navigator" ).get(),
02473                                             getter_Copies( text ) );
02474             navigatorText = text;
02475             turboBundle->GetStringFromName( NS_LITERAL_STRING( "Editor" ).get(),
02476                                             getter_Copies( text ) );
02477             editorText = text;
02478         }
02479         if (isMail) {
02480             mailBundle->GetStringFromName( NS_LITERAL_STRING( "MailNews" ).get(),
02481                                            getter_Copies( text ) );
02482             mailText = text;
02483             mailBundle->GetStringFromName( NS_LITERAL_STRING( "Addressbook" ).get(),
02484                                            getter_Copies( text ) );
02485             addressbookText = text;
02486         }
02487 
02488         if ( exitText.IsEmpty() ) {
02489             exitText.Assign( NS_LITERAL_STRING( "E&xit " ) );
02490             exitText.Append( NS_LITERAL_STRING( NS_STRINGIFY(MOZ_APP_DISPLAYNAME) ) );
02491         }
02492 
02493         if ( disableText.IsEmpty() )
02494             disableText.Assign( NS_LITERAL_STRING("&Disable Quick Launch") );
02495 
02496         if ( navigatorText.IsEmpty() )
02497             navigatorText.Assign( NS_LITERAL_STRING("&Navigator") );
02498 
02499         if ( editorText.IsEmpty() )
02500             editorText.Assign( NS_LITERAL_STRING("&Composer") );
02501 
02502         if ( isMail ) {
02503             if ( mailText.IsEmpty() )
02504               mailText.Assign( NS_LITERAL_STRING("&Mail && Newsgroups") );
02505             if ( addressbookText.IsEmpty() )
02506               addressbookText.Assign( NS_LITERAL_STRING("&Address Book") );
02507         }
02508         // Create menu and add item.
02509         mTrayIconMenu = ::CreatePopupMenu();
02510         ::AppendMenuW( mTrayIconMenu, MF_STRING, TURBO_NAVIGATOR, navigatorText.get() );
02511         if ( ::GetLastError() == ERROR_CALL_NOT_IMPLEMENTED ) {
02512             AppendMenuItem( mTrayIconMenu, TURBO_NAVIGATOR, navigatorText );
02513             if ( isMail )
02514                 AppendMenuItem( mTrayIconMenu, TURBO_MAIL, mailText );
02515             AppendMenuItem( mTrayIconMenu, TURBO_EDITOR, editorText );
02516             if ( isMail )
02517                 AppendMenuItem( mTrayIconMenu, TURBO_ADDRESSBOOK, addressbookText );
02518             ::AppendMenu( mTrayIconMenu, MF_SEPARATOR, NULL, NULL );
02519             AppendMenuItem( mTrayIconMenu, TURBO_DISABLE, disableText );
02520             AppendMenuItem( mTrayIconMenu, TURBO_EXIT, exitText );
02521         }
02522         else {
02523             if (isMail)
02524                 ::AppendMenuW( mTrayIconMenu, MF_STRING, TURBO_MAIL, mailText.get() );
02525             ::AppendMenuW( mTrayIconMenu, MF_STRING, TURBO_EDITOR, editorText.get() );
02526             if (isMail)
02527                 ::AppendMenuW( mTrayIconMenu, MF_STRING, TURBO_ADDRESSBOOK, addressbookText.get() );
02528             ::AppendMenuW( mTrayIconMenu, MF_SEPARATOR, NULL, NULL );
02529             ::AppendMenuW( mTrayIconMenu, MF_STRING, TURBO_DISABLE, disableText.get() );
02530             ::AppendMenuW( mTrayIconMenu, MF_STRING, TURBO_EXIT, exitText.get() );
02531         }
02532     }
02533 
02534     // Add the tray icon.
02535 
02536     /* The tray icon will be removed if explorer restarts. Therefore, we are registering
02537     the following window message so we know when the taskbar is created. Explorer will send
02538     this message when explorer restarts.*/
02539     mTrayRestart = ::RegisterWindowMessage(TEXT("TaskbarCreated"));
02540     ::Shell_NotifyIcon( NIM_ADD, &mIconData );
02541 }
02542 
02543 // Utility function to remove the system tray icon.
02544 void
02545 nsNativeAppSupportWin::RemoveSysTrayIcon() {
02546     // Remove the tray icon.
02547     mTrayRestart = 0;
02548     ::Shell_NotifyIcon( NIM_DELETE, &mIconData );
02549     // Delete the menu.
02550     ::DestroyMenu( mTrayIconMenu );
02551 }
02552 
02553 
02554 
02555 //   This opens a special browser window for purposes of priming the pump for
02556 //   server mode (getting stuff into the caching, loading .dlls, etc.).  The
02557 //   window will have these attributes:
02558 //     - Load about:blank (no home page)
02559 //     - No toolbar (so there's no sidebar panels loaded, either)
02560 //     - Pass magic arg to cause window to close in onload handler.
02561 NS_IMETHODIMP
02562 nsNativeAppSupportWin::StartServerMode() {
02563 
02564     // Turn on system tray icon.
02565     SetupSysTrayIcon();
02566 
02567     if (mShouldShowUI) {
02568         // We dont have to anything anymore. The native UI
02569         // will create the window
02570         return NS_OK;
02571     } else {
02572         // Sometimes a window will have been opened even though mShouldShowUI is false
02573         // (e.g., seamonkey -mail -turbo).  Detect that by testing whether there's a
02574         // window already open.
02575         nsCOMPtr<nsIDOMWindowInternal> win;
02576         GetMostRecentWindow( 0, getter_AddRefs( win ) );
02577         if ( win ) {
02578             // Window already opened, don't need this special Nav window.
02579             return NS_OK;
02580         }
02581     }
02582 
02583     // Since native UI wont create any window, we create a hidden window
02584     // so thing work alright.
02585 
02586     // Create some of the objects we'll need.
02587     nsCOMPtr<nsIWindowWatcher>   ww(do_GetService(NS_WINDOWWATCHER_CONTRACTID));
02588     nsCOMPtr<nsISupportsString> arg1(do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID));
02589     nsCOMPtr<nsISupportsString> arg2(do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID));
02590     if ( !ww || !arg1 || !arg2 ) {
02591         return NS_OK;
02592     }
02593 
02594     // Create the array for the arguments.
02595     nsCOMPtr<nsISupportsArray> argArray = do_CreateInstance(NS_SUPPORTSARRAY_CONTRACTID);
02596     if ( !argArray ) {
02597         return NS_OK;
02598     }
02599 
02600     // arg1 is the url to load.
02601     // arg2 is the string that tells navigator.js to auto-close.
02602     arg1->SetData( NS_LITERAL_STRING( "about:blank" ) );
02603     arg2->SetData( NS_LITERAL_STRING( "turbo=yes" ) );
02604 
02605     // Put args into array.
02606     if ( NS_FAILED( argArray->AppendElement( arg1 ) ) ||
02607         NS_FAILED( argArray->AppendElement( arg2 ) ) ) {
02608         return NS_OK;
02609     }
02610 
02611     // Now open the window.
02612     nsCOMPtr<nsIDOMWindow> newWindow;
02613     ww->OpenWindow( 0,
02614         "chrome://navigator/content",
02615         "_blank",
02616         "chrome,dialog=no,toolbar=no",
02617         argArray,
02618         getter_AddRefs( newWindow ) );
02619 
02620     if ( !newWindow ) {
02621         return NS_OK;
02622     }
02623     mInitialWindowActive = PR_TRUE;
02624 
02625     // Hide this window by re-parenting it (to ensure it doesn't appear).
02626     ReParent( newWindow, (HWND)MessageWindow() );
02627 
02628     return NS_OK;
02629 }
02630 
02631 NS_IMETHODIMP
02632 nsNativeAppSupportWin::SetIsServerMode( PRBool isServerMode ) {
02633     // If it is being turned off, remove systray icon.
02634     if ( mServerMode && !isServerMode ) {
02635         RemoveSysTrayIcon();
02636     }
02637     else if ( !mServerMode && isServerMode) {
02638         SetupSysTrayIcon();
02639     }
02640     return nsNativeAppSupportBase::SetIsServerMode( isServerMode );
02641 }
02642 
02643 NS_IMETHODIMP
02644 nsNativeAppSupportWin::OnLastWindowClosing() {
02645 
02646     if ( !mServerMode )
02647         return NS_OK;
02648 
02649     // If the last window closed is our special "turbo" window made
02650     // in StartServerMode(), don't do anything.
02651     if ( mInitialWindowActive ) {
02652         mInitialWindowActive = PR_FALSE;
02653         mShouldShowUI = PR_TRUE;
02654         return NS_OK;
02655     }
02656 
02657     // If the last window closed is our confirmation dialog,
02658     // don't do anything.
02659     if ( mLastWindowIsConfirmation ) {
02660         mLastWindowIsConfirmation = PR_FALSE;
02661         return NS_OK;
02662     }
02663 
02664 
02665     nsresult rv;
02666 
02667     // If activated by the browser.turbo.singleProfileOnly pref,
02668     // check for multi-profile situation and turn off turbo mode
02669     // if there are multiple profiles.
02670     PRBool singleProfileOnly = PR_FALSE;
02671     nsCOMPtr<nsIPrefBranch> prefService( do_GetService( NS_PREFSERVICE_CONTRACTID, &rv ) );
02672     if ( NS_SUCCEEDED( rv ) ) {
02673         prefService->GetBoolPref( "browser.turbo.singleProfileOnly", &singleProfileOnly );
02674     }
02675     if ( singleProfileOnly ) {
02676         nsCOMPtr<nsIProfile> profileMgr( do_GetService( NS_PROFILE_CONTRACTID, &rv ) );
02677         if ( NS_SUCCEEDED( rv ) ) {
02678             PRInt32 profileCount = 0;
02679             if ( NS_SUCCEEDED( profileMgr->GetProfileCount( &profileCount ) ) &&
02680                  profileCount > 1 ) {
02681                 // Turn off turbo mode and quit the application.
02682                 SetIsServerMode( PR_FALSE );
02683                 nsCOMPtr<nsIAppStartup> appStartup
02684                     (do_GetService(NS_APPSTARTUP_CONTRACTID, &rv));
02685                 if ( NS_SUCCEEDED( rv ) ) {
02686                     appStartup->Quit(nsIAppStartup::eAttemptQuit);
02687                 }
02688                 return NS_OK;
02689             }
02690         }
02691     }
02692 
02693     if ( !mShownTurboDialog ) {
02694         PRBool showDialog = PR_TRUE;
02695         if ( NS_SUCCEEDED( rv ) )
02696             prefService->GetBoolPref( "browser.turbo.showDialog", &showDialog );
02697 
02698         if ( showDialog ) {
02699           /* show turbo dialog, unparented. at this point in the application
02700              shutdown process the last window is largely torn down and
02701              unsuitable for parenthood.
02702           */
02703           nsCOMPtr<nsIWindowWatcher> wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID));
02704           if ( wwatch ) {
02705               nsCOMPtr<nsIDOMWindow> newWindow;
02706               mShownTurboDialog = PR_TRUE;
02707               mLastWindowIsConfirmation = PR_TRUE;
02708               rv = wwatch->OpenWindow(0, "chrome://navigator/content/turboDialog.xul",
02709                                       "_blank", "chrome,modal,titlebar,centerscreen,dialog",
02710                                       0, getter_AddRefs(newWindow));
02711           }
02712         }
02713     }
02714 
02715     nsCOMPtr<nsIAppStartup> appStartup
02716         (do_GetService(NS_APPSTARTUP_CONTRACTID, &rv));
02717     if ( NS_SUCCEEDED( rv ) ) {
02718         // Instead of staying alive, launch a new instance of the application and then
02719         // terminate for real.  We take steps to ensure that the new instance will run
02720         // as a "server process" and not try to pawn off its request back on this
02721         // instance.
02722 
02723         // Grab mutex.  Process termination will release it.
02724         Mutex mutexLock = Mutex(mMutexName);
02725         NS_ENSURE_TRUE(mutexLock.Lock(MOZ_DDE_START_TIMEOUT), NS_ERROR_FAILURE );
02726 
02727         // Turn off MessageWindow so the other process can't see us.
02728         MessageWindow mw;
02729         mw.Destroy();
02730 
02731         // Launch another instance.
02732         char buffer[ _MAX_PATH ];
02733         // Same application as this one.
02734         ::GetModuleFileName( 0, buffer, sizeof buffer );
02735         // Clean up name so we don't have to worry about enclosing it in quotes.
02736         ::GetShortPathName( buffer, buffer, sizeof buffer );
02737         nsCAutoString cmdLine( buffer );
02738         // The new process must run in turbo mode (no splash screen, no window, etc.).
02739         cmdLine.Append( " -turbo" );
02740 
02741         // Now do the Win32 stuff...
02742         STARTUPINFO startupInfo;
02743         ::GetStartupInfo( &startupInfo );
02744         PROCESS_INFORMATION processInfo;
02745         DWORD rc = ::CreateProcess( 0,
02746                               (LPTSTR)cmdLine.get(),
02747                               0,
02748                               0,
02749                               0,
02750                               0,
02751                               0,
02752                               0,
02753                               &startupInfo,
02754                               &processInfo );
02755 
02756         // Turn off turbo mode and quit the application.
02757         SetIsServerMode( PR_FALSE );
02758         appStartup->Quit(nsIAppStartup::eAttemptQuit);
02759 
02760         // Done.  This app will now commence shutdown.
02761     }
02762     return NS_OK;
02763 }