Bug 575855 - Fix for transitions from fullscreen briefly show an aero basic window...
[mozilla-central.git] / toolkit / xre / nsNativeAppSupportWin.cpp
blob4a85b7c13e8611bf282a9422e3b186c3f16962db
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
15 * The Original Code is Mozilla Communicator client code.
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998
20 * the Initial Developer. All Rights Reserved.
22 * Contributor(s):
23 * Bill Law law@netscape.com
24 * Robert Strong robert.bugzilla@gmail.com
26 * Alternatively, the contents of this file may be used under the terms of
27 * either the GNU General Public License Version 2 or later (the "GPL"), or
28 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the MPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the MPL, the GPL or the LGPL.
38 * ***** END LICENSE BLOCK ***** */
40 #include "nsNativeAppSupportBase.h"
41 #include "nsNativeAppSupportWin.h"
42 #include "nsAppRunner.h"
43 #include "nsXULAppAPI.h"
44 #include "nsString.h"
45 #include "nsIBrowserDOMWindow.h"
46 #include "nsICommandLineRunner.h"
47 #include "nsCOMPtr.h"
48 #include "nsXPIDLString.h"
49 #include "nsIComponentManager.h"
50 #include "nsIServiceManager.h"
51 #include "nsIDOMChromeWindow.h"
52 #include "nsXPCOM.h"
53 #include "nsISupportsPrimitives.h"
54 #include "nsISupportsArray.h"
55 #include "nsIWindowWatcher.h"
56 #include "nsPIDOMWindow.h"
57 #include "nsIDocShell.h"
58 #include "nsIDocShellTreeItem.h"
59 #include "nsIBaseWindow.h"
60 #include "nsIWidget.h"
61 #include "nsIAppShellService.h"
62 #include "nsIXULWindow.h"
63 #include "nsIInterfaceRequestor.h"
64 #include "nsIInterfaceRequestorUtils.h"
65 #include "nsIPromptService.h"
66 #include "nsNetCID.h"
67 #include "nsNetUtil.h"
68 #include "nsIObserver.h"
69 #include "nsIObserverService.h"
70 #include "nsIDOMLocation.h"
71 #include "nsIJSContextStack.h"
72 #include "nsIWebNavigation.h"
73 #include "nsIWindowMediator.h"
74 #include "nsNativeCharsetUtils.h"
75 #include "nsIAppStartup.h"
77 #include <windows.h>
78 #include <shellapi.h>
79 #ifndef WINCE
80 #include <ddeml.h>
81 #endif
82 #include <stdlib.h>
83 #include <stdio.h>
84 #include <io.h>
85 #include <direct.h>
86 #include <fcntl.h>
88 static HWND hwndForDOMWindow( nsISupports * );
90 static
91 nsresult
92 GetMostRecentWindow(const PRUnichar* aType, nsIDOMWindowInternal** aWindow) {
93 nsresult rv;
94 nsCOMPtr<nsIWindowMediator> med( do_GetService( NS_WINDOWMEDIATOR_CONTRACTID, &rv ) );
95 if ( NS_FAILED( rv ) )
96 return rv;
98 if ( med )
99 return med->GetMostRecentWindow( aType, aWindow );
101 return NS_ERROR_FAILURE;
104 static
105 void
106 activateWindow( nsIDOMWindowInternal *win ) {
107 // Try to get native window handle.
108 HWND hwnd = hwndForDOMWindow( win );
109 if ( hwnd ) {
110 #ifndef WINCE // Can't iconify windows on windows ce
111 // Restore the window if it is minimized.
112 if ( ::IsIconic( hwnd ) ) {
113 ::ShowWindow( hwnd, SW_RESTORE );
115 #endif
116 // Use the OS call, if possible.
117 ::SetForegroundWindow( hwnd );
118 } else {
119 // Use internal method.
120 win->Focus();
125 #ifdef DEBUG_law
126 #undef MOZ_DEBUG_DDE
127 #define MOZ_DEBUG_DDE 1
128 #endif
130 // Simple Win32 mutex wrapper.
131 struct Mutex {
132 Mutex( const PRUnichar *name )
133 : mName( name ),
134 mHandle( 0 ),
135 mState( -1 ) {
136 mHandle = CreateMutexW( 0, FALSE, mName.get() );
137 #if MOZ_DEBUG_DDE
138 printf( "CreateMutex error = 0x%08X\n", (int)GetLastError() );
139 #endif
141 ~Mutex() {
142 if ( mHandle ) {
143 // Make sure we release it if we own it.
144 Unlock();
146 BOOL rc = CloseHandle( mHandle );
147 #if MOZ_DEBUG_DDE
148 if ( !rc ) {
149 printf( "CloseHandle error = 0x%08X\n", (int)GetLastError() );
151 #endif
154 BOOL Lock( DWORD timeout ) {
155 if ( mHandle ) {
156 #if MOZ_DEBUG_DDE
157 printf( "Waiting (%d msec) for DDE mutex...\n", (int)timeout );
158 #endif
159 mState = WaitForSingleObject( mHandle, timeout );
160 #if MOZ_DEBUG_DDE
161 printf( "...wait complete, result = 0x%08X, GetLastError=0x%08X\n", (int)mState, (int)::GetLastError() );
162 #endif
163 return mState == WAIT_OBJECT_0 || mState == WAIT_ABANDONED;
164 } else {
165 return FALSE;
168 void Unlock() {
169 if ( mHandle && mState == WAIT_OBJECT_0 ) {
170 #if MOZ_DEBUG_DDE
171 printf( "Releasing DDE mutex\n" );
172 #endif
173 ReleaseMutex( mHandle );
174 mState = -1;
177 private:
178 nsString mName;
179 HANDLE mHandle;
180 DWORD mState;
183 /* DDE Notes
185 * This section describes the Win32 DDE service implementation for
186 * Mozilla. DDE is used on Win32 platforms to communicate between
187 * separate instances of mozilla.exe (or other Mozilla-based
188 * executables), or, between the Win32 desktop shell and Mozilla.
190 * The first instance of Mozilla will become the "server" and
191 * subsequent executables (and the shell) will use DDE to send
192 * requests to that process. The requests are DDE "execute" requests
193 * that pass the command line arguments.
195 * Mozilla registers the DDE application "Mozilla" and currently
196 * supports only the "WWW_OpenURL" topic. This should be reasonably
197 * compatible with applications that interfaced with Netscape
198 * Communicator (and its predecessors?). Note that even that topic
199 * may not be supported in a compatible fashion as the command-line
200 * options for Mozilla are different than for Communiator.
202 * It is imperative that at most one instance of Mozilla execute in
203 * "server mode" at any one time. The "native app support" in Mozilla
204 * on Win32 ensures that only the server process performs XPCOM
205 * initialization (that is not required for subsequent client processes
206 * to communicate with the server process).
208 * To guarantee that only one server starts up, a Win32 "mutex" is used
209 * to ensure only one process executes the server-detection code. That
210 * code consists of initializing DDE and doing a DdeConnect to Mozilla's
211 * application/topic. If that connection succeeds, then a server process
212 * must be running already.
214 * Otherwise, no server has started. In that case, the current process
215 * calls DdeNameService to register that application/topic. Only at that
216 * point does the mutex get released.
218 * There are a couple of subtleties that one should be aware of:
220 * 1. It is imperative that DdeInitialize be called only after the mutex
221 * lock has been obtained. The reason is that at shutdown, DDE
222 * notifications go out to all initialized DDE processes. Thus, if
223 * the mutex is owned by a terminating intance of Mozilla, then
224 * calling DdeInitialize and then WaitForSingleObject will cause the
225 * DdeUninitialize from the terminating process to "hang" until the
226 * process waiting for the mutex times out (and can then service the
227 * notification that the DDE server is terminating). So, don't mess
228 * with the sequence of things in the startup/shutdown logic.
230 * 2. All mutex requests are made with a reasonably long timeout value and
231 * are designed to "fail safe" (i.e., a timeout is treated as failure).
233 * 3. An attempt has been made to minimize the degree to which the main
234 * Mozilla application logic needs to be aware of the DDE mechanisms
235 * implemented herein. As a result, this module surfaces a very
236 * large-grained interface, consisting of simple start/stop methods.
237 * As a consequence, details of certain scenarios can be "lost."
238 * Particularly, incoming DDE requests can arrive after this module
239 * initiates the DDE server, but before Mozilla is initialized to the
240 * point where those requests can be serviced (e.g., open a browser
241 * window to a particular URL). Since the client process sends the
242 * request early on, it may not be prepared to respond to that error.
243 * Thus, such situations may fail silently. The design goal is that
244 * they fail harmlessly. Refinements on this point will be made as
245 * details emerge (and time permits).
248 /* Update 2001 March
250 * A significant DDE bug in Windows is causing Mozilla to get wedged at
251 * startup. This is detailed in Bugzill bug 53952
252 * (http://bugzilla.mozilla.org/show_bug.cgi?id=53952).
254 * To resolve this, we are using a new strategy:
255 * o Use a "message window" to detect that Mozilla is already running and
256 * to pass requests from a second instance back to the first;
257 * o Run only as a "DDE server" (not as DDE client); this avoids the
258 * problematic call to DDEConnect().
260 * We still use the mutex semaphore to protect the code that detects
261 * whether Mozilla is already running.
264 /* Update 2007 January
266 * A change in behavior was implemented in July 2004 which made the
267 * application on launch to add and on quit to remove the ddexec registry key.
268 * See bug 246078.
269 * Windows Vista has changed the methods used to set an application as default
270 * and the new methods are incompatible with removing the ddeexec registry key.
271 * See bug 353089.
273 * OS DDE Sequence:
274 * 1. OS checks if the dde name is registered.
275 * 2. If it is registered the OS sends a DDE request with the WWW_OpenURL topic
276 * and the params as specified in the default value of the ddeexec registry
277 * key for the verb (e.g. open).
278 * 3. If it isn't registered the OS launches the executable defined in the
279 * verb's (e.g. open) command registry key.
280 * 4. If the ifexec registry key is not present the OS sends a DDE request with
281 * the WWW_OpenURL topic and the params as specified in the default value of
282 * the ddeexec registry key for the verb (e.g. open).
283 * 5. If the ifexec registry key is present the OS sends a DDE request with the
284 * WWW_OpenURL topic and the params as specified in the ifexec registry key
285 * for the verb (e.g. open).
287 * Application DDE Sequence:
288 * 1. If the application is running a DDE request is received with the
289 * WWW_OpenURL topic and the params as specified in the default value of the
290 * ddeexec registry key (e.g. "%1",,0,0,,,, where '%1' is the url to open)
291 * for the verb (e.g. open).
292 * 2. If the application is not running it is launched with the -requestPending
293 * and the -url argument.
294 * 2.1 If the application does not need to restart and the -requestPending
295 * argument is present the accompanying url will not be used. Instead the
296 * application will wait for the DDE message to open the url.
297 * 2.2 If the application needs to restart the -requestPending argument is
298 * removed from the arguments used to restart the application and the url
299 * will be handled normally.
301 * Note: Due to a bug in IE the ifexec key should not be used (see bug 355650).
304 class nsNativeAppSupportWin : public nsNativeAppSupportBase,
305 public nsIObserver
307 public:
308 NS_DECL_NSIOBSERVER
309 NS_DECL_ISUPPORTS_INHERITED
311 // Overrides of base implementation.
312 NS_IMETHOD Start( PRBool *aResult );
313 NS_IMETHOD Stop( PRBool *aResult );
314 NS_IMETHOD Quit();
315 NS_IMETHOD Enable();
316 #ifndef WINCE
317 // The "old" Start method (renamed).
318 NS_IMETHOD StartDDE();
319 #endif
320 // Utility function to handle a Win32-specific command line
321 // option: "-console", which dynamically creates a Windows
322 // console.
323 void CheckConsole();
325 private:
326 static void HandleCommandLine(const char* aCmdLineString, nsIFile* aWorkingDir, PRUint32 aState);
327 #ifndef WINCE
328 static HDDEDATA CALLBACK HandleDDENotification( UINT uType,
329 UINT uFmt,
330 HCONV hconv,
331 HSZ hsz1,
332 HSZ hsz2,
333 HDDEDATA hdata,
334 ULONG_PTR dwData1,
335 ULONG_PTR dwData2 );
336 static void ParseDDEArg( HSZ args, int index, nsString& string);
337 static void ParseDDEArg( const WCHAR* args, int index, nsString& aString);
338 static HDDEDATA CreateDDEData( DWORD value );
339 static HDDEDATA CreateDDEData( LPBYTE value, DWORD len );
340 static PRBool InitTopicStrings();
341 static int FindTopic( HSZ topic );
342 #endif
343 static void ActivateLastWindow();
344 static nsresult OpenWindow( const char *urlstr, const char *args );
345 static nsresult OpenBrowserWindow();
346 static void SetupSysTrayIcon();
347 static void RemoveSysTrayIcon();
349 static int mConversations;
350 enum {
351 topicOpenURL,
352 topicActivate,
353 topicCancelProgress,
354 topicVersion,
355 topicRegisterViewer,
356 topicUnRegisterViewer,
357 topicGetWindowInfo,
358 // Note: Insert new values above this line!!!!!
359 topicCount // Count of the number of real topics
361 #ifndef WINCE
362 static HSZ mApplication, mTopics[ topicCount ];
363 #endif
364 static DWORD mInstance;
365 static PRBool mCanHandleRequests;
366 static PRUnichar mMutexName[];
367 friend struct MessageWindow;
368 }; // nsNativeAppSupportWin
370 NS_INTERFACE_MAP_BEGIN(nsNativeAppSupportWin)
371 NS_INTERFACE_MAP_ENTRY(nsIObserver)
372 NS_INTERFACE_MAP_END_INHERITING(nsNativeAppSupportBase)
374 NS_IMPL_ADDREF_INHERITED(nsNativeAppSupportWin, nsNativeAppSupportBase)
375 NS_IMPL_RELEASE_INHERITED(nsNativeAppSupportWin, nsNativeAppSupportBase)
377 void
378 nsNativeAppSupportWin::CheckConsole() {
379 for ( int i = 1; i < gArgc; i++ ) {
380 if ( strcmp( "-console", gArgv[i] ) == 0
382 strcmp( "/console", gArgv[i] ) == 0 ) {
383 // Users wants to make sure we have a console.
384 // Try to allocate one.
385 #ifndef WINCE
386 BOOL rc = ::AllocConsole();
387 if ( rc ) {
388 // Console allocated. Fix it up so that output works in
389 // all cases. See http://support.microsoft.com/support/kb/articles/q105/3/05.asp.
391 // stdout
392 int hCrt = ::_open_osfhandle( (intptr_t)GetStdHandle( STD_OUTPUT_HANDLE ),
393 _O_TEXT );
394 if ( hCrt != -1 ) {
395 FILE *hf = ::_fdopen( hCrt, "w" );
396 if ( hf ) {
397 *stdout = *hf;
398 #ifdef DEBUG
399 ::fprintf( stdout, "stdout directed to dynamic console\n" );
400 #endif
404 // stderr
405 hCrt = ::_open_osfhandle( (intptr_t)::GetStdHandle( STD_ERROR_HANDLE ),
406 _O_TEXT );
407 if ( hCrt != -1 ) {
408 FILE *hf = ::_fdopen( hCrt, "w" );
409 if ( hf ) {
410 *stderr = *hf;
411 #ifdef DEBUG
412 ::fprintf( stderr, "stderr directed to dynamic console\n" );
413 #endif
417 // stdin?
418 /* Don't bother for now.
419 hCrt = ::_open_osfhandle( (long)::GetStdHandle( STD_INPUT_HANDLE ),
420 _O_TEXT );
421 if ( hCrt != -1 ) {
422 FILE *hf = ::_fdopen( hCrt, "r" );
423 if ( hf ) {
424 *stdin = *hf;
428 } else {
429 // Failed. Probably because there already is one.
430 // There's little we can do, in any case.
432 #endif
433 // Remove the console argument from the command line.
434 do {
435 gArgv[i] = gArgv[i + 1];
436 ++i;
437 } while (gArgv[i]);
439 --gArgc;
441 // Don't bother doing this more than once.
442 break;
446 return;
450 // Create and return an instance of class nsNativeAppSupportWin.
451 nsresult
452 NS_CreateNativeAppSupport( nsINativeAppSupport **aResult ) {
453 nsNativeAppSupportWin *pNative = new nsNativeAppSupportWin;
454 if (!pNative) return NS_ERROR_OUT_OF_MEMORY;
456 // Check for dynamic console creation request.
457 pNative->CheckConsole();
459 *aResult = pNative;
460 NS_ADDREF( *aResult );
462 return NS_OK;
465 // Constants
466 #define MOZ_DDE_APPLICATION "Mozilla"
467 #define MOZ_MUTEX_NAMESPACE L"Local\\"
468 #define MOZ_STARTUP_MUTEX_NAME L"StartupMutex"
469 #define MOZ_DDE_START_TIMEOUT 30000
470 #define MOZ_DDE_STOP_TIMEOUT 15000
471 #define MOZ_DDE_EXEC_TIMEOUT 15000
473 // The array entries must match the enum ordering!
474 const char * const topicNames[] = { "WWW_OpenURL",
475 "WWW_Activate",
476 "WWW_CancelProgress",
477 "WWW_Version",
478 "WWW_RegisterViewer",
479 "WWW_UnRegisterViewer",
480 "WWW_GetWindowInfo" };
482 // Static member definitions.
483 int nsNativeAppSupportWin::mConversations = 0;
484 #ifndef WINCE
485 HSZ nsNativeAppSupportWin::mApplication = 0;
486 HSZ nsNativeAppSupportWin::mTopics[nsNativeAppSupportWin::topicCount] = { 0 };
487 #endif
488 DWORD nsNativeAppSupportWin::mInstance = 0;
489 PRBool nsNativeAppSupportWin::mCanHandleRequests = PR_FALSE;
491 PRUnichar nsNativeAppSupportWin::mMutexName[ 128 ] = { 0 };
494 // Message window encapsulation.
495 struct MessageWindow {
496 // ctor/dtor are simplistic
497 MessageWindow() {
498 // Try to find window.
499 mHandle = ::FindWindowW( className(), 0 );
502 // Act like an HWND.
503 operator HWND() {
504 return mHandle;
507 // Class name: appName + "MessageWindow"
508 static const PRUnichar *className() {
509 static PRUnichar classNameBuffer[128];
510 static PRUnichar *mClassName = 0;
511 if ( !mClassName ) {
512 ::_snwprintf(classNameBuffer,
513 128, // size of classNameBuffer in PRUnichars
514 L"%s%s",
515 NS_ConvertUTF8toUTF16(gAppData->name).get(),
516 L"MessageWindow" );
517 mClassName = classNameBuffer;
519 return mClassName;
522 // Create: Register class and create window.
523 NS_IMETHOD Create() {
524 WNDCLASSW classStruct = { 0, // style
525 &MessageWindow::WindowProc, // lpfnWndProc
526 0, // cbClsExtra
527 0, // cbWndExtra
528 0, // hInstance
529 0, // hIcon
530 0, // hCursor
531 0, // hbrBackground
532 0, // lpszMenuName
533 className() }; // lpszClassName
535 // Register the window class.
536 NS_ENSURE_TRUE( ::RegisterClassW( &classStruct ), NS_ERROR_FAILURE );
538 // Create the window.
539 NS_ENSURE_TRUE( ( mHandle = ::CreateWindowW(className(),
540 0, // title
541 WS_CAPTION, // style
542 0,0,0,0, // x, y, cx, cy
543 0, // parent
544 0, // menu
545 0, // instance
546 0 ) ), // create struct
547 NS_ERROR_FAILURE );
549 #if MOZ_DEBUG_DDE
550 printf( "Message window = 0x%08X\n", (int)mHandle );
551 #endif
553 return NS_OK;
556 // Destory: Get rid of window and reset mHandle.
557 NS_IMETHOD Destroy() {
558 nsresult retval = NS_OK;
560 if ( mHandle ) {
561 // DestroyWindow can only destroy windows created from
562 // the same thread.
563 BOOL desRes = DestroyWindow( mHandle );
564 if ( FALSE != desRes ) {
565 mHandle = NULL;
567 else {
568 retval = NS_ERROR_FAILURE;
572 return retval;
575 // SendRequest: Pass the command line via WM_COPYDATA to message window.
576 NS_IMETHOD SendRequest() {
577 WCHAR *cmd = ::GetCommandLineW();
578 WCHAR cwd[MAX_PATH];
579 _wgetcwd(cwd, MAX_PATH);
581 // Construct a narrow UTF8 buffer <commandline>\0<workingdir>\0
582 #ifdef WINCE
583 // For WinCE, we're stuck with providing our own argv[0] for the remote
584 // command-line.
585 NS_ConvertUTF16toUTF8 utf8buffer(L"dummy ");
586 AppendUTF16toUTF8(cmd, utf8buffer);
587 #else
588 NS_ConvertUTF16toUTF8 utf8buffer(cmd);
589 #endif
590 utf8buffer.Append('\0');
591 AppendUTF16toUTF8(cwd, utf8buffer);
592 utf8buffer.Append('\0');
594 // We used to set dwData to zero, when we didn't send the working dir.
595 // Now we're using it as a version number.
596 COPYDATASTRUCT cds = {
598 utf8buffer.Length(),
599 (void*) utf8buffer.get()
601 // Bring the already running Mozilla process to the foreground.
602 // nsWindow will restore the window (if minimized) and raise it.
603 #ifdef WINCE
604 // for activating the existing window on wince we need "| 0x01"
605 // see http://msdn.microsoft.com/en-us/library/ms940024.aspx for details
606 ::SetForegroundWindow( (HWND)(((ULONG) mHandle) | 0x01) );
607 #else
608 ::SetForegroundWindow( mHandle );
609 #endif
610 ::SendMessage( mHandle, WM_COPYDATA, 0, (LPARAM)&cds );
611 return NS_OK;
614 // Window proc.
615 static LRESULT CALLBACK WindowProc( HWND msgWindow, UINT msg, WPARAM wp, LPARAM lp ) {
616 if ( msg == WM_COPYDATA ) {
617 if (!nsNativeAppSupportWin::mCanHandleRequests)
618 return FALSE;
620 // This is an incoming request.
621 COPYDATASTRUCT *cds = (COPYDATASTRUCT*)lp;
622 #if MOZ_DEBUG_DDE
623 printf( "Incoming request: %s\n", (const char*)cds->lpData );
624 #endif
625 nsCOMPtr<nsILocalFile> workingDir;
627 if (1 >= cds->dwData) {
628 char* wdpath = (char*) cds->lpData;
629 // skip the command line, and get the working dir of the
630 // other process, which is after the first null char
631 while (*wdpath)
632 ++wdpath;
634 ++wdpath;
636 #ifdef MOZ_DEBUG_DDE
637 printf( "Working dir: %s\n", wdpath);
638 #endif
640 NS_NewLocalFile(NS_ConvertUTF8toUTF16(wdpath),
641 PR_FALSE,
642 getter_AddRefs(workingDir));
644 (void)nsNativeAppSupportWin::HandleCommandLine((char*)cds->lpData, workingDir, nsICommandLine::STATE_REMOTE_AUTO);
646 // Get current window and return its window handle.
647 nsCOMPtr<nsIDOMWindowInternal> win;
648 GetMostRecentWindow( 0, getter_AddRefs( win ) );
649 return win ? (LRESULT)hwndForDOMWindow( win ) : 0;
651 return DefWindowProc( msgWindow, msg, wp, lp );
654 private:
655 HWND mHandle;
656 }; // struct MessageWindow
658 /* Start: Tries to find the "message window" to determine if it
659 * exists. If so, then Mozilla is already running. In that
660 * case, we use the handle to the "message" window and send
661 * a request corresponding to this process's command line
662 * options.
664 * If not, then this is the first instance of Mozilla. In
665 * that case, we create and set up the message window.
667 * The checking for existence of the message window must
668 * be protected by use of a mutex semaphore.
670 NS_IMETHODIMP
671 nsNativeAppSupportWin::Start( PRBool *aResult ) {
672 NS_ENSURE_ARG( aResult );
673 NS_ENSURE_TRUE( mInstance == 0, NS_ERROR_NOT_INITIALIZED );
674 NS_ENSURE_STATE( gAppData );
676 if (getenv("MOZ_NO_REMOTE"))
678 *aResult = PR_TRUE;
679 return NS_OK;
682 nsresult rv = NS_ERROR_FAILURE;
683 *aResult = PR_FALSE;
685 // Grab mutex first.
687 // Build mutex name from app name.
688 ::_snwprintf(mMutexName, sizeof mMutexName / sizeof(PRUnichar), L"%s%s%s",
689 MOZ_MUTEX_NAMESPACE,
690 NS_ConvertUTF8toUTF16(gAppData->name).get(),
691 MOZ_STARTUP_MUTEX_NAME );
692 Mutex startupLock = Mutex( mMutexName );
694 NS_ENSURE_TRUE( startupLock.Lock( MOZ_DDE_START_TIMEOUT ), NS_ERROR_FAILURE );
696 // Search for existing message window.
697 MessageWindow msgWindow;
698 if ( (HWND)msgWindow ) {
699 // We are a client process. Pass request to message window.
700 rv = msgWindow.SendRequest();
701 } else {
702 // We will be server.
703 rv = msgWindow.Create();
704 if ( NS_SUCCEEDED( rv ) ) {
705 #ifndef WINCE
706 // Start up DDE server.
707 this->StartDDE();
708 #endif
709 // Tell caller to spin message loop.
710 *aResult = PR_TRUE;
714 startupLock.Unlock();
716 return rv;
718 #ifndef WINCE
719 PRBool
720 nsNativeAppSupportWin::InitTopicStrings() {
721 for ( int i = 0; i < topicCount; i++ ) {
722 if ( !( mTopics[ i ] = DdeCreateStringHandleA( mInstance, const_cast<char *>(topicNames[ i ]), CP_WINANSI ) ) ) {
723 return PR_FALSE;
726 return PR_TRUE;
730 nsNativeAppSupportWin::FindTopic( HSZ topic ) {
731 for ( int i = 0; i < topicCount; i++ ) {
732 if ( DdeCmpStringHandles( topic, mTopics[i] ) == 0 ) {
733 return i;
736 return -1;
740 // Start DDE server.
742 // This used to be the Start() method when we were using DDE as the
743 // primary IPC mechanism between secondary Mozilla processes and the
744 // initial "server" process.
746 // Now, it simply initializes the DDE server. The caller must check
747 // that this process is to be the server, and, must acquire the DDE
748 // startup mutex semaphore prior to calling this routine. See ::Start(),
749 // above.
750 NS_IMETHODIMP
751 nsNativeAppSupportWin::StartDDE() {
752 NS_ENSURE_TRUE( mInstance == 0, NS_ERROR_NOT_INITIALIZED );
754 // Initialize DDE.
755 NS_ENSURE_TRUE( DMLERR_NO_ERROR == DdeInitialize( &mInstance,
756 nsNativeAppSupportWin::HandleDDENotification,
757 APPCLASS_STANDARD,
758 0 ),
759 NS_ERROR_FAILURE );
761 // Allocate DDE strings.
762 NS_ENSURE_TRUE( ( mApplication = DdeCreateStringHandleA( mInstance, (char*) gAppData->name, CP_WINANSI ) ) && InitTopicStrings(),
763 NS_ERROR_FAILURE );
765 // Next step is to register a DDE service.
766 NS_ENSURE_TRUE( DdeNameService( mInstance, mApplication, 0, DNS_REGISTER ), NS_ERROR_FAILURE );
768 #if MOZ_DEBUG_DDE
769 printf( "DDE server started\n" );
770 #endif
772 return NS_OK;
774 #endif /* WINCE */
775 // If no DDE conversations are pending, terminate DDE.
776 NS_IMETHODIMP
777 nsNativeAppSupportWin::Stop( PRBool *aResult ) {
778 NS_ENSURE_ARG( aResult );
779 NS_ENSURE_TRUE( mInstance, NS_ERROR_NOT_INITIALIZED );
781 nsresult rv = NS_OK;
782 *aResult = PR_TRUE;
784 Mutex ddeLock( mMutexName );
786 if ( ddeLock.Lock( MOZ_DDE_STOP_TIMEOUT ) ) {
787 if ( mConversations == 0 ) {
788 this->Quit();
789 } else {
790 *aResult = PR_FALSE;
793 ddeLock.Unlock();
795 else {
796 // No DDE application name specified, but that's OK. Just
797 // forge ahead.
798 *aResult = PR_TRUE;
801 return rv;
804 NS_IMETHODIMP
805 nsNativeAppSupportWin::Observe(nsISupports* aSubject, const char* aTopic,
806 const PRUnichar* aData)
808 if (strcmp(aTopic, "quit-application") == 0) {
809 Quit();
810 } else {
811 NS_ERROR("Unexpected observer topic.");
814 return NS_OK;
817 // Terminate DDE regardless.
818 NS_IMETHODIMP
819 nsNativeAppSupportWin::Quit() {
820 // If another process wants to look for the message window, they need
821 // to wait to hold the lock, in which case they will not find the
822 // window as we will destroy ours under our lock.
823 // When the mutex goes off the stack, it is unlocked via destructor.
824 Mutex mutexLock(mMutexName);
825 NS_ENSURE_TRUE(mutexLock.Lock(MOZ_DDE_START_TIMEOUT), NS_ERROR_FAILURE);
827 // If we've got a message window to receive IPC or new window requests,
828 // get rid of it as we are shutting down.
829 // Note: Destroy calls DestroyWindow, which will only work on a window
830 // created by the same thread.
831 MessageWindow mw;
832 mw.Destroy();
834 if ( mInstance ) {
835 // Unregister application name.
836 #ifndef WINCE
837 DdeNameService( mInstance, mApplication, 0, DNS_UNREGISTER );
838 // Clean up strings.
839 if ( mApplication ) {
840 DdeFreeStringHandle( mInstance, mApplication );
841 mApplication = 0;
843 for ( int i = 0; i < topicCount; i++ ) {
844 if ( mTopics[i] ) {
845 DdeFreeStringHandle( mInstance, mTopics[i] );
846 mTopics[i] = 0;
849 DdeUninitialize( mInstance );
850 #endif
851 mInstance = 0;
852 #if MOZ_DEBUG_DDE
853 printf( "DDE server stopped\n" );
854 #endif
857 return NS_OK;
860 NS_IMETHODIMP
861 nsNativeAppSupportWin::Enable()
863 mCanHandleRequests = PR_TRUE;
865 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
866 if (obs) {
867 obs->AddObserver(this, "quit-application", PR_FALSE);
868 } else {
869 NS_ERROR("No observer service?");
872 return NS_OK;
875 #if MOZ_DEBUG_DDE
876 // Macro to generate case statement for a given XTYP value.
877 #define XTYP_CASE(t) \
878 case t: result = #t; break
880 static nsCString uTypeDesc( UINT uType ) {
881 nsCString result;
882 switch ( uType ) {
883 XTYP_CASE(XTYP_ADVSTART);
884 XTYP_CASE(XTYP_CONNECT);
885 XTYP_CASE(XTYP_ADVREQ);
886 XTYP_CASE(XTYP_REQUEST);
887 XTYP_CASE(XTYP_WILDCONNECT);
888 XTYP_CASE(XTYP_ADVDATA);
889 XTYP_CASE(XTYP_EXECUTE);
890 XTYP_CASE(XTYP_POKE);
891 XTYP_CASE(XTYP_ADVSTOP);
892 XTYP_CASE(XTYP_CONNECT_CONFIRM);
893 XTYP_CASE(XTYP_DISCONNECT);
894 XTYP_CASE(XTYP_ERROR);
895 XTYP_CASE(XTYP_MONITOR);
896 XTYP_CASE(XTYP_REGISTER);
897 XTYP_CASE(XTYP_XACT_COMPLETE);
898 XTYP_CASE(XTYP_UNREGISTER);
899 default: result = "XTYP_?????";
901 return result;
904 static nsCString hszValue( DWORD instance, HSZ hsz ) {
905 // Extract string from HSZ.
906 nsCString result("[");
907 DWORD len = DdeQueryString( instance, hsz, NULL, NULL, CP_WINANSI );
908 if ( len ) {
909 char buffer[ 256 ];
910 DdeQueryString( instance, hsz, buffer, sizeof buffer, CP_WINANSI );
911 result += buffer;
913 result += "]";
914 return result;
916 #elif !defined(WINCE)
917 // These are purely a safety measure to avoid the infamous "won't
918 // build non-debug" type Tinderbox flames.
919 static nsCString uTypeDesc( UINT ) {
920 return nsCString( "?" );
922 static nsCString hszValue( DWORD, HSZ ) {
923 return nsCString( "?" );
925 #endif
928 // Utility function to escape double-quotes within a string.
929 static void escapeQuotes( nsAString &aString ) {
930 PRInt32 offset = -1;
931 while( 1 ) {
932 // Find next '"'.
933 offset = aString.FindChar( '"', ++offset );
934 if ( offset == kNotFound ) {
935 // No more quotes, exit.
936 break;
937 } else {
938 // Insert back-slash ahead of the '"'.
939 aString.Insert( PRUnichar('\\'), offset );
940 // Increment offset because we just inserted a slash
941 offset++;
944 return;
947 #ifndef WINCE
948 HDDEDATA CALLBACK
949 nsNativeAppSupportWin::HandleDDENotification( UINT uType, // transaction type
950 UINT uFmt, // clipboard data format
951 HCONV hconv, // handle to the conversation
952 HSZ hsz1, // handle to a string
953 HSZ hsz2, // handle to a string
954 HDDEDATA hdata, // handle to a global memory object
955 ULONG_PTR dwData1, // transaction-specific data
956 ULONG_PTR dwData2 ) { // transaction-specific data
958 if (!mCanHandleRequests)
959 return 0;
962 #if MOZ_DEBUG_DDE
963 printf( "DDE: uType =%s\n", uTypeDesc( uType ).get() );
964 printf( " uFmt =%u\n", (unsigned)uFmt );
965 printf( " hconv =%08x\n", (int)hconv );
966 printf( " hsz1 =%08x:%s\n", (int)hsz1, hszValue( mInstance, hsz1 ).get() );
967 printf( " hsz2 =%08x:%s\n", (int)hsz2, hszValue( mInstance, hsz2 ).get() );
968 printf( " hdata =%08x\n", (int)hdata );
969 printf( " dwData1=%08x\n", (int)dwData1 );
970 printf( " dwData2=%08x\n", (int)dwData2 );
971 #endif
973 HDDEDATA result = 0;
974 if ( uType & XCLASS_BOOL ) {
975 switch ( uType ) {
976 case XTYP_CONNECT:
977 // Make sure its for our service/topic.
978 if ( FindTopic( hsz1 ) != -1 ) {
979 // We support this connection.
980 result = (HDDEDATA)1;
982 break;
983 case XTYP_CONNECT_CONFIRM:
984 // We don't care about the conversation handle, at this point.
985 result = (HDDEDATA)1;
986 break;
988 } else if ( uType & XCLASS_DATA ) {
989 if ( uType == XTYP_REQUEST ) {
990 switch ( FindTopic( hsz1 ) ) {
991 case topicOpenURL: {
992 // Open a given URL...
994 // Get the URL from the first argument in the command.
995 nsAutoString url;
996 ParseDDEArg(hsz2, 0, url);
998 // Read the 3rd argument in the command to determine if a
999 // new window is to be used.
1000 nsAutoString windowID;
1001 ParseDDEArg(hsz2, 2, windowID);
1002 // "" means to open the URL in a new window.
1003 if ( windowID.IsEmpty() ) {
1004 url.Insert(NS_LITERAL_STRING("mozilla -new-window "), 0);
1006 else {
1007 url.Insert(NS_LITERAL_STRING("mozilla -url "), 0);
1010 #if MOZ_DEBUG_DDE
1011 printf( "Handling dde XTYP_REQUEST request: [%s]...\n", NS_ConvertUTF16toUTF8(url).get() );
1012 #endif
1013 // Now handle it.
1014 HandleCommandLine(NS_ConvertUTF16toUTF8(url).get(), nsnull, nsICommandLine::STATE_REMOTE_EXPLICIT);
1016 // Return pseudo window ID.
1017 result = CreateDDEData( 1 );
1018 break;
1020 case topicGetWindowInfo: {
1021 // This topic has to get the current URL, get the current
1022 // page title and then format the output into the DDE
1023 // return string. The return value is "URL","Page Title",
1024 // "Window ID" however the window ID is not used for this
1025 // command, therefore it is returned as a null string
1027 // This isn't really a loop. We just use "break"
1028 // statements to bypass the remaining steps when
1029 // something goes wrong.
1030 do {
1031 // Get most recently used Nav window.
1032 nsCOMPtr<nsIDOMWindowInternal> navWin;
1033 GetMostRecentWindow( NS_LITERAL_STRING( "navigator:browser" ).get(),
1034 getter_AddRefs( navWin ) );
1035 if ( !navWin ) {
1036 // There is not a window open
1037 break;
1039 // Get content window.
1040 nsCOMPtr<nsIDOMWindow> content;
1041 navWin->GetContent( getter_AddRefs( content ) );
1042 if ( !content ) {
1043 break;
1045 // Convert that to internal interface.
1046 nsCOMPtr<nsPIDOMWindow> internalContent( do_QueryInterface( content ) );
1047 if ( !internalContent ) {
1048 break;
1050 // Get location.
1051 nsCOMPtr<nsIDOMLocation> location;
1052 internalContent->GetLocation( getter_AddRefs( location ) );
1053 if ( !location ) {
1054 break;
1056 // Get href for URL.
1057 nsAutoString url;
1058 if ( NS_FAILED( location->GetHref( url ) ) ) {
1059 break;
1061 // Escape any double-quotes.
1062 escapeQuotes( url );
1064 // Now for the title...
1066 // Get the base window from the doc shell...
1067 nsCOMPtr<nsIBaseWindow> baseWindow =
1068 do_QueryInterface( internalContent->GetDocShell() );
1069 if ( !baseWindow ) {
1070 break;
1072 // And from the base window we can get the title.
1073 nsXPIDLString title;
1074 if(!baseWindow) {
1075 break;
1077 baseWindow->GetTitle(getter_Copies(title));
1078 // Escape any double-quotes in the title.
1079 escapeQuotes( title );
1081 // Use a string buffer for the output data, first
1082 // save a quote.
1083 nsCAutoString outpt( NS_LITERAL_CSTRING("\"") );
1084 // Now copy the URL converting the Unicode string
1085 // to a single-byte ASCII string
1086 nsCAutoString tmpNativeStr;
1087 NS_CopyUnicodeToNative( url, tmpNativeStr );
1088 outpt.Append( tmpNativeStr );
1089 // Add the "," used to separate the URL and the page
1090 // title
1091 outpt.Append( NS_LITERAL_CSTRING("\",\"") );
1092 // Now copy the current page title to the return string
1093 NS_CopyUnicodeToNative( title, tmpNativeStr );
1094 outpt.Append( tmpNativeStr );
1095 // Fill out the return string with the remainin ",""
1096 outpt.Append( NS_LITERAL_CSTRING( "\",\"\"" ));
1098 // Create a DDE handle to a char string for the data
1099 // being returned, this copies and creates a "shared"
1100 // copy of the DDE response until the calling APP
1101 // reads it and says it can be freed.
1102 result = CreateDDEData( (LPBYTE)(const char*)outpt.get(),
1103 outpt.Length() + 1 );
1104 #if MOZ_DEBUG_DDE
1105 printf( "WWW_GetWindowInfo->%s\n", outpt.get() );
1106 #endif
1107 } while ( PR_FALSE );
1108 break;
1110 case topicActivate: {
1111 // Activate a Nav window...
1112 nsAutoString windowID;
1113 ParseDDEArg(hsz2, 0, windowID);
1114 // 4294967295 is decimal for 0xFFFFFFFF which is also a
1115 // correct value to do that Activate last window stuff
1116 if ( windowID.EqualsLiteral( "-1" ) ||
1117 windowID.EqualsLiteral( "4294967295" ) ) {
1118 // We only support activating the most recent window (or a new one).
1119 ActivateLastWindow();
1120 // Return pseudo window ID.
1121 result = CreateDDEData( 1 );
1123 break;
1125 case topicVersion: {
1126 // Return version. We're restarting at 1.0!
1127 DWORD version = 1 << 16; // "1.0"
1128 result = CreateDDEData( version );
1129 break;
1131 case topicRegisterViewer: {
1132 // Register new viewer (not implemented).
1133 result = CreateDDEData( PR_FALSE );
1134 break;
1136 case topicUnRegisterViewer: {
1137 // Unregister new viewer (not implemented).
1138 result = CreateDDEData( PR_FALSE );
1139 break;
1141 default:
1142 break;
1144 } else if ( uType & XTYP_POKE ) {
1145 switch ( FindTopic( hsz1 ) ) {
1146 case topicCancelProgress: {
1147 // "Handle" progress cancel (actually, pretty much ignored).
1148 result = (HDDEDATA)DDE_FACK;
1149 break;
1151 default:
1152 break;
1155 } else if ( uType & XCLASS_FLAGS ) {
1156 if ( uType == XTYP_EXECUTE ) {
1157 // Prove that we received the request.
1158 DWORD bytes;
1159 LPBYTE request = DdeAccessData( hdata, &bytes );
1160 #if MOZ_DEBUG_DDE
1161 printf( "Handling dde request: [%s]...\n", (char*)request );
1162 #endif
1164 nsAutoString url;
1165 ParseDDEArg((const WCHAR*) request, 0, url);
1167 // Read the 3rd argument in the command to determine if a
1168 // new window is to be used.
1169 nsAutoString windowID;
1170 ParseDDEArg((const WCHAR*) request, 2, windowID);
1172 // "" means to open the URL in a new window.
1173 if ( windowID.IsEmpty() ) {
1174 url.Insert(NS_LITERAL_STRING("mozilla -new-window "), 0);
1176 else {
1177 url.Insert(NS_LITERAL_STRING("mozilla -url "), 0);
1179 #if MOZ_DEBUG_DDE
1180 printf( "Handling dde XTYP_REQUEST request: [%s]...\n", NS_ConvertUTF16toUTF8(url).get() );
1181 #endif
1182 // Now handle it.
1183 HandleCommandLine(NS_ConvertUTF16toUTF8(url).get(), nsnull, nsICommandLine::STATE_REMOTE_EXPLICIT);
1185 // Release the data.
1186 DdeUnaccessData( hdata );
1187 result = (HDDEDATA)DDE_FACK;
1188 } else {
1189 result = (HDDEDATA)DDE_FNOTPROCESSED;
1191 } else if ( uType & XCLASS_NOTIFICATION ) {
1193 #if MOZ_DEBUG_DDE
1194 printf( "DDE result=%d (0x%08X)\n", (int)result, (int)result );
1195 #endif
1196 return result;
1198 #endif /* WINCE */
1199 // Utility function to advance to end of quoted string.
1200 // p+offset must point to the comma preceding the arg on entry.
1201 // On return, p+result points to the closing '"' (or end of the string
1202 // if the closing '"' is missing) if the arg is quoted. If the arg
1203 // is not quoted, then p+result will point to the first character
1204 // of the arg.
1205 static PRInt32 advanceToEndOfQuotedArg( const WCHAR *p, PRInt32 offset, PRInt32 len ) {
1206 // Check whether the current arg is quoted.
1207 if ( p[++offset] == '"' ) {
1208 // Advance past the closing quote.
1209 while ( offset < len && p[++offset] != '"' ) {
1210 // If the current character is a backslash, then the
1211 // next character can't be a *real* '"', so skip it.
1212 if ( p[offset] == '\\' ) {
1213 offset++;
1217 return offset;
1220 #ifndef WINCE
1221 void nsNativeAppSupportWin::ParseDDEArg( const WCHAR* args, int index, nsString& aString) {
1222 if ( args ) {
1223 nsDependentString temp(args);
1225 // offset points to the comma preceding the desired arg.
1226 PRInt32 offset = -1;
1227 // Skip commas till we get to the arg we want.
1228 while( index-- ) {
1229 // If this arg is quoted, then go to closing quote.
1230 offset = advanceToEndOfQuotedArg( args, offset, temp.Length());
1231 // Find next comma.
1232 offset = temp.FindChar( ',', offset );
1233 if ( offset == kNotFound ) {
1234 // No more commas, give up.
1235 aString = args;
1236 return;
1239 // The desired argument starts just past the preceding comma,
1240 // which offset points to, and extends until the following
1241 // comma (or the end of the string).
1243 // Since the argument might be enclosed in quotes, we need to
1244 // deal with that before searching for the terminating comma.
1245 // We advance offset so it ends up pointing to the start of
1246 // the argument we want.
1247 PRInt32 end = advanceToEndOfQuotedArg( args, offset++, temp.Length() );
1248 // Find next comma (or end of string).
1249 end = temp.FindChar( ',', end );
1250 if ( end == kNotFound ) {
1251 // Arg is the rest of the string.
1252 end = temp.Length();
1254 // Extract result.
1255 aString.Assign( args + offset, end - offset );
1257 return;
1260 // Utility to parse out argument from a DDE item string.
1261 void nsNativeAppSupportWin::ParseDDEArg( HSZ args, int index, nsString& aString) {
1262 DWORD argLen = DdeQueryStringW( mInstance, args, NULL, 0, CP_WINUNICODE );
1263 // there wasn't any string, so return empty string
1264 if ( !argLen ) return;
1265 nsAutoString temp;
1266 // Ensure result's buffer is sufficiently big.
1267 temp.SetLength( argLen );
1268 // Now get the string contents.
1269 DdeQueryString( mInstance, args, temp.BeginWriting(), temp.Length(), CP_WINUNICODE );
1270 // Parse out the given arg.
1271 ParseDDEArg(temp.get(), index, aString);
1272 return;
1275 HDDEDATA nsNativeAppSupportWin::CreateDDEData( DWORD value ) {
1276 return CreateDDEData( (LPBYTE)&value, sizeof value );
1279 HDDEDATA nsNativeAppSupportWin::CreateDDEData( LPBYTE value, DWORD len ) {
1280 HDDEDATA result = DdeCreateDataHandle( mInstance,
1281 value,
1282 len,
1284 mApplication,
1285 CF_TEXT,
1286 0 );
1287 return result;
1289 #endif /* WINCE */
1291 void nsNativeAppSupportWin::ActivateLastWindow() {
1292 nsCOMPtr<nsIDOMWindowInternal> navWin;
1293 GetMostRecentWindow( NS_LITERAL_STRING("navigator:browser").get(), getter_AddRefs( navWin ) );
1294 if ( navWin ) {
1295 // Activate that window.
1296 activateWindow( navWin );
1297 } else {
1298 // Need to create a Navigator window, then.
1299 OpenBrowserWindow();
1303 void
1304 nsNativeAppSupportWin::HandleCommandLine(const char* aCmdLineString,
1305 nsIFile* aWorkingDir,
1306 PRUint32 aState)
1308 nsresult rv;
1310 int justCounting = 1;
1311 char **argv = 0;
1312 // Flags, etc.
1313 int init = 1;
1314 int between, quoted, bSlashCount;
1315 int argc;
1316 const char *p;
1317 nsCAutoString arg;
1319 nsCOMPtr<nsICommandLineRunner> cmdLine
1320 (do_CreateInstance("@mozilla.org/toolkit/command-line;1"));
1321 if (!cmdLine) {
1322 NS_ERROR("Couldn't create command line!");
1323 return;
1326 // Parse command line args according to MS spec
1327 // (see "Parsing C++ Command-Line Arguments" at
1328 // http://msdn.microsoft.com/library/devprods/vs6/visualc/vclang/_pluslang_parsing_c.2b2b_.command.2d.line_arguments.htm).
1329 // We loop if we've not finished the second pass through.
1330 while ( 1 ) {
1331 // Initialize if required.
1332 if ( init ) {
1333 p = aCmdLineString;
1334 between = 1;
1335 argc = quoted = bSlashCount = 0;
1337 init = 0;
1339 if ( between ) {
1340 // We are traversing whitespace between args.
1341 // Check for start of next arg.
1342 if ( *p != 0 && !isspace( *p ) ) {
1343 // Start of another arg.
1344 between = 0;
1345 arg = "";
1346 switch ( *p ) {
1347 case '\\':
1348 // Count the backslash.
1349 bSlashCount = 1;
1350 break;
1351 case '"':
1352 // Remember we're inside quotes.
1353 quoted = 1;
1354 break;
1355 default:
1356 // Add character to arg.
1357 arg += *p;
1358 break;
1360 } else {
1361 // Another space between args, ignore it.
1363 } else {
1364 // We are processing the contents of an argument.
1365 // Check for whitespace or end.
1366 if ( *p == 0 || ( !quoted && isspace( *p ) ) ) {
1367 // Process pending backslashes (interpret them
1368 // literally since they're not followed by a ").
1369 while( bSlashCount ) {
1370 arg += '\\';
1371 bSlashCount--;
1373 // End current arg.
1374 if ( !justCounting ) {
1375 argv[argc] = new char[ arg.Length() + 1 ];
1376 strcpy( argv[argc], arg.get() );
1378 argc++;
1379 // We're now between args.
1380 between = 1;
1381 } else {
1382 // Still inside argument, process the character.
1383 switch ( *p ) {
1384 case '"':
1385 // First, digest preceding backslashes (if any).
1386 while ( bSlashCount > 1 ) {
1387 // Put one backsplash in arg for each pair.
1388 arg += '\\';
1389 bSlashCount -= 2;
1391 if ( bSlashCount ) {
1392 // Quote is literal.
1393 arg += '"';
1394 bSlashCount = 0;
1395 } else {
1396 // Quote starts or ends a quoted section.
1397 if ( quoted ) {
1398 // Check for special case of consecutive double
1399 // quotes inside a quoted section.
1400 if ( *(p+1) == '"' ) {
1401 // This implies a literal double-quote. Fake that
1402 // out by causing next double-quote to look as
1403 // if it was preceded by a backslash.
1404 bSlashCount = 1;
1405 } else {
1406 quoted = 0;
1408 } else {
1409 quoted = 1;
1412 break;
1413 case '\\':
1414 // Add to count.
1415 bSlashCount++;
1416 break;
1417 default:
1418 // Accept any preceding backslashes literally.
1419 while ( bSlashCount ) {
1420 arg += '\\';
1421 bSlashCount--;
1423 // Just add next char to the current arg.
1424 arg += *p;
1425 break;
1429 // Check for end of input.
1430 if ( *p ) {
1431 // Go to next character.
1432 p++;
1433 } else {
1434 // If on first pass, go on to second.
1435 if ( justCounting ) {
1436 // Allocate argv array.
1437 argv = new char*[ argc ];
1439 // Start second pass
1440 justCounting = 0;
1441 init = 1;
1442 } else {
1443 // Quit.
1444 break;
1449 rv = cmdLine->Init(argc, argv, aWorkingDir, aState);
1451 // Cleanup.
1452 while ( argc ) {
1453 delete [] argv[ --argc ];
1455 delete [] argv;
1457 if (NS_FAILED(rv)) {
1458 NS_ERROR("Error initializing command line.");
1459 return;
1462 cmdLine->Run();
1465 nsresult
1466 nsNativeAppSupportWin::OpenWindow( const char*urlstr, const char *args ) {
1468 nsresult rv = NS_ERROR_FAILURE;
1470 nsCOMPtr<nsIWindowWatcher> wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID));
1471 nsCOMPtr<nsISupportsCString> sarg(do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID));
1472 if (sarg)
1473 sarg->SetData(nsDependentCString(args));
1475 if (wwatch && sarg) {
1476 nsCOMPtr<nsIDOMWindow> newWindow;
1477 rv = wwatch->OpenWindow(0, urlstr, "_blank", "chrome,dialog=no,all",
1478 sarg, getter_AddRefs(newWindow));
1479 #if MOZ_DEBUG_DDE
1480 } else {
1481 printf("Get WindowWatcher (or create string) failed\n");
1482 #endif
1484 return rv;
1487 HWND hwndForDOMWindow( nsISupports *window ) {
1488 nsCOMPtr<nsPIDOMWindow> pidomwindow( do_QueryInterface(window) );
1489 if ( !pidomwindow ) {
1490 return 0;
1493 nsCOMPtr<nsIBaseWindow> ppBaseWindow =
1494 do_QueryInterface( pidomwindow->GetDocShell() );
1495 if ( !ppBaseWindow ) {
1496 return 0;
1499 nsCOMPtr<nsIWidget> ppWidget;
1500 ppBaseWindow->GetMainWidget( getter_AddRefs( ppWidget ) );
1502 return (HWND)( ppWidget->GetNativeData( NS_NATIVE_WIDGET ) );
1505 static const char sJSStackContractID[] = "@mozilla.org/js/xpc/ContextStack;1";
1507 class SafeJSContext {
1508 public:
1509 SafeJSContext();
1510 ~SafeJSContext();
1512 nsresult Push();
1513 JSContext *get() { return mContext; }
1515 protected:
1516 nsCOMPtr<nsIThreadJSContextStack> mService;
1517 JSContext *mContext;
1520 SafeJSContext::SafeJSContext() : mContext(nsnull) {
1523 SafeJSContext::~SafeJSContext() {
1524 JSContext *cx;
1525 nsresult rv;
1527 if(mContext) {
1528 rv = mService->Pop(&cx);
1529 NS_ASSERTION(NS_SUCCEEDED(rv) && cx == mContext, "JSContext push/pop mismatch");
1533 nsresult SafeJSContext::Push() {
1534 if (mContext) // only once
1535 return NS_ERROR_FAILURE;
1537 mService = do_GetService(sJSStackContractID);
1538 if(mService) {
1539 JSContext *cx;
1540 if (NS_SUCCEEDED(mService->GetSafeJSContext(&cx)) &&
1541 cx &&
1542 NS_SUCCEEDED(mService->Push(cx))) {
1543 // Save cx in mContext to indicate need to pop.
1544 mContext = cx;
1547 return mContext ? NS_OK : NS_ERROR_FAILURE;
1551 nsresult
1552 nsNativeAppSupportWin::OpenBrowserWindow()
1554 nsresult rv = NS_OK;
1556 // Open the argument URL in the most recently used Navigator window.
1557 // If there is no Nav window, open a new one.
1559 // If at all possible, hand the request off to the most recent
1560 // browser window.
1562 nsCOMPtr<nsIDOMWindowInternal> navWin;
1563 GetMostRecentWindow( NS_LITERAL_STRING( "navigator:browser" ).get(), getter_AddRefs( navWin ) );
1565 // This isn't really a loop. We just use "break" statements to fall
1566 // out to the OpenWindow call when things go awry.
1567 do {
1568 // If caller requires a new window, then don't use an existing one.
1569 if ( !navWin ) {
1570 // Have to open a new one.
1571 break;
1574 nsCOMPtr<nsIBrowserDOMWindow> bwin;
1575 { // scope a bunch of temporary cruft used to generate bwin
1576 nsCOMPtr<nsIWebNavigation> navNav( do_GetInterface( navWin ) );
1577 nsCOMPtr<nsIDocShellTreeItem> navItem( do_QueryInterface( navNav ) );
1578 if ( navItem ) {
1579 nsCOMPtr<nsIDocShellTreeItem> rootItem;
1580 navItem->GetRootTreeItem( getter_AddRefs( rootItem ) );
1581 nsCOMPtr<nsIDOMWindow> rootWin( do_GetInterface( rootItem ) );
1582 nsCOMPtr<nsIDOMChromeWindow> chromeWin(do_QueryInterface(rootWin));
1583 if ( chromeWin )
1584 chromeWin->GetBrowserDOMWindow( getter_AddRefs ( bwin ) );
1587 if ( bwin ) {
1588 nsCOMPtr<nsIURI> uri;
1589 NS_NewURI( getter_AddRefs( uri ), NS_LITERAL_CSTRING("about:blank"), 0, 0 );
1590 if ( uri ) {
1591 nsCOMPtr<nsIDOMWindow> container;
1592 rv = bwin->OpenURI( uri, 0,
1593 nsIBrowserDOMWindow::OPEN_DEFAULTWINDOW,
1594 nsIBrowserDOMWindow::OPEN_EXTERNAL,
1595 getter_AddRefs( container ) );
1596 if ( NS_SUCCEEDED( rv ) )
1597 return NS_OK;
1601 NS_ERROR("failed to hand off external URL to extant window");
1602 } while ( PR_FALSE );
1604 // open a new window if caller requested it or if anything above failed
1606 char* argv[] = { 0 };
1607 nsCOMPtr<nsICommandLineRunner> cmdLine
1608 (do_CreateInstance("@mozilla.org/toolkit/command-line;1"));
1609 NS_ENSURE_TRUE(cmdLine, NS_ERROR_FAILURE);
1611 rv = cmdLine->Init(0, argv, nsnull, nsICommandLine::STATE_REMOTE_EXPLICIT);
1612 NS_ENSURE_SUCCESS(rv, rv);
1614 return cmdLine->Run();