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  *   Robert Strong  robert.bugzilla@gmail.com
00025  *
00026  * Alternatively, the contents of this file may be used under the terms of
00027  * either the GNU General Public License Version 2 or later (the "GPL"), or
00028  * 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 "nsNativeAppSupportBase.h"
00041 #include "nsNativeAppSupportWin.h"
00042 #include "nsAppRunner.h"
00043 #include "nsXULAppAPI.h"
00044 #include "nsString.h"
00045 #include "nsIBrowserDOMWindow.h"
00046 #include "nsICommandLineRunner.h"
00047 #include "nsCOMPtr.h"
00048 #include "nsXPIDLString.h"
00049 #include "nsIComponentManager.h"
00050 #include "nsIServiceManager.h"
00051 #include "nsIDOMWindow.h"
00052 #include "nsIDOMChromeWindow.h"
00053 #include "nsXPCOM.h"
00054 #include "nsISupportsPrimitives.h"
00055 #include "nsISupportsArray.h"
00056 #include "nsIWindowWatcher.h"
00057 #include "nsIDOMWindowInternal.h"
00058 #include "nsIScriptGlobalObject.h"
00059 #include "nsIDocShell.h"
00060 #include "nsIDocShellTreeItem.h"
00061 #include "nsIBaseWindow.h"
00062 #include "nsIWidget.h"
00063 #include "nsIAppShellService.h"
00064 #include "nsIProfileInternal.h"
00065 #include "nsIXULWindow.h"
00066 #include "nsIInterfaceRequestor.h"
00067 #include "nsIInterfaceRequestorUtils.h"
00068 #include "nsIPromptService.h"
00069 #include "nsNetCID.h"
00070 #include "nsNetUtil.h"
00071 #include "nsIObserver.h"
00072 #include "nsIObserverService.h"
00073 #ifdef MOZ_PHOENIX
00074 #include "nsIShellService.h"
00075 #endif
00076 #include "nsIDOMLocation.h"
00077 #include "nsIJSContextStack.h"
00078 #include "nsIWebNavigation.h"
00079 #include "nsIWindowMediator.h"
00080 #include "nsNativeCharsetUtils.h"
00081 
00082 #include <windows.h>
00083 #include <shellapi.h>
00084 #include <ddeml.h>
00085 #include <stdlib.h>
00086 #include <stdio.h>
00087 #include <io.h>
00088 #include <direct.h>
00089 #include <fcntl.h>
00090 
00091 static HWND hwndForDOMWindow( nsISupports * );
00092 
00093 static
00094 nsresult
00095 GetMostRecentWindow(const PRUnichar* aType, nsIDOMWindowInternal** aWindow) {
00096     nsresult rv;
00097     nsCOMPtr<nsIWindowMediator> med( do_GetService( NS_WINDOWMEDIATOR_CONTRACTID, &rv ) );
00098     if ( NS_FAILED( rv ) )
00099         return rv;
00100 
00101     if ( med )
00102         return med->GetMostRecentWindow( aType, aWindow );
00103 
00104     return NS_ERROR_FAILURE;
00105 }
00106 
00107 static
00108 void
00109 activateWindow( nsIDOMWindowInternal *win ) {
00110     // Try to get native window handle.
00111     HWND hwnd = hwndForDOMWindow( win );
00112     if ( hwnd ) {
00113         // Restore the window if it is minimized.
00114         if ( ::IsIconic( hwnd ) ) {
00115             ::ShowWindow( hwnd, SW_RESTORE );
00116         }
00117         // Use the OS call, if possible.
00118         ::SetForegroundWindow( hwnd );
00119     } else {
00120         // Use internal method.
00121         win->Focus();
00122     }
00123 }
00124 
00125 
00126 #ifdef DEBUG_law
00127 #undef MOZ_DEBUG_DDE
00128 #define MOZ_DEBUG_DDE 1
00129 #endif
00130 
00131 // Simple Win32 mutex wrapper.
00132 struct Mutex {
00133     Mutex( const char *name )
00134         : mName( name ),
00135           mHandle( 0 ),
00136           mState( -1 ) {
00137         mHandle = CreateMutex( 0, FALSE, mName.get() );
00138 #if MOZ_DEBUG_DDE
00139         printf( "CreateMutex error = 0x%08X\n", (int)GetLastError() );
00140 #endif
00141     }
00142     ~Mutex() {
00143         if ( mHandle ) {
00144             // Make sure we release it if we own it.
00145             Unlock();
00146 
00147             BOOL rc = CloseHandle( mHandle );
00148 #if MOZ_DEBUG_DDE
00149             if ( !rc ) {
00150                 printf( "CloseHandle error = 0x%08X\n", (int)GetLastError() );
00151             }
00152 #endif
00153         }
00154     }
00155     BOOL Lock( DWORD timeout ) {
00156         if ( mHandle ) {
00157 #if MOZ_DEBUG_DDE
00158             printf( "Waiting (%d msec) for DDE mutex...\n", (int)timeout );
00159 #endif
00160             mState = WaitForSingleObject( mHandle, timeout );
00161 #if MOZ_DEBUG_DDE
00162             printf( "...wait complete, result = 0x%08X, GetLastError=0x%08X\n", (int)mState, (int)::GetLastError() );
00163 #endif
00164             return mState == WAIT_OBJECT_0 || mState == WAIT_ABANDONED;
00165         } else {
00166             return FALSE;
00167         }
00168     }
00169     void Unlock() {
00170         if ( mHandle && mState == WAIT_OBJECT_0 ) {
00171 #if MOZ_DEBUG_DDE
00172             printf( "Releasing DDE mutex\n" );
00173 #endif
00174             ReleaseMutex( mHandle );
00175             mState = -1;
00176         }
00177     }
00178 private:
00179     nsCString mName;
00180     HANDLE    mHandle;
00181     DWORD     mState;
00182 };
00183 
00184 /* DDE Notes
00185  *
00186  * This section describes the Win32 DDE service implementation for
00187  * Mozilla.  DDE is used on Win32 platforms to communicate between
00188  * separate instances of mozilla.exe (or other Mozilla-based
00189  * executables), or, between the Win32 desktop shell and Mozilla.
00190  *
00191  * The first instance of Mozilla will become the "server" and
00192  * subsequent executables (and the shell) will use DDE to send
00193  * requests to that process.  The requests are DDE "execute" requests
00194  * that pass the command line arguments.
00195  *
00196  * Mozilla registers the DDE application "Mozilla" and currently
00197  * supports only the "WWW_OpenURL" topic.  This should be reasonably
00198  * compatible with applications that interfaced with Netscape
00199  * Communicator (and its predecessors?).  Note that even that topic
00200  * may not be supported in a compatible fashion as the command-line
00201  * options for Mozilla are different than for Communiator.
00202  *
00203  * It is imperative that at most one instance of Mozilla execute in
00204  * "server mode" at any one time.  The "native app support" in Mozilla
00205  * on Win32 ensures that only the server process performs XPCOM
00206  * initialization (that is not required for subsequent client processes
00207  * to communicate with the server process).
00208  *
00209  * To guarantee that only one server starts up, a Win32 "mutex" is used
00210  * to ensure only one process executes the server-detection code.  That
00211  * code consists of initializing DDE and doing a DdeConnect to Mozilla's
00212  * application/topic.  If that connection succeeds, then a server process
00213  * must be running already.
00214  *
00215  * Otherwise, no server has started.  In that case, the current process
00216  * calls DdeNameService to register that application/topic.  Only at that
00217  * point does the mutex get released.
00218  *
00219  * There are a couple of subtleties that one should be aware of:
00220  *
00221  * 1. It is imperative that DdeInitialize be called only after the mutex
00222  *    lock has been obtained.  The reason is that at shutdown, DDE
00223  *    notifications go out to all initialized DDE processes.  Thus, if
00224  *    the mutex is owned by a terminating intance of Mozilla, then
00225  *    calling DdeInitialize and then WaitForSingleObject will cause the
00226  *    DdeUninitialize from the terminating process to "hang" until the
00227  *    process waiting for the mutex times out (and can then service the
00228  *    notification that the DDE server is terminating).  So, don't mess
00229  *    with the sequence of things in the startup/shutdown logic.
00230  *
00231  * 2. All mutex requests are made with a reasonably long timeout value and
00232  *    are designed to "fail safe" (i.e., a timeout is treated as failure).
00233  *
00234  * 3. An attempt has been made to minimize the degree to which the main
00235  *    Mozilla application logic needs to be aware of the DDE mechanisms
00236  *    implemented herein.  As a result, this module surfaces a very
00237  *    large-grained interface, consisting of simple start/stop methods.
00238  *    As a consequence, details of certain scenarios can be "lost."
00239  *    Particularly, incoming DDE requests can arrive after this module
00240  *    initiates the DDE server, but before Mozilla is initialized to the
00241  *    point where those requests can be serviced (e.g., open a browser
00242  *    window to a particular URL).  Since the client process sends the
00243  *    request early on, it may not be prepared to respond to that error.
00244  *    Thus, such situations may fail silently.  The design goal is that
00245  *    they fail harmlessly.  Refinements on this point will be made as
00246  *    details emerge (and time permits).
00247  */
00248 
00249 /* Update 2001 March
00250  *
00251  * A significant DDE bug in Windows is causing Mozilla to get wedged at
00252  * startup.  This is detailed in Bugzill bug 53952
00253  * (http://bugzilla.mozilla.org/show_bug.cgi?id=53952).
00254  *
00255  * To resolve this, we are using a new strategy:
00256  *   o Use a "message window" to detect that Mozilla is already running and
00257  *     to pass requests from a second instance back to the first;
00258  *   o Run only as a "DDE server" (not as DDE client); this avoids the
00259  *     problematic call to DDEConnect().
00260  *
00261  * We still use the mutex semaphore to protect the code that detects
00262  * whether Mozilla is already running.
00263  */
00264 
00265 /* Update 2007 January
00266  *
00267  * A change in behavior was implemented in July 2004 which made the
00268  * application on launch to add and on quit to remove the ddexec registry key.
00269  * See bug 246078.
00270  * Windows Vista has changed the methods used to set an application as default
00271  * and the new methods are incompatible with removing the ddeexec registry key.
00272  * See bug 353089.
00273  *
00274  * OS DDE Sequence:
00275  * 1. OS checks if the dde name is registered.
00276  * 2. If it is registered the OS sends a DDE request with the WWW_OpenURL topic
00277  *    and the params as specified in the default value of the ddeexec registry
00278  *    key for the verb (e.g. open).
00279  * 3. If it isn't registered the OS launches the executable defined in the
00280  *    verb's (e.g. open) command registry key.
00281  * 4. If the ifexec registry key is not present the OS sends a DDE request with
00282  *    the WWW_OpenURL topic and the params as specified in the default value of
00283  *    the ddeexec registry key for the verb (e.g. open).
00284  * 5. If the ifexec registry key is present the OS sends a DDE request with the
00285  *    WWW_OpenURL topic and the params as specified in the ifexec registry key
00286  *    for the verb (e.g. open).
00287  *
00288  * Application DDE Sequence:
00289  * 1. If the application is running a DDE request is received with the
00290  *    WWW_OpenURL topic and the params as specified in the default value of the
00291  *    ddeexec registry key (e.g. "%1",,0,0,,,, where '%1' is the url to open)
00292  *    for the verb (e.g. open).
00293  * 2. If the application is not running it is launched with the -requestPending
00294  *    and the -url argument.
00295  * 2.1  If the application does not need to restart and the -requestPending
00296  *      argument is present the accompanying url will not be used. Instead the
00297  *      application will wait for the DDE message to open the url.
00298  * 2.2  If the application needs to restart the -requestPending argument is
00299  *      removed from the arguments used to restart the application and the url
00300  *      will be handled normally.
00301  *
00302  * Note: Due to a bug in IE the ifexec key should not be used (see bug 355650).
00303  */
00304 
00305 class nsNativeAppSupportWin : public nsNativeAppSupportBase,
00306                               public nsIObserver
00307 {
00308 public:
00309     NS_DECL_NSIOBSERVER
00310     NS_DECL_ISUPPORTS_INHERITED
00311 
00312     // Overrides of base implementation.
00313     NS_IMETHOD Start( PRBool *aResult );
00314     NS_IMETHOD Stop( PRBool *aResult );
00315     NS_IMETHOD Quit();
00316     NS_IMETHOD Enable();
00317 
00318     // The "old" Start method (renamed).
00319     NS_IMETHOD StartDDE();
00320 
00321     // Utility function to handle a Win32-specific command line
00322     // option: "-console", which dynamically creates a Windows
00323     // console.
00324     void CheckConsole();
00325 
00326 private:
00327     static HDDEDATA CALLBACK HandleDDENotification( UINT     uType,
00328                                                     UINT     uFmt,
00329                                                     HCONV    hconv,
00330                                                     HSZ      hsz1,
00331                                                     HSZ      hsz2,
00332                                                     HDDEDATA hdata,
00333                                                     ULONG    dwData1,
00334                                                     ULONG    dwData2 );
00335     static void HandleCommandLine(const char* aCmdLineString, nsIFile* aWorkingDir, PRUint32 aState);
00336     static void ParseDDEArg( HSZ args, int index, nsCString& string);
00337     static void ParseDDEArg( const char* args, int index, nsCString& aString);
00338     static void ActivateLastWindow();
00339     static HDDEDATA CreateDDEData( DWORD value );
00340     static HDDEDATA CreateDDEData( LPBYTE value, DWORD len );
00341     static PRBool   InitTopicStrings();
00342     static int      FindTopic( HSZ topic );
00343     static nsresult OpenWindow( const char *urlstr, const char *args );
00344     static nsresult OpenBrowserWindow();
00345     static nsresult ReParent( nsISupports *window, HWND newParent );
00346     static void     SetupSysTrayIcon();
00347     static void     RemoveSysTrayIcon();
00348 
00349     static int   mConversations;
00350     enum {
00351         topicOpenURL,
00352         topicActivate,
00353         topicCancelProgress,
00354         topicVersion,
00355         topicRegisterViewer,
00356         topicUnRegisterViewer,
00357         topicGetWindowInfo,
00358         // Note: Insert new values above this line!!!!!
00359         topicCount // Count of the number of real topics
00360     };
00361 
00362     static HSZ   mApplication, mTopics[ topicCount ];
00363     static DWORD mInstance;
00364     static PRBool mCanHandleRequests;
00365     static PRBool mSupportingDDEExec;
00366     static char mMutexName[];
00367     friend struct MessageWindow;
00368 }; // nsNativeAppSupportWin
00369 
00370 NS_INTERFACE_MAP_BEGIN(nsNativeAppSupportWin)
00371     NS_INTERFACE_MAP_ENTRY(nsIObserver)
00372 NS_INTERFACE_MAP_END_INHERITING(nsNativeAppSupportBase)
00373 
00374 NS_IMPL_ADDREF_INHERITED(nsNativeAppSupportWin, nsNativeAppSupportBase)
00375 NS_IMPL_RELEASE_INHERITED(nsNativeAppSupportWin, nsNativeAppSupportBase)
00376 
00377 void
00378 nsNativeAppSupportWin::CheckConsole() {
00379     for ( int i = 1; i < gArgc; i++ ) {
00380         if ( strcmp( "-console", gArgv[i] ) == 0
00381              ||
00382              strcmp( "/console", gArgv[i] ) == 0 ) {
00383             // Users wants to make sure we have a console.
00384             // Try to allocate one.
00385             BOOL rc = ::AllocConsole();
00386             if ( rc ) {
00387                 // Console allocated.  Fix it up so that output works in
00388                 // all cases.  See http://support.microsoft.com/support/kb/articles/q105/3/05.asp.
00389 
00390                 // stdout
00391                 int hCrt = ::_open_osfhandle( (long)GetStdHandle( STD_OUTPUT_HANDLE ),
00392                                             _O_TEXT );
00393                 if ( hCrt != -1 ) {
00394                     FILE *hf = ::_fdopen( hCrt, "w" );
00395                     if ( hf ) {
00396                         *stdout = *hf;
00397 #ifdef DEBUG
00398                         ::fprintf( stdout, "stdout directed to dynamic console\n" );
00399 #endif
00400                     }
00401                 }
00402 
00403                 // stderr
00404                 hCrt = ::_open_osfhandle( (long)::GetStdHandle( STD_ERROR_HANDLE ),
00405                                           _O_TEXT );
00406                 if ( hCrt != -1 ) {
00407                     FILE *hf = ::_fdopen( hCrt, "w" );
00408                     if ( hf ) {
00409                         *stderr = *hf;
00410 #ifdef DEBUG
00411                         ::fprintf( stderr, "stderr directed to dynamic console\n" );
00412 #endif
00413                     }
00414                 }
00415 
00416                 // stdin?
00417                 /* Don't bother for now.
00418                 hCrt = ::_open_osfhandle( (long)::GetStdHandle( STD_INPUT_HANDLE ),
00419                                           _O_TEXT );
00420                 if ( hCrt != -1 ) {
00421                     FILE *hf = ::_fdopen( hCrt, "r" );
00422                     if ( hf ) {
00423                         *stdin = *hf;
00424                     }
00425                 }
00426                 */
00427             } else {
00428                 // Failed.  Probably because there already is one.
00429                 // There's little we can do, in any case.
00430             }
00431 
00432             // Remove the console argument from the command line.
00433             do {
00434                 gArgv[i] = gArgv[i + 1];
00435                 ++i;
00436             } while (gArgv[i]);
00437 
00438             --gArgc;
00439 
00440             // Don't bother doing this more than once.
00441             break;
00442         }
00443     }
00444 
00445     return;
00446 }
00447 
00448 
00449 // Create and return an instance of class nsNativeAppSupportWin.
00450 nsresult
00451 NS_CreateNativeAppSupport( nsINativeAppSupport **aResult ) {
00452     nsNativeAppSupportWin *pNative = new nsNativeAppSupportWin;
00453     if (!pNative) return NS_ERROR_OUT_OF_MEMORY;
00454 
00455     // Check for dynamic console creation request.
00456     pNative->CheckConsole();
00457 
00458     *aResult = pNative;
00459     NS_ADDREF( *aResult );
00460 
00461     return NS_OK;
00462 }
00463 
00464 // Constants
00465 #define MOZ_DDE_APPLICATION    "Mozilla"
00466 #define MOZ_STARTUP_MUTEX_NAME "StartupMutex"
00467 #define MOZ_DDE_START_TIMEOUT 30000
00468 #define MOZ_DDE_STOP_TIMEOUT  15000
00469 #define MOZ_DDE_EXEC_TIMEOUT  15000
00470 
00471 // The array entries must match the enum ordering!
00472 const char * const topicNames[] = { "WWW_OpenURL",
00473                                     "WWW_Activate",
00474                                     "WWW_CancelProgress",
00475                                     "WWW_Version",
00476                                     "WWW_RegisterViewer",
00477                                     "WWW_UnRegisterViewer",
00478                                     "WWW_GetWindowInfo" };
00479 
00480 // Static member definitions.
00481 int   nsNativeAppSupportWin::mConversations = 0;
00482 HSZ   nsNativeAppSupportWin::mApplication   = 0;
00483 HSZ   nsNativeAppSupportWin::mTopics[nsNativeAppSupportWin::topicCount] = { 0 };
00484 DWORD nsNativeAppSupportWin::mInstance      = 0;
00485 PRBool nsNativeAppSupportWin::mCanHandleRequests   = PR_FALSE;
00486 PRBool nsNativeAppSupportWin::mSupportingDDEExec   = PR_FALSE;
00487 
00488 char nsNativeAppSupportWin::mMutexName[ 128 ] = { 0 };
00489 
00490 
00491 // Message window encapsulation.
00492 struct MessageWindow {
00493     // ctor/dtor are simplistic
00494     MessageWindow() {
00495         // Try to find window.
00496         mHandle = ::FindWindow( className(), 0 );
00497     }
00498 
00499     // Act like an HWND.
00500     operator HWND() {
00501         return mHandle;
00502     }
00503 
00504     // Class name: appName + "MessageWindow"
00505     static const char *className() {
00506         static char classNameBuffer[128];
00507         static char *mClassName = 0;
00508         if ( !mClassName ) {
00509             ::_snprintf( classNameBuffer,
00510                          sizeof classNameBuffer,
00511                          "%s%s",
00512                          gAppData->name,
00513                          "MessageWindow" );
00514             mClassName = classNameBuffer;
00515         }
00516         return mClassName;
00517     }
00518 
00519     // Create: Register class and create window.
00520     NS_IMETHOD Create() {
00521         WNDCLASS classStruct = { 0,                          // style
00522                                  &MessageWindow::WindowProc, // lpfnWndProc
00523                                  0,                          // cbClsExtra
00524                                  0,                          // cbWndExtra
00525                                  0,                          // hInstance
00526                                  0,                          // hIcon
00527                                  0,                          // hCursor
00528                                  0,                          // hbrBackground
00529                                  0,                          // lpszMenuName
00530                                  className() };              // lpszClassName
00531 
00532         // Register the window class.
00533         NS_ENSURE_TRUE( ::RegisterClass( &classStruct ), NS_ERROR_FAILURE );
00534 
00535         // Create the window.
00536         NS_ENSURE_TRUE( ( mHandle = ::CreateWindow( className(),
00537                                                     0,          // title
00538                                                     WS_CAPTION, // style
00539                                                     0,0,0,0,    // x, y, cx, cy
00540                                                     0,          // parent
00541                                                     0,          // menu
00542                                                     0,          // instance
00543                                                     0 ) ),      // create struct
00544                         NS_ERROR_FAILURE );
00545 
00546 #if MOZ_DEBUG_DDE
00547         printf( "Message window = 0x%08X\n", (int)mHandle );
00548 #endif
00549 
00550         return NS_OK;
00551     }
00552 
00553     // Destory:  Get rid of window and reset mHandle.
00554     NS_IMETHOD Destroy() {
00555         nsresult retval = NS_OK;
00556 
00557         if ( mHandle ) {
00558             // DestroyWindow can only destroy windows created from
00559             //  the same thread.
00560             BOOL desRes = DestroyWindow( mHandle );
00561             if ( FALSE != desRes ) {
00562                 mHandle = NULL;
00563             }
00564             else {
00565                 retval = NS_ERROR_FAILURE;
00566             }
00567         }
00568 
00569         return retval;
00570     }
00571 
00572     // SendRequest: Pass string via WM_COPYDATA to message window.
00573     NS_IMETHOD SendRequest( const char *cmd ) {
00574         // Construct a data buffer <commandline>\0<workingdir>\0
00575         int cmdlen = strlen(cmd);
00576         char* cmdbuf = (char*) malloc(cmdlen + MAX_PATH + 1);
00577         if (!cmdbuf)
00578             return NS_ERROR_OUT_OF_MEMORY;
00579 
00580         strcpy(cmdbuf, cmd);
00581         _getcwd(cmdbuf + cmdlen + 1, MAX_PATH);
00582 
00583         // We used to set dwData to zero, when we didn't send the working dir.
00584         // Now we're using it as a version number.
00585         COPYDATASTRUCT cds = {
00586             1,
00587             cmdlen + strlen(cmdbuf + cmdlen + 1) + 2,
00588             (void*) cmdbuf
00589         };
00590         // Bring the already running Mozilla process to the foreground.
00591         // nsWindow will restore the window (if minimized) and raise it.
00592         ::SetForegroundWindow( mHandle );
00593         ::SendMessage( mHandle, WM_COPYDATA, 0, (LPARAM)&cds );
00594         free (cmdbuf);
00595         return NS_OK;
00596     }
00597 
00598     // Window proc.
00599     static long CALLBACK WindowProc( HWND msgWindow, UINT msg, WPARAM wp, LPARAM lp ) {
00600         if ( msg == WM_COPYDATA ) {
00601             if (!nsNativeAppSupportWin::mCanHandleRequests)
00602                 return 0;
00603 
00604             // This is an incoming request.
00605             COPYDATASTRUCT *cds = (COPYDATASTRUCT*)lp;
00606 #if MOZ_DEBUG_DDE
00607             printf( "Incoming request: %s\n", (const char*)cds->lpData );
00608 #endif
00609             nsCOMPtr<nsILocalFile> workingDir;
00610 
00611             if (1 >= cds->dwData) {
00612                 char* wdpath = (char*) cds->lpData;
00613                 // skip the command line, and get the working dir of the
00614                 // other process, which is after the first null char
00615                 while (*wdpath)
00616                     ++wdpath;
00617 
00618                 ++wdpath;
00619 
00620 #ifdef MOZ_DEBUG_DDE
00621                 printf( "Working dir: %s\n", wdpath);
00622 #endif
00623 
00624                 NS_NewNativeLocalFile(nsDependentCString(wdpath),
00625                                       PR_FALSE,
00626                                       getter_AddRefs(workingDir));
00627             }
00628             (void)nsNativeAppSupportWin::HandleCommandLine((char*)cds->lpData, workingDir, nsICommandLine::STATE_REMOTE_AUTO);
00629 
00630             // Get current window and return its window handle.
00631             nsCOMPtr<nsIDOMWindowInternal> win;
00632             GetMostRecentWindow( 0, getter_AddRefs( win ) );
00633             return win ? (long)hwndForDOMWindow( win ) : 0;
00634         } else if ( msg == WM_QUERYENDSESSION ) {
00635             if (!nsNativeAppSupportWin::mCanHandleRequests)
00636                 return 0;
00637             // Invoke "-killAll" cmd line handler.  That will close all open windows,
00638             // and display dialog asking whether to save/don't save/cancel.  If the
00639             // user says cancel, then we pass that indicator along to the system
00640             // in order to stop the system shutdown/logoff.
00641             nsCOMPtr<nsICommandLineRunner> cmdLine
00642                 (do_CreateInstance("@mozilla.org/toolkit/command-line;1"));
00643             char* argv[] = { "-killAll", 0 };
00644             if (cmdLine &&
00645                 NS_SUCCEEDED(cmdLine->Init(1, argv, nsnull,
00646                                            nsICommandLine::STATE_REMOTE_AUTO))) {
00647                 return cmdLine->Run() != NS_ERROR_ABORT;
00648             }
00649         }
00650         return DefWindowProc( msgWindow, msg, wp, lp );
00651     }
00652 
00653 private:
00654     HWND mHandle;
00655 }; // struct MessageWindow
00656 
00657 /* Start: Tries to find the "message window" to determine if it
00658  *        exists.  If so, then Mozilla is already running.  In that
00659  *        case, we use the handle to the "message" window and send
00660  *        a request corresponding to this process's command line
00661  *        options.
00662  *
00663  *        If not, then this is the first instance of Mozilla.  In
00664  *        that case, we create and set up the message window.
00665  *
00666  *        The checking for existance of the message window must
00667  *        be protected by use of a mutex semaphore.
00668  */
00669 NS_IMETHODIMP
00670 nsNativeAppSupportWin::Start( PRBool *aResult ) {
00671     NS_ENSURE_ARG( aResult );
00672     NS_ENSURE_TRUE( mInstance == 0, NS_ERROR_NOT_INITIALIZED );
00673     NS_ENSURE_STATE( gAppData );
00674 
00675     if (getenv("MOZ_NO_REMOTE"))
00676     {
00677         *aResult = PR_TRUE;
00678         return NS_OK;
00679     }
00680 
00681     nsresult rv = NS_ERROR_FAILURE;
00682     *aResult = PR_FALSE;
00683 
00684     // Grab mutex first.
00685 
00686     // Build mutex name from app name.
00687     ::_snprintf( mMutexName, sizeof mMutexName, "%s%s", gAppData->name, MOZ_STARTUP_MUTEX_NAME );
00688     Mutex startupLock = Mutex( mMutexName );
00689 
00690     NS_ENSURE_TRUE( startupLock.Lock( MOZ_DDE_START_TIMEOUT ), NS_ERROR_FAILURE );
00691 
00692     // Search for existing message window.
00693     MessageWindow msgWindow;
00694     if ( (HWND)msgWindow ) {
00695         // We are a client process.  Pass request to message window.
00696         LPTSTR cmd = ::GetCommandLine();
00697         rv = msgWindow.SendRequest( cmd );
00698     } else {
00699         // We will be server.
00700         rv = msgWindow.Create();
00701         if ( NS_SUCCEEDED( rv ) ) {
00702             // Start up DDE server.
00703             this->StartDDE();
00704             // Tell caller to spin message loop.
00705             *aResult = PR_TRUE;
00706         }
00707     }
00708 
00709     startupLock.Unlock();
00710 
00711     return rv;
00712 }
00713 
00714 PRBool
00715 nsNativeAppSupportWin::InitTopicStrings() {
00716     for ( int i = 0; i < topicCount; i++ ) {
00717         if ( !( mTopics[ i ] = DdeCreateStringHandle( mInstance, NS_CONST_CAST(char *,topicNames[ i ]), CP_WINANSI ) ) ) {
00718             return PR_FALSE;
00719         }
00720     }
00721     return PR_TRUE;
00722 }
00723 
00724 int
00725 nsNativeAppSupportWin::FindTopic( HSZ topic ) {
00726     for ( int i = 0; i < topicCount; i++ ) {
00727         if ( DdeCmpStringHandles( topic, mTopics[i] ) == 0 ) {
00728             return i;
00729         }
00730     }
00731     return -1;
00732 }
00733 
00734 // Utility function that determines if we're handling http Internet shortcuts.
00735 static PRBool isDefaultBrowser() 
00736 {
00737 #ifdef MOZ_PHOENIX
00738   nsCOMPtr<nsIShellService> shell(do_GetService("@mozilla.org/browser/shell-service;1"));
00739   PRBool isDefault;
00740   shell->IsDefaultBrowser(PR_FALSE, &isDefault);
00741   return isDefault;
00742 #else
00743   return FALSE;
00744 #endif
00745 }
00746 
00747 // Start DDE server.
00748 //
00749 // This used to be the Start() method when we were using DDE as the
00750 // primary IPC mechanism between secondary Mozilla processes and the
00751 // initial "server" process.
00752 //
00753 // Now, it simply initializes the DDE server.  The caller must check
00754 // that this process is to be the server, and, must acquire the DDE
00755 // startup mutex semaphore prior to calling this routine.  See ::Start(),
00756 // above.
00757 NS_IMETHODIMP
00758 nsNativeAppSupportWin::StartDDE() {
00759     NS_ENSURE_TRUE( mInstance == 0, NS_ERROR_NOT_INITIALIZED );
00760 
00761     // Initialize DDE.
00762     NS_ENSURE_TRUE( DMLERR_NO_ERROR == DdeInitialize( &mInstance,
00763                                                       nsNativeAppSupportWin::HandleDDENotification,
00764                                                       APPCLASS_STANDARD,
00765                                                       0 ),
00766                     NS_ERROR_FAILURE );
00767 
00768     // Allocate DDE strings.
00769     NS_ENSURE_TRUE( ( mApplication = DdeCreateStringHandle( mInstance, (char*) gAppData->name, CP_WINANSI ) ) && InitTopicStrings(),
00770                     NS_ERROR_FAILURE );
00771 
00772     // Next step is to register a DDE service.
00773     NS_ENSURE_TRUE( DdeNameService( mInstance, mApplication, 0, DNS_REGISTER ), NS_ERROR_FAILURE );
00774 
00775 #if MOZ_DEBUG_DDE
00776     printf( "DDE server started\n" );
00777 #endif
00778 
00779     return NS_OK;
00780 }
00781 
00782 // If no DDE conversations are pending, terminate DDE.
00783 NS_IMETHODIMP
00784 nsNativeAppSupportWin::Stop( PRBool *aResult ) {
00785     NS_ENSURE_ARG( aResult );
00786     NS_ENSURE_TRUE( mInstance, NS_ERROR_NOT_INITIALIZED );
00787 
00788     nsresult rv = NS_OK;
00789     *aResult = PR_TRUE;
00790 
00791     Mutex ddeLock( mMutexName );
00792 
00793     if ( ddeLock.Lock( MOZ_DDE_STOP_TIMEOUT ) ) {
00794         if ( mConversations == 0 ) {
00795             this->Quit();
00796         } else {
00797             *aResult = PR_FALSE;
00798         }
00799 
00800         ddeLock.Unlock();
00801     }
00802     else {
00803         // No DDE application name specified, but that's OK.  Just
00804         // forge ahead.
00805         *aResult = PR_TRUE;
00806     }
00807 
00808     return rv;
00809 }
00810 
00811 NS_IMETHODIMP
00812 nsNativeAppSupportWin::Observe(nsISupports* aSubject, const char* aTopic,
00813                                const PRUnichar* aData)
00814 {
00815     if (strcmp(aTopic, "quit-application") == 0) {
00816         Quit();
00817     } else {
00818         NS_ERROR("Unexpected observer topic.");
00819     }
00820 
00821     return NS_OK;
00822 }
00823 
00824 // Terminate DDE regardless.
00825 NS_IMETHODIMP
00826 nsNativeAppSupportWin::Quit() {
00827     // If another process wants to look for the message window, they need
00828     // to wait to hold the lock, in which case they will not find the
00829     // window as we will destroy ours under our lock.
00830     // When the mutex goes off the stack, it is unlocked via destructor.
00831     Mutex mutexLock(mMutexName);
00832     NS_ENSURE_TRUE(mutexLock.Lock(MOZ_DDE_START_TIMEOUT), NS_ERROR_FAILURE);
00833 
00834     // If we've got a message window to receive IPC or new window requests,
00835     // get rid of it as we are shutting down.
00836     // Note:  Destroy calls DestroyWindow, which will only work on a window
00837     //  created by the same thread.
00838     MessageWindow mw;
00839     mw.Destroy();
00840 
00841     if ( mInstance ) {
00842         // Unregister application name.
00843         DdeNameService( mInstance, mApplication, 0, DNS_UNREGISTER );
00844         // Clean up strings.
00845         if ( mApplication ) {
00846             DdeFreeStringHandle( mInstance, mApplication );
00847             mApplication = 0;
00848         }
00849         for ( int i = 0; i < topicCount; i++ ) {
00850             if ( mTopics[i] ) {
00851                 DdeFreeStringHandle( mInstance, mTopics[i] );
00852                 mTopics[i] = 0;
00853             }
00854         }
00855         DdeUninitialize( mInstance );
00856         mInstance = 0;
00857 #if MOZ_DEBUG_DDE
00858     printf( "DDE server stopped\n" );
00859 #endif
00860     }
00861 
00862     return NS_OK;
00863 }
00864 
00865 NS_IMETHODIMP
00866 nsNativeAppSupportWin::Enable()
00867 {
00868     mCanHandleRequests = PR_TRUE;
00869 
00870     nsCOMPtr<nsIObserverService> obs
00871         (do_GetService("@mozilla.org/observer-service;1"));
00872     if (obs) {
00873         obs->AddObserver(this, "quit-application", PR_FALSE);
00874     } else {
00875         NS_ERROR("No observer service?");
00876     }
00877 
00878     return NS_OK;
00879 }
00880 
00881 #if MOZ_DEBUG_DDE
00882 // Macro to generate case statement for a given XTYP value.
00883 #define XTYP_CASE(t) \
00884     case t: result = #t; break
00885 
00886 static nsCString uTypeDesc( UINT uType ) {
00887     nsCString result;
00888     switch ( uType ) {
00889     XTYP_CASE(XTYP_ADVSTART);
00890     XTYP_CASE(XTYP_CONNECT);
00891     XTYP_CASE(XTYP_ADVREQ);
00892     XTYP_CASE(XTYP_REQUEST);
00893     XTYP_CASE(XTYP_WILDCONNECT);
00894     XTYP_CASE(XTYP_ADVDATA);
00895     XTYP_CASE(XTYP_EXECUTE);
00896     XTYP_CASE(XTYP_POKE);
00897     XTYP_CASE(XTYP_ADVSTOP);
00898     XTYP_CASE(XTYP_CONNECT_CONFIRM);
00899     XTYP_CASE(XTYP_DISCONNECT);
00900     XTYP_CASE(XTYP_ERROR);
00901     XTYP_CASE(XTYP_MONITOR);
00902     XTYP_CASE(XTYP_REGISTER);
00903     XTYP_CASE(XTYP_XACT_COMPLETE);
00904     XTYP_CASE(XTYP_UNREGISTER);
00905     default: result = "XTYP_?????";
00906     }
00907     return result;
00908 }
00909 
00910 static nsCString hszValue( DWORD instance, HSZ hsz ) {
00911     // Extract string from HSZ.
00912     nsCString result("[");
00913     DWORD len = DdeQueryString( instance, hsz, NULL, NULL, CP_WINANSI );
00914     if ( len ) {
00915         char buffer[ 256 ];
00916         DdeQueryString( instance, hsz, buffer, sizeof buffer, CP_WINANSI );
00917         result += buffer;
00918     }
00919     result += "]";
00920     return result;
00921 }
00922 #else
00923 // These are purely a safety measure to avoid the infamous "won't
00924 // build non-debug" type Tinderbox flames.
00925 static nsCString uTypeDesc( UINT ) {
00926     return nsCString( "?" );
00927 }
00928 static nsCString hszValue( DWORD, HSZ ) {
00929     return nsCString( "?" );
00930 }
00931 #endif
00932 
00933 
00934 // Utility function to escape double-quotes within a string.
00935 static void escapeQuotes( nsAString &aString ) {
00936     PRInt32 offset = -1;
00937     while( 1 ) {
00938        // Find next '"'.
00939        offset = aString.FindChar( '"', ++offset );
00940        if ( offset == kNotFound ) {
00941            // No more quotes, exit.
00942            break;
00943        } else {
00944            // Insert back-slash ahead of the '"'.
00945            aString.Insert( PRUnichar('\\'), offset );
00946            // Increment offset because we just inserted a slash
00947            offset++;
00948        }
00949     }
00950     return;
00951 }
00952 
00953 HDDEDATA CALLBACK
00954 nsNativeAppSupportWin::HandleDDENotification( UINT uType,       // transaction type
00955                                               UINT uFmt,        // clipboard data format
00956                                               HCONV hconv,      // handle to the conversation
00957                                               HSZ hsz1,         // handle to a string
00958                                               HSZ hsz2,         // handle to a string
00959                                               HDDEDATA hdata,   // handle to a global memory object
00960                                               ULONG dwData1,    // transaction-specific data
00961                                               ULONG dwData2 ) { // transaction-specific data
00962 
00963     if (!mCanHandleRequests)
00964         return 0;
00965 
00966 
00967 #if MOZ_DEBUG_DDE
00968     printf( "DDE: uType  =%s\n",      uTypeDesc( uType ).get() );
00969     printf( "     uFmt   =%u\n",      (unsigned)uFmt );
00970     printf( "     hconv  =%08x\n",    (int)hconv );
00971     printf( "     hsz1   =%08x:%s\n", (int)hsz1, hszValue( mInstance, hsz1 ).get() );
00972     printf( "     hsz2   =%08x:%s\n", (int)hsz2, hszValue( mInstance, hsz2 ).get() );
00973     printf( "     hdata  =%08x\n",    (int)hdata );
00974     printf( "     dwData1=%08x\n",    (int)dwData1 );
00975     printf( "     dwData2=%08x\n",    (int)dwData2 );
00976 #endif
00977 
00978     HDDEDATA result = 0;
00979     if ( uType & XCLASS_BOOL ) {
00980         switch ( uType ) {
00981             case XTYP_CONNECT:
00982                 // Make sure its for our service/topic.
00983                 if ( FindTopic( hsz1 ) != -1 ) {
00984                     // We support this connection.
00985                     result = (HDDEDATA)1;
00986                 }
00987                 break;
00988             case XTYP_CONNECT_CONFIRM:
00989                 // We don't care about the conversation handle, at this point.
00990                 result = (HDDEDATA)1;
00991                 break;
00992         }
00993     } else if ( uType & XCLASS_DATA ) {
00994         if ( uType == XTYP_REQUEST ) {
00995             switch ( FindTopic( hsz1 ) ) {
00996                 case topicOpenURL: {
00997                     // Open a given URL...
00998 
00999                     // Get the URL from the first argument in the command.
01000                     nsCAutoString url;
01001                     ParseDDEArg(hsz2, 0, url);
01002 
01003                     // Read the 3rd argument in the command to determine if a
01004                     // new window is to be used.
01005                     nsCAutoString windowID;
01006                     ParseDDEArg(hsz2, 2, windowID);
01007                     // "" means to open the URL in a new window.
01008                     if ( windowID.Equals( "" ) ) {
01009                         url.Insert("mozilla -new-window ", 0);
01010                     }
01011                     else {
01012                         url.Insert("mozilla -url ", 0);
01013                     }
01014 
01015 #if MOZ_DEBUG_DDE
01016                     printf( "Handling dde XTYP_REQUEST request: [%s]...\n", url.get() );
01017 #endif
01018                     // Now handle it.
01019                     HandleCommandLine(url.get(), nsnull, nsICommandLine::STATE_REMOTE_EXPLICIT);
01020 
01021                     // Return pseudo window ID.
01022                     result = CreateDDEData( 1 );
01023                     break;
01024                 }
01025                 case topicGetWindowInfo: {
01026                     // This topic has to get the current URL, get the current
01027                     // page title and then format the output into the DDE
01028                     // return string.  The return value is "URL","Page Title",
01029                     // "Window ID" however the window ID is not used for this
01030                     // command, therefore it is returned as a null string
01031 
01032                     // This isn't really a loop.  We just use "break"
01033                     // statements to bypass the remaining steps when
01034                     // something goes wrong.
01035                     do {
01036                         // Get most recently used Nav window.
01037                         nsCOMPtr<nsIDOMWindowInternal> navWin;
01038                         GetMostRecentWindow( NS_LITERAL_STRING( "navigator:browser" ).get(),
01039                                              getter_AddRefs( navWin ) );
01040                         if ( !navWin ) {
01041                             // There is not a window open
01042                             break;
01043                         }
01044                         // Get content window.
01045                         nsCOMPtr<nsIDOMWindow> content;
01046                         navWin->GetContent( getter_AddRefs( content ) );
01047                         if ( !content ) {
01048                             break;
01049                         }
01050                         // Convert that to internal interface.
01051                         nsCOMPtr<nsIDOMWindowInternal> internalContent( do_QueryInterface( content ) );
01052                         if ( !internalContent ) {
01053                             break;
01054                         }
01055                         // Get location.
01056                         nsCOMPtr<nsIDOMLocation> location;
01057                         internalContent->GetLocation( getter_AddRefs( location ) );
01058                         if ( !location ) {
01059                             break;
01060                         }
01061                         // Get href for URL.
01062                         nsAutoString url;
01063                         if ( NS_FAILED( location->GetHref( url ) ) ) {
01064                             break;
01065                         }
01066                         // Escape any double-quotes.
01067                         escapeQuotes( url );
01068 
01069                         // Now for the title; first, get the "window" script global object.
01070                         nsCOMPtr<nsIScriptGlobalObject> scrGlobalObj( do_QueryInterface( internalContent ) );
01071                         if ( !scrGlobalObj ) {
01072                             break;
01073                         }
01074                         // Then from its doc shell get the base window...
01075                         nsCOMPtr<nsIBaseWindow> baseWindow =
01076                             do_QueryInterface( scrGlobalObj->GetDocShell() );
01077                         if ( !baseWindow ) {
01078                             break;
01079                         }
01080                         // And from the base window we can get the title.
01081                         nsXPIDLString title;
01082                         if(!baseWindow) {
01083                             break;
01084                         }
01085                         baseWindow->GetTitle(getter_Copies(title));
01086                         // Escape any double-quotes in the title.
01087                         escapeQuotes( title );
01088 
01089                         // Use a string buffer for the output data, first
01090                         // save a quote.
01091                         nsCAutoString   outpt( NS_LITERAL_CSTRING("\"") );
01092                         // Now copy the URL converting the Unicode string
01093                         // to a single-byte ASCII string
01094                         nsCAutoString tmpNativeStr;
01095                         NS_CopyUnicodeToNative( url, tmpNativeStr );
01096                         outpt.Append( tmpNativeStr );
01097                         // Add the "," used to separate the URL and the page
01098                         // title
01099                         outpt.Append( NS_LITERAL_CSTRING("\",\"") );
01100                         // Now copy the current page title to the return string
01101                         NS_CopyUnicodeToNative( title, tmpNativeStr );
01102                         outpt.Append( tmpNativeStr );
01103                         // Fill out the return string with the remainin ",""
01104                         outpt.Append( NS_LITERAL_CSTRING( "\",\"\"" ));
01105 
01106                         // Create a DDE handle to a char string for the data
01107                         // being returned, this copies and creates a "shared"
01108                         // copy of the DDE response until the calling APP
01109                         // reads it and says it can be freed.
01110                         result = CreateDDEData( (LPBYTE)(const char*)outpt.get(),
01111                                                 outpt.Length() + 1 );
01112 #if MOZ_DEBUG_DDE
01113                         printf( "WWW_GetWindowInfo->%s\n", outpt.get() );
01114 #endif
01115                     } while ( PR_FALSE );
01116                     break;
01117                 }
01118                 case topicActivate: {
01119                     // Activate a Nav window...
01120                     nsCAutoString windowID;
01121                     ParseDDEArg(hsz2, 0, windowID);
01122                     // 4294967295 is decimal for 0xFFFFFFFF which is also a
01123                     //   correct value to do that Activate last window stuff
01124                     if ( windowID.Equals( "-1" ) ||
01125                          windowID.Equals( "4294967295" ) ) {
01126                         // We only support activating the most recent window (or a new one).
01127                         ActivateLastWindow();
01128                         // Return pseudo window ID.
01129                         result = CreateDDEData( 1 );
01130                     }
01131                     break;
01132                 }
01133                 case topicVersion: {
01134                     // Return version.  We're restarting at 1.0!
01135                     DWORD version = 1 << 16; // "1.0"
01136                     result = CreateDDEData( version );
01137                     break;
01138                 }
01139                 case topicRegisterViewer: {
01140                     // Register new viewer (not implemented).
01141                     result = CreateDDEData( PR_FALSE );
01142                     break;
01143                 }
01144                 case topicUnRegisterViewer: {
01145                     // Unregister new viewer (not implemented).
01146                     result = CreateDDEData( PR_FALSE );
01147                     break;
01148                 }
01149                 default:
01150                     break;
01151             }
01152         } else if ( uType & XTYP_POKE ) {
01153             switch ( FindTopic( hsz1 ) ) {
01154                 case topicCancelProgress: {
01155                     // "Handle" progress cancel (actually, pretty much ignored).
01156                     result = (HDDEDATA)DDE_FACK;
01157                     break;
01158                 }
01159                 default:
01160                     break;
01161             }
01162         }
01163     } else if ( uType & XCLASS_FLAGS ) {
01164         if ( uType == XTYP_EXECUTE ) {
01165             // Prove that we received the request.
01166             DWORD bytes;
01167             LPBYTE request = DdeAccessData( hdata, &bytes );
01168 #if MOZ_DEBUG_DDE
01169             printf( "Handling dde request: [%s]...\n", (char*)request );
01170 #endif
01171             // Default is to open in current window.
01172             PRBool new_window = PR_FALSE;
01173 
01174             nsCAutoString url;
01175             ParseDDEArg((const char*) request, 0, url);
01176 
01177             // Read the 3rd argument in the command to determine if a
01178             // new window is to be used.
01179             nsCAutoString windowID;
01180             ParseDDEArg((const char*) request, 2, windowID);
01181 
01182             // "" means to open the URL in a new window.
01183             if ( windowID.Equals( "" ) ) {
01184                 url.Insert("mozilla -new-window ", 0);
01185             }
01186             else {
01187                 url.Insert("mozilla -url ", 0);
01188             }
01189 #if MOZ_DEBUG_DDE
01190             printf( "Handling dde XTYP_REQUEST request: [%s]...\n", url.get() );
01191 #endif
01192             // Now handle it.
01193             HandleCommandLine(url.get(), nsnull, nsICommandLine::STATE_REMOTE_EXPLICIT);
01194 
01195             // Release the data.
01196             DdeUnaccessData( hdata );
01197             result = (HDDEDATA)DDE_FACK;
01198         } else {
01199             result = (HDDEDATA)DDE_FNOTPROCESSED;
01200         }
01201     } else if ( uType & XCLASS_NOTIFICATION ) {
01202     }
01203 #if MOZ_DEBUG_DDE
01204     printf( "DDE result=%d (0x%08X)\n", (int)result, (int)result );
01205 #endif
01206     return result;
01207 }
01208 
01209 // Utility function to advance to end of quoted string.
01210 // p+offset must point to the comma preceding the arg on entry.
01211 // On return, p+result points to the closing '"' (or end of the string
01212 // if the closing '"' is missing) if the arg is quoted.  If the arg
01213 // is not quoted, then p+result will point to the first character
01214 // of the arg.
01215 static PRInt32 advanceToEndOfQuotedArg( const char *p, PRInt32 offset, PRInt32 len ) {
01216     // Check whether the current arg is quoted.
01217     if ( p[++offset] == '"' ) {
01218         // Advance past the closing quote.
01219         while ( offset < len && p[++offset] != '"' ) {
01220             // If the current character is a backslash, then the
01221             // next character can't be a *real* '"', so skip it.
01222             if ( p[offset] == '\\' ) {
01223                 offset++;
01224             }
01225         }
01226     }
01227     return offset;
01228 }
01229 
01230 void nsNativeAppSupportWin::ParseDDEArg( const char* args, int index, nsCString& aString) {
01231     if ( args ) {
01232         int argLen = strlen(args);
01233         nsDependentCString temp(args, argLen);
01234 
01235         // offset points to the comma preceding the desired arg.
01236         PRInt32 offset = -1;
01237         // Skip commas till we get to the arg we want.
01238         while( index-- ) {
01239             // If this arg is quoted, then go to closing quote.
01240             offset = advanceToEndOfQuotedArg( args, offset, argLen);
01241             // Find next comma.
01242             offset = temp.FindChar( ',', offset );
01243             if ( offset == kNotFound ) {
01244                 // No more commas, give up.
01245                 aString = args;
01246                 return;
01247             }
01248         }
01249         // The desired argument starts just past the preceding comma,
01250         // which offset points to, and extends until the following
01251         // comma (or the end of the string).
01252         //
01253         // Since the argument might be enclosed in quotes, we need to
01254         // deal with that before searching for the terminating comma.
01255         // We advance offset so it ends up pointing to the start of
01256         // the argument we want.
01257         PRInt32 end = advanceToEndOfQuotedArg( args, offset++, argLen );
01258         // Find next comma (or end of string).
01259         end = temp.FindChar( ',', end );
01260         if ( end == kNotFound ) {
01261             // Arg is the rest of the string.
01262             end = argLen;
01263         }
01264         // Extract result.
01265         aString.Assign( args + offset, end - offset );
01266     }
01267     return;
01268 }
01269 
01270 // Utility to parse out argument from a DDE item string.
01271 void nsNativeAppSupportWin::ParseDDEArg( HSZ args, int index, nsCString& aString) {
01272     DWORD argLen = DdeQueryString( mInstance, args, NULL, NULL, CP_WINANSI );
01273     // there wasn't any string, so return empty string
01274     if ( !argLen ) return;
01275     nsCAutoString temp;
01276     // Ensure result's buffer is sufficiently big.
01277     temp.SetLength( argLen );
01278     // Now get the string contents.
01279     DdeQueryString( mInstance, args, temp.BeginWriting(), temp.Length(), CP_WINANSI );
01280     // Parse out the given arg.
01281     ParseDDEArg(temp.get(), index, aString);
01282     return;
01283 }
01284 
01285 void nsNativeAppSupportWin::ActivateLastWindow() {
01286     nsCOMPtr<nsIDOMWindowInternal> navWin;
01287     GetMostRecentWindow( NS_LITERAL_STRING("navigator:browser").get(), getter_AddRefs( navWin ) );
01288     if ( navWin ) {
01289         // Activate that window.
01290         activateWindow( navWin );
01291     } else {
01292         // Need to create a Navigator window, then.
01293         OpenBrowserWindow();
01294     }
01295 }
01296 
01297 HDDEDATA nsNativeAppSupportWin::CreateDDEData( DWORD value ) {
01298     return CreateDDEData( (LPBYTE)&value, sizeof value );
01299 }
01300 
01301 HDDEDATA nsNativeAppSupportWin::CreateDDEData( LPBYTE value, DWORD len ) {
01302     HDDEDATA result = DdeCreateDataHandle( mInstance,
01303                                            value,
01304                                            len,
01305                                            0,
01306                                            mApplication,
01307                                            CF_TEXT,
01308                                            0 );
01309     return result;
01310 }
01311 
01312 void
01313 nsNativeAppSupportWin::HandleCommandLine(const char* aCmdLineString,
01314                                          nsIFile* aWorkingDir,
01315                                          PRUint32 aState)
01316 {
01317     nsresult rv;
01318 
01319     int justCounting = 1;
01320     char **argv = 0;
01321     // Flags, etc.
01322     int init = 1;
01323     int between, quoted, bSlashCount;
01324     int argc;
01325     const char *p;
01326     nsCAutoString arg;
01327 
01328     nsCOMPtr<nsICommandLineRunner> cmdLine
01329         (do_CreateInstance("@mozilla.org/toolkit/command-line;1"));
01330     if (!cmdLine) {
01331         NS_ERROR("Couldn't create command line!");
01332         return;
01333     }
01334 
01335     // Parse command line args according to MS spec
01336     // (see "Parsing C++ Command-Line Arguments" at
01337     // http://msdn.microsoft.com/library/devprods/vs6/visualc/vclang/_pluslang_parsing_c.2b2b_.command.2d.line_arguments.htm).
01338     // We loop if we've not finished the second pass through.
01339     while ( 1 ) {
01340         // Initialize if required.
01341         if ( init ) {
01342             p = aCmdLineString;
01343             between = 1;
01344             argc = quoted = bSlashCount = 0;
01345 
01346             init = 0;
01347         }
01348         if ( between ) {
01349             // We are traversing whitespace between args.
01350             // Check for start of next arg.
01351             if (  *p != 0 && !isspace( *p ) ) {
01352                 // Start of another arg.
01353                 between = 0;
01354                 arg = "";
01355                 switch ( *p ) {
01356                     case '\\':
01357                         // Count the backslash.
01358                         bSlashCount = 1;
01359                         break;
01360                     case '"':
01361                         // Remember we're inside quotes.
01362                         quoted = 1;
01363                         break;
01364                     default:
01365                         // Add character to arg.
01366                         arg += *p;
01367                         break;
01368                 }
01369             } else {
01370                 // Another space between args, ignore it.
01371             }
01372         } else {
01373             // We are processing the contents of an argument.
01374             // Check for whitespace or end.
01375             if ( *p == 0 || ( !quoted && isspace( *p ) ) ) {
01376                 // Process pending backslashes (interpret them
01377                 // literally since they're not followed by a ").
01378                 while( bSlashCount ) {
01379                     arg += '\\';
01380                     bSlashCount--;
01381                 }
01382                 // End current arg.
01383                 if ( !justCounting ) {
01384                     argv[argc] = new char[ arg.Length() + 1 ];
01385                     strcpy( argv[argc], arg.get() );
01386                 }
01387                 argc++;
01388                 // We're now between args.
01389                 between = 1;
01390             } else {
01391                 // Still inside argument, process the character.
01392                 switch ( *p ) {
01393                     case '"':
01394                         // First, digest preceding backslashes (if any).
01395                         while ( bSlashCount > 1 ) {
01396                             // Put one backsplash in arg for each pair.
01397                             arg += '\\';
01398                             bSlashCount -= 2;
01399                         }
01400                         if ( bSlashCount ) {
01401                             // Quote is literal.
01402                             arg += '"';
01403                             bSlashCount = 0;
01404                         } else {
01405                             // Quote starts or ends a quoted section.
01406                             if ( quoted ) {
01407                                 // Check for special case of consecutive double
01408                                 // quotes inside a quoted section.
01409                                 if ( *(p+1) == '"' ) {
01410                                     // This implies a literal double-quote.  Fake that
01411                                     // out by causing next double-quote to look as
01412                                     // if it was preceded by a backslash.
01413                                     bSlashCount = 1;
01414                                 } else {
01415                                     quoted = 0;
01416                                 }
01417                             } else {
01418                                 quoted = 1;
01419                             }
01420                         }
01421                         break;
01422                     case '\\':
01423                         // Add to count.
01424                         bSlashCount++;
01425                         break;
01426                     default:
01427                         // Accept any preceding backslashes literally.
01428                         while ( bSlashCount ) {
01429                             arg += '\\';
01430                             bSlashCount--;
01431                         }
01432                         // Just add next char to the current arg.
01433                         arg += *p;
01434                         break;
01435                 }
01436             }
01437         }
01438         // Check for end of input.
01439         if ( *p ) {
01440             // Go to next character.
01441             p++;
01442         } else {
01443             // If on first pass, go on to second.
01444             if ( justCounting ) {
01445                 // Allocate argv array.
01446                 argv = new char*[ argc ];
01447 
01448                 // Start second pass
01449                 justCounting = 0;
01450                 init = 1;
01451             } else {
01452                 // Quit.
01453                 break;
01454             }
01455         }
01456     }
01457 
01458     rv = cmdLine->Init(argc, argv, aWorkingDir, aState);
01459 
01460     // Cleanup.
01461     while ( argc ) {
01462         delete [] argv[ --argc ];
01463     }
01464     delete [] argv;
01465 
01466     if (NS_FAILED(rv)) {
01467         NS_ERROR("Error initializing command line.");
01468         return;
01469     }
01470 
01471     cmdLine->Run();
01472 }
01473 
01474 nsresult
01475 nsNativeAppSupportWin::OpenWindow( const char*urlstr, const char *args ) {
01476 
01477   nsresult rv = NS_ERROR_FAILURE;
01478 
01479   nsCOMPtr<nsIWindowWatcher> wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID));
01480   nsCOMPtr<nsISupportsCString> sarg(do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID));
01481   if (sarg)
01482     sarg->SetData(nsDependentCString(args));
01483 
01484   if (wwatch && sarg) {
01485     nsCOMPtr<nsIDOMWindow> newWindow;
01486     rv = wwatch->OpenWindow(0, urlstr, "_blank", "chrome,dialog=no,all",
01487                    sarg, getter_AddRefs(newWindow));
01488 #if MOZ_DEBUG_DDE
01489   } else {
01490       printf("Get WindowWatcher (or create string) failed\n");
01491 #endif
01492   }
01493   return rv;
01494 }
01495 
01496 HWND hwndForDOMWindow( nsISupports *window ) {
01497     nsCOMPtr<nsIScriptGlobalObject> ppScriptGlobalObj( do_QueryInterface(window) );
01498     if ( !ppScriptGlobalObj ) {
01499         return 0;
01500     }
01501 
01502     nsCOMPtr<nsIBaseWindow> ppBaseWindow =
01503         do_QueryInterface( ppScriptGlobalObj->GetDocShell() );
01504     if ( !ppBaseWindow ) {
01505         return 0;
01506     }
01507 
01508     nsCOMPtr<nsIWidget> ppWidget;
01509     ppBaseWindow->GetMainWidget( getter_AddRefs( ppWidget ) );
01510 
01511     return (HWND)( ppWidget->GetNativeData( NS_NATIVE_WIDGET ) );
01512 }
01513 
01514 static const char sJSStackContractID[] = "@mozilla.org/js/xpc/ContextStack;1";
01515 
01516 class SafeJSContext {
01517 public:
01518   SafeJSContext();
01519   ~SafeJSContext();
01520 
01521   nsresult   Push();
01522   JSContext *get() { return mContext; }
01523 
01524 protected:
01525   nsCOMPtr<nsIThreadJSContextStack>  mService;
01526   JSContext                         *mContext;
01527 };
01528 
01529 SafeJSContext::SafeJSContext() : mContext(nsnull) {
01530 }
01531 
01532 SafeJSContext::~SafeJSContext() {
01533   JSContext *cx;
01534   nsresult   rv;
01535 
01536   if(mContext) {
01537     rv = mService->Pop(&cx);
01538     NS_ASSERTION(NS_SUCCEEDED(rv) && cx == mContext, "JSContext push/pop mismatch");
01539   }
01540 }
01541 
01542 nsresult SafeJSContext::Push() {
01543   if (mContext) // only once
01544     return NS_ERROR_FAILURE;
01545 
01546   mService = do_GetService(sJSStackContractID);
01547   if(mService) {
01548     JSContext *cx;
01549     if (NS_SUCCEEDED(mService->GetSafeJSContext(&cx)) &&
01550         cx &&
01551         NS_SUCCEEDED(mService->Push(cx))) {
01552       // Save cx in mContext to indicate need to pop.
01553       mContext = cx;
01554     }
01555   }
01556   return mContext ? NS_OK : NS_ERROR_FAILURE;
01557 }
01558 
01559 
01560 nsresult
01561 nsNativeAppSupportWin::OpenBrowserWindow()
01562 {
01563     nsresult rv = NS_OK;
01564 
01565     // Open the argument URL in the most recently used Navigator window.
01566     // If there is no Nav window, open a new one.
01567 
01568     // If at all possible, hand the request off to the most recent
01569     // browser window.
01570 
01571     nsCOMPtr<nsIDOMWindowInternal> navWin;
01572     GetMostRecentWindow( NS_LITERAL_STRING( "navigator:browser" ).get(), getter_AddRefs( navWin ) );
01573 
01574     // This isn't really a loop.  We just use "break" statements to fall
01575     // out to the OpenWindow call when things go awry.
01576     do {
01577         // If caller requires a new window, then don't use an existing one.
01578         if ( !navWin ) {
01579             // Have to open a new one.
01580             break;
01581         }
01582 
01583         nsCOMPtr<nsIBrowserDOMWindow> bwin;
01584         { // scope a bunch of temporary cruft used to generate bwin
01585           nsCOMPtr<nsIWebNavigation> navNav( do_GetInterface( navWin ) );
01586           nsCOMPtr<nsIDocShellTreeItem> navItem( do_QueryInterface( navNav ) );
01587           if ( navItem ) {
01588             nsCOMPtr<nsIDocShellTreeItem> rootItem;
01589             navItem->GetRootTreeItem( getter_AddRefs( rootItem ) );
01590             nsCOMPtr<nsIDOMWindow> rootWin( do_GetInterface( rootItem ) );
01591             nsCOMPtr<nsIDOMChromeWindow> chromeWin(do_QueryInterface(rootWin));
01592             if ( chromeWin )
01593               chromeWin->GetBrowserDOMWindow( getter_AddRefs ( bwin ) );
01594           }
01595         }
01596         if ( bwin ) {
01597           nsCOMPtr<nsIURI> uri;
01598           NS_NewURI( getter_AddRefs( uri ), NS_LITERAL_CSTRING("about:blank"), 0, 0 );
01599           if ( uri ) {
01600             nsCOMPtr<nsIDOMWindow> container;
01601             rv = bwin->OpenURI( uri, 0,
01602                                 nsIBrowserDOMWindow::OPEN_DEFAULTWINDOW,
01603                                 nsIBrowserDOMWindow::OPEN_EXTERNAL,
01604                                 getter_AddRefs( container ) );
01605             if ( NS_SUCCEEDED( rv ) )
01606               return NS_OK;
01607           }
01608         }
01609 
01610         NS_ERROR("failed to hand off external URL to extant window\n");
01611     } while ( PR_FALSE );
01612 
01613     // open a new window if caller requested it or if anything above failed
01614 
01615     char* argv[] = { 0 };
01616     nsCOMPtr<nsICommandLineRunner> cmdLine
01617         (do_CreateInstance("@mozilla.org/toolkit/command-line;1"));
01618     NS_ENSURE_TRUE(cmdLine, NS_ERROR_FAILURE);
01619 
01620     rv = cmdLine->Init(0, argv, nsnull, nsICommandLine::STATE_REMOTE_EXPLICIT);
01621     NS_ENSURE_SUCCESS(rv, rv);
01622 
01623     return cmdLine->Run();
01624 }
01625