New native app support to enable Win32 dde

This commit is contained in:
law%netscape.com 2000-05-12 22:37:20 +00:00
parent e14b7f93eb
commit a97ea28d00
7 changed files with 826 additions and 46 deletions

View File

@ -26,13 +26,13 @@
%{C++
#include "nscore.h"
#include "nsCom.h"
#include "nsISplashScreen.h"
#include "nsINativeAppSupport.h"
%}
interface nsIXULWindow;
interface nsIURI;
interface nsICmdLineService;
interface nsISplashScreen;
interface nsINativeAppSupport;
[ptr] native JSContext(JSContext);
@ -58,8 +58,16 @@ interface nsIAppShellService : nsISupports
* Required initialization routine.
* @param aCmdLineService is stored and passed to appshell components as
* they are initialized.
* @param aNativeAppSupportOrSplashScreen is an object that can be QI'd to
* either an nsINativeAppSupport or nsISplashScreen;
* this object will be used to implement HideSplashScreen.
*/
void Initialize(in nsICmdLineService aCmdLineService, in nsISplashScreen splashScreen );
void Initialize(in nsICmdLineService aCmdLineService, in nsISupports nativeAppSupportOrSplashScreen );
/**
* Getter for "native app support."
*/
readonly attribute nsINativeAppSupport nativeAppSupport;
/**
* Runs an application event loop: normally the main event pump which

View File

@ -81,6 +81,7 @@ nsAppShellService::nsAppShellService() :
mHiddenWindow( nsnull ),
mDeleteCalled( PR_FALSE ),
mSplashScreen( nsnull ),
mNativeAppSupport( nsnull ),
mShuttingDown( PR_FALSE )
{
NS_INIT_REFCNT();
@ -116,15 +117,20 @@ NS_INTERFACE_MAP_END_THREADSAFE
NS_IMETHODIMP
nsAppShellService::Initialize( nsICmdLineService *aCmdLineService,
nsISplashScreen *aSplashScreen )
nsISupports *aNativeAppSupportOrSplashScreen )
{
nsresult rv;
// Remember cmd line service.
mCmdLineService = aCmdLineService;
// Remember the splash screen.
mSplashScreen = aSplashScreen;
// Remember where the native app support lives.
mNativeAppSupport = do_QueryInterface( aNativeAppSupportOrSplashScreen );
// Or, remember the splash screen (for backward compatibility).
if ( !mNativeAppSupport ) {
mSplashScreen = do_QueryInterface( aNativeAppSupportOrSplashScreen );
}
// Create the Event Queue for the UI thread...
NS_WITH_SERVICE(nsIEventQueueService, eventQService, kEventQueueServiceCID,
@ -443,7 +449,7 @@ nsAppShellService::Quit()
return rv;
}
void*
void* PR_CALLBACK
nsAppShellService::HandleExitEvent(PLEvent* aEvent)
{
ExitEvent* event = NS_REINTERPRET_CAST(ExitEvent*, aEvent);
@ -457,7 +463,7 @@ nsAppShellService::HandleExitEvent(PLEvent* aEvent)
return nsnull;
}
void
void PR_CALLBACK
nsAppShellService::DestroyExitEvent(PLEvent* aEvent)
{
ExitEvent* event = NS_REINTERPRET_CAST(ExitEvent*, aEvent);
@ -782,9 +788,19 @@ void nsAppShellService::RegisterObserver(PRBool aRegister)
NS_IMETHODIMP
nsAppShellService::HideSplashScreen() {
// Hide the splash screen (and release it) if there is one.
if ( mSplashScreen ) {
// Hide the splash screen.
if ( mNativeAppSupport ) {
mNativeAppSupport->HideSplashScreen();
} else if ( mSplashScreen ) {
mSplashScreen->Hide();
}
return NS_OK;
}
NS_IMETHODIMP
nsAppShellService::GetNativeAppSupport( nsINativeAppSupport **aResult ) {
NS_ENSURE_ARG( aResult );
*aResult = mNativeAppSupport;
NS_IF_ADDREF( *aResult );
return NS_OK;
}

View File

@ -66,6 +66,7 @@ protected:
nsCOMPtr<nsIXULWindow> mHiddenWindow;
PRBool mDeleteCalled;
nsCOMPtr<nsISplashScreen> mSplashScreen;
nsCOMPtr<nsINativeAppSupport> mNativeAppSupport;
// Set when the appshell service is going away.
PRBool mShuttingDown;
@ -76,8 +77,8 @@ protected:
nsAppShellService* mService;
};
static void* HandleExitEvent(PLEvent* aEvent);
static void DestroyExitEvent(PLEvent* aEvent);
static void* PR_CALLBACK HandleExitEvent(PLEvent* aEvent);
static void PR_CALLBACK DestroyExitEvent(PLEvent* aEvent);
};
#endif

View File

@ -30,12 +30,14 @@ MODULE=raptor
CPPSRCS= \
nsAppRunner.cpp \
nsSetupRegistry.cpp \
nsNativeAppSupportBase.cpp \
nsNativeAppSupportWin.cpp \
$(NULL)
CPP_OBJS= \
.\$(OBJDIR)\nsAppRunner.obj \
.\$(OBJDIR)\nsSetupRegistry.obj \
.\$(OBJDIR)\nsNativeAppSupportBase.obj \
.\$(OBJDIR)\nsNativeAppSupportWin.obj \
$(NULL)
@ -56,7 +58,7 @@ LINCS=-I$(PUBLIC)\raptor \
-I$(PUBLIC)\uconv \
-I$(PUBLIC)\strres \
-I$(PUBLIC)\locale \
-I$(PUBLIC)\lwbrk \
-I$(PUBLIC)\lwbrk \
-I$(PUBLIC)\unicharutil \
-I$(PUBLIC)\intl \
-I$(PUBLIC)\xpfe\components \

View File

@ -166,6 +166,36 @@ PRBool NS_CanRun()
}
#endif
/*********************************************/
// Default implementation for new and improved
// native app support. If your platform
// implements nsINativeAppSupport then implement
// this function and if def out this code.
//
// Note: For now, the default imiplementation returns 0 and
// the code that calls this will defalt to use the old
// nsISplashScreen interface directly. At some point
// this function will return an instance of
// nsNativeAppSupportBase which will use the older
// "splash screen" interface. The code below will
// then rely on nsINativeAppSupport and its use of
// nsISplashScreen will be removed.
//
#if !defined( NS_WIN32 )
nsresult NS_CreateNativeAppSupport( nsINativeAppSupport **aResult )
{
nsresult rv = NS_OK;
if ( aResult ) {
*aResult = 0;
} else {
rv = NS_ERROR_NULL_POINTER;
}
return rv;
}
#endif
/*
* This routine translates the nsresult into a platform specific return
* code for the application...
@ -619,10 +649,10 @@ static nsresult Ensure1Window( nsICmdLineService* cmdLineArgs)
#include <floatingpoint.h>
#endif
// Note: splashScreen is an owning reference that this function has responsibility
// Note: nativeApp is an owning reference that this function has responsibility
// to release. This responsibility is delegated to the app shell service
// (see nsAppShellService::Initialize call, below).
static nsresult main1(int argc, char* argv[], nsISplashScreen *splashScreen )
static nsresult main1(int argc, char* argv[], nsISupports *nativeApp )
{
nsresult rv;
@ -696,13 +726,27 @@ static nsresult main1(int argc, char* argv[], nsISplashScreen *splashScreen )
NS_WITH_SERVICE(nsIAppShellService, appShell, kAppShellServiceCID, &rv);
NS_ASSERTION(NS_SUCCEEDED(rv), "failed to get the appshell service");
if (NS_FAILED(rv)) {
splashScreen->Hide();
// See if platform supports nsINativeAppSupport.
nsCOMPtr<nsINativeAppSupport> nativeAppSupport = do_QueryInterface( nativeApp );
if ( nativeAppSupport ) {
// Use that interface to remove splash screen.
nativeAppSupport->HideSplashScreen();
} else {
// See if platform supports nsISplashScreen, instead.
nsCOMPtr<nsISplashScreen> splashScreen = do_QueryInterface( nativeApp );
if ( splashScreen ) {
splashScreen->Hide();
}
}
// Release argument object.
NS_IF_RELEASE( nativeApp );
return rv;
}
rv = appShell->Initialize( cmdLineArgs, splashScreen );
// We are done with the splash screen here; the app shell owns it now.
NS_IF_RELEASE( splashScreen );
rv = appShell->Initialize( cmdLineArgs, nativeApp );
// We are done with the native app (or splash screen) object here;
// the app shell owns it now.
NS_IF_RELEASE( nativeApp );
NS_ASSERTION(NS_SUCCEEDED(rv), "failed to initialize appshell");
if ( NS_FAILED(rv) ) return rv;
@ -957,8 +1001,25 @@ int main(int argc, char* argv[])
setupProfilingStuff();
#endif
if( !NS_CanRun() )
return 1;
// Try to allocate "native app support."
// Note: this object is not released here. It is passed to main1 which
// has responsibility to release it.
nsINativeAppSupport *nativeApp = 0;
rv = NS_CreateNativeAppSupport( &nativeApp );
// See if we can run.
if ( nativeApp ) {
PRBool canRun = PR_FALSE;
rv = nativeApp->Start( &canRun );
if ( !canRun ) {
return 1;
}
} else {
// If platform doesn't implement nsINativeAppSupport, fall
// back to old method.
if( !NS_CanRun() )
return 1;
}
// Note: this object is not released here. It is passed to main1 which
// has responsibility to release it.
nsISplashScreen *splash = 0;
@ -983,19 +1044,23 @@ int main(int argc, char* argv[])
dosplash = PR_FALSE;
}
#endif
if (dosplash) {
if (dosplash && !nativeApp) {
// If showing splash screen and platform doesn't implement
// nsINativeAppSupport, then use older nsISplashScreen interface.
rv = NS_CreateSplashScreen( &splash );
NS_ASSERTION( NS_SUCCEEDED(rv), "NS_CreateSplashScreen failed" );
}
// If the platform has a splash screen, show it ASAP.
if ( splash ) {
if ( dosplash && nativeApp ) {
nativeApp->ShowSplashScreen();
} else if ( splash ) {
splash->Show();
}
rv = NS_InitXPCOM(NULL, NULL);
NS_ASSERTION( NS_SUCCEEDED(rv), "NS_InitXPCOM failed" );
nsresult result = main1( argc, argv, splash );
nsresult result = main1( argc, argv, nativeApp ? (nsISupports*)nativeApp : (nsISupports*)splash );
{

View File

@ -20,10 +20,11 @@
* Contributor(s):
*/
#include "prtypes.h"
#include "nsISplashScreen.h"
#include "nsINativeAppSupport.h"
nsresult NS_CreateSplashScreen( nsISplashScreen **aResult );
PRBool NS_CanRun();
nsresult NS_CreateNativeAppSupport( nsINativeAppSupport **aResult );

View File

@ -20,14 +20,31 @@
* Contributor(s):
* Bill Law law@netscape.com
*/
#include "nsNativeAppSupport.h"
#include "nsNativeAppSupportBase.h"
#include "nsNativeAppSupportWin.h"
#include "nsString.h"
#include "nsICmdLineService.h"
#include "nsCOMPtr.h"
#include "nsIComponentManager.h"
#include "nsIServiceManager.h"
#include "nsIAppShellService.h"
#include "nsAppShellCIDs.h"
#include "nsIDOMWindow.h"
#include <windows.h>
#include <ddeml.h>
#include <stdlib.h>
#include <stdio.h>
#include <io.h>
#include <fcntl.h>
// Define this macro to 1 to get DDE debugging output.
#define MOZ_DEBUG_DDE 0
#ifdef DEBUG_law
#undef MOZ_DEBUG_DDE
#define MOZ_DEBUG_DDE 1
#endif
class nsSplashScreenWin : public nsISplashScreen {
public:
nsSplashScreenWin();
@ -72,7 +89,6 @@ public:
void SetDialog( HWND dlg );
void LoadBitmap();
static void CheckConsole();
static nsSplashScreenWin* GetPointer( HWND dlg );
static BOOL CALLBACK DialogProc( HWND dlg, UINT msg, WPARAM wp, LPARAM lp );
@ -84,12 +100,158 @@ public:
nsrefcnt mRefCnt;
}; // class nsSplashScreenWin
// Simple Win32 mutex wrapper.
struct Mutex {
Mutex( const char *name )
: mName( name ),
mHandle( 0 ),
mState( -1 ) {
mHandle = CreateMutex( 0, FALSE, mName.GetBuffer() );
#if MOZ_DEBUG_DDE
printf( "CreateMutex error = 0x%08X\n", (int)GetLastError() );
#endif
}
~Mutex() {
if ( mHandle ) {
// Make sure we release it if we own it.
Unlock();
BOOL rc = CloseHandle( mHandle );
#if MOZ_DEBUG_DDE
if ( !rc ) {
printf( "CloseHandle error = 0x%08X\n", (int)GetLastError() );
}
#endif
}
}
BOOL Lock( DWORD timeout ) {
if ( mHandle ) {
#if MOZ_DEBUG_DDE
printf( "Waiting (%d msec) for DDE mutex...\n", (int)timeout );
#endif
mState = WaitForSingleObject( mHandle, timeout );
#if MOZ_DEBUG_DDE
printf( "...wait complete, result = 0x%08X\n", (int)mState );
#endif
return mState == WAIT_OBJECT_0;
} else {
return FALSE;
}
}
void Unlock() {
if ( mHandle && mState == WAIT_OBJECT_0 ) {
#if MOZ_DEBUG_DDE
printf( "Releasing DDE mutex\n" );
#endif
ReleaseMutex( mHandle );
mState = -1;
}
}
private:
nsCString mName;
HANDLE mHandle;
DWORD mState;
};
/* DDE Notes
*
* This section describes the Win32 DDE service implementation for
* Mozilla. DDE is used on Win32 platforms to communicate between
* separate instances of mozilla.exe (or other Mozilla-based
* executables), or, between the Win32 desktop shell and Mozilla.
*
* The first instance of Mozilla will become the "server" and
* subsequent executables (and the shell) will use DDE to send
* requests to that process. The requests are DDE "execute" requests
* that pass the command line arguments.
*
* Mozilla registers the DDE application "Mozilla" and currently
* supports only the "WWW_OpenURL" topic. This should be reasonably
* compatible with applications that interfaced with Netscape
* Communicator (and its predecessors?). Note that even that topic
* may not be supported in a compatible fashion as the command-line
* options for Mozilla are different than for Communiator.
*
* It is imperative that at most one instance of Mozilla execute in
* "server mode" at any one time. The "native app support" in Mozilla
* on Win32 ensures that only the server process performs XPCOM
* initialization (that is not required for subsequent client processes
* to communicate with the server process).
*
* To guarantee that only one server starts up, a Win32 "mutex" is used
* to ensure only one process executes the server-detection code. That
* code consists of initializing DDE and doing a DdeConnect to Mozilla's
* application/topic. If that connection succeeds, then a server process
* must be running already.
*
* Otherwise, no server has started. In that case, the current process
* calls DdeNameService to register that application/topic. Only at that
* point does the mutex get released.
*
* There are a couple of subtleties that one should be aware of:
*
* 1. It is imperative that DdeInitialize be called only after the mutex
* lock has been obtained. The reason is that at shutdown, DDE
* notifications go out to all initialized DDE processes. Thus, if
* the mutex is owned by a terminating intance of Mozilla, then
* calling DdeInitialize and then WaitForSingleObject will cause the
* DdeUninitialize from the terminating process to "hang" until the
* process waiting for the mutex times out (and can then service the
* notification that the DDE server is terminating). So, don't mess
* with the sequence of things in the startup/shutdown logic.
*
* 2. All mutex requests are made with a reasonably long timeout value and
* are designed to "fail safe" (i.e., a timeout is treated as failure).
*
* 3. An attempt has been made to minimize the degree to which the main
* Mozilla application logic needs to be aware of the DDE mechanisms
* implemented herein. As a result, this module surfaces a very
* large-grained interface, consisting of simple start/stop methods.
* As a consequence, details of certain scenarios can be "lost."
* Particularly, incoming DDE requests can arrive after this module
* initiates the DDE server, but before Mozilla is initialized to the
* point where those requests can be serviced (e.g., open a browser
* window to a particular URL). Since the client process sends the
* request early on, it may not be prepared to respond to that error.
* Thus, such situations may fail silently. The design goal is that
* they fail harmlessly. Refinements on this point will be made as
* details emerge (and time permits).
*/
class nsNativeAppSupportWin : public nsNativeAppSupportBase {
public:
// Overrides of base implementation.
NS_IMETHOD Start( PRBool *aResult );
NS_IMETHOD Stop( PRBool *aResult );
NS_IMETHOD Quit();
// Utility function to handle a Win32-specific command line
// option: "-console", which dynamically creates a Windows
// console.
static void CheckConsole();
private:
static HDDEDATA CALLBACK HandleDDENotification( UINT uType,
UINT uFmt,
HCONV hconv,
HSZ hsz1,
HSZ hsz2,
HDDEDATA hdata,
ULONG dwData1,
ULONG dwData2 );
static void HandleRequest( LPBYTE request );
static nsresult GetCmdLineArgs( LPBYTE request, nsICmdLineService **aResult );
static nsresult OpenWindow( const char *urlstr, const char *args );
static int mConversations;
static HSZ mApplication, mTopic;
static DWORD mInstance;
}; // nsNativeAppSupportWin
nsSplashScreenWin::nsSplashScreenWin()
: mDlg( 0 ), mBitmap( 0 ), mRefCnt( 0 ) {
}
nsSplashScreenWin::~nsSplashScreenWin() {
#ifdef DEBUG_law
#if MOZ_DEBUG_DDE
printf( "splash screen dtor called\n" );
#endif
// Make sure dialog is gone.
@ -254,46 +416,46 @@ DWORD WINAPI nsSplashScreenWin::ThreadProc( LPVOID splashScreen ) {
}
void
nsSplashScreenWin::CheckConsole() {
nsNativeAppSupportWin::CheckConsole() {
for ( int i = 1; i < __argc; i++ ) {
if ( strcmp( "-console", __argv[i] ) == 0
||
strcmp( "/console", __argv[i] ) == 0 ) {
// Users wants to make sure we have a console.
// Try to allocate one.
BOOL rc = AllocConsole();
BOOL rc = ::AllocConsole();
if ( rc ) {
// Console allocated. Fix it up so that output works in
// all cases. See http://support.microsoft.com/support/kb/articles/q105/3/05.asp.
// stdout
int hCrt = _open_osfhandle( (long)GetStdHandle( STD_OUTPUT_HANDLE ),
int hCrt = ::_open_osfhandle( (long)GetStdHandle( STD_OUTPUT_HANDLE ),
_O_TEXT );
if ( hCrt != -1 ) {
FILE *hf = _fdopen( hCrt, "w" );
FILE *hf = ::_fdopen( hCrt, "w" );
if ( hf ) {
*stdout = *hf;
fprintf( stdout, "stdout directed to dynamic console\n" );
::fprintf( stdout, "stdout directed to dynamic console\n" );
}
}
// stderr
hCrt = _open_osfhandle( (long)GetStdHandle( STD_ERROR_HANDLE ),
_O_TEXT );
hCrt = ::_open_osfhandle( (long)::GetStdHandle( STD_ERROR_HANDLE ),
_O_TEXT );
if ( hCrt != -1 ) {
FILE *hf = _fdopen( hCrt, "w" );
FILE *hf = ::_fdopen( hCrt, "w" );
if ( hf ) {
*stderr = *hf;
fprintf( stderr, "stderr directed to dynamic console\n" );
::fprintf( stderr, "stderr directed to dynamic console\n" );
}
}
// stdin?
/* Don't bother for now.
hCrt = _open_osfhandle( (long)GetStdHandle( STD_INPUT_HANDLE ),
_O_TEXT );
hCrt = ::_open_osfhandle( (long)::GetStdHandle( STD_INPUT_HANDLE ),
_O_TEXT );
if ( hCrt != -1 ) {
FILE *hf = _fdopen( hCrt, "r" );
FILE *hf = ::_fdopen( hCrt, "r" );
if ( hf ) {
*stdin = *hf;
}
@ -310,7 +472,29 @@ nsSplashScreenWin::CheckConsole() {
return;
}
nsresult NS_CreateSplashScreen( nsISplashScreen **aResult ) {
// Create and return an instance of class nsNativeAppSupportWin.
nsresult
NS_CreateNativeAppSupport( nsINativeAppSupport **aResult ) {
if ( aResult ) {
*aResult = new nsNativeAppSupportWin;
if ( *aResult ) {
NS_ADDREF( *aResult );
} else {
return NS_ERROR_OUT_OF_MEMORY;
}
} else {
return NS_ERROR_NULL_POINTER;
}
// Check for dynamic console creation request.
nsNativeAppSupportWin::CheckConsole();
return NS_OK;
}
// Create instance of Windows splash screen object.
nsresult
NS_CreateSplashScreen( nsISplashScreen **aResult ) {
if ( aResult ) {
*aResult = 0;
for ( int i = 1; i < __argc; i++ ) {
@ -331,13 +515,516 @@ nsresult NS_CreateSplashScreen( nsISplashScreen **aResult ) {
return NS_ERROR_NULL_POINTER;
}
// Check for dynamic console creation request.
nsSplashScreenWin::CheckConsole();
return NS_OK;
}
// Constants
#define MOZ_DDE_APPLICATION "Mozilla"
#define MOZ_DDE_TOPIC "WWW_OpenURL"
#define MOZ_DDE_MUTEX_NAME "MozillaDDEMutex"
#define MOZ_DDE_START_TIMEOUT 30000
#define MOZ_DDE_STOP_TIMEOUT 15000
#define MOZ_DDE_EXEC_TIMEOUT 15000
// Static member definitions.
int nsNativeAppSupportWin::mConversations = 0;
HSZ nsNativeAppSupportWin::mApplication = 0;
HSZ nsNativeAppSupportWin::mTopic = 0;
DWORD nsNativeAppSupportWin::mInstance = 0;
// Try to initiate DDE conversation. If that succeeds, pass
// request to server process. Otherwise, register application
// and topic (i.e., become server process).
NS_IMETHODIMP
nsNativeAppSupportWin::Start( PRBool *aResult ) {
NS_ENSURE_ARG( aResult );
NS_ENSURE_TRUE( mInstance == 0, NS_ERROR_NOT_INITIALIZED );
nsresult rv = NS_ERROR_FAILURE;
*aResult = PR_FALSE;
// Grab mutex before doing DdeInitialize! This is
// important (see comment above).
Mutex ddeLock = Mutex( MOZ_DDE_MUTEX_NAME );
if ( ddeLock.Lock( MOZ_DDE_START_TIMEOUT ) ) {
// Initialize DDE.
UINT rc = DdeInitialize( &mInstance,
nsNativeAppSupportWin::HandleDDENotification,
APPCLASS_STANDARD,
0 );
if ( rc == DMLERR_NO_ERROR ) {
mApplication = DdeCreateStringHandle( mInstance,
MOZ_DDE_APPLICATION,
CP_WINANSI );
mTopic = DdeCreateStringHandle( mInstance,
MOZ_DDE_TOPIC,
CP_WINANSI );
if ( mApplication && mTopic ) {
// Everything OK so far, try to connect to previusly
// started Mozilla.
HCONV hconv = DdeConnect( mInstance, mApplication, mTopic, 0 );
if ( hconv ) {
// We're the client...
// Get command line to pass to server.
LPTSTR cmd = GetCommandLine();
#if MOZ_DEBUG_DDE
printf( "Acting as DDE client, cmd=%s\n", cmd );
#endif
rc = (UINT)DdeClientTransaction( (LPBYTE)cmd,
strlen( cmd ) + 1,
hconv,
0,
0,
XTYP_EXECUTE,
MOZ_DDE_EXEC_TIMEOUT,
0 );
if ( rc ) {
// Inform caller that request was issued.
rv = NS_OK;
} else {
// Something went wrong. Not much we can do, though...
#if MOZ_DEBUG_DDE
printf( "DdeClientTransaction failed, error = 0x%08X\n",
(int)DdeGetLastError( mInstance ) );
#endif
}
} else {
// We're going to be the server...
#if MOZ_DEBUG_DDE
printf( "Setting up DDE server...\n" );
#endif
// Next step is to register a DDE service.
rc = (UINT)DdeNameService( mInstance, mApplication, 0, DNS_REGISTER );
if ( rc ) {
#if MOZ_DEBUG_DDE
printf( "...DDE server started\n" );
#endif
// Tell app to do its thing.
*aResult = PR_TRUE;
rv = NS_OK;
} else {
#if MOZ_DEBUG_DDE
printf( "DdeNameService failed, error = 0x%08X\n",
(int)DdeGetLastError( mInstance ) );
#endif
}
}
} else {
#if MOZ_DEBUG_DDE
printf( "DdeCreateStringHandle failed, error = 0x%08X\n",
(int)DdeGetLastError( mInstance ) );
#endif
}
} else {
#if MOZ_DEBUG_DDE
printf( "DdeInitialize failed, error = 0x%08X\n", (int)rc );
#endif
}
// Release mutex.
ddeLock.Unlock();
}
// Clean up. The only case in which we need to preserve DDE stuff
// is if we're going to be acting as server.
if ( !*aResult ) {
Quit();
}
return rv;
}
// If no DDE conversations are pending, terminate DDE.
NS_IMETHODIMP
nsNativeAppSupportWin::Stop( PRBool *aResult ) {
NS_ENSURE_ARG( aResult );
NS_ENSURE_TRUE( mInstance, NS_ERROR_NOT_INITIALIZED );
nsresult rv = NS_OK;
*aResult = PR_TRUE;
Mutex ddeLock( MOZ_DDE_MUTEX_NAME );
if ( ddeLock.Lock( MOZ_DDE_STOP_TIMEOUT ) ) {
if ( mConversations == 0 ) {
this->Quit();
} else {
*aResult = PR_FALSE;
}
ddeLock.Unlock();
}
return rv;
}
// Terminate DDE regardless.
NS_IMETHODIMP
nsNativeAppSupportWin::Quit() {
if ( mInstance ) {
// Clean up strings.
if ( mApplication ) {
DdeFreeStringHandle( mInstance, mApplication );
mApplication = 0;
}
if ( mTopic ) {
DdeFreeStringHandle( mInstance, mTopic );
mTopic = 0;
}
DdeUninitialize( mInstance );
mInstance = 0;
}
return NS_OK;
}
PRBool NS_CanRun()
PRBool NS_CanRun()
{
return PR_TRUE;
return PR_TRUE;
}
#if MOZ_DEBUG_DDE
// Macro to generate case statement for a given XTYP value.
#define XTYP_CASE(t) \
case t: result = #t; break
static nsCString uTypeDesc( UINT uType ) {
nsCString result;
switch ( uType ) {
XTYP_CASE(XTYP_ADVSTART);
XTYP_CASE(XTYP_CONNECT);
XTYP_CASE(XTYP_ADVREQ);
XTYP_CASE(XTYP_REQUEST);
XTYP_CASE(XTYP_WILDCONNECT);
XTYP_CASE(XTYP_ADVDATA);
XTYP_CASE(XTYP_EXECUTE);
XTYP_CASE(XTYP_POKE);
XTYP_CASE(XTYP_ADVSTOP);
XTYP_CASE(XTYP_CONNECT_CONFIRM);
XTYP_CASE(XTYP_DISCONNECT);
XTYP_CASE(XTYP_ERROR);
XTYP_CASE(XTYP_MONITOR);
XTYP_CASE(XTYP_REGISTER);
XTYP_CASE(XTYP_XACT_COMPLETE);
XTYP_CASE(XTYP_UNREGISTER);
default: result = "XTYP_?????";
}
return result;
}
static nsCString hszValue( DWORD instance, HSZ hsz ) {
// Extract string from HSZ.
nsCString result = "[";
DWORD len = DdeQueryString( instance, hsz, NULL, NULL, CP_WINANSI );
if ( len ) {
char buffer[ 256 ];
DdeQueryString( instance, hsz, buffer, sizeof buffer, CP_WINANSI );
result += buffer;
}
result += "]";
return result;
}
#else
// These are purely a safety measure to avoid the infamous "won't
// build non-debug" type Tinderbox flames.
static nsCString uTypeDesc( UINT ) {
return nsCString( "?" );
}
static nsCString hszValue( DWORD, HSZ ) {
return nsCString( "?" );
}
#endif
HDDEDATA CALLBACK
nsNativeAppSupportWin::HandleDDENotification( UINT uType, // transaction type
UINT uFmt, // clipboard data format
HCONV hconv, // handle to the conversation
HSZ hsz1, // handle to a string
HSZ hsz2, // handle to a string
HDDEDATA hdata, // handle to a global memory object
ULONG dwData1, // transaction-specific data
ULONG dwData2 ) { // transaction-specific data
#if MOZ_DEBUG_DDE
printf( "DDE: uType =%s\n", uTypeDesc( uType ).GetBuffer() );
printf( " uFmt =%u\n", (unsigned)uFmt );
printf( " hconv =%08x\n", (int)hconv );
printf( " hsz1 =%08x:%s\n", (int)hsz1, hszValue( mInstance, hsz1 ).GetBuffer() );
printf( " hsz2 =%08x:%s\n", (int)hsz2, hszValue( mInstance, hsz2 ).GetBuffer() );
printf( " hdata =%08x\n", (int)hdata );
printf( " dwData1=%08x\n", (int)dwData1 );
printf( " dwData2=%08x\n", (int)dwData2 );
#endif
HDDEDATA result = 0;
if ( uType & XCLASS_BOOL ) {
switch ( uType ) {
case XTYP_CONNECT:
case XTYP_CONNECT_CONFIRM:
// Make sure its for our service/topic.
if ( DdeCmpStringHandles( hsz1, mTopic ) == 0
&&
DdeCmpStringHandles( hsz2, mApplication ) == 0 ) {
// We support this connection.
result = (HDDEDATA)1;
}
}
} else if ( uType & XCLASS_DATA ) {
} else if ( uType & XCLASS_FLAGS ) {
if ( uType == XTYP_EXECUTE ) {
// Prove that we received the request.
DWORD bytes;
LPBYTE request = DdeAccessData( hdata, &bytes );
#if MOZ_DEBUG_DDE
printf( "Handling dde request: [%s]...\n", (char*)request );
#endif
HandleRequest( request );
result = (HDDEDATA)DDE_FACK;
} else {
result = (HDDEDATA)DDE_FNOTPROCESSED;
}
} else if ( uType & XCLASS_NOTIFICATION ) {
}
return result;
}
// Handle DDE request. The argument is the command line received by the
// DDE client process. We convert that string to an nsICmdLineService
// object via GetCmdLineArgs. Then, we look for certain well-known cmd
// arguments. This replicates code elsewhere, to some extent,
// unfortunately (if you can fix that, please do).
void
nsNativeAppSupportWin::HandleRequest( LPBYTE request ) {
// Parse command line.
nsCOMPtr<nsICmdLineService> args;
nsresult rv = GetCmdLineArgs( request, getter_AddRefs( args ) );
if ( NS_SUCCEEDED( rv ) ) {
char *arg;
if ( NS_SUCCEEDED( args->GetURLToLoad( &arg ) ) && arg ) {
// Launch browser.
#if MOZ_DEBUG_DDE
printf( "Launching browser on url [%s]...\n", arg );
#endif
(void)OpenWindow( "chrome://navigator/content/", arg );
} else if ( NS_SUCCEEDED( args->GetCmdLineValue( "-chrome", &arg ) ) && arg ) {
// Launch chrome.
#if MOZ_DEBUG_DDE
printf( "Launching chrome url [%s]...\n", arg );
#endif
(void)OpenWindow( arg, "" );
} else if ( NS_SUCCEEDED( args->GetCmdLineValue( "-edit", &arg ) ) && arg ) {
// Launch composer.
#if MOZ_DEBUG_DDE
printf( "Launching editor on url [%s]...\n", arg );
#endif
(void)OpenWindow( "chrome://editor/content/", arg );
} else if ( NS_SUCCEEDED( args->GetCmdLineValue( "-mail", &arg ) ) && arg ) {
// Launch composer.
#if MOZ_DEBUG_DDE
printf( "Launching mail...\n" );
#endif
(void)OpenWindow( "chrome://messenger/content/", "" );
} else {
#if MOZ_DEBUG_DDE
printf( "Unknown request [%s]\n", (char*) request );
#endif
}
}
}
// Parse command line args according to MS spec
// (see "Parsing C++ Command-Line Arguments" at
// http://msdn.microsoft.com/library/devprods/vs6/visualc/vclang/_pluslang_parsing_c.2b2b_.command.2d.line_arguments.htm).
nsresult
nsNativeAppSupportWin::GetCmdLineArgs( LPBYTE request, nsICmdLineService **aResult ) {
nsresult rv = NS_OK;
int justCounting = 1;
char **argv = 0;
// Flags, etc.
int init = 1;
int between, quoted, bSlashCount;
int argc;
char *p;
nsCString arg;
// We loop if we've not finished the second pass through.
while ( 1 ) {
// Initialize if required.
if ( init ) {
p = (char*)request;
between = 1;
argc = quoted = bSlashCount = 0;
init = 0;
}
if ( between ) {
// We are traversing whitespace between args.
// Check for start of next arg.
if ( *p != 0 && !isspace( *p ) ) {
// Start of another arg.
between = 0;
arg = "";
switch ( *p ) {
case '\\':
// Count the backslash.
bSlashCount = 1;
break;
case '"':
// Remember we're inside quotes.
quoted = 1;
break;
default:
// Add character to arg.
arg += *p;
break;
}
} else {
// Another space between args, ignore it.
}
} else {
// We are processing the contents of an argument.
// Check for whitespace or end.
if ( *p == 0 || ( !quoted && isspace( *p ) ) ) {
// Process pending backslashes (interpret them
// literally since they're not followed by a ").
while( bSlashCount ) {
arg += '\\';
bSlashCount--;
}
// End current arg.
if ( !justCounting ) {
argv[argc] = new char[ arg.Length() + 1 ];
strcpy( argv[argc], arg.GetBuffer() );
}
argc++;
// We're now between args.
between = 1;
} else {
// Still inside argument, process the character.
switch ( *p ) {
case '"':
// First, digest preceding backslashes (if any).
while ( bSlashCount > 1 ) {
// Put one backsplash in arg for each pair.
arg += '\\';
bSlashCount -= 2;
}
if ( bSlashCount ) {
// Quote is literal.
arg += '"';
bSlashCount = 0;
} else {
// Quote starts or ends a quoted section.
if ( quoted ) {
// Check for special case of consecutive double
// quotes inside a quoted section.
if ( *(p+1) == '"' ) {
// This implies a literal double-quote. Fake that
// out by causing next double-quote to look as
// if it was preceded by a backslash.
bSlashCount = 1;
} else {
quoted = 0;
}
} else {
quoted = 1;
}
}
break;
case '\\':
// Add to count.
bSlashCount++;
break;
default:
// Accept any preceding backslashes literally.
while ( bSlashCount ) {
arg += '\\';
bSlashCount--;
}
// Just add next char to the current arg.
arg += *p;
break;
}
}
}
// Check for end of input.
if ( *p ) {
// Go to next character.
p++;
} else {
// If on first pass, go on to second.
if ( justCounting ) {
// Allocate argv array.
argv = new char*[ argc ];
// Start second pass
justCounting = 0;
init = 1;
} else {
// Quit.
break;
}
}
}
// OK, now create nsICmdLineService object from argc/argv.
static NS_DEFINE_CID( kCmdLineServiceCID, NS_COMMANDLINE_SERVICE_CID );
rv = nsComponentManager::CreateInstance( kCmdLineServiceCID,
0,
NS_GET_IID( nsICmdLineService ),
(void**)aResult );
if ( NS_FAILED( rv ) || NS_FAILED( ( rv = (*aResult)->Initialize( argc, argv ) ) ) ) {
#if MOZ_DEBUG_DDE
printf( "Error creating command line service = 0x%08X\n", (int)rv );
#endif
}
// Cleanup.
while ( argc ) {
delete [] argv[ --argc ];
}
delete [] argv;
return rv;
}
nsresult
nsNativeAppSupportWin::OpenWindow( const char*urlstr, const char *args ) {
nsresult rv;
static NS_DEFINE_CID( kAppShellServiceCID, NS_APPSHELL_SERVICE_CID );
NS_WITH_SERVICE(nsIAppShellService, appShellService, kAppShellServiceCID, &rv)
if ( NS_SUCCEEDED( rv ) ) {
nsCOMPtr<nsIDOMWindow> hiddenWindow;
JSContext *jsContext;
rv = appShellService->GetHiddenWindowAndJSContext( getter_AddRefs( hiddenWindow ),
&jsContext );
if ( NS_SUCCEEDED( rv ) ) {
void *stackPtr;
jsval *argv = JS_PushArguments( jsContext,
&stackPtr,
"ssss",
urlstr,
"_blank",
"chrome,dialog=no,all",
args );
if( argv ) {
nsCOMPtr<nsIDOMWindow> newWindow;
rv = hiddenWindow->OpenDialog( jsContext,
argv,
4,
getter_AddRefs( newWindow ) );
JS_PopArguments( jsContext, stackPtr );
}
} else {
#ifdef MOZ_DEBUG_DDE
printf( "GetHiddenWindowAndJSContext failed, rv=0x%08X\n", (int)rv );
#endif
}
}
return rv;
}