Bug 25699; Add DDE support to get Acrobat weblinks working; r=syd, sr=mscott, a=brendan

This commit is contained in:
law%netscape.com 2001-06-06 08:36:59 +00:00
Родитель ab59e1338b
Коммит 5ad8b920a1
1 изменённых файлов: 315 добавлений и 35 удалений

Просмотреть файл

@ -37,6 +37,12 @@
#include "nsIDocShell.h"
#include "nsIBaseWindow.h"
#include "nsIAppShellService.h"
// These are needed to load a URL in a browser window.
#include "nsIDOMLocation.h"
#include "nsIJSContextStack.h"
#include "nsIWindowMediator.h"
#include <windows.h>
#include <ddeml.h>
#include <stdlib.h>
@ -268,13 +274,28 @@ private:
ULONG dwData1,
ULONG dwData2 );
static void HandleRequest( LPBYTE request );
static nsCString ParseDDEArg( HSZ args, int index );
static void ActivateLastWindow();
static HDDEDATA CreateDDEData( DWORD value );
static PRBool InitTopicStrings();
static int FindTopic( HSZ topic );
static nsresult GetCmdLineArgs( LPBYTE request, nsICmdLineService **aResult );
static nsresult OpenWindow( const char *urlstr, const char *args, nsIDOMWindow **aResult = 0 );
static nsresult OpenBrowserWindow( const char *args );
static nsresult ReParent( nsIDOMWindowInternal *window, HWND newParent );
static nsCOMPtr<nsIDOMWindowInternal> mCachedWin;
static int mConversations;
static HSZ mApplication, mTopic;
enum {
topicOpenURL,
topicActivate,
topicCancelProgress,
topicVersion,
topicRegisterViewer,
topicUnRegisterViewer,
// Note: Insert new values above this line!!!!!
topicCount // Count of the number of real topics
};
static HSZ mApplication, mTopics[ topicCount ];
static DWORD mInstance;
static char *mAppName;
friend struct MessageWindow;
@ -569,17 +590,24 @@ NS_CreateSplashScreen( nsISplashScreen **aResult ) {
// Constants
#define MOZ_DDE_APPLICATION "Mozilla"
#define MOZ_DDE_TOPIC "WWW_OpenURL"
#define MOZ_STARTUP_MUTEX_NAME "StartupMutex"
#define MOZ_DDE_START_TIMEOUT 30000
#define MOZ_DDE_STOP_TIMEOUT 15000
#define MOZ_DDE_EXEC_TIMEOUT 15000
// The array entries must match the enum ordering!
const char * const topicNames[] = { "WWW_OpenURL",
"WWW_Activate",
"WWW_CancelProgress",
"WWW_Version",
"WWW_RegisterViewer",
"WWW_UnRegisterViewer" };
// Static member definitions.
nsCOMPtr<nsIDOMWindowInternal> nsNativeAppSupportWin::mCachedWin = 0;
int nsNativeAppSupportWin::mConversations = 0;
HSZ nsNativeAppSupportWin::mApplication = 0;
HSZ nsNativeAppSupportWin::mTopic = 0;
HSZ nsNativeAppSupportWin::mTopics[nsNativeAppSupportWin::topicCount] = { 0 };
DWORD nsNativeAppSupportWin::mInstance = 0;
// Message window encapsulation.
@ -730,6 +758,26 @@ nsNativeAppSupportWin::Start( PRBool *aResult ) {
return rv;
}
PRBool
nsNativeAppSupportWin::InitTopicStrings() {
for ( int i = 0; i < topicCount; i++ ) {
if ( !( mTopics[ i ] = DdeCreateStringHandle( mInstance, topicNames[ i ], CP_WINANSI ) ) ) {
return PR_FALSE;
}
}
return PR_TRUE;
}
int
nsNativeAppSupportWin::FindTopic( HSZ topic ) {
for ( int i = 0; i < topicCount; i++ ) {
if ( DdeCmpStringHandles( topic, mTopics[i] ) == 0 ) {
return i;
}
}
return -1;
}
// Start DDE server.
//
// This used to be the Start() method when we were using DDE as the
@ -752,8 +800,7 @@ nsNativeAppSupportWin::StartDDE() {
NS_ERROR_FAILURE );
// Allocate DDE strings.
NS_ENSURE_TRUE( ( mApplication = DdeCreateStringHandle( mInstance, mAppName, CP_WINANSI ) ) &&
( mTopic = DdeCreateStringHandle( mInstance, MOZ_DDE_TOPIC, CP_WINANSI ) ),
NS_ENSURE_TRUE( ( mApplication = DdeCreateStringHandle( mInstance, mAppName, CP_WINANSI ) ) && InitTopicStrings(),
NS_ERROR_FAILURE );
// Next step is to register a DDE service.
@ -801,9 +848,11 @@ nsNativeAppSupportWin::Quit() {
DdeFreeStringHandle( mInstance, mApplication );
mApplication = 0;
}
if ( mTopic ) {
DdeFreeStringHandle( mInstance, mTopic );
mTopic = 0;
for ( int i = 0; i < topicCount; i++ ) {
if ( mTopics[i] ) {
DdeFreeStringHandle( mInstance, mTopics[i] );
mTopics[i] = 0;
}
}
DdeUninitialize( mInstance );
mInstance = 0;
@ -895,16 +944,75 @@ nsNativeAppSupportWin::HandleDDENotification( UINT uType, // transaction t
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 ) {
if ( FindTopic( hsz1 ) < topicCount ) {
// We support this connection.
result = (HDDEDATA)1;
}
break;
case XTYP_CONNECT_CONFIRM:
// We don't care about the conversation handle, at this point.
result = (HDDEDATA)1;
break;
}
} else if ( uType & XCLASS_DATA ) {
if ( uType == XTYP_REQUEST ) {
switch ( FindTopic( hsz1 ) ) {
case topicOpenURL: {
// Open a given URL...
nsCString url = ParseDDEArg( hsz2, 0 );
// Make it look like command line args.
url.Insert( "mozilla -url ", 0 );
#if MOZ_DEBUG_DDE
printf( "Handling dde XTYP_REQUEST request: [%s]...\n", url.get() );
#endif
// Now handle it.
HandleRequest( LPBYTE( url.get() ) );
// Return pseudo window ID.
result = CreateDDEData( 1 );
break;
}
case topicActivate: {
// Activate a Nav window...
nsCString windowID = ParseDDEArg( hsz2, 0 );
if ( windowID.Equals( "-1" ) ) {
// We only support activating the most recent window (or a new one).
ActivateLastWindow();
// Return pseudo window ID.
result = CreateDDEData( 1 );
}
break;
}
case topicVersion: {
// Return version. We're restarting at 1.0!
DWORD version = 1 << 16; // "1.0"
result = CreateDDEData( version );
break;
}
case topicRegisterViewer: {
// Register new viewer (not implemented).
result = CreateDDEData( PR_FALSE );
break;
}
case topicUnRegisterViewer: {
// Unregister new viewer (not implemented).
result = CreateDDEData( PR_FALSE );
break;
}
default:
break;
}
} else if ( uType & XTYP_POKE ) {
switch ( FindTopic( hsz1 ) ) {
case topicCancelProgress: {
// "Handle" progress cancel (actually, pretty much ignored).
result = (HDDEDATA)DDE_FACK;
break;
}
default:
break;
}
}
} else if ( uType & XCLASS_FLAGS ) {
if ( uType == XTYP_EXECUTE ) {
// Prove that we received the request.
@ -920,6 +1028,80 @@ nsNativeAppSupportWin::HandleDDENotification( UINT uType, // transaction t
}
} else if ( uType & XCLASS_NOTIFICATION ) {
}
#if MOZ_DEBUG_DDE
printf( "DDE result=%d (0x%08X)\n", (int)result, (int)result );
#endif
return result;
}
// Utility to parse out argument from a DDE item string.
nsCString nsNativeAppSupportWin::ParseDDEArg( HSZ args, int index ) {
nsCString result;
DWORD argLen = DdeQueryString( mInstance, args, NULL, NULL, CP_WINANSI );
if ( argLen ) {
nsCString temp;
// Ensure result's buffer is sufficiently big.
temp.SetLength( argLen + 1 );
// Now get the string contents.
DdeQueryString( mInstance, args, (char*)temp.get(), temp.Length(), CP_WINANSI );
// Parse out the given arg. We assume there's no commas within quoted strings
// (which may not be accurate, but makes life so much easier).
const char *p = temp.get();
// Skip index commas.
PRInt32 offset = index ? temp.FindChar( ',', PR_FALSE, 0, index ) : 0;
if ( offset != -1 ) {
// Desired argument starts there and ends at next comma.
PRInt32 end = temp.FindChar( ',', PR_FALSE, offset+1 );
if ( end == -1 ) {
// Rest of string.
end = temp.Length();
}
// Extract result.
result.Assign( temp.get() + offset, end - offset );
}
}
return result;
}
static NS_DEFINE_CID(kWindowMediatorCID, NS_WINDOWMEDIATOR_CID);
static HWND hwndForDOMWindow( nsIDOMWindowInternal * );
void nsNativeAppSupportWin::ActivateLastWindow() {
nsCOMPtr<nsIWindowMediator> med( do_GetService( kWindowMediatorCID ) );
if ( med ) {
// Get most recently used Nav window.
nsCOMPtr<nsIDOMWindowInternal> navWin;
med->GetMostRecentWindow( NS_LITERAL_STRING("navigator:browser").get(), getter_AddRefs( navWin ) );
if ( navWin ) {
// Activate that window.
HWND hwnd = hwndForDOMWindow( navWin );
if ( hwnd ) {
// Restore the window in case it is minimized.
::ShowWindow( hwnd, SW_RESTORE );
// Use the OS call, if possible.
::SetForegroundWindow( hwnd );
} else {
// Use internal method.
navWin->Focus();
}
} else {
// Need to create a Navigator window, then.
OpenBrowserWindow( "about:blank" );
}
}
}
HDDEDATA nsNativeAppSupportWin::CreateDDEData( DWORD value ) {
HDDEDATA result = DdeCreateDataHandle( mInstance,
(LPBYTE)&value,
sizeof value,
0,
mApplication,
CF_TEXT,
0 );
return result;
}
@ -1206,25 +1388,30 @@ static LRESULT CALLBACK focusFilterProc( HWND hwnd, UINT uMsg, WPARAM wParam, LP
}
}
nsresult
nsNativeAppSupportWin::ReParent( nsIDOMWindowInternal *window, HWND newParent ) {
HWND hwndForDOMWindow( nsIDOMWindowInternal *window ) {
nsCOMPtr<nsIScriptGlobalObject> ppScriptGlobalObj( do_QueryInterface(window) );
if ( !ppScriptGlobalObj ) {
return NS_ERROR_FAILURE;
return 0;
}
nsCOMPtr<nsIDocShell> ppDocShell;
ppScriptGlobalObj->GetDocShell( getter_AddRefs( ppDocShell ) );
if ( !ppDocShell ) {
return NS_ERROR_FAILURE;
return 0;
}
nsCOMPtr<nsIBaseWindow> ppBaseWindow( do_QueryInterface( ppDocShell ) );
if ( !ppBaseWindow ) {
return NS_ERROR_FAILURE;
return 0;
}
nsCOMPtr<nsIWidget> ppWidget;
ppBaseWindow->GetMainWidget( getter_AddRefs( ppWidget ) );
HWND hMainFrame = (HWND)ppWidget->GetNativeData( NS_NATIVE_WIDGET );
return (HWND)( ppWidget->GetNativeData( NS_NATIVE_WIDGET ) );
}
nsresult
nsNativeAppSupportWin::ReParent( nsIDOMWindowInternal *window, HWND newParent ) {
HWND hMainFrame = hwndForDOMWindow( window );
if ( !hMainFrame ) {
return NS_ERROR_FAILURE;
}
@ -1258,21 +1445,114 @@ nsNativeAppSupportWin::ReParent( nsIDOMWindowInternal *window, HWND newParent )
return NS_OK;
}
static const char sJSStackContractID[] = "@mozilla.org/js/xpc/ContextStack;1";
class SafeJSContext {
public:
SafeJSContext();
~SafeJSContext();
nsresult Push();
JSContext *get() { return mContext; }
protected:
nsCOMPtr<nsIThreadJSContextStack> mService;
JSContext *mContext;
};
SafeJSContext::SafeJSContext() : mContext(nsnull) {
}
SafeJSContext::~SafeJSContext() {
JSContext *cx;
nsresult rv;
if(mContext) {
rv = mService->Pop(&cx);
NS_ASSERTION(NS_SUCCEEDED(rv) && cx == mContext, "JSContext push/pop mismatch");
}
}
nsresult SafeJSContext::Push() {
nsresult rv;
if (mContext) // only once
return NS_ERROR_FAILURE;
mService = do_GetService(sJSStackContractID);
if(mService) {
rv = mService->GetSafeJSContext(&mContext);
if (NS_SUCCEEDED(rv) && mContext) {
rv = mService->Push(mContext);
if (NS_FAILED(rv))
mContext = 0;
}
}
return mContext ? NS_OK : NS_ERROR_FAILURE;
}
nsresult
nsNativeAppSupportWin::OpenBrowserWindow( const char *args ) {
nsresult rv = NS_OK;
// Check if we have a cached (hidden) window.
if ( mCachedWin ) {
nsCOMPtr<nsIDOMWindowInternal> win = mCachedWin;
// Open the argument URL in the most recently used Navigator window.
// If there is no Nav window, open a new one.
nsCOMPtr<nsIWindowMediator> med( do_GetService( kWindowMediatorCID ) );
nsCOMPtr<nsIDOMWindowInternal> navWin;
nsCOMPtr<nsIDOMWindow> content;
nsCOMPtr<nsIDOMWindowInternal> internalContent;
nsCOMPtr<nsIDOMLocation> location;
// This isn't really a loop. We just use "break" statements to fall
// out to the OpenWindow call when things go awry.
do {
if ( !med ) {
break;
}
// Get most recently used Nav window.
med->GetMostRecentWindow( NS_LITERAL_STRING( "navigator:browser" ).get(), getter_AddRefs( navWin ) );
if ( !navWin ) {
// Have to open a new one.
break;
} else {
// Check if we're using the cached (hidden) window.
if ( mCachedWin == navWin ) {
// Drop window from cache.
mCachedWin = 0;
// Show it.
ReParent( win, 0 );
} else {
// No cached window, open a new one.
rv = OpenWindow( "chrome://navigator/content", args );
ReParent( navWin, 0 );
}
return rv;
}
// Get content window.
navWin->GetContent( getter_AddRefs( content ) );
if ( !content ) {
break;
}
// Convert that to internal interface.
internalContent = do_QueryInterface( content );
if ( !internalContent ) {
break;
}
// Get location.
internalContent->GetLocation( getter_AddRefs( location ) );
if ( !location ) {
break;
}
// Set up environment.
SafeJSContext context;
if ( NS_FAILED( context.Push() ) ) {
break;
}
// Set href.
nsAutoString url; url.AssignWithConversion( args );
if ( NS_FAILED( location->SetHref( url ) ) ) {
break;
}
// Finally, if we get here, we're done.
return NS_OK;
} while ( PR_FALSE );
// Last resort is to open a brand new window.
return OpenWindow( "chrome://navigator/content", args );
}
NS_IMETHODIMP