Merge m-c to b-i.
[gecko.git] / toolkit / xre / nsNativeAppSupportWin.cpp
blobbceda1069c6cd6cff2c6b2d135967f6155eeb3f0
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "nsNativeAppSupportBase.h"
7 #include "nsNativeAppSupportWin.h"
8 #include "nsAppRunner.h"
9 #include "nsXULAppAPI.h"
10 #include "nsString.h"
11 #include "nsIBrowserDOMWindow.h"
12 #include "nsICommandLineRunner.h"
13 #include "nsCOMPtr.h"
14 #include "nsXPIDLString.h"
15 #include "nsIComponentManager.h"
16 #include "nsIServiceManager.h"
17 #include "nsIDOMChromeWindow.h"
18 #include "nsXPCOM.h"
19 #include "nsISupportsPrimitives.h"
20 #include "nsIWindowWatcher.h"
21 #include "nsPIDOMWindow.h"
22 #include "nsIDocShell.h"
23 #include "nsIDocShellTreeItem.h"
24 #include "nsIBaseWindow.h"
25 #include "nsIWidget.h"
26 #include "nsIAppShellService.h"
27 #include "nsIXULWindow.h"
28 #include "nsIInterfaceRequestor.h"
29 #include "nsIInterfaceRequestorUtils.h"
30 #include "nsIPromptService.h"
31 #include "nsNetCID.h"
32 #include "nsNetUtil.h"
33 #include "nsIObserver.h"
34 #include "nsIObserverService.h"
35 #include "nsIDOMLocation.h"
36 #include "nsIWebNavigation.h"
37 #include "nsIWindowMediator.h"
38 #include "nsNativeCharsetUtils.h"
39 #include "nsIAppStartup.h"
41 #include <windows.h>
42 #include <shellapi.h>
43 #include <ddeml.h>
44 #include <stdlib.h>
45 #include <stdio.h>
46 #include <io.h>
47 #include <direct.h>
48 #include <fcntl.h>
50 using namespace mozilla;
52 static HWND hwndForDOMWindow( nsISupports * );
54 static
55 nsresult
56 GetMostRecentWindow(const PRUnichar* aType, nsIDOMWindow** aWindow) {
57 nsresult rv;
58 nsCOMPtr<nsIWindowMediator> med( do_GetService( NS_WINDOWMEDIATOR_CONTRACTID, &rv ) );
59 if ( NS_FAILED( rv ) )
60 return rv;
62 if ( med )
63 return med->GetMostRecentWindow( aType, aWindow );
65 return NS_ERROR_FAILURE;
68 static
69 void
70 activateWindow( nsIDOMWindow *win ) {
71 // Try to get native window handle.
72 HWND hwnd = hwndForDOMWindow( win );
73 if ( hwnd ) {
74 // Restore the window if it is minimized.
75 if ( ::IsIconic( hwnd ) ) {
76 ::ShowWindow( hwnd, SW_RESTORE );
78 // Use the OS call, if possible.
79 ::SetForegroundWindow( hwnd );
80 } else {
81 // Use internal method.
82 win->Focus();
87 #ifdef DEBUG_law
88 #undef MOZ_DEBUG_DDE
89 #define MOZ_DEBUG_DDE 1
90 #endif
92 // Simple Win32 mutex wrapper.
93 struct Mutex {
94 Mutex( const PRUnichar *name )
95 : mName( name ),
96 mHandle( 0 ),
97 mState( -1 ) {
98 mHandle = CreateMutexW( 0, FALSE, mName.get() );
99 #if MOZ_DEBUG_DDE
100 printf( "CreateMutex error = 0x%08X\n", (int)GetLastError() );
101 #endif
103 ~Mutex() {
104 if ( mHandle ) {
105 // Make sure we release it if we own it.
106 Unlock();
108 BOOL rc = CloseHandle( mHandle );
109 #if MOZ_DEBUG_DDE
110 if ( !rc ) {
111 printf( "CloseHandle error = 0x%08X\n", (int)GetLastError() );
113 #endif
116 BOOL Lock( DWORD timeout ) {
117 if ( mHandle ) {
118 #if MOZ_DEBUG_DDE
119 printf( "Waiting (%d msec) for DDE mutex...\n", (int)timeout );
120 #endif
121 mState = WaitForSingleObject( mHandle, timeout );
122 #if MOZ_DEBUG_DDE
123 printf( "...wait complete, result = 0x%08X, GetLastError=0x%08X\n", (int)mState, (int)::GetLastError() );
124 #endif
125 return mState == WAIT_OBJECT_0 || mState == WAIT_ABANDONED;
126 } else {
127 return FALSE;
130 void Unlock() {
131 if ( mHandle && mState == WAIT_OBJECT_0 ) {
132 #if MOZ_DEBUG_DDE
133 printf( "Releasing DDE mutex\n" );
134 #endif
135 ReleaseMutex( mHandle );
136 mState = -1;
139 private:
140 nsString mName;
141 HANDLE mHandle;
142 DWORD mState;
145 /* DDE Notes
147 * This section describes the Win32 DDE service implementation for
148 * Mozilla. DDE is used on Win32 platforms to communicate between
149 * separate instances of mozilla.exe (or other Mozilla-based
150 * executables), or, between the Win32 desktop shell and Mozilla.
152 * The first instance of Mozilla will become the "server" and
153 * subsequent executables (and the shell) will use DDE to send
154 * requests to that process. The requests are DDE "execute" requests
155 * that pass the command line arguments.
157 * Mozilla registers the DDE application "Mozilla" and currently
158 * supports only the "WWW_OpenURL" topic. This should be reasonably
159 * compatible with applications that interfaced with Netscape
160 * Communicator (and its predecessors?). Note that even that topic
161 * may not be supported in a compatible fashion as the command-line
162 * options for Mozilla are different than for Communiator.
164 * It is imperative that at most one instance of Mozilla execute in
165 * "server mode" at any one time. The "native app support" in Mozilla
166 * on Win32 ensures that only the server process performs XPCOM
167 * initialization (that is not required for subsequent client processes
168 * to communicate with the server process).
170 * To guarantee that only one server starts up, a Win32 "mutex" is used
171 * to ensure only one process executes the server-detection code. That
172 * code consists of initializing DDE and doing a DdeConnect to Mozilla's
173 * application/topic. If that connection succeeds, then a server process
174 * must be running already.
176 * Otherwise, no server has started. In that case, the current process
177 * calls DdeNameService to register that application/topic. Only at that
178 * point does the mutex get released.
180 * There are a couple of subtleties that one should be aware of:
182 * 1. It is imperative that DdeInitialize be called only after the mutex
183 * lock has been obtained. The reason is that at shutdown, DDE
184 * notifications go out to all initialized DDE processes. Thus, if
185 * the mutex is owned by a terminating intance of Mozilla, then
186 * calling DdeInitialize and then WaitForSingleObject will cause the
187 * DdeUninitialize from the terminating process to "hang" until the
188 * process waiting for the mutex times out (and can then service the
189 * notification that the DDE server is terminating). So, don't mess
190 * with the sequence of things in the startup/shutdown logic.
192 * 2. All mutex requests are made with a reasonably long timeout value and
193 * are designed to "fail safe" (i.e., a timeout is treated as failure).
195 * 3. An attempt has been made to minimize the degree to which the main
196 * Mozilla application logic needs to be aware of the DDE mechanisms
197 * implemented herein. As a result, this module surfaces a very
198 * large-grained interface, consisting of simple start/stop methods.
199 * As a consequence, details of certain scenarios can be "lost."
200 * Particularly, incoming DDE requests can arrive after this module
201 * initiates the DDE server, but before Mozilla is initialized to the
202 * point where those requests can be serviced (e.g., open a browser
203 * window to a particular URL). Since the client process sends the
204 * request early on, it may not be prepared to respond to that error.
205 * Thus, such situations may fail silently. The design goal is that
206 * they fail harmlessly. Refinements on this point will be made as
207 * details emerge (and time permits).
210 /* Update 2001 March
212 * A significant DDE bug in Windows is causing Mozilla to get wedged at
213 * startup. This is detailed in Bugzill bug 53952
214 * (http://bugzilla.mozilla.org/show_bug.cgi?id=53952).
216 * To resolve this, we are using a new strategy:
217 * o Use a "message window" to detect that Mozilla is already running and
218 * to pass requests from a second instance back to the first;
219 * o Run only as a "DDE server" (not as DDE client); this avoids the
220 * problematic call to DDEConnect().
222 * We still use the mutex semaphore to protect the code that detects
223 * whether Mozilla is already running.
226 /* Update 2007 January
228 * A change in behavior was implemented in July 2004 which made the
229 * application on launch to add and on quit to remove the ddexec registry key.
230 * See bug 246078.
231 * Windows Vista has changed the methods used to set an application as default
232 * and the new methods are incompatible with removing the ddeexec registry key.
233 * See bug 353089.
235 * OS DDE Sequence:
236 * 1. OS checks if the dde name is registered.
237 * 2. If it is registered the OS sends a DDE request with the WWW_OpenURL topic
238 * and the params as specified in the default value of the ddeexec registry
239 * key for the verb (e.g. open).
240 * 3. If it isn't registered the OS launches the executable defined in the
241 * verb's (e.g. open) command registry key.
242 * 4. If the ifexec registry key is not present the OS sends a DDE request with
243 * the WWW_OpenURL topic and the params as specified in the default value of
244 * the ddeexec registry key for the verb (e.g. open).
245 * 5. If the ifexec registry key is present the OS sends a DDE request with the
246 * WWW_OpenURL topic and the params as specified in the ifexec registry key
247 * for the verb (e.g. open).
249 * Application DDE Sequence:
250 * 1. If the application is running a DDE request is received with the
251 * WWW_OpenURL topic and the params as specified in the default value of the
252 * ddeexec registry key (e.g. "%1",,0,0,,,, where '%1' is the url to open)
253 * for the verb (e.g. open).
254 * 2. If the application is not running it is launched with the -requestPending
255 * and the -url argument.
256 * 2.1 If the application does not need to restart and the -requestPending
257 * argument is present the accompanying url will not be used. Instead the
258 * application will wait for the DDE message to open the url.
259 * 2.2 If the application needs to restart the -requestPending argument is
260 * removed from the arguments used to restart the application and the url
261 * will be handled normally.
263 * Note: Due to a bug in IE the ifexec key should not be used (see bug 355650).
266 class nsNativeAppSupportWin : public nsNativeAppSupportBase,
267 public nsIObserver
269 public:
270 NS_DECL_NSIOBSERVER
271 NS_DECL_ISUPPORTS_INHERITED
273 // Overrides of base implementation.
274 NS_IMETHOD Start( bool *aResult );
275 NS_IMETHOD Stop( bool *aResult );
276 NS_IMETHOD Quit();
277 NS_IMETHOD Enable();
278 // The "old" Start method (renamed).
279 NS_IMETHOD StartDDE();
280 // Utility function to handle a Win32-specific command line
281 // option: "-console", which dynamically creates a Windows
282 // console.
283 void CheckConsole();
285 private:
286 static void HandleCommandLine(const char* aCmdLineString, nsIFile* aWorkingDir, uint32_t aState);
287 static HDDEDATA CALLBACK HandleDDENotification( UINT uType,
288 UINT uFmt,
289 HCONV hconv,
290 HSZ hsz1,
291 HSZ hsz2,
292 HDDEDATA hdata,
293 ULONG_PTR dwData1,
294 ULONG_PTR dwData2 );
295 static void ParseDDEArg( HSZ args, int index, nsString& string);
296 static void ParseDDEArg( const WCHAR* args, int index, nsString& aString);
297 static HDDEDATA CreateDDEData( DWORD value );
298 static HDDEDATA CreateDDEData( LPBYTE value, DWORD len );
299 static bool InitTopicStrings();
300 static int FindTopic( HSZ topic );
301 static void ActivateLastWindow();
302 static nsresult OpenWindow( const char *urlstr, const char *args );
303 static nsresult OpenBrowserWindow();
304 static void SetupSysTrayIcon();
305 static void RemoveSysTrayIcon();
307 static int mConversations;
308 enum {
309 topicOpenURL,
310 topicActivate,
311 topicCancelProgress,
312 topicVersion,
313 topicRegisterViewer,
314 topicUnRegisterViewer,
315 topicGetWindowInfo,
316 // Note: Insert new values above this line!!!!!
317 topicCount // Count of the number of real topics
319 static HSZ mApplication, mTopics[ topicCount ];
320 static DWORD mInstance;
321 static bool mCanHandleRequests;
322 static PRUnichar mMutexName[];
323 friend struct MessageWindow;
324 }; // nsNativeAppSupportWin
326 NS_INTERFACE_MAP_BEGIN(nsNativeAppSupportWin)
327 NS_INTERFACE_MAP_ENTRY(nsIObserver)
328 NS_INTERFACE_MAP_END_INHERITING(nsNativeAppSupportBase)
330 NS_IMPL_ADDREF_INHERITED(nsNativeAppSupportWin, nsNativeAppSupportBase)
331 NS_IMPL_RELEASE_INHERITED(nsNativeAppSupportWin, nsNativeAppSupportBase)
333 void
334 nsNativeAppSupportWin::CheckConsole() {
335 for ( int i = 1; i < gArgc; i++ ) {
336 if ( strcmp( "-console", gArgv[i] ) == 0
338 strcmp( "/console", gArgv[i] ) == 0 ) {
339 // Users wants to make sure we have a console.
340 // Try to allocate one.
341 BOOL rc = ::AllocConsole();
342 if ( rc ) {
343 // Console allocated. Fix it up so that output works in
344 // all cases. See http://support.microsoft.com/support/kb/articles/q105/3/05.asp.
346 // stdout
347 int hCrt = ::_open_osfhandle( (intptr_t)GetStdHandle( STD_OUTPUT_HANDLE ),
348 _O_TEXT );
349 if ( hCrt != -1 ) {
350 FILE *hf = ::_fdopen( hCrt, "w" );
351 if ( hf ) {
352 *stdout = *hf;
353 #ifdef DEBUG
354 ::fprintf( stdout, "stdout directed to dynamic console\n" );
355 #endif
359 // stderr
360 hCrt = ::_open_osfhandle( (intptr_t)::GetStdHandle( STD_ERROR_HANDLE ),
361 _O_TEXT );
362 if ( hCrt != -1 ) {
363 FILE *hf = ::_fdopen( hCrt, "w" );
364 if ( hf ) {
365 *stderr = *hf;
366 #ifdef DEBUG
367 ::fprintf( stderr, "stderr directed to dynamic console\n" );
368 #endif
372 // stdin?
373 /* Don't bother for now.
374 hCrt = ::_open_osfhandle( (long)::GetStdHandle( STD_INPUT_HANDLE ),
375 _O_TEXT );
376 if ( hCrt != -1 ) {
377 FILE *hf = ::_fdopen( hCrt, "r" );
378 if ( hf ) {
379 *stdin = *hf;
383 } else {
384 // Failed. Probably because there already is one.
385 // There's little we can do, in any case.
387 // Remove the console argument from the command line.
388 do {
389 gArgv[i] = gArgv[i + 1];
390 ++i;
391 } while (gArgv[i]);
393 --gArgc;
395 } else if ( strcmp( "-attach-console", gArgv[i] ) == 0
397 strcmp( "/attach-console", gArgv[i] ) == 0 ) {
398 // Try to attach console to the parent process.
399 // It will succeed when the parent process is a command line,
400 // so that stdio will be displayed in it.
401 if (AttachConsole(ATTACH_PARENT_PROCESS)) {
402 // Change std handles to refer to new console handles.
403 // Before doing so, ensure that stdout/stderr haven't been
404 // redirected to a valid file
405 if (_fileno(stdout) == -1 ||
406 _get_osfhandle(fileno(stdout)) == -1)
407 freopen("CONOUT$", "w", stdout);
408 // Merge stderr into CONOUT$ since there isn't any `CONERR$`.
409 // http://msdn.microsoft.com/en-us/library/windows/desktop/ms683231%28v=vs.85%29.aspx
410 if (_fileno(stderr) == -1 ||
411 _get_osfhandle(fileno(stderr)) == -1)
412 freopen("CONOUT$", "w", stderr);
413 if (_fileno(stdin) == -1 || _get_osfhandle(fileno(stdin)) == -1)
414 freopen("CONIN$", "r", stdin);
419 return;
423 // Create and return an instance of class nsNativeAppSupportWin.
424 nsresult
425 NS_CreateNativeAppSupport( nsINativeAppSupport **aResult ) {
426 nsNativeAppSupportWin *pNative = new nsNativeAppSupportWin;
427 if (!pNative) return NS_ERROR_OUT_OF_MEMORY;
429 // Check for dynamic console creation request.
430 pNative->CheckConsole();
432 *aResult = pNative;
433 NS_ADDREF( *aResult );
435 return NS_OK;
438 // Constants
439 #define MOZ_DDE_APPLICATION "Mozilla"
440 #define MOZ_MUTEX_NAMESPACE L"Local\\"
441 #define MOZ_STARTUP_MUTEX_NAME L"StartupMutex"
442 #define MOZ_DDE_START_TIMEOUT 30000
443 #define MOZ_DDE_STOP_TIMEOUT 15000
444 #define MOZ_DDE_EXEC_TIMEOUT 15000
446 // The array entries must match the enum ordering!
447 const char * const topicNames[] = { "WWW_OpenURL",
448 "WWW_Activate",
449 "WWW_CancelProgress",
450 "WWW_Version",
451 "WWW_RegisterViewer",
452 "WWW_UnRegisterViewer",
453 "WWW_GetWindowInfo" };
455 // Static member definitions.
456 int nsNativeAppSupportWin::mConversations = 0;
457 HSZ nsNativeAppSupportWin::mApplication = 0;
458 HSZ nsNativeAppSupportWin::mTopics[nsNativeAppSupportWin::topicCount] = { 0 };
459 DWORD nsNativeAppSupportWin::mInstance = 0;
460 bool nsNativeAppSupportWin::mCanHandleRequests = false;
462 PRUnichar nsNativeAppSupportWin::mMutexName[ 128 ] = { 0 };
465 // Message window encapsulation.
466 struct MessageWindow {
467 // ctor/dtor are simplistic
468 MessageWindow() {
469 // Try to find window.
470 mHandle = ::FindWindowW( className(), 0 );
473 // Act like an HWND.
474 operator HWND() {
475 return mHandle;
478 // Class name: appName + "MessageWindow"
479 static const wchar_t *className() {
480 static wchar_t classNameBuffer[128];
481 static wchar_t *mClassName = 0;
482 if ( !mClassName ) {
483 ::_snwprintf(classNameBuffer,
484 128, // size of classNameBuffer in PRUnichars
485 L"%s%s",
486 NS_ConvertUTF8toUTF16(gAppData->name).get(),
487 L"MessageWindow" );
488 mClassName = classNameBuffer;
490 return mClassName;
493 // Create: Register class and create window.
494 NS_IMETHOD Create() {
495 WNDCLASSW classStruct = { 0, // style
496 &MessageWindow::WindowProc, // lpfnWndProc
497 0, // cbClsExtra
498 0, // cbWndExtra
499 0, // hInstance
500 0, // hIcon
501 0, // hCursor
502 0, // hbrBackground
503 0, // lpszMenuName
504 className() }; // lpszClassName
506 // Register the window class.
507 NS_ENSURE_TRUE( ::RegisterClassW( &classStruct ), NS_ERROR_FAILURE );
509 // Create the window.
510 NS_ENSURE_TRUE( ( mHandle = ::CreateWindowW(className(),
511 0, // title
512 WS_CAPTION, // style
513 0,0,0,0, // x, y, cx, cy
514 0, // parent
515 0, // menu
516 0, // instance
517 0 ) ), // create struct
518 NS_ERROR_FAILURE );
520 #if MOZ_DEBUG_DDE
521 printf( "Message window = 0x%08X\n", (int)mHandle );
522 #endif
524 return NS_OK;
527 // Destory: Get rid of window and reset mHandle.
528 NS_IMETHOD Destroy() {
529 nsresult retval = NS_OK;
531 if ( mHandle ) {
532 // DestroyWindow can only destroy windows created from
533 // the same thread.
534 BOOL desRes = DestroyWindow( mHandle );
535 if ( FALSE != desRes ) {
536 mHandle = nullptr;
538 else {
539 retval = NS_ERROR_FAILURE;
543 return retval;
546 // SendRequest: Pass the command line via WM_COPYDATA to message window.
547 NS_IMETHOD SendRequest() {
548 WCHAR *cmd = ::GetCommandLineW();
549 WCHAR cwd[MAX_PATH];
550 _wgetcwd(cwd, MAX_PATH);
552 // Construct a narrow UTF8 buffer <commandline>\0<workingdir>\0
553 NS_ConvertUTF16toUTF8 utf8buffer(cmd);
554 utf8buffer.Append('\0');
555 AppendUTF16toUTF8(cwd, utf8buffer);
556 utf8buffer.Append('\0');
558 // We used to set dwData to zero, when we didn't send the working dir.
559 // Now we're using it as a version number.
560 COPYDATASTRUCT cds = {
562 utf8buffer.Length(),
563 (void*) utf8buffer.get()
565 // Bring the already running Mozilla process to the foreground.
566 // nsWindow will restore the window (if minimized) and raise it.
567 ::SetForegroundWindow( mHandle );
568 ::SendMessage( mHandle, WM_COPYDATA, 0, (LPARAM)&cds );
569 return NS_OK;
572 // Window proc.
573 static LRESULT CALLBACK WindowProc( HWND msgWindow, UINT msg, WPARAM wp, LPARAM lp ) {
574 if ( msg == WM_COPYDATA ) {
575 if (!nsNativeAppSupportWin::mCanHandleRequests)
576 return FALSE;
578 // This is an incoming request.
579 COPYDATASTRUCT *cds = (COPYDATASTRUCT*)lp;
580 #if MOZ_DEBUG_DDE
581 printf( "Incoming request: %s\n", (const char*)cds->lpData );
582 #endif
583 nsCOMPtr<nsIFile> workingDir;
585 if (1 >= cds->dwData) {
586 char* wdpath = (char*) cds->lpData;
587 // skip the command line, and get the working dir of the
588 // other process, which is after the first null char
589 while (*wdpath)
590 ++wdpath;
592 ++wdpath;
594 #ifdef MOZ_DEBUG_DDE
595 printf( "Working dir: %s\n", wdpath);
596 #endif
598 NS_NewLocalFile(NS_ConvertUTF8toUTF16(wdpath),
599 false,
600 getter_AddRefs(workingDir));
602 (void)nsNativeAppSupportWin::HandleCommandLine((char*)cds->lpData, workingDir, nsICommandLine::STATE_REMOTE_AUTO);
604 // Get current window and return its window handle.
605 nsCOMPtr<nsIDOMWindow> win;
606 GetMostRecentWindow( 0, getter_AddRefs( win ) );
607 return win ? (LRESULT)hwndForDOMWindow( win ) : 0;
609 return DefWindowProc( msgWindow, msg, wp, lp );
612 private:
613 HWND mHandle;
614 }; // struct MessageWindow
616 /* Start: Tries to find the "message window" to determine if it
617 * exists. If so, then Mozilla is already running. In that
618 * case, we use the handle to the "message" window and send
619 * a request corresponding to this process's command line
620 * options.
622 * If not, then this is the first instance of Mozilla. In
623 * that case, we create and set up the message window.
625 * The checking for existence of the message window must
626 * be protected by use of a mutex semaphore.
628 NS_IMETHODIMP
629 nsNativeAppSupportWin::Start( bool *aResult ) {
630 NS_ENSURE_ARG( aResult );
631 NS_ENSURE_TRUE( mInstance == 0, NS_ERROR_NOT_INITIALIZED );
632 NS_ENSURE_STATE( gAppData );
634 if (getenv("MOZ_NO_REMOTE"))
636 *aResult = true;
637 return NS_OK;
640 nsresult rv = NS_ERROR_FAILURE;
641 *aResult = false;
643 // Grab mutex first.
645 // Build mutex name from app name.
646 ::_snwprintf(reinterpret_cast<wchar_t*>(mMutexName),
647 sizeof mMutexName / sizeof(PRUnichar), L"%s%s%s",
648 MOZ_MUTEX_NAMESPACE,
649 NS_ConvertUTF8toUTF16(gAppData->name).get(),
650 MOZ_STARTUP_MUTEX_NAME );
651 Mutex startupLock = Mutex( mMutexName );
653 NS_ENSURE_TRUE( startupLock.Lock( MOZ_DDE_START_TIMEOUT ), NS_ERROR_FAILURE );
655 // Search for existing message window.
656 MessageWindow msgWindow;
657 if ( (HWND)msgWindow ) {
658 // We are a client process. Pass request to message window.
659 rv = msgWindow.SendRequest();
660 } else {
661 // We will be server.
662 rv = msgWindow.Create();
663 if ( NS_SUCCEEDED( rv ) ) {
664 // Start up DDE server.
665 this->StartDDE();
666 // Tell caller to spin message loop.
667 *aResult = true;
671 startupLock.Unlock();
673 return rv;
676 bool
677 nsNativeAppSupportWin::InitTopicStrings() {
678 for ( int i = 0; i < topicCount; i++ ) {
679 if ( !( mTopics[ i ] = DdeCreateStringHandleA( mInstance, const_cast<char *>(topicNames[ i ]), CP_WINANSI ) ) ) {
680 return false;
683 return true;
687 nsNativeAppSupportWin::FindTopic( HSZ topic ) {
688 for ( int i = 0; i < topicCount; i++ ) {
689 if ( DdeCmpStringHandles( topic, mTopics[i] ) == 0 ) {
690 return i;
693 return -1;
697 // Start DDE server.
699 // This used to be the Start() method when we were using DDE as the
700 // primary IPC mechanism between secondary Mozilla processes and the
701 // initial "server" process.
703 // Now, it simply initializes the DDE server. The caller must check
704 // that this process is to be the server, and, must acquire the DDE
705 // startup mutex semaphore prior to calling this routine. See ::Start(),
706 // above.
707 NS_IMETHODIMP
708 nsNativeAppSupportWin::StartDDE() {
709 NS_ENSURE_TRUE( mInstance == 0, NS_ERROR_NOT_INITIALIZED );
711 // Initialize DDE.
712 NS_ENSURE_TRUE( DMLERR_NO_ERROR == DdeInitialize( &mInstance,
713 nsNativeAppSupportWin::HandleDDENotification,
714 APPCLASS_STANDARD,
715 0 ),
716 NS_ERROR_FAILURE );
718 // Allocate DDE strings.
719 NS_ENSURE_TRUE( ( mApplication = DdeCreateStringHandleA( mInstance, (char*) gAppData->name, CP_WINANSI ) ) && InitTopicStrings(),
720 NS_ERROR_FAILURE );
722 // Next step is to register a DDE service.
723 NS_ENSURE_TRUE( DdeNameService( mInstance, mApplication, 0, DNS_REGISTER ), NS_ERROR_FAILURE );
725 #if MOZ_DEBUG_DDE
726 printf( "DDE server started\n" );
727 #endif
729 return NS_OK;
732 // If no DDE conversations are pending, terminate DDE.
733 NS_IMETHODIMP
734 nsNativeAppSupportWin::Stop( bool *aResult ) {
735 NS_ENSURE_ARG( aResult );
736 NS_ENSURE_TRUE( mInstance, NS_ERROR_NOT_INITIALIZED );
738 nsresult rv = NS_OK;
739 *aResult = true;
741 Mutex ddeLock( mMutexName );
743 if ( ddeLock.Lock( MOZ_DDE_STOP_TIMEOUT ) ) {
744 if ( mConversations == 0 ) {
745 this->Quit();
746 } else {
747 *aResult = false;
750 ddeLock.Unlock();
752 else {
753 // No DDE application name specified, but that's OK. Just
754 // forge ahead.
755 *aResult = true;
758 return rv;
761 NS_IMETHODIMP
762 nsNativeAppSupportWin::Observe(nsISupports* aSubject, const char* aTopic,
763 const PRUnichar* aData)
765 if (strcmp(aTopic, "quit-application") == 0) {
766 Quit();
767 } else {
768 NS_ERROR("Unexpected observer topic.");
771 return NS_OK;
774 // Terminate DDE regardless.
775 NS_IMETHODIMP
776 nsNativeAppSupportWin::Quit() {
777 // If another process wants to look for the message window, they need
778 // to wait to hold the lock, in which case they will not find the
779 // window as we will destroy ours under our lock.
780 // When the mutex goes off the stack, it is unlocked via destructor.
781 Mutex mutexLock(mMutexName);
782 NS_ENSURE_TRUE(mutexLock.Lock(MOZ_DDE_START_TIMEOUT), NS_ERROR_FAILURE);
784 // If we've got a message window to receive IPC or new window requests,
785 // get rid of it as we are shutting down.
786 // Note: Destroy calls DestroyWindow, which will only work on a window
787 // created by the same thread.
788 MessageWindow mw;
789 mw.Destroy();
791 if ( mInstance ) {
792 // Unregister application name.
793 DdeNameService( mInstance, mApplication, 0, DNS_UNREGISTER );
794 // Clean up strings.
795 if ( mApplication ) {
796 DdeFreeStringHandle( mInstance, mApplication );
797 mApplication = 0;
799 for ( int i = 0; i < topicCount; i++ ) {
800 if ( mTopics[i] ) {
801 DdeFreeStringHandle( mInstance, mTopics[i] );
802 mTopics[i] = 0;
805 DdeUninitialize( mInstance );
806 mInstance = 0;
807 #if MOZ_DEBUG_DDE
808 printf( "DDE server stopped\n" );
809 #endif
812 return NS_OK;
815 NS_IMETHODIMP
816 nsNativeAppSupportWin::Enable()
818 mCanHandleRequests = true;
820 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
821 if (obs) {
822 obs->AddObserver(this, "quit-application", false);
823 } else {
824 NS_ERROR("No observer service?");
827 return NS_OK;
830 #if MOZ_DEBUG_DDE
831 // Macro to generate case statement for a given XTYP value.
832 #define XTYP_CASE(t) \
833 case t: result = #t; break
835 static nsCString uTypeDesc( UINT uType ) {
836 nsCString result;
837 switch ( uType ) {
838 XTYP_CASE(XTYP_ADVSTART);
839 XTYP_CASE(XTYP_CONNECT);
840 XTYP_CASE(XTYP_ADVREQ);
841 XTYP_CASE(XTYP_REQUEST);
842 XTYP_CASE(XTYP_WILDCONNECT);
843 XTYP_CASE(XTYP_ADVDATA);
844 XTYP_CASE(XTYP_EXECUTE);
845 XTYP_CASE(XTYP_POKE);
846 XTYP_CASE(XTYP_ADVSTOP);
847 XTYP_CASE(XTYP_CONNECT_CONFIRM);
848 XTYP_CASE(XTYP_DISCONNECT);
849 XTYP_CASE(XTYP_ERROR);
850 XTYP_CASE(XTYP_MONITOR);
851 XTYP_CASE(XTYP_REGISTER);
852 XTYP_CASE(XTYP_XACT_COMPLETE);
853 XTYP_CASE(XTYP_UNREGISTER);
854 default: result = "XTYP_?????";
856 return result;
859 static nsCString hszValue( DWORD instance, HSZ hsz ) {
860 // Extract string from HSZ.
861 nsCString result("[");
862 DWORD len = DdeQueryString( instance, hsz, nullptr, nullptr, CP_WINANSI );
863 if ( len ) {
864 char buffer[ 256 ];
865 DdeQueryString( instance, hsz, buffer, sizeof buffer, CP_WINANSI );
866 result += buffer;
868 result += "]";
869 return result;
871 #else
872 // These are purely a safety measure to avoid the infamous "won't
873 // build non-debug" type Tinderbox flames.
874 static nsCString uTypeDesc( UINT ) {
875 return nsCString( "?" );
877 static nsCString hszValue( DWORD, HSZ ) {
878 return nsCString( "?" );
880 #endif
883 // Utility function to escape double-quotes within a string.
884 static void escapeQuotes( nsAString &aString ) {
885 int32_t offset = -1;
886 while( 1 ) {
887 // Find next '"'.
888 offset = aString.FindChar( '"', ++offset );
889 if ( offset == kNotFound ) {
890 // No more quotes, exit.
891 break;
892 } else {
893 // Insert back-slash ahead of the '"'.
894 aString.Insert( PRUnichar('\\'), offset );
895 // Increment offset because we just inserted a slash
896 offset++;
899 return;
902 HDDEDATA CALLBACK
903 nsNativeAppSupportWin::HandleDDENotification( UINT uType, // transaction type
904 UINT uFmt, // clipboard data format
905 HCONV hconv, // handle to the conversation
906 HSZ hsz1, // handle to a string
907 HSZ hsz2, // handle to a string
908 HDDEDATA hdata, // handle to a global memory object
909 ULONG_PTR dwData1, // transaction-specific data
910 ULONG_PTR dwData2 ) { // transaction-specific data
912 if (!mCanHandleRequests)
913 return 0;
916 #if MOZ_DEBUG_DDE
917 printf( "DDE: uType =%s\n", uTypeDesc( uType ).get() );
918 printf( " uFmt =%u\n", (unsigned)uFmt );
919 printf( " hconv =%08x\n", (int)hconv );
920 printf( " hsz1 =%08x:%s\n", (int)hsz1, hszValue( mInstance, hsz1 ).get() );
921 printf( " hsz2 =%08x:%s\n", (int)hsz2, hszValue( mInstance, hsz2 ).get() );
922 printf( " hdata =%08x\n", (int)hdata );
923 printf( " dwData1=%08x\n", (int)dwData1 );
924 printf( " dwData2=%08x\n", (int)dwData2 );
925 #endif
927 HDDEDATA result = 0;
928 if ( uType & XCLASS_BOOL ) {
929 switch ( uType ) {
930 case XTYP_CONNECT:
931 // Make sure its for our service/topic.
932 if ( FindTopic( hsz1 ) != -1 ) {
933 // We support this connection.
934 result = (HDDEDATA)1;
936 break;
937 case XTYP_CONNECT_CONFIRM:
938 // We don't care about the conversation handle, at this point.
939 result = (HDDEDATA)1;
940 break;
942 } else if ( uType & XCLASS_DATA ) {
943 if ( uType == XTYP_REQUEST ) {
944 switch ( FindTopic( hsz1 ) ) {
945 case topicOpenURL: {
946 // Open a given URL...
948 // Get the URL from the first argument in the command.
949 nsAutoString url;
950 ParseDDEArg(hsz2, 0, url);
952 // Read the 3rd argument in the command to determine if a
953 // new window is to be used.
954 nsAutoString windowID;
955 ParseDDEArg(hsz2, 2, windowID);
956 // "" means to open the URL in a new window.
957 if ( windowID.IsEmpty() ) {
958 url.Insert(NS_LITERAL_STRING("mozilla -new-window "), 0);
960 else {
961 url.Insert(NS_LITERAL_STRING("mozilla -url "), 0);
964 #if MOZ_DEBUG_DDE
965 printf( "Handling dde XTYP_REQUEST request: [%s]...\n", NS_ConvertUTF16toUTF8(url).get() );
966 #endif
967 // Now handle it.
968 HandleCommandLine(NS_ConvertUTF16toUTF8(url).get(), nullptr, nsICommandLine::STATE_REMOTE_EXPLICIT);
970 // Return pseudo window ID.
971 result = CreateDDEData( 1 );
972 break;
974 case topicGetWindowInfo: {
975 // This topic has to get the current URL, get the current
976 // page title and then format the output into the DDE
977 // return string. The return value is "URL","Page Title",
978 // "Window ID" however the window ID is not used for this
979 // command, therefore it is returned as a null string
981 // This isn't really a loop. We just use "break"
982 // statements to bypass the remaining steps when
983 // something goes wrong.
984 do {
985 // Get most recently used Nav window.
986 nsCOMPtr<nsIDOMWindow> navWin;
987 GetMostRecentWindow( NS_LITERAL_STRING( "navigator:browser" ).get(),
988 getter_AddRefs( navWin ) );
989 if ( !navWin ) {
990 // There is not a window open
991 break;
993 // Get content window.
994 nsCOMPtr<nsIDOMWindow> content;
995 navWin->GetContent( getter_AddRefs( content ) );
996 if ( !content ) {
997 break;
999 // Convert that to internal interface.
1000 nsCOMPtr<nsPIDOMWindow> internalContent( do_QueryInterface( content ) );
1001 if ( !internalContent ) {
1002 break;
1004 // Get location.
1005 nsCOMPtr<nsIDOMLocation> location;
1006 internalContent->GetLocation( getter_AddRefs( location ) );
1007 if ( !location ) {
1008 break;
1010 // Get href for URL.
1011 nsAutoString url;
1012 if ( NS_FAILED( location->GetHref( url ) ) ) {
1013 break;
1015 // Escape any double-quotes.
1016 escapeQuotes( url );
1018 // Now for the title...
1020 // Get the base window from the doc shell...
1021 nsCOMPtr<nsIBaseWindow> baseWindow =
1022 do_QueryInterface( internalContent->GetDocShell() );
1023 if ( !baseWindow ) {
1024 break;
1026 // And from the base window we can get the title.
1027 nsXPIDLString title;
1028 if(!baseWindow) {
1029 break;
1031 baseWindow->GetTitle(getter_Copies(title));
1032 // Escape any double-quotes in the title.
1033 escapeQuotes( title );
1035 // Use a string buffer for the output data, first
1036 // save a quote.
1037 nsAutoCString outpt( NS_LITERAL_CSTRING("\"") );
1038 // Now copy the URL converting the Unicode string
1039 // to a single-byte ASCII string
1040 nsAutoCString tmpNativeStr;
1041 NS_CopyUnicodeToNative( url, tmpNativeStr );
1042 outpt.Append( tmpNativeStr );
1043 // Add the "," used to separate the URL and the page
1044 // title
1045 outpt.Append( NS_LITERAL_CSTRING("\",\"") );
1046 // Now copy the current page title to the return string
1047 NS_CopyUnicodeToNative( title, tmpNativeStr );
1048 outpt.Append( tmpNativeStr );
1049 // Fill out the return string with the remainin ",""
1050 outpt.Append( NS_LITERAL_CSTRING( "\",\"\"" ));
1052 // Create a DDE handle to a char string for the data
1053 // being returned, this copies and creates a "shared"
1054 // copy of the DDE response until the calling APP
1055 // reads it and says it can be freed.
1056 result = CreateDDEData( (LPBYTE)(const char*)outpt.get(),
1057 outpt.Length() + 1 );
1058 #if MOZ_DEBUG_DDE
1059 printf( "WWW_GetWindowInfo->%s\n", outpt.get() );
1060 #endif
1061 } while ( false );
1062 break;
1064 case topicActivate: {
1065 // Activate a Nav window...
1066 nsAutoString windowID;
1067 ParseDDEArg(hsz2, 0, windowID);
1068 // 4294967295 is decimal for 0xFFFFFFFF which is also a
1069 // correct value to do that Activate last window stuff
1070 if ( windowID.EqualsLiteral( "-1" ) ||
1071 windowID.EqualsLiteral( "4294967295" ) ) {
1072 // We only support activating the most recent window (or a new one).
1073 ActivateLastWindow();
1074 // Return pseudo window ID.
1075 result = CreateDDEData( 1 );
1077 break;
1079 case topicVersion: {
1080 // Return version. We're restarting at 1.0!
1081 DWORD version = 1 << 16; // "1.0"
1082 result = CreateDDEData( version );
1083 break;
1085 case topicRegisterViewer: {
1086 // Register new viewer (not implemented).
1087 result = CreateDDEData( false );
1088 break;
1090 case topicUnRegisterViewer: {
1091 // Unregister new viewer (not implemented).
1092 result = CreateDDEData( false );
1093 break;
1095 default:
1096 break;
1098 } else if ( uType & XTYP_POKE ) {
1099 switch ( FindTopic( hsz1 ) ) {
1100 case topicCancelProgress: {
1101 // "Handle" progress cancel (actually, pretty much ignored).
1102 result = (HDDEDATA)DDE_FACK;
1103 break;
1105 default:
1106 break;
1109 } else if ( uType & XCLASS_FLAGS ) {
1110 if ( uType == XTYP_EXECUTE ) {
1111 // Prove that we received the request.
1112 DWORD bytes;
1113 LPBYTE request = DdeAccessData( hdata, &bytes );
1114 #if MOZ_DEBUG_DDE
1115 printf( "Handling dde request: [%s]...\n", (char*)request );
1116 #endif
1118 nsAutoString url;
1119 ParseDDEArg((const WCHAR*) request, 0, url);
1121 // Read the 3rd argument in the command to determine if a
1122 // new window is to be used.
1123 nsAutoString windowID;
1124 ParseDDEArg((const WCHAR*) request, 2, windowID);
1126 // "" means to open the URL in a new window.
1127 if ( windowID.IsEmpty() ) {
1128 url.Insert(NS_LITERAL_STRING("mozilla -new-window "), 0);
1130 else {
1131 url.Insert(NS_LITERAL_STRING("mozilla -url "), 0);
1133 #if MOZ_DEBUG_DDE
1134 printf( "Handling dde XTYP_REQUEST request: [%s]...\n", NS_ConvertUTF16toUTF8(url).get() );
1135 #endif
1136 // Now handle it.
1137 HandleCommandLine(NS_ConvertUTF16toUTF8(url).get(), nullptr, nsICommandLine::STATE_REMOTE_EXPLICIT);
1139 // Release the data.
1140 DdeUnaccessData( hdata );
1141 result = (HDDEDATA)DDE_FACK;
1142 } else {
1143 result = (HDDEDATA)DDE_FNOTPROCESSED;
1145 } else if ( uType & XCLASS_NOTIFICATION ) {
1147 #if MOZ_DEBUG_DDE
1148 printf( "DDE result=%d (0x%08X)\n", (int)result, (int)result );
1149 #endif
1150 return result;
1153 // Utility function to advance to end of quoted string.
1154 // p+offset must point to the comma preceding the arg on entry.
1155 // On return, p+result points to the closing '"' (or end of the string
1156 // if the closing '"' is missing) if the arg is quoted. If the arg
1157 // is not quoted, then p+result will point to the first character
1158 // of the arg.
1159 static int32_t advanceToEndOfQuotedArg( const WCHAR *p, int32_t offset, int32_t len ) {
1160 // Check whether the current arg is quoted.
1161 if ( p[++offset] == '"' ) {
1162 // Advance past the closing quote.
1163 while ( offset < len && p[++offset] != '"' ) {
1164 // If the current character is a backslash, then the
1165 // next character can't be a *real* '"', so skip it.
1166 if ( p[offset] == '\\' ) {
1167 offset++;
1171 return offset;
1174 void nsNativeAppSupportWin::ParseDDEArg( const WCHAR* args, int index, nsString& aString) {
1175 if ( args ) {
1176 nsDependentString temp(args);
1178 // offset points to the comma preceding the desired arg.
1179 int32_t offset = -1;
1180 // Skip commas till we get to the arg we want.
1181 while( index-- ) {
1182 // If this arg is quoted, then go to closing quote.
1183 offset = advanceToEndOfQuotedArg( args, offset, temp.Length());
1184 // Find next comma.
1185 offset = temp.FindChar( ',', offset );
1186 if ( offset == kNotFound ) {
1187 // No more commas, give up.
1188 aString = args;
1189 return;
1192 // The desired argument starts just past the preceding comma,
1193 // which offset points to, and extends until the following
1194 // comma (or the end of the string).
1196 // Since the argument might be enclosed in quotes, we need to
1197 // deal with that before searching for the terminating comma.
1198 // We advance offset so it ends up pointing to the start of
1199 // the argument we want.
1200 int32_t end = advanceToEndOfQuotedArg( args, offset++, temp.Length() );
1201 // Find next comma (or end of string).
1202 end = temp.FindChar( ',', end );
1203 if ( end == kNotFound ) {
1204 // Arg is the rest of the string.
1205 end = temp.Length();
1207 // Extract result.
1208 aString.Assign( args + offset, end - offset );
1210 return;
1213 // Utility to parse out argument from a DDE item string.
1214 void nsNativeAppSupportWin::ParseDDEArg( HSZ args, int index, nsString& aString) {
1215 DWORD argLen = DdeQueryStringW( mInstance, args, nullptr, 0, CP_WINUNICODE );
1216 // there wasn't any string, so return empty string
1217 if ( !argLen ) return;
1218 nsAutoString temp;
1219 // Ensure result's buffer is sufficiently big.
1220 temp.SetLength( argLen );
1221 // Now get the string contents.
1222 DdeQueryString( mInstance, args, reinterpret_cast<wchar_t*>(temp.BeginWriting()), temp.Length(), CP_WINUNICODE );
1223 // Parse out the given arg.
1224 ParseDDEArg(temp.get(), index, aString);
1225 return;
1228 HDDEDATA nsNativeAppSupportWin::CreateDDEData( DWORD value ) {
1229 return CreateDDEData( (LPBYTE)&value, sizeof value );
1232 HDDEDATA nsNativeAppSupportWin::CreateDDEData( LPBYTE value, DWORD len ) {
1233 HDDEDATA result = DdeCreateDataHandle( mInstance,
1234 value,
1235 len,
1237 mApplication,
1238 CF_TEXT,
1239 0 );
1240 return result;
1243 void nsNativeAppSupportWin::ActivateLastWindow() {
1244 nsCOMPtr<nsIDOMWindow> navWin;
1245 GetMostRecentWindow( NS_LITERAL_STRING("navigator:browser").get(), getter_AddRefs( navWin ) );
1246 if ( navWin ) {
1247 // Activate that window.
1248 activateWindow( navWin );
1249 } else {
1250 // Need to create a Navigator window, then.
1251 OpenBrowserWindow();
1255 void
1256 nsNativeAppSupportWin::HandleCommandLine(const char* aCmdLineString,
1257 nsIFile* aWorkingDir,
1258 uint32_t aState)
1260 nsresult rv;
1262 int justCounting = 1;
1263 char **argv = 0;
1264 // Flags, etc.
1265 int init = 1;
1266 int between, quoted, bSlashCount;
1267 int argc;
1268 const char *p;
1269 nsAutoCString arg;
1271 nsCOMPtr<nsICommandLineRunner> cmdLine
1272 (do_CreateInstance("@mozilla.org/toolkit/command-line;1"));
1273 if (!cmdLine) {
1274 NS_ERROR("Couldn't create command line!");
1275 return;
1278 // Parse command line args according to MS spec
1279 // (see "Parsing C++ Command-Line Arguments" at
1280 // http://msdn.microsoft.com/library/devprods/vs6/visualc/vclang/_pluslang_parsing_c.2b2b_.command.2d.line_arguments.htm).
1281 // We loop if we've not finished the second pass through.
1282 while ( 1 ) {
1283 // Initialize if required.
1284 if ( init ) {
1285 p = aCmdLineString;
1286 between = 1;
1287 argc = quoted = bSlashCount = 0;
1289 init = 0;
1291 if ( between ) {
1292 // We are traversing whitespace between args.
1293 // Check for start of next arg.
1294 if ( *p != 0 && !isspace( *p ) ) {
1295 // Start of another arg.
1296 between = 0;
1297 arg = "";
1298 switch ( *p ) {
1299 case '\\':
1300 // Count the backslash.
1301 bSlashCount = 1;
1302 break;
1303 case '"':
1304 // Remember we're inside quotes.
1305 quoted = 1;
1306 break;
1307 default:
1308 // Add character to arg.
1309 arg += *p;
1310 break;
1312 } else {
1313 // Another space between args, ignore it.
1315 } else {
1316 // We are processing the contents of an argument.
1317 // Check for whitespace or end.
1318 if ( *p == 0 || ( !quoted && isspace( *p ) ) ) {
1319 // Process pending backslashes (interpret them
1320 // literally since they're not followed by a ").
1321 while( bSlashCount ) {
1322 arg += '\\';
1323 bSlashCount--;
1325 // End current arg.
1326 if ( !justCounting ) {
1327 argv[argc] = new char[ arg.Length() + 1 ];
1328 strcpy( argv[argc], arg.get() );
1330 argc++;
1331 // We're now between args.
1332 between = 1;
1333 } else {
1334 // Still inside argument, process the character.
1335 switch ( *p ) {
1336 case '"':
1337 // First, digest preceding backslashes (if any).
1338 while ( bSlashCount > 1 ) {
1339 // Put one backsplash in arg for each pair.
1340 arg += '\\';
1341 bSlashCount -= 2;
1343 if ( bSlashCount ) {
1344 // Quote is literal.
1345 arg += '"';
1346 bSlashCount = 0;
1347 } else {
1348 // Quote starts or ends a quoted section.
1349 if ( quoted ) {
1350 // Check for special case of consecutive double
1351 // quotes inside a quoted section.
1352 if ( *(p+1) == '"' ) {
1353 // This implies a literal double-quote. Fake that
1354 // out by causing next double-quote to look as
1355 // if it was preceded by a backslash.
1356 bSlashCount = 1;
1357 } else {
1358 quoted = 0;
1360 } else {
1361 quoted = 1;
1364 break;
1365 case '\\':
1366 // Add to count.
1367 bSlashCount++;
1368 break;
1369 default:
1370 // Accept any preceding backslashes literally.
1371 while ( bSlashCount ) {
1372 arg += '\\';
1373 bSlashCount--;
1375 // Just add next char to the current arg.
1376 arg += *p;
1377 break;
1381 // Check for end of input.
1382 if ( *p ) {
1383 // Go to next character.
1384 p++;
1385 } else {
1386 // If on first pass, go on to second.
1387 if ( justCounting ) {
1388 // Allocate argv array.
1389 argv = new char*[ argc ];
1391 // Start second pass
1392 justCounting = 0;
1393 init = 1;
1394 } else {
1395 // Quit.
1396 break;
1401 rv = cmdLine->Init(argc, argv, aWorkingDir, aState);
1403 // Cleanup.
1404 while ( argc ) {
1405 delete [] argv[ --argc ];
1407 delete [] argv;
1409 if (NS_FAILED(rv)) {
1410 NS_ERROR("Error initializing command line.");
1411 return;
1414 cmdLine->Run();
1417 nsresult
1418 nsNativeAppSupportWin::OpenWindow( const char*urlstr, const char *args ) {
1420 nsresult rv = NS_ERROR_FAILURE;
1422 nsCOMPtr<nsIWindowWatcher> wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID));
1423 nsCOMPtr<nsISupportsCString> sarg(do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID));
1424 if (sarg)
1425 sarg->SetData(nsDependentCString(args));
1427 if (wwatch && sarg) {
1428 nsCOMPtr<nsIDOMWindow> newWindow;
1429 rv = wwatch->OpenWindow(0, urlstr, "_blank", "chrome,dialog=no,all",
1430 sarg, getter_AddRefs(newWindow));
1431 #if MOZ_DEBUG_DDE
1432 } else {
1433 printf("Get WindowWatcher (or create string) failed\n");
1434 #endif
1436 return rv;
1439 HWND hwndForDOMWindow( nsISupports *window ) {
1440 nsCOMPtr<nsPIDOMWindow> pidomwindow( do_QueryInterface(window) );
1441 if ( !pidomwindow ) {
1442 return 0;
1445 nsCOMPtr<nsIBaseWindow> ppBaseWindow =
1446 do_QueryInterface( pidomwindow->GetDocShell() );
1447 if ( !ppBaseWindow ) {
1448 return 0;
1451 nsCOMPtr<nsIWidget> ppWidget;
1452 ppBaseWindow->GetMainWidget( getter_AddRefs( ppWidget ) );
1454 return (HWND)( ppWidget->GetNativeData( NS_NATIVE_WIDGET ) );
1457 nsresult
1458 nsNativeAppSupportWin::OpenBrowserWindow()
1460 nsresult rv = NS_OK;
1462 // Open the argument URL in the most recently used Navigator window.
1463 // If there is no Nav window, open a new one.
1465 // If at all possible, hand the request off to the most recent
1466 // browser window.
1468 nsCOMPtr<nsIDOMWindow> navWin;
1469 GetMostRecentWindow( NS_LITERAL_STRING( "navigator:browser" ).get(), getter_AddRefs( navWin ) );
1471 // This isn't really a loop. We just use "break" statements to fall
1472 // out to the OpenWindow call when things go awry.
1473 do {
1474 // If caller requires a new window, then don't use an existing one.
1475 if ( !navWin ) {
1476 // Have to open a new one.
1477 break;
1480 nsCOMPtr<nsIBrowserDOMWindow> bwin;
1481 { // scope a bunch of temporary cruft used to generate bwin
1482 nsCOMPtr<nsIWebNavigation> navNav( do_GetInterface( navWin ) );
1483 nsCOMPtr<nsIDocShellTreeItem> navItem( do_QueryInterface( navNav ) );
1484 if ( navItem ) {
1485 nsCOMPtr<nsIDocShellTreeItem> rootItem;
1486 navItem->GetRootTreeItem( getter_AddRefs( rootItem ) );
1487 nsCOMPtr<nsIDOMWindow> rootWin( do_GetInterface( rootItem ) );
1488 nsCOMPtr<nsIDOMChromeWindow> chromeWin(do_QueryInterface(rootWin));
1489 if ( chromeWin )
1490 chromeWin->GetBrowserDOMWindow( getter_AddRefs ( bwin ) );
1493 if ( bwin ) {
1494 nsCOMPtr<nsIURI> uri;
1495 NS_NewURI( getter_AddRefs( uri ), NS_LITERAL_CSTRING("about:blank"), 0, 0 );
1496 if ( uri ) {
1497 nsCOMPtr<nsIDOMWindow> container;
1498 rv = bwin->OpenURI( uri, 0,
1499 nsIBrowserDOMWindow::OPEN_DEFAULTWINDOW,
1500 nsIBrowserDOMWindow::OPEN_EXTERNAL,
1501 getter_AddRefs( container ) );
1502 if ( NS_SUCCEEDED( rv ) )
1503 return NS_OK;
1507 NS_ERROR("failed to hand off external URL to extant window");
1508 } while ( false );
1510 // open a new window if caller requested it or if anything above failed
1512 char* argv[] = { 0 };
1513 nsCOMPtr<nsICommandLineRunner> cmdLine
1514 (do_CreateInstance("@mozilla.org/toolkit/command-line;1"));
1515 NS_ENSURE_TRUE(cmdLine, NS_ERROR_FAILURE);
1517 rv = cmdLine->Init(0, argv, nullptr, nsICommandLine::STATE_REMOTE_EXPLICIT);
1518 NS_ENSURE_SUCCESS(rv, rv);
1520 return cmdLine->Run();