1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/toolkit/xre/nsNativeAppSupportWin.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1522 @@ 1.4 +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "nsNativeAppSupportBase.h" 1.10 +#include "nsNativeAppSupportWin.h" 1.11 +#include "nsAppRunner.h" 1.12 +#include "nsXULAppAPI.h" 1.13 +#include "nsString.h" 1.14 +#include "nsIBrowserDOMWindow.h" 1.15 +#include "nsICommandLineRunner.h" 1.16 +#include "nsCOMPtr.h" 1.17 +#include "nsXPIDLString.h" 1.18 +#include "nsIComponentManager.h" 1.19 +#include "nsIServiceManager.h" 1.20 +#include "nsIDOMChromeWindow.h" 1.21 +#include "nsXPCOM.h" 1.22 +#include "nsISupportsPrimitives.h" 1.23 +#include "nsIWindowWatcher.h" 1.24 +#include "nsPIDOMWindow.h" 1.25 +#include "nsIDocShell.h" 1.26 +#include "nsIDocShellTreeItem.h" 1.27 +#include "nsIBaseWindow.h" 1.28 +#include "nsIWidget.h" 1.29 +#include "nsIAppShellService.h" 1.30 +#include "nsIXULWindow.h" 1.31 +#include "nsIInterfaceRequestor.h" 1.32 +#include "nsIInterfaceRequestorUtils.h" 1.33 +#include "nsIPromptService.h" 1.34 +#include "nsNetCID.h" 1.35 +#include "nsNetUtil.h" 1.36 +#include "nsIObserver.h" 1.37 +#include "nsIObserverService.h" 1.38 +#include "nsIDOMLocation.h" 1.39 +#include "nsIWebNavigation.h" 1.40 +#include "nsIWindowMediator.h" 1.41 +#include "nsNativeCharsetUtils.h" 1.42 +#include "nsIAppStartup.h" 1.43 + 1.44 +#include <windows.h> 1.45 +#include <shellapi.h> 1.46 +#include <ddeml.h> 1.47 +#include <stdlib.h> 1.48 +#include <stdio.h> 1.49 +#include <io.h> 1.50 +#include <direct.h> 1.51 +#include <fcntl.h> 1.52 + 1.53 +using namespace mozilla; 1.54 + 1.55 +static HWND hwndForDOMWindow( nsISupports * ); 1.56 + 1.57 +static 1.58 +nsresult 1.59 +GetMostRecentWindow(const char16_t* aType, nsIDOMWindow** aWindow) { 1.60 + nsresult rv; 1.61 + nsCOMPtr<nsIWindowMediator> med( do_GetService( NS_WINDOWMEDIATOR_CONTRACTID, &rv ) ); 1.62 + if ( NS_FAILED( rv ) ) 1.63 + return rv; 1.64 + 1.65 + if ( med ) 1.66 + return med->GetMostRecentWindow( aType, aWindow ); 1.67 + 1.68 + return NS_ERROR_FAILURE; 1.69 +} 1.70 + 1.71 +static 1.72 +void 1.73 +activateWindow( nsIDOMWindow *win ) { 1.74 + // Try to get native window handle. 1.75 + HWND hwnd = hwndForDOMWindow( win ); 1.76 + if ( hwnd ) { 1.77 + // Restore the window if it is minimized. 1.78 + if ( ::IsIconic( hwnd ) ) { 1.79 + ::ShowWindow( hwnd, SW_RESTORE ); 1.80 + } 1.81 + // Use the OS call, if possible. 1.82 + ::SetForegroundWindow( hwnd ); 1.83 + } else { 1.84 + // Use internal method. 1.85 + win->Focus(); 1.86 + } 1.87 +} 1.88 + 1.89 + 1.90 +#ifdef DEBUG_law 1.91 +#undef MOZ_DEBUG_DDE 1.92 +#define MOZ_DEBUG_DDE 1 1.93 +#endif 1.94 + 1.95 +// Simple Win32 mutex wrapper. 1.96 +struct Mutex { 1.97 + Mutex( const char16_t *name ) 1.98 + : mName( name ), 1.99 + mHandle( 0 ), 1.100 + mState( -1 ) { 1.101 + mHandle = CreateMutexW( 0, FALSE, mName.get() ); 1.102 +#if MOZ_DEBUG_DDE 1.103 + printf( "CreateMutex error = 0x%08X\n", (int)GetLastError() ); 1.104 +#endif 1.105 + } 1.106 + ~Mutex() { 1.107 + if ( mHandle ) { 1.108 + // Make sure we release it if we own it. 1.109 + Unlock(); 1.110 + 1.111 + BOOL rc = CloseHandle( mHandle ); 1.112 +#if MOZ_DEBUG_DDE 1.113 + if ( !rc ) { 1.114 + printf( "CloseHandle error = 0x%08X\n", (int)GetLastError() ); 1.115 + } 1.116 +#endif 1.117 + } 1.118 + } 1.119 + BOOL Lock( DWORD timeout ) { 1.120 + if ( mHandle ) { 1.121 +#if MOZ_DEBUG_DDE 1.122 + printf( "Waiting (%d msec) for DDE mutex...\n", (int)timeout ); 1.123 +#endif 1.124 + mState = WaitForSingleObject( mHandle, timeout ); 1.125 +#if MOZ_DEBUG_DDE 1.126 + printf( "...wait complete, result = 0x%08X, GetLastError=0x%08X\n", (int)mState, (int)::GetLastError() ); 1.127 +#endif 1.128 + return mState == WAIT_OBJECT_0 || mState == WAIT_ABANDONED; 1.129 + } else { 1.130 + return FALSE; 1.131 + } 1.132 + } 1.133 + void Unlock() { 1.134 + if ( mHandle && mState == WAIT_OBJECT_0 ) { 1.135 +#if MOZ_DEBUG_DDE 1.136 + printf( "Releasing DDE mutex\n" ); 1.137 +#endif 1.138 + ReleaseMutex( mHandle ); 1.139 + mState = -1; 1.140 + } 1.141 + } 1.142 +private: 1.143 + nsString mName; 1.144 + HANDLE mHandle; 1.145 + DWORD mState; 1.146 +}; 1.147 + 1.148 +/* DDE Notes 1.149 + * 1.150 + * This section describes the Win32 DDE service implementation for 1.151 + * Mozilla. DDE is used on Win32 platforms to communicate between 1.152 + * separate instances of mozilla.exe (or other Mozilla-based 1.153 + * executables), or, between the Win32 desktop shell and Mozilla. 1.154 + * 1.155 + * The first instance of Mozilla will become the "server" and 1.156 + * subsequent executables (and the shell) will use DDE to send 1.157 + * requests to that process. The requests are DDE "execute" requests 1.158 + * that pass the command line arguments. 1.159 + * 1.160 + * Mozilla registers the DDE application "Mozilla" and currently 1.161 + * supports only the "WWW_OpenURL" topic. This should be reasonably 1.162 + * compatible with applications that interfaced with Netscape 1.163 + * Communicator (and its predecessors?). Note that even that topic 1.164 + * may not be supported in a compatible fashion as the command-line 1.165 + * options for Mozilla are different than for Communiator. 1.166 + * 1.167 + * It is imperative that at most one instance of Mozilla execute in 1.168 + * "server mode" at any one time. The "native app support" in Mozilla 1.169 + * on Win32 ensures that only the server process performs XPCOM 1.170 + * initialization (that is not required for subsequent client processes 1.171 + * to communicate with the server process). 1.172 + * 1.173 + * To guarantee that only one server starts up, a Win32 "mutex" is used 1.174 + * to ensure only one process executes the server-detection code. That 1.175 + * code consists of initializing DDE and doing a DdeConnect to Mozilla's 1.176 + * application/topic. If that connection succeeds, then a server process 1.177 + * must be running already. 1.178 + * 1.179 + * Otherwise, no server has started. In that case, the current process 1.180 + * calls DdeNameService to register that application/topic. Only at that 1.181 + * point does the mutex get released. 1.182 + * 1.183 + * There are a couple of subtleties that one should be aware of: 1.184 + * 1.185 + * 1. It is imperative that DdeInitialize be called only after the mutex 1.186 + * lock has been obtained. The reason is that at shutdown, DDE 1.187 + * notifications go out to all initialized DDE processes. Thus, if 1.188 + * the mutex is owned by a terminating intance of Mozilla, then 1.189 + * calling DdeInitialize and then WaitForSingleObject will cause the 1.190 + * DdeUninitialize from the terminating process to "hang" until the 1.191 + * process waiting for the mutex times out (and can then service the 1.192 + * notification that the DDE server is terminating). So, don't mess 1.193 + * with the sequence of things in the startup/shutdown logic. 1.194 + * 1.195 + * 2. All mutex requests are made with a reasonably long timeout value and 1.196 + * are designed to "fail safe" (i.e., a timeout is treated as failure). 1.197 + * 1.198 + * 3. An attempt has been made to minimize the degree to which the main 1.199 + * Mozilla application logic needs to be aware of the DDE mechanisms 1.200 + * implemented herein. As a result, this module surfaces a very 1.201 + * large-grained interface, consisting of simple start/stop methods. 1.202 + * As a consequence, details of certain scenarios can be "lost." 1.203 + * Particularly, incoming DDE requests can arrive after this module 1.204 + * initiates the DDE server, but before Mozilla is initialized to the 1.205 + * point where those requests can be serviced (e.g., open a browser 1.206 + * window to a particular URL). Since the client process sends the 1.207 + * request early on, it may not be prepared to respond to that error. 1.208 + * Thus, such situations may fail silently. The design goal is that 1.209 + * they fail harmlessly. Refinements on this point will be made as 1.210 + * details emerge (and time permits). 1.211 + */ 1.212 + 1.213 +/* Update 2001 March 1.214 + * 1.215 + * A significant DDE bug in Windows is causing Mozilla to get wedged at 1.216 + * startup. This is detailed in Bugzill bug 53952 1.217 + * (http://bugzilla.mozilla.org/show_bug.cgi?id=53952). 1.218 + * 1.219 + * To resolve this, we are using a new strategy: 1.220 + * o Use a "message window" to detect that Mozilla is already running and 1.221 + * to pass requests from a second instance back to the first; 1.222 + * o Run only as a "DDE server" (not as DDE client); this avoids the 1.223 + * problematic call to DDEConnect(). 1.224 + * 1.225 + * We still use the mutex semaphore to protect the code that detects 1.226 + * whether Mozilla is already running. 1.227 + */ 1.228 + 1.229 +/* Update 2007 January 1.230 + * 1.231 + * A change in behavior was implemented in July 2004 which made the 1.232 + * application on launch to add and on quit to remove the ddexec registry key. 1.233 + * See bug 246078. 1.234 + * Windows Vista has changed the methods used to set an application as default 1.235 + * and the new methods are incompatible with removing the ddeexec registry key. 1.236 + * See bug 353089. 1.237 + * 1.238 + * OS DDE Sequence: 1.239 + * 1. OS checks if the dde name is registered. 1.240 + * 2. If it is registered the OS sends a DDE request with the WWW_OpenURL topic 1.241 + * and the params as specified in the default value of the ddeexec registry 1.242 + * key for the verb (e.g. open). 1.243 + * 3. If it isn't registered the OS launches the executable defined in the 1.244 + * verb's (e.g. open) command registry key. 1.245 + * 4. If the ifexec registry key is not present the OS sends a DDE request with 1.246 + * the WWW_OpenURL topic and the params as specified in the default value of 1.247 + * the ddeexec registry key for the verb (e.g. open). 1.248 + * 5. If the ifexec registry key is present the OS sends a DDE request with the 1.249 + * WWW_OpenURL topic and the params as specified in the ifexec registry key 1.250 + * for the verb (e.g. open). 1.251 + * 1.252 + * Application DDE Sequence: 1.253 + * 1. If the application is running a DDE request is received with the 1.254 + * WWW_OpenURL topic and the params as specified in the default value of the 1.255 + * ddeexec registry key (e.g. "%1",,0,0,,,, where '%1' is the url to open) 1.256 + * for the verb (e.g. open). 1.257 + * 2. If the application is not running it is launched with the -requestPending 1.258 + * and the -url argument. 1.259 + * 2.1 If the application does not need to restart and the -requestPending 1.260 + * argument is present the accompanying url will not be used. Instead the 1.261 + * application will wait for the DDE message to open the url. 1.262 + * 2.2 If the application needs to restart the -requestPending argument is 1.263 + * removed from the arguments used to restart the application and the url 1.264 + * will be handled normally. 1.265 + * 1.266 + * Note: Due to a bug in IE the ifexec key should not be used (see bug 355650). 1.267 + */ 1.268 + 1.269 +class nsNativeAppSupportWin : public nsNativeAppSupportBase, 1.270 + public nsIObserver 1.271 +{ 1.272 +public: 1.273 + NS_DECL_NSIOBSERVER 1.274 + NS_DECL_ISUPPORTS_INHERITED 1.275 + 1.276 + // Overrides of base implementation. 1.277 + NS_IMETHOD Start( bool *aResult ); 1.278 + NS_IMETHOD Stop( bool *aResult ); 1.279 + NS_IMETHOD Quit(); 1.280 + NS_IMETHOD Enable(); 1.281 + // The "old" Start method (renamed). 1.282 + NS_IMETHOD StartDDE(); 1.283 + // Utility function to handle a Win32-specific command line 1.284 + // option: "-console", which dynamically creates a Windows 1.285 + // console. 1.286 + void CheckConsole(); 1.287 + 1.288 +private: 1.289 + static void HandleCommandLine(const char* aCmdLineString, nsIFile* aWorkingDir, uint32_t aState); 1.290 + static HDDEDATA CALLBACK HandleDDENotification( UINT uType, 1.291 + UINT uFmt, 1.292 + HCONV hconv, 1.293 + HSZ hsz1, 1.294 + HSZ hsz2, 1.295 + HDDEDATA hdata, 1.296 + ULONG_PTR dwData1, 1.297 + ULONG_PTR dwData2 ); 1.298 + static void ParseDDEArg( HSZ args, int index, nsString& string); 1.299 + static void ParseDDEArg( const WCHAR* args, int index, nsString& aString); 1.300 + static HDDEDATA CreateDDEData( DWORD value ); 1.301 + static HDDEDATA CreateDDEData( LPBYTE value, DWORD len ); 1.302 + static bool InitTopicStrings(); 1.303 + static int FindTopic( HSZ topic ); 1.304 + static void ActivateLastWindow(); 1.305 + static nsresult OpenWindow( const char *urlstr, const char *args ); 1.306 + static nsresult OpenBrowserWindow(); 1.307 + static void SetupSysTrayIcon(); 1.308 + static void RemoveSysTrayIcon(); 1.309 + 1.310 + static int mConversations; 1.311 + enum { 1.312 + topicOpenURL, 1.313 + topicActivate, 1.314 + topicCancelProgress, 1.315 + topicVersion, 1.316 + topicRegisterViewer, 1.317 + topicUnRegisterViewer, 1.318 + topicGetWindowInfo, 1.319 + // Note: Insert new values above this line!!!!! 1.320 + topicCount // Count of the number of real topics 1.321 + }; 1.322 + static HSZ mApplication, mTopics[ topicCount ]; 1.323 + static DWORD mInstance; 1.324 + static bool mCanHandleRequests; 1.325 + static char16_t mMutexName[]; 1.326 + friend struct MessageWindow; 1.327 +}; // nsNativeAppSupportWin 1.328 + 1.329 +NS_INTERFACE_MAP_BEGIN(nsNativeAppSupportWin) 1.330 + NS_INTERFACE_MAP_ENTRY(nsIObserver) 1.331 +NS_INTERFACE_MAP_END_INHERITING(nsNativeAppSupportBase) 1.332 + 1.333 +NS_IMPL_ADDREF_INHERITED(nsNativeAppSupportWin, nsNativeAppSupportBase) 1.334 +NS_IMPL_RELEASE_INHERITED(nsNativeAppSupportWin, nsNativeAppSupportBase) 1.335 + 1.336 +void 1.337 +nsNativeAppSupportWin::CheckConsole() { 1.338 + for ( int i = 1; i < gArgc; i++ ) { 1.339 + if ( strcmp( "-console", gArgv[i] ) == 0 1.340 + || 1.341 + strcmp( "/console", gArgv[i] ) == 0 ) { 1.342 + // Users wants to make sure we have a console. 1.343 + // Try to allocate one. 1.344 + BOOL rc = ::AllocConsole(); 1.345 + if ( rc ) { 1.346 + // Console allocated. Fix it up so that output works in 1.347 + // all cases. See http://support.microsoft.com/support/kb/articles/q105/3/05.asp. 1.348 + 1.349 + // stdout 1.350 + int hCrt = ::_open_osfhandle( (intptr_t)GetStdHandle( STD_OUTPUT_HANDLE ), 1.351 + _O_TEXT ); 1.352 + if ( hCrt != -1 ) { 1.353 + FILE *hf = ::_fdopen( hCrt, "w" ); 1.354 + if ( hf ) { 1.355 + *stdout = *hf; 1.356 +#ifdef DEBUG 1.357 + ::fprintf( stdout, "stdout directed to dynamic console\n" ); 1.358 +#endif 1.359 + } 1.360 + } 1.361 + 1.362 + // stderr 1.363 + hCrt = ::_open_osfhandle( (intptr_t)::GetStdHandle( STD_ERROR_HANDLE ), 1.364 + _O_TEXT ); 1.365 + if ( hCrt != -1 ) { 1.366 + FILE *hf = ::_fdopen( hCrt, "w" ); 1.367 + if ( hf ) { 1.368 + *stderr = *hf; 1.369 +#ifdef DEBUG 1.370 + ::fprintf( stderr, "stderr directed to dynamic console\n" ); 1.371 +#endif 1.372 + } 1.373 + } 1.374 + 1.375 + // stdin? 1.376 + /* Don't bother for now. 1.377 + hCrt = ::_open_osfhandle( (long)::GetStdHandle( STD_INPUT_HANDLE ), 1.378 + _O_TEXT ); 1.379 + if ( hCrt != -1 ) { 1.380 + FILE *hf = ::_fdopen( hCrt, "r" ); 1.381 + if ( hf ) { 1.382 + *stdin = *hf; 1.383 + } 1.384 + } 1.385 + */ 1.386 + } else { 1.387 + // Failed. Probably because there already is one. 1.388 + // There's little we can do, in any case. 1.389 + } 1.390 + // Remove the console argument from the command line. 1.391 + do { 1.392 + gArgv[i] = gArgv[i + 1]; 1.393 + ++i; 1.394 + } while (gArgv[i]); 1.395 + 1.396 + --gArgc; 1.397 + 1.398 + } else if ( strcmp( "-attach-console", gArgv[i] ) == 0 1.399 + || 1.400 + strcmp( "/attach-console", gArgv[i] ) == 0 ) { 1.401 + // Try to attach console to the parent process. 1.402 + // It will succeed when the parent process is a command line, 1.403 + // so that stdio will be displayed in it. 1.404 + if (AttachConsole(ATTACH_PARENT_PROCESS)) { 1.405 + // Change std handles to refer to new console handles. 1.406 + // Before doing so, ensure that stdout/stderr haven't been 1.407 + // redirected to a valid file 1.408 + if (_fileno(stdout) == -1 || 1.409 + _get_osfhandle(fileno(stdout)) == -1) 1.410 + freopen("CONOUT$", "w", stdout); 1.411 + // Merge stderr into CONOUT$ since there isn't any `CONERR$`. 1.412 + // http://msdn.microsoft.com/en-us/library/windows/desktop/ms683231%28v=vs.85%29.aspx 1.413 + if (_fileno(stderr) == -1 || 1.414 + _get_osfhandle(fileno(stderr)) == -1) 1.415 + freopen("CONOUT$", "w", stderr); 1.416 + if (_fileno(stdin) == -1 || _get_osfhandle(fileno(stdin)) == -1) 1.417 + freopen("CONIN$", "r", stdin); 1.418 + } 1.419 + } 1.420 + } 1.421 + 1.422 + return; 1.423 +} 1.424 + 1.425 + 1.426 +// Create and return an instance of class nsNativeAppSupportWin. 1.427 +nsresult 1.428 +NS_CreateNativeAppSupport( nsINativeAppSupport **aResult ) { 1.429 + nsNativeAppSupportWin *pNative = new nsNativeAppSupportWin; 1.430 + if (!pNative) return NS_ERROR_OUT_OF_MEMORY; 1.431 + 1.432 + // Check for dynamic console creation request. 1.433 + pNative->CheckConsole(); 1.434 + 1.435 + *aResult = pNative; 1.436 + NS_ADDREF( *aResult ); 1.437 + 1.438 + return NS_OK; 1.439 +} 1.440 + 1.441 +// Constants 1.442 +#define MOZ_DDE_APPLICATION "Mozilla" 1.443 +#define MOZ_MUTEX_NAMESPACE L"Local\\" 1.444 +#define MOZ_STARTUP_MUTEX_NAME L"StartupMutex" 1.445 +#define MOZ_DDE_START_TIMEOUT 30000 1.446 +#define MOZ_DDE_STOP_TIMEOUT 15000 1.447 +#define MOZ_DDE_EXEC_TIMEOUT 15000 1.448 + 1.449 +// The array entries must match the enum ordering! 1.450 +const char * const topicNames[] = { "WWW_OpenURL", 1.451 + "WWW_Activate", 1.452 + "WWW_CancelProgress", 1.453 + "WWW_Version", 1.454 + "WWW_RegisterViewer", 1.455 + "WWW_UnRegisterViewer", 1.456 + "WWW_GetWindowInfo" }; 1.457 + 1.458 +// Static member definitions. 1.459 +int nsNativeAppSupportWin::mConversations = 0; 1.460 +HSZ nsNativeAppSupportWin::mApplication = 0; 1.461 +HSZ nsNativeAppSupportWin::mTopics[nsNativeAppSupportWin::topicCount] = { 0 }; 1.462 +DWORD nsNativeAppSupportWin::mInstance = 0; 1.463 +bool nsNativeAppSupportWin::mCanHandleRequests = false; 1.464 + 1.465 +char16_t nsNativeAppSupportWin::mMutexName[ 128 ] = { 0 }; 1.466 + 1.467 + 1.468 +// Message window encapsulation. 1.469 +struct MessageWindow { 1.470 + // ctor/dtor are simplistic 1.471 + MessageWindow() { 1.472 + // Try to find window. 1.473 + mHandle = ::FindWindowW( className(), 0 ); 1.474 + } 1.475 + 1.476 + // Act like an HWND. 1.477 + operator HWND() { 1.478 + return mHandle; 1.479 + } 1.480 + 1.481 + // Class name: appName + "MessageWindow" 1.482 + static const wchar_t *className() { 1.483 + static wchar_t classNameBuffer[128]; 1.484 + static wchar_t *mClassName = 0; 1.485 + if ( !mClassName ) { 1.486 + ::_snwprintf(classNameBuffer, 1.487 + 128, // size of classNameBuffer in PRUnichars 1.488 + L"%s%s", 1.489 + NS_ConvertUTF8toUTF16(gAppData->name).get(), 1.490 + L"MessageWindow" ); 1.491 + mClassName = classNameBuffer; 1.492 + } 1.493 + return mClassName; 1.494 + } 1.495 + 1.496 + // Create: Register class and create window. 1.497 + NS_IMETHOD Create() { 1.498 + WNDCLASSW classStruct = { 0, // style 1.499 + &MessageWindow::WindowProc, // lpfnWndProc 1.500 + 0, // cbClsExtra 1.501 + 0, // cbWndExtra 1.502 + 0, // hInstance 1.503 + 0, // hIcon 1.504 + 0, // hCursor 1.505 + 0, // hbrBackground 1.506 + 0, // lpszMenuName 1.507 + className() }; // lpszClassName 1.508 + 1.509 + // Register the window class. 1.510 + NS_ENSURE_TRUE( ::RegisterClassW( &classStruct ), NS_ERROR_FAILURE ); 1.511 + 1.512 + // Create the window. 1.513 + NS_ENSURE_TRUE( ( mHandle = ::CreateWindowW(className(), 1.514 + 0, // title 1.515 + WS_CAPTION, // style 1.516 + 0,0,0,0, // x, y, cx, cy 1.517 + 0, // parent 1.518 + 0, // menu 1.519 + 0, // instance 1.520 + 0 ) ), // create struct 1.521 + NS_ERROR_FAILURE ); 1.522 + 1.523 +#if MOZ_DEBUG_DDE 1.524 + printf( "Message window = 0x%08X\n", (int)mHandle ); 1.525 +#endif 1.526 + 1.527 + return NS_OK; 1.528 + } 1.529 + 1.530 + // Destory: Get rid of window and reset mHandle. 1.531 + NS_IMETHOD Destroy() { 1.532 + nsresult retval = NS_OK; 1.533 + 1.534 + if ( mHandle ) { 1.535 + // DestroyWindow can only destroy windows created from 1.536 + // the same thread. 1.537 + BOOL desRes = DestroyWindow( mHandle ); 1.538 + if ( FALSE != desRes ) { 1.539 + mHandle = nullptr; 1.540 + } 1.541 + else { 1.542 + retval = NS_ERROR_FAILURE; 1.543 + } 1.544 + } 1.545 + 1.546 + return retval; 1.547 + } 1.548 + 1.549 + // SendRequest: Pass the command line via WM_COPYDATA to message window. 1.550 + NS_IMETHOD SendRequest() { 1.551 + WCHAR *cmd = ::GetCommandLineW(); 1.552 + WCHAR cwd[MAX_PATH]; 1.553 + _wgetcwd(cwd, MAX_PATH); 1.554 + 1.555 + // Construct a narrow UTF8 buffer <commandline>\0<workingdir>\0 1.556 + NS_ConvertUTF16toUTF8 utf8buffer(cmd); 1.557 + utf8buffer.Append('\0'); 1.558 + AppendUTF16toUTF8(cwd, utf8buffer); 1.559 + utf8buffer.Append('\0'); 1.560 + 1.561 + // We used to set dwData to zero, when we didn't send the working dir. 1.562 + // Now we're using it as a version number. 1.563 + COPYDATASTRUCT cds = { 1.564 + 1, 1.565 + utf8buffer.Length(), 1.566 + (void*) utf8buffer.get() 1.567 + }; 1.568 + // Bring the already running Mozilla process to the foreground. 1.569 + // nsWindow will restore the window (if minimized) and raise it. 1.570 + ::SetForegroundWindow( mHandle ); 1.571 + ::SendMessage( mHandle, WM_COPYDATA, 0, (LPARAM)&cds ); 1.572 + return NS_OK; 1.573 + } 1.574 + 1.575 + // Window proc. 1.576 + static LRESULT CALLBACK WindowProc( HWND msgWindow, UINT msg, WPARAM wp, LPARAM lp ) { 1.577 + if ( msg == WM_COPYDATA ) { 1.578 + if (!nsNativeAppSupportWin::mCanHandleRequests) 1.579 + return FALSE; 1.580 + 1.581 + // This is an incoming request. 1.582 + COPYDATASTRUCT *cds = (COPYDATASTRUCT*)lp; 1.583 +#if MOZ_DEBUG_DDE 1.584 + printf( "Incoming request: %s\n", (const char*)cds->lpData ); 1.585 +#endif 1.586 + nsCOMPtr<nsIFile> workingDir; 1.587 + 1.588 + if (1 >= cds->dwData) { 1.589 + char* wdpath = (char*) cds->lpData; 1.590 + // skip the command line, and get the working dir of the 1.591 + // other process, which is after the first null char 1.592 + while (*wdpath) 1.593 + ++wdpath; 1.594 + 1.595 + ++wdpath; 1.596 + 1.597 +#ifdef MOZ_DEBUG_DDE 1.598 + printf( "Working dir: %s\n", wdpath); 1.599 +#endif 1.600 + 1.601 + NS_NewLocalFile(NS_ConvertUTF8toUTF16(wdpath), 1.602 + false, 1.603 + getter_AddRefs(workingDir)); 1.604 + } 1.605 + (void)nsNativeAppSupportWin::HandleCommandLine((char*)cds->lpData, workingDir, nsICommandLine::STATE_REMOTE_AUTO); 1.606 + 1.607 + // Get current window and return its window handle. 1.608 + nsCOMPtr<nsIDOMWindow> win; 1.609 + GetMostRecentWindow( 0, getter_AddRefs( win ) ); 1.610 + return win ? (LRESULT)hwndForDOMWindow( win ) : 0; 1.611 + } 1.612 + return DefWindowProc( msgWindow, msg, wp, lp ); 1.613 + } 1.614 + 1.615 +private: 1.616 + HWND mHandle; 1.617 +}; // struct MessageWindow 1.618 + 1.619 +/* Start: Tries to find the "message window" to determine if it 1.620 + * exists. If so, then Mozilla is already running. In that 1.621 + * case, we use the handle to the "message" window and send 1.622 + * a request corresponding to this process's command line 1.623 + * options. 1.624 + * 1.625 + * If not, then this is the first instance of Mozilla. In 1.626 + * that case, we create and set up the message window. 1.627 + * 1.628 + * The checking for existence of the message window must 1.629 + * be protected by use of a mutex semaphore. 1.630 + */ 1.631 +NS_IMETHODIMP 1.632 +nsNativeAppSupportWin::Start( bool *aResult ) { 1.633 + NS_ENSURE_ARG( aResult ); 1.634 + NS_ENSURE_TRUE( mInstance == 0, NS_ERROR_NOT_INITIALIZED ); 1.635 + NS_ENSURE_STATE( gAppData ); 1.636 + 1.637 + if (getenv("MOZ_NO_REMOTE")) 1.638 + { 1.639 + *aResult = true; 1.640 + return NS_OK; 1.641 + } 1.642 + 1.643 + nsresult rv = NS_ERROR_FAILURE; 1.644 + *aResult = false; 1.645 + 1.646 + // Grab mutex first. 1.647 + 1.648 + // Build mutex name from app name. 1.649 + ::_snwprintf(reinterpret_cast<wchar_t*>(mMutexName), 1.650 + sizeof mMutexName / sizeof(char16_t), L"%s%s%s", 1.651 + MOZ_MUTEX_NAMESPACE, 1.652 + NS_ConvertUTF8toUTF16(gAppData->name).get(), 1.653 + MOZ_STARTUP_MUTEX_NAME ); 1.654 + Mutex startupLock = Mutex( mMutexName ); 1.655 + 1.656 + NS_ENSURE_TRUE( startupLock.Lock( MOZ_DDE_START_TIMEOUT ), NS_ERROR_FAILURE ); 1.657 + 1.658 + // Search for existing message window. 1.659 + MessageWindow msgWindow; 1.660 + if ( (HWND)msgWindow ) { 1.661 + // We are a client process. Pass request to message window. 1.662 + rv = msgWindow.SendRequest(); 1.663 + } else { 1.664 + // We will be server. 1.665 + rv = msgWindow.Create(); 1.666 + if ( NS_SUCCEEDED( rv ) ) { 1.667 + // Start up DDE server. 1.668 + this->StartDDE(); 1.669 + // Tell caller to spin message loop. 1.670 + *aResult = true; 1.671 + } 1.672 + } 1.673 + 1.674 + startupLock.Unlock(); 1.675 + 1.676 + return rv; 1.677 +} 1.678 + 1.679 +bool 1.680 +nsNativeAppSupportWin::InitTopicStrings() { 1.681 + for ( int i = 0; i < topicCount; i++ ) { 1.682 + if ( !( mTopics[ i ] = DdeCreateStringHandleA( mInstance, const_cast<char *>(topicNames[ i ]), CP_WINANSI ) ) ) { 1.683 + return false; 1.684 + } 1.685 + } 1.686 + return true; 1.687 +} 1.688 + 1.689 +int 1.690 +nsNativeAppSupportWin::FindTopic( HSZ topic ) { 1.691 + for ( int i = 0; i < topicCount; i++ ) { 1.692 + if ( DdeCmpStringHandles( topic, mTopics[i] ) == 0 ) { 1.693 + return i; 1.694 + } 1.695 + } 1.696 + return -1; 1.697 +} 1.698 + 1.699 + 1.700 +// Start DDE server. 1.701 +// 1.702 +// This used to be the Start() method when we were using DDE as the 1.703 +// primary IPC mechanism between secondary Mozilla processes and the 1.704 +// initial "server" process. 1.705 +// 1.706 +// Now, it simply initializes the DDE server. The caller must check 1.707 +// that this process is to be the server, and, must acquire the DDE 1.708 +// startup mutex semaphore prior to calling this routine. See ::Start(), 1.709 +// above. 1.710 +NS_IMETHODIMP 1.711 +nsNativeAppSupportWin::StartDDE() { 1.712 + NS_ENSURE_TRUE( mInstance == 0, NS_ERROR_NOT_INITIALIZED ); 1.713 + 1.714 + // Initialize DDE. 1.715 + NS_ENSURE_TRUE( DMLERR_NO_ERROR == DdeInitialize( &mInstance, 1.716 + nsNativeAppSupportWin::HandleDDENotification, 1.717 + APPCLASS_STANDARD, 1.718 + 0 ), 1.719 + NS_ERROR_FAILURE ); 1.720 + 1.721 + // Allocate DDE strings. 1.722 + NS_ENSURE_TRUE( ( mApplication = DdeCreateStringHandleA( mInstance, (char*) gAppData->name, CP_WINANSI ) ) && InitTopicStrings(), 1.723 + NS_ERROR_FAILURE ); 1.724 + 1.725 + // Next step is to register a DDE service. 1.726 + NS_ENSURE_TRUE( DdeNameService( mInstance, mApplication, 0, DNS_REGISTER ), NS_ERROR_FAILURE ); 1.727 + 1.728 +#if MOZ_DEBUG_DDE 1.729 + printf( "DDE server started\n" ); 1.730 +#endif 1.731 + 1.732 + return NS_OK; 1.733 +} 1.734 + 1.735 +// If no DDE conversations are pending, terminate DDE. 1.736 +NS_IMETHODIMP 1.737 +nsNativeAppSupportWin::Stop( bool *aResult ) { 1.738 + NS_ENSURE_ARG( aResult ); 1.739 + NS_ENSURE_TRUE( mInstance, NS_ERROR_NOT_INITIALIZED ); 1.740 + 1.741 + nsresult rv = NS_OK; 1.742 + *aResult = true; 1.743 + 1.744 + Mutex ddeLock( mMutexName ); 1.745 + 1.746 + if ( ddeLock.Lock( MOZ_DDE_STOP_TIMEOUT ) ) { 1.747 + if ( mConversations == 0 ) { 1.748 + this->Quit(); 1.749 + } else { 1.750 + *aResult = false; 1.751 + } 1.752 + 1.753 + ddeLock.Unlock(); 1.754 + } 1.755 + else { 1.756 + // No DDE application name specified, but that's OK. Just 1.757 + // forge ahead. 1.758 + *aResult = true; 1.759 + } 1.760 + 1.761 + return rv; 1.762 +} 1.763 + 1.764 +NS_IMETHODIMP 1.765 +nsNativeAppSupportWin::Observe(nsISupports* aSubject, const char* aTopic, 1.766 + const char16_t* aData) 1.767 +{ 1.768 + if (strcmp(aTopic, "quit-application") == 0) { 1.769 + Quit(); 1.770 + } else { 1.771 + NS_ERROR("Unexpected observer topic."); 1.772 + } 1.773 + 1.774 + return NS_OK; 1.775 +} 1.776 + 1.777 +// Terminate DDE regardless. 1.778 +NS_IMETHODIMP 1.779 +nsNativeAppSupportWin::Quit() { 1.780 + // If another process wants to look for the message window, they need 1.781 + // to wait to hold the lock, in which case they will not find the 1.782 + // window as we will destroy ours under our lock. 1.783 + // When the mutex goes off the stack, it is unlocked via destructor. 1.784 + Mutex mutexLock(mMutexName); 1.785 + NS_ENSURE_TRUE(mutexLock.Lock(MOZ_DDE_START_TIMEOUT), NS_ERROR_FAILURE); 1.786 + 1.787 + // If we've got a message window to receive IPC or new window requests, 1.788 + // get rid of it as we are shutting down. 1.789 + // Note: Destroy calls DestroyWindow, which will only work on a window 1.790 + // created by the same thread. 1.791 + MessageWindow mw; 1.792 + mw.Destroy(); 1.793 + 1.794 + if ( mInstance ) { 1.795 + // Unregister application name. 1.796 + DdeNameService( mInstance, mApplication, 0, DNS_UNREGISTER ); 1.797 + // Clean up strings. 1.798 + if ( mApplication ) { 1.799 + DdeFreeStringHandle( mInstance, mApplication ); 1.800 + mApplication = 0; 1.801 + } 1.802 + for ( int i = 0; i < topicCount; i++ ) { 1.803 + if ( mTopics[i] ) { 1.804 + DdeFreeStringHandle( mInstance, mTopics[i] ); 1.805 + mTopics[i] = 0; 1.806 + } 1.807 + } 1.808 + DdeUninitialize( mInstance ); 1.809 + mInstance = 0; 1.810 +#if MOZ_DEBUG_DDE 1.811 + printf( "DDE server stopped\n" ); 1.812 +#endif 1.813 + } 1.814 + 1.815 + return NS_OK; 1.816 +} 1.817 + 1.818 +NS_IMETHODIMP 1.819 +nsNativeAppSupportWin::Enable() 1.820 +{ 1.821 + mCanHandleRequests = true; 1.822 + 1.823 + nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); 1.824 + if (obs) { 1.825 + obs->AddObserver(this, "quit-application", false); 1.826 + } else { 1.827 + NS_ERROR("No observer service?"); 1.828 + } 1.829 + 1.830 + return NS_OK; 1.831 +} 1.832 + 1.833 +#if MOZ_DEBUG_DDE 1.834 +// Macro to generate case statement for a given XTYP value. 1.835 +#define XTYP_CASE(t) \ 1.836 + case t: result = #t; break 1.837 + 1.838 +static nsCString uTypeDesc( UINT uType ) { 1.839 + nsCString result; 1.840 + switch ( uType ) { 1.841 + XTYP_CASE(XTYP_ADVSTART); 1.842 + XTYP_CASE(XTYP_CONNECT); 1.843 + XTYP_CASE(XTYP_ADVREQ); 1.844 + XTYP_CASE(XTYP_REQUEST); 1.845 + XTYP_CASE(XTYP_WILDCONNECT); 1.846 + XTYP_CASE(XTYP_ADVDATA); 1.847 + XTYP_CASE(XTYP_EXECUTE); 1.848 + XTYP_CASE(XTYP_POKE); 1.849 + XTYP_CASE(XTYP_ADVSTOP); 1.850 + XTYP_CASE(XTYP_CONNECT_CONFIRM); 1.851 + XTYP_CASE(XTYP_DISCONNECT); 1.852 + XTYP_CASE(XTYP_ERROR); 1.853 + XTYP_CASE(XTYP_MONITOR); 1.854 + XTYP_CASE(XTYP_REGISTER); 1.855 + XTYP_CASE(XTYP_XACT_COMPLETE); 1.856 + XTYP_CASE(XTYP_UNREGISTER); 1.857 + default: result = "XTYP_?????"; 1.858 + } 1.859 + return result; 1.860 +} 1.861 + 1.862 +static nsCString hszValue( DWORD instance, HSZ hsz ) { 1.863 + // Extract string from HSZ. 1.864 + nsCString result("["); 1.865 + DWORD len = DdeQueryString( instance, hsz, nullptr, nullptr, CP_WINANSI ); 1.866 + if ( len ) { 1.867 + char buffer[ 256 ]; 1.868 + DdeQueryString( instance, hsz, buffer, sizeof buffer, CP_WINANSI ); 1.869 + result += buffer; 1.870 + } 1.871 + result += "]"; 1.872 + return result; 1.873 +} 1.874 +#else 1.875 +// These are purely a safety measure to avoid the infamous "won't 1.876 +// build non-debug" type Tinderbox flames. 1.877 +static nsCString uTypeDesc( UINT ) { 1.878 + return nsCString( "?" ); 1.879 +} 1.880 +static nsCString hszValue( DWORD, HSZ ) { 1.881 + return nsCString( "?" ); 1.882 +} 1.883 +#endif 1.884 + 1.885 + 1.886 +// Utility function to escape double-quotes within a string. 1.887 +static void escapeQuotes( nsAString &aString ) { 1.888 + int32_t offset = -1; 1.889 + while( 1 ) { 1.890 + // Find next '"'. 1.891 + offset = aString.FindChar( '"', ++offset ); 1.892 + if ( offset == kNotFound ) { 1.893 + // No more quotes, exit. 1.894 + break; 1.895 + } else { 1.896 + // Insert back-slash ahead of the '"'. 1.897 + aString.Insert( char16_t('\\'), offset ); 1.898 + // Increment offset because we just inserted a slash 1.899 + offset++; 1.900 + } 1.901 + } 1.902 + return; 1.903 +} 1.904 + 1.905 +HDDEDATA CALLBACK 1.906 +nsNativeAppSupportWin::HandleDDENotification( UINT uType, // transaction type 1.907 + UINT uFmt, // clipboard data format 1.908 + HCONV hconv, // handle to the conversation 1.909 + HSZ hsz1, // handle to a string 1.910 + HSZ hsz2, // handle to a string 1.911 + HDDEDATA hdata, // handle to a global memory object 1.912 + ULONG_PTR dwData1, // transaction-specific data 1.913 + ULONG_PTR dwData2 ) { // transaction-specific data 1.914 + 1.915 + if (!mCanHandleRequests) 1.916 + return 0; 1.917 + 1.918 + 1.919 +#if MOZ_DEBUG_DDE 1.920 + printf( "DDE: uType =%s\n", uTypeDesc( uType ).get() ); 1.921 + printf( " uFmt =%u\n", (unsigned)uFmt ); 1.922 + printf( " hconv =%08x\n", (int)hconv ); 1.923 + printf( " hsz1 =%08x:%s\n", (int)hsz1, hszValue( mInstance, hsz1 ).get() ); 1.924 + printf( " hsz2 =%08x:%s\n", (int)hsz2, hszValue( mInstance, hsz2 ).get() ); 1.925 + printf( " hdata =%08x\n", (int)hdata ); 1.926 + printf( " dwData1=%08x\n", (int)dwData1 ); 1.927 + printf( " dwData2=%08x\n", (int)dwData2 ); 1.928 +#endif 1.929 + 1.930 + HDDEDATA result = 0; 1.931 + if ( uType & XCLASS_BOOL ) { 1.932 + switch ( uType ) { 1.933 + case XTYP_CONNECT: 1.934 + // Make sure its for our service/topic. 1.935 + if ( FindTopic( hsz1 ) != -1 ) { 1.936 + // We support this connection. 1.937 + result = (HDDEDATA)1; 1.938 + } 1.939 + break; 1.940 + case XTYP_CONNECT_CONFIRM: 1.941 + // We don't care about the conversation handle, at this point. 1.942 + result = (HDDEDATA)1; 1.943 + break; 1.944 + } 1.945 + } else if ( uType & XCLASS_DATA ) { 1.946 + if ( uType == XTYP_REQUEST ) { 1.947 + switch ( FindTopic( hsz1 ) ) { 1.948 + case topicOpenURL: { 1.949 + // Open a given URL... 1.950 + 1.951 + // Get the URL from the first argument in the command. 1.952 + nsAutoString url; 1.953 + ParseDDEArg(hsz2, 0, url); 1.954 + 1.955 + // Read the 3rd argument in the command to determine if a 1.956 + // new window is to be used. 1.957 + nsAutoString windowID; 1.958 + ParseDDEArg(hsz2, 2, windowID); 1.959 + // "" means to open the URL in a new window. 1.960 + if ( windowID.IsEmpty() ) { 1.961 + url.Insert(NS_LITERAL_STRING("mozilla -new-window "), 0); 1.962 + } 1.963 + else { 1.964 + url.Insert(NS_LITERAL_STRING("mozilla -url "), 0); 1.965 + } 1.966 + 1.967 +#if MOZ_DEBUG_DDE 1.968 + printf( "Handling dde XTYP_REQUEST request: [%s]...\n", NS_ConvertUTF16toUTF8(url).get() ); 1.969 +#endif 1.970 + // Now handle it. 1.971 + HandleCommandLine(NS_ConvertUTF16toUTF8(url).get(), nullptr, nsICommandLine::STATE_REMOTE_EXPLICIT); 1.972 + 1.973 + // Return pseudo window ID. 1.974 + result = CreateDDEData( 1 ); 1.975 + break; 1.976 + } 1.977 + case topicGetWindowInfo: { 1.978 + // This topic has to get the current URL, get the current 1.979 + // page title and then format the output into the DDE 1.980 + // return string. The return value is "URL","Page Title", 1.981 + // "Window ID" however the window ID is not used for this 1.982 + // command, therefore it is returned as a null string 1.983 + 1.984 + // This isn't really a loop. We just use "break" 1.985 + // statements to bypass the remaining steps when 1.986 + // something goes wrong. 1.987 + do { 1.988 + // Get most recently used Nav window. 1.989 + nsCOMPtr<nsIDOMWindow> navWin; 1.990 + GetMostRecentWindow( NS_LITERAL_STRING( "navigator:browser" ).get(), 1.991 + getter_AddRefs( navWin ) ); 1.992 + if ( !navWin ) { 1.993 + // There is not a window open 1.994 + break; 1.995 + } 1.996 + // Get content window. 1.997 + nsCOMPtr<nsIDOMWindow> content; 1.998 + navWin->GetContent( getter_AddRefs( content ) ); 1.999 + if ( !content ) { 1.1000 + break; 1.1001 + } 1.1002 + // Convert that to internal interface. 1.1003 + nsCOMPtr<nsPIDOMWindow> internalContent( do_QueryInterface( content ) ); 1.1004 + if ( !internalContent ) { 1.1005 + break; 1.1006 + } 1.1007 + // Get location. 1.1008 + nsCOMPtr<nsIDOMLocation> location; 1.1009 + internalContent->GetLocation( getter_AddRefs( location ) ); 1.1010 + if ( !location ) { 1.1011 + break; 1.1012 + } 1.1013 + // Get href for URL. 1.1014 + nsAutoString url; 1.1015 + if ( NS_FAILED( location->GetHref( url ) ) ) { 1.1016 + break; 1.1017 + } 1.1018 + // Escape any double-quotes. 1.1019 + escapeQuotes( url ); 1.1020 + 1.1021 + // Now for the title... 1.1022 + 1.1023 + // Get the base window from the doc shell... 1.1024 + nsCOMPtr<nsIBaseWindow> baseWindow = 1.1025 + do_QueryInterface( internalContent->GetDocShell() ); 1.1026 + if ( !baseWindow ) { 1.1027 + break; 1.1028 + } 1.1029 + // And from the base window we can get the title. 1.1030 + nsXPIDLString title; 1.1031 + if(!baseWindow) { 1.1032 + break; 1.1033 + } 1.1034 + baseWindow->GetTitle(getter_Copies(title)); 1.1035 + // Escape any double-quotes in the title. 1.1036 + escapeQuotes( title ); 1.1037 + 1.1038 + // Use a string buffer for the output data, first 1.1039 + // save a quote. 1.1040 + nsAutoCString outpt( NS_LITERAL_CSTRING("\"") ); 1.1041 + // Now copy the URL converting the Unicode string 1.1042 + // to a single-byte ASCII string 1.1043 + nsAutoCString tmpNativeStr; 1.1044 + NS_CopyUnicodeToNative( url, tmpNativeStr ); 1.1045 + outpt.Append( tmpNativeStr ); 1.1046 + // Add the "," used to separate the URL and the page 1.1047 + // title 1.1048 + outpt.Append( NS_LITERAL_CSTRING("\",\"") ); 1.1049 + // Now copy the current page title to the return string 1.1050 + NS_CopyUnicodeToNative( title, tmpNativeStr ); 1.1051 + outpt.Append( tmpNativeStr ); 1.1052 + // Fill out the return string with the remainin ","" 1.1053 + outpt.Append( NS_LITERAL_CSTRING( "\",\"\"" )); 1.1054 + 1.1055 + // Create a DDE handle to a char string for the data 1.1056 + // being returned, this copies and creates a "shared" 1.1057 + // copy of the DDE response until the calling APP 1.1058 + // reads it and says it can be freed. 1.1059 + result = CreateDDEData( (LPBYTE)(const char*)outpt.get(), 1.1060 + outpt.Length() + 1 ); 1.1061 +#if MOZ_DEBUG_DDE 1.1062 + printf( "WWW_GetWindowInfo->%s\n", outpt.get() ); 1.1063 +#endif 1.1064 + } while ( false ); 1.1065 + break; 1.1066 + } 1.1067 + case topicActivate: { 1.1068 + // Activate a Nav window... 1.1069 + nsAutoString windowID; 1.1070 + ParseDDEArg(hsz2, 0, windowID); 1.1071 + // 4294967295 is decimal for 0xFFFFFFFF which is also a 1.1072 + // correct value to do that Activate last window stuff 1.1073 + if ( windowID.EqualsLiteral( "-1" ) || 1.1074 + windowID.EqualsLiteral( "4294967295" ) ) { 1.1075 + // We only support activating the most recent window (or a new one). 1.1076 + ActivateLastWindow(); 1.1077 + // Return pseudo window ID. 1.1078 + result = CreateDDEData( 1 ); 1.1079 + } 1.1080 + break; 1.1081 + } 1.1082 + case topicVersion: { 1.1083 + // Return version. We're restarting at 1.0! 1.1084 + DWORD version = 1 << 16; // "1.0" 1.1085 + result = CreateDDEData( version ); 1.1086 + break; 1.1087 + } 1.1088 + case topicRegisterViewer: { 1.1089 + // Register new viewer (not implemented). 1.1090 + result = CreateDDEData( false ); 1.1091 + break; 1.1092 + } 1.1093 + case topicUnRegisterViewer: { 1.1094 + // Unregister new viewer (not implemented). 1.1095 + result = CreateDDEData( false ); 1.1096 + break; 1.1097 + } 1.1098 + default: 1.1099 + break; 1.1100 + } 1.1101 + } else if ( uType & XTYP_POKE ) { 1.1102 + switch ( FindTopic( hsz1 ) ) { 1.1103 + case topicCancelProgress: { 1.1104 + // "Handle" progress cancel (actually, pretty much ignored). 1.1105 + result = (HDDEDATA)DDE_FACK; 1.1106 + break; 1.1107 + } 1.1108 + default: 1.1109 + break; 1.1110 + } 1.1111 + } 1.1112 + } else if ( uType & XCLASS_FLAGS ) { 1.1113 + if ( uType == XTYP_EXECUTE ) { 1.1114 + // Prove that we received the request. 1.1115 + DWORD bytes; 1.1116 + LPBYTE request = DdeAccessData( hdata, &bytes ); 1.1117 +#if MOZ_DEBUG_DDE 1.1118 + printf( "Handling dde request: [%s]...\n", (char*)request ); 1.1119 +#endif 1.1120 + 1.1121 + nsAutoString url; 1.1122 + ParseDDEArg((const WCHAR*) request, 0, url); 1.1123 + 1.1124 + // Read the 3rd argument in the command to determine if a 1.1125 + // new window is to be used. 1.1126 + nsAutoString windowID; 1.1127 + ParseDDEArg((const WCHAR*) request, 2, windowID); 1.1128 + 1.1129 + // "" means to open the URL in a new window. 1.1130 + if ( windowID.IsEmpty() ) { 1.1131 + url.Insert(NS_LITERAL_STRING("mozilla -new-window "), 0); 1.1132 + } 1.1133 + else { 1.1134 + url.Insert(NS_LITERAL_STRING("mozilla -url "), 0); 1.1135 + } 1.1136 +#if MOZ_DEBUG_DDE 1.1137 + printf( "Handling dde XTYP_REQUEST request: [%s]...\n", NS_ConvertUTF16toUTF8(url).get() ); 1.1138 +#endif 1.1139 + // Now handle it. 1.1140 + HandleCommandLine(NS_ConvertUTF16toUTF8(url).get(), nullptr, nsICommandLine::STATE_REMOTE_EXPLICIT); 1.1141 + 1.1142 + // Release the data. 1.1143 + DdeUnaccessData( hdata ); 1.1144 + result = (HDDEDATA)DDE_FACK; 1.1145 + } else { 1.1146 + result = (HDDEDATA)DDE_FNOTPROCESSED; 1.1147 + } 1.1148 + } else if ( uType & XCLASS_NOTIFICATION ) { 1.1149 + } 1.1150 +#if MOZ_DEBUG_DDE 1.1151 + printf( "DDE result=%d (0x%08X)\n", (int)result, (int)result ); 1.1152 +#endif 1.1153 + return result; 1.1154 +} 1.1155 + 1.1156 +// Utility function to advance to end of quoted string. 1.1157 +// p+offset must point to the comma preceding the arg on entry. 1.1158 +// On return, p+result points to the closing '"' (or end of the string 1.1159 +// if the closing '"' is missing) if the arg is quoted. If the arg 1.1160 +// is not quoted, then p+result will point to the first character 1.1161 +// of the arg. 1.1162 +static int32_t advanceToEndOfQuotedArg( const WCHAR *p, int32_t offset, int32_t len ) { 1.1163 + // Check whether the current arg is quoted. 1.1164 + if ( p[++offset] == '"' ) { 1.1165 + // Advance past the closing quote. 1.1166 + while ( offset < len && p[++offset] != '"' ) { 1.1167 + // If the current character is a backslash, then the 1.1168 + // next character can't be a *real* '"', so skip it. 1.1169 + if ( p[offset] == '\\' ) { 1.1170 + offset++; 1.1171 + } 1.1172 + } 1.1173 + } 1.1174 + return offset; 1.1175 +} 1.1176 + 1.1177 +void nsNativeAppSupportWin::ParseDDEArg( const WCHAR* args, int index, nsString& aString) { 1.1178 + if ( args ) { 1.1179 + nsDependentString temp(args); 1.1180 + 1.1181 + // offset points to the comma preceding the desired arg. 1.1182 + int32_t offset = -1; 1.1183 + // Skip commas till we get to the arg we want. 1.1184 + while( index-- ) { 1.1185 + // If this arg is quoted, then go to closing quote. 1.1186 + offset = advanceToEndOfQuotedArg( args, offset, temp.Length()); 1.1187 + // Find next comma. 1.1188 + offset = temp.FindChar( ',', offset ); 1.1189 + if ( offset == kNotFound ) { 1.1190 + // No more commas, give up. 1.1191 + aString = args; 1.1192 + return; 1.1193 + } 1.1194 + } 1.1195 + // The desired argument starts just past the preceding comma, 1.1196 + // which offset points to, and extends until the following 1.1197 + // comma (or the end of the string). 1.1198 + // 1.1199 + // Since the argument might be enclosed in quotes, we need to 1.1200 + // deal with that before searching for the terminating comma. 1.1201 + // We advance offset so it ends up pointing to the start of 1.1202 + // the argument we want. 1.1203 + int32_t end = advanceToEndOfQuotedArg( args, offset++, temp.Length() ); 1.1204 + // Find next comma (or end of string). 1.1205 + end = temp.FindChar( ',', end ); 1.1206 + if ( end == kNotFound ) { 1.1207 + // Arg is the rest of the string. 1.1208 + end = temp.Length(); 1.1209 + } 1.1210 + // Extract result. 1.1211 + aString.Assign( args + offset, end - offset ); 1.1212 + } 1.1213 + return; 1.1214 +} 1.1215 + 1.1216 +// Utility to parse out argument from a DDE item string. 1.1217 +void nsNativeAppSupportWin::ParseDDEArg( HSZ args, int index, nsString& aString) { 1.1218 + DWORD argLen = DdeQueryStringW( mInstance, args, nullptr, 0, CP_WINUNICODE ); 1.1219 + // there wasn't any string, so return empty string 1.1220 + if ( !argLen ) return; 1.1221 + nsAutoString temp; 1.1222 + // Ensure result's buffer is sufficiently big. 1.1223 + temp.SetLength( argLen ); 1.1224 + // Now get the string contents. 1.1225 + DdeQueryString( mInstance, args, reinterpret_cast<wchar_t*>(temp.BeginWriting()), temp.Length(), CP_WINUNICODE ); 1.1226 + // Parse out the given arg. 1.1227 + ParseDDEArg(temp.get(), index, aString); 1.1228 + return; 1.1229 +} 1.1230 + 1.1231 +HDDEDATA nsNativeAppSupportWin::CreateDDEData( DWORD value ) { 1.1232 + return CreateDDEData( (LPBYTE)&value, sizeof value ); 1.1233 +} 1.1234 + 1.1235 +HDDEDATA nsNativeAppSupportWin::CreateDDEData( LPBYTE value, DWORD len ) { 1.1236 + HDDEDATA result = DdeCreateDataHandle( mInstance, 1.1237 + value, 1.1238 + len, 1.1239 + 0, 1.1240 + mApplication, 1.1241 + CF_TEXT, 1.1242 + 0 ); 1.1243 + return result; 1.1244 +} 1.1245 + 1.1246 +void nsNativeAppSupportWin::ActivateLastWindow() { 1.1247 + nsCOMPtr<nsIDOMWindow> navWin; 1.1248 + GetMostRecentWindow( MOZ_UTF16("navigator:browser"), getter_AddRefs( navWin ) ); 1.1249 + if ( navWin ) { 1.1250 + // Activate that window. 1.1251 + activateWindow( navWin ); 1.1252 + } else { 1.1253 + // Need to create a Navigator window, then. 1.1254 + OpenBrowserWindow(); 1.1255 + } 1.1256 +} 1.1257 + 1.1258 +void 1.1259 +nsNativeAppSupportWin::HandleCommandLine(const char* aCmdLineString, 1.1260 + nsIFile* aWorkingDir, 1.1261 + uint32_t aState) 1.1262 +{ 1.1263 + nsresult rv; 1.1264 + 1.1265 + int justCounting = 1; 1.1266 + char **argv = 0; 1.1267 + // Flags, etc. 1.1268 + int init = 1; 1.1269 + int between, quoted, bSlashCount; 1.1270 + int argc; 1.1271 + const char *p; 1.1272 + nsAutoCString arg; 1.1273 + 1.1274 + nsCOMPtr<nsICommandLineRunner> cmdLine 1.1275 + (do_CreateInstance("@mozilla.org/toolkit/command-line;1")); 1.1276 + if (!cmdLine) { 1.1277 + NS_ERROR("Couldn't create command line!"); 1.1278 + return; 1.1279 + } 1.1280 + 1.1281 + // Parse command line args according to MS spec 1.1282 + // (see "Parsing C++ Command-Line Arguments" at 1.1283 + // http://msdn.microsoft.com/library/devprods/vs6/visualc/vclang/_pluslang_parsing_c.2b2b_.command.2d.line_arguments.htm). 1.1284 + // We loop if we've not finished the second pass through. 1.1285 + while ( 1 ) { 1.1286 + // Initialize if required. 1.1287 + if ( init ) { 1.1288 + p = aCmdLineString; 1.1289 + between = 1; 1.1290 + argc = quoted = bSlashCount = 0; 1.1291 + 1.1292 + init = 0; 1.1293 + } 1.1294 + if ( between ) { 1.1295 + // We are traversing whitespace between args. 1.1296 + // Check for start of next arg. 1.1297 + if ( *p != 0 && !isspace( *p ) ) { 1.1298 + // Start of another arg. 1.1299 + between = 0; 1.1300 + arg = ""; 1.1301 + switch ( *p ) { 1.1302 + case '\\': 1.1303 + // Count the backslash. 1.1304 + bSlashCount = 1; 1.1305 + break; 1.1306 + case '"': 1.1307 + // Remember we're inside quotes. 1.1308 + quoted = 1; 1.1309 + break; 1.1310 + default: 1.1311 + // Add character to arg. 1.1312 + arg += *p; 1.1313 + break; 1.1314 + } 1.1315 + } else { 1.1316 + // Another space between args, ignore it. 1.1317 + } 1.1318 + } else { 1.1319 + // We are processing the contents of an argument. 1.1320 + // Check for whitespace or end. 1.1321 + if ( *p == 0 || ( !quoted && isspace( *p ) ) ) { 1.1322 + // Process pending backslashes (interpret them 1.1323 + // literally since they're not followed by a "). 1.1324 + while( bSlashCount ) { 1.1325 + arg += '\\'; 1.1326 + bSlashCount--; 1.1327 + } 1.1328 + // End current arg. 1.1329 + if ( !justCounting ) { 1.1330 + argv[argc] = new char[ arg.Length() + 1 ]; 1.1331 + strcpy( argv[argc], arg.get() ); 1.1332 + } 1.1333 + argc++; 1.1334 + // We're now between args. 1.1335 + between = 1; 1.1336 + } else { 1.1337 + // Still inside argument, process the character. 1.1338 + switch ( *p ) { 1.1339 + case '"': 1.1340 + // First, digest preceding backslashes (if any). 1.1341 + while ( bSlashCount > 1 ) { 1.1342 + // Put one backsplash in arg for each pair. 1.1343 + arg += '\\'; 1.1344 + bSlashCount -= 2; 1.1345 + } 1.1346 + if ( bSlashCount ) { 1.1347 + // Quote is literal. 1.1348 + arg += '"'; 1.1349 + bSlashCount = 0; 1.1350 + } else { 1.1351 + // Quote starts or ends a quoted section. 1.1352 + if ( quoted ) { 1.1353 + // Check for special case of consecutive double 1.1354 + // quotes inside a quoted section. 1.1355 + if ( *(p+1) == '"' ) { 1.1356 + // This implies a literal double-quote. Fake that 1.1357 + // out by causing next double-quote to look as 1.1358 + // if it was preceded by a backslash. 1.1359 + bSlashCount = 1; 1.1360 + } else { 1.1361 + quoted = 0; 1.1362 + } 1.1363 + } else { 1.1364 + quoted = 1; 1.1365 + } 1.1366 + } 1.1367 + break; 1.1368 + case '\\': 1.1369 + // Add to count. 1.1370 + bSlashCount++; 1.1371 + break; 1.1372 + default: 1.1373 + // Accept any preceding backslashes literally. 1.1374 + while ( bSlashCount ) { 1.1375 + arg += '\\'; 1.1376 + bSlashCount--; 1.1377 + } 1.1378 + // Just add next char to the current arg. 1.1379 + arg += *p; 1.1380 + break; 1.1381 + } 1.1382 + } 1.1383 + } 1.1384 + // Check for end of input. 1.1385 + if ( *p ) { 1.1386 + // Go to next character. 1.1387 + p++; 1.1388 + } else { 1.1389 + // If on first pass, go on to second. 1.1390 + if ( justCounting ) { 1.1391 + // Allocate argv array. 1.1392 + argv = new char*[ argc ]; 1.1393 + 1.1394 + // Start second pass 1.1395 + justCounting = 0; 1.1396 + init = 1; 1.1397 + } else { 1.1398 + // Quit. 1.1399 + break; 1.1400 + } 1.1401 + } 1.1402 + } 1.1403 + 1.1404 + rv = cmdLine->Init(argc, argv, aWorkingDir, aState); 1.1405 + 1.1406 + // Cleanup. 1.1407 + while ( argc ) { 1.1408 + delete [] argv[ --argc ]; 1.1409 + } 1.1410 + delete [] argv; 1.1411 + 1.1412 + if (NS_FAILED(rv)) { 1.1413 + NS_ERROR("Error initializing command line."); 1.1414 + return; 1.1415 + } 1.1416 + 1.1417 + cmdLine->Run(); 1.1418 +} 1.1419 + 1.1420 +nsresult 1.1421 +nsNativeAppSupportWin::OpenWindow( const char*urlstr, const char *args ) { 1.1422 + 1.1423 + nsresult rv = NS_ERROR_FAILURE; 1.1424 + 1.1425 + nsCOMPtr<nsIWindowWatcher> wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID)); 1.1426 + nsCOMPtr<nsISupportsCString> sarg(do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID)); 1.1427 + if (sarg) 1.1428 + sarg->SetData(nsDependentCString(args)); 1.1429 + 1.1430 + if (wwatch && sarg) { 1.1431 + nsCOMPtr<nsIDOMWindow> newWindow; 1.1432 + rv = wwatch->OpenWindow(0, urlstr, "_blank", "chrome,dialog=no,all", 1.1433 + sarg, getter_AddRefs(newWindow)); 1.1434 +#if MOZ_DEBUG_DDE 1.1435 + } else { 1.1436 + printf("Get WindowWatcher (or create string) failed\n"); 1.1437 +#endif 1.1438 + } 1.1439 + return rv; 1.1440 +} 1.1441 + 1.1442 +HWND hwndForDOMWindow( nsISupports *window ) { 1.1443 + nsCOMPtr<nsPIDOMWindow> pidomwindow( do_QueryInterface(window) ); 1.1444 + if ( !pidomwindow ) { 1.1445 + return 0; 1.1446 + } 1.1447 + 1.1448 + nsCOMPtr<nsIBaseWindow> ppBaseWindow = 1.1449 + do_QueryInterface( pidomwindow->GetDocShell() ); 1.1450 + if ( !ppBaseWindow ) { 1.1451 + return 0; 1.1452 + } 1.1453 + 1.1454 + nsCOMPtr<nsIWidget> ppWidget; 1.1455 + ppBaseWindow->GetMainWidget( getter_AddRefs( ppWidget ) ); 1.1456 + 1.1457 + return (HWND)( ppWidget->GetNativeData( NS_NATIVE_WIDGET ) ); 1.1458 +} 1.1459 + 1.1460 +nsresult 1.1461 +nsNativeAppSupportWin::OpenBrowserWindow() 1.1462 +{ 1.1463 + nsresult rv = NS_OK; 1.1464 + 1.1465 + // Open the argument URL in the most recently used Navigator window. 1.1466 + // If there is no Nav window, open a new one. 1.1467 + 1.1468 + // If at all possible, hand the request off to the most recent 1.1469 + // browser window. 1.1470 + 1.1471 + nsCOMPtr<nsIDOMWindow> navWin; 1.1472 + GetMostRecentWindow( NS_LITERAL_STRING( "navigator:browser" ).get(), getter_AddRefs( navWin ) ); 1.1473 + 1.1474 + // This isn't really a loop. We just use "break" statements to fall 1.1475 + // out to the OpenWindow call when things go awry. 1.1476 + do { 1.1477 + // If caller requires a new window, then don't use an existing one. 1.1478 + if ( !navWin ) { 1.1479 + // Have to open a new one. 1.1480 + break; 1.1481 + } 1.1482 + 1.1483 + nsCOMPtr<nsIBrowserDOMWindow> bwin; 1.1484 + { // scope a bunch of temporary cruft used to generate bwin 1.1485 + nsCOMPtr<nsIWebNavigation> navNav( do_GetInterface( navWin ) ); 1.1486 + nsCOMPtr<nsIDocShellTreeItem> navItem( do_QueryInterface( navNav ) ); 1.1487 + if ( navItem ) { 1.1488 + nsCOMPtr<nsIDocShellTreeItem> rootItem; 1.1489 + navItem->GetRootTreeItem( getter_AddRefs( rootItem ) ); 1.1490 + nsCOMPtr<nsIDOMWindow> rootWin( do_GetInterface( rootItem ) ); 1.1491 + nsCOMPtr<nsIDOMChromeWindow> chromeWin(do_QueryInterface(rootWin)); 1.1492 + if ( chromeWin ) 1.1493 + chromeWin->GetBrowserDOMWindow( getter_AddRefs ( bwin ) ); 1.1494 + } 1.1495 + } 1.1496 + if ( bwin ) { 1.1497 + nsCOMPtr<nsIURI> uri; 1.1498 + NS_NewURI( getter_AddRefs( uri ), NS_LITERAL_CSTRING("about:blank"), 0, 0 ); 1.1499 + if ( uri ) { 1.1500 + nsCOMPtr<nsIDOMWindow> container; 1.1501 + rv = bwin->OpenURI( uri, 0, 1.1502 + nsIBrowserDOMWindow::OPEN_DEFAULTWINDOW, 1.1503 + nsIBrowserDOMWindow::OPEN_EXTERNAL, 1.1504 + getter_AddRefs( container ) ); 1.1505 + if ( NS_SUCCEEDED( rv ) ) 1.1506 + return NS_OK; 1.1507 + } 1.1508 + } 1.1509 + 1.1510 + NS_ERROR("failed to hand off external URL to extant window"); 1.1511 + } while ( false ); 1.1512 + 1.1513 + // open a new window if caller requested it or if anything above failed 1.1514 + 1.1515 + char* argv[] = { 0 }; 1.1516 + nsCOMPtr<nsICommandLineRunner> cmdLine 1.1517 + (do_CreateInstance("@mozilla.org/toolkit/command-line;1")); 1.1518 + NS_ENSURE_TRUE(cmdLine, NS_ERROR_FAILURE); 1.1519 + 1.1520 + rv = cmdLine->Init(0, argv, nullptr, nsICommandLine::STATE_REMOTE_EXPLICIT); 1.1521 + NS_ENSURE_SUCCESS(rv, rv); 1.1522 + 1.1523 + return cmdLine->Run(); 1.1524 +} 1.1525 +