Bug 470455 - test_database_sync_embed_visits.js leaks, r=sdwilsh
[wine-gecko.git] / toolkit / xre / nsNativeAppSupportWin.cpp
blob0d4bf8e7b4c19fb693d3a3eafb19ea25ccc0e19b
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 #include <ddeml.h>
80 #include <stdlib.h>
81 #include <stdio.h>
82 #include <io.h>
83 #include <direct.h>
84 #include <fcntl.h>
86 static HWND hwndForDOMWindow( nsISupports * );
88 static
89 nsresult
90 GetMostRecentWindow(const PRUnichar* aType, nsIDOMWindowInternal** aWindow) {
91 nsresult rv;
92 nsCOMPtr<nsIWindowMediator> med( do_GetService( NS_WINDOWMEDIATOR_CONTRACTID, &rv ) );
93 if ( NS_FAILED( rv ) )
94 return rv;
96 if ( med )
97 return med->GetMostRecentWindow( aType, aWindow );
99 return NS_ERROR_FAILURE;
102 static
103 void
104 activateWindow( nsIDOMWindowInternal *win ) {
105 // Try to get native window handle.
106 HWND hwnd = hwndForDOMWindow( win );
107 if ( hwnd ) {
108 // Restore the window if it is minimized.
109 if ( ::IsIconic( hwnd ) ) {
110 ::ShowWindow( hwnd, SW_RESTORE );
112 // Use the OS call, if possible.
113 ::SetForegroundWindow( hwnd );
114 } else {
115 // Use internal method.
116 win->Focus();
121 #ifdef DEBUG_law
122 #undef MOZ_DEBUG_DDE
123 #define MOZ_DEBUG_DDE 1
124 #endif
126 // Simple Win32 mutex wrapper.
127 struct Mutex {
128 Mutex( const char *name )
129 : mName( name ),
130 mHandle( 0 ),
131 mState( -1 ) {
132 mHandle = CreateMutexA( 0, FALSE, mName.get() );
133 #if MOZ_DEBUG_DDE
134 printf( "CreateMutex error = 0x%08X\n", (int)GetLastError() );
135 #endif
137 ~Mutex() {
138 if ( mHandle ) {
139 // Make sure we release it if we own it.
140 Unlock();
142 BOOL rc = CloseHandle( mHandle );
143 #if MOZ_DEBUG_DDE
144 if ( !rc ) {
145 printf( "CloseHandle error = 0x%08X\n", (int)GetLastError() );
147 #endif
150 BOOL Lock( DWORD timeout ) {
151 if ( mHandle ) {
152 #if MOZ_DEBUG_DDE
153 printf( "Waiting (%d msec) for DDE mutex...\n", (int)timeout );
154 #endif
155 mState = WaitForSingleObject( mHandle, timeout );
156 #if MOZ_DEBUG_DDE
157 printf( "...wait complete, result = 0x%08X, GetLastError=0x%08X\n", (int)mState, (int)::GetLastError() );
158 #endif
159 return mState == WAIT_OBJECT_0 || mState == WAIT_ABANDONED;
160 } else {
161 return FALSE;
164 void Unlock() {
165 if ( mHandle && mState == WAIT_OBJECT_0 ) {
166 #if MOZ_DEBUG_DDE
167 printf( "Releasing DDE mutex\n" );
168 #endif
169 ReleaseMutex( mHandle );
170 mState = -1;
173 private:
174 nsCString mName;
175 HANDLE mHandle;
176 DWORD mState;
179 /* DDE Notes
181 * This section describes the Win32 DDE service implementation for
182 * Mozilla. DDE is used on Win32 platforms to communicate between
183 * separate instances of mozilla.exe (or other Mozilla-based
184 * executables), or, between the Win32 desktop shell and Mozilla.
186 * The first instance of Mozilla will become the "server" and
187 * subsequent executables (and the shell) will use DDE to send
188 * requests to that process. The requests are DDE "execute" requests
189 * that pass the command line arguments.
191 * Mozilla registers the DDE application "Mozilla" and currently
192 * supports only the "WWW_OpenURL" topic. This should be reasonably
193 * compatible with applications that interfaced with Netscape
194 * Communicator (and its predecessors?). Note that even that topic
195 * may not be supported in a compatible fashion as the command-line
196 * options for Mozilla are different than for Communiator.
198 * It is imperative that at most one instance of Mozilla execute in
199 * "server mode" at any one time. The "native app support" in Mozilla
200 * on Win32 ensures that only the server process performs XPCOM
201 * initialization (that is not required for subsequent client processes
202 * to communicate with the server process).
204 * To guarantee that only one server starts up, a Win32 "mutex" is used
205 * to ensure only one process executes the server-detection code. That
206 * code consists of initializing DDE and doing a DdeConnect to Mozilla's
207 * application/topic. If that connection succeeds, then a server process
208 * must be running already.
210 * Otherwise, no server has started. In that case, the current process
211 * calls DdeNameService to register that application/topic. Only at that
212 * point does the mutex get released.
214 * There are a couple of subtleties that one should be aware of:
216 * 1. It is imperative that DdeInitialize be called only after the mutex
217 * lock has been obtained. The reason is that at shutdown, DDE
218 * notifications go out to all initialized DDE processes. Thus, if
219 * the mutex is owned by a terminating intance of Mozilla, then
220 * calling DdeInitialize and then WaitForSingleObject will cause the
221 * DdeUninitialize from the terminating process to "hang" until the
222 * process waiting for the mutex times out (and can then service the
223 * notification that the DDE server is terminating). So, don't mess
224 * with the sequence of things in the startup/shutdown logic.
226 * 2. All mutex requests are made with a reasonably long timeout value and
227 * are designed to "fail safe" (i.e., a timeout is treated as failure).
229 * 3. An attempt has been made to minimize the degree to which the main
230 * Mozilla application logic needs to be aware of the DDE mechanisms
231 * implemented herein. As a result, this module surfaces a very
232 * large-grained interface, consisting of simple start/stop methods.
233 * As a consequence, details of certain scenarios can be "lost."
234 * Particularly, incoming DDE requests can arrive after this module
235 * initiates the DDE server, but before Mozilla is initialized to the
236 * point where those requests can be serviced (e.g., open a browser
237 * window to a particular URL). Since the client process sends the
238 * request early on, it may not be prepared to respond to that error.
239 * Thus, such situations may fail silently. The design goal is that
240 * they fail harmlessly. Refinements on this point will be made as
241 * details emerge (and time permits).
244 /* Update 2001 March
246 * A significant DDE bug in Windows is causing Mozilla to get wedged at
247 * startup. This is detailed in Bugzill bug 53952
248 * (http://bugzilla.mozilla.org/show_bug.cgi?id=53952).
250 * To resolve this, we are using a new strategy:
251 * o Use a "message window" to detect that Mozilla is already running and
252 * to pass requests from a second instance back to the first;
253 * o Run only as a "DDE server" (not as DDE client); this avoids the
254 * problematic call to DDEConnect().
256 * We still use the mutex semaphore to protect the code that detects
257 * whether Mozilla is already running.
260 /* Update 2007 January
262 * A change in behavior was implemented in July 2004 which made the
263 * application on launch to add and on quit to remove the ddexec registry key.
264 * See bug 246078.
265 * Windows Vista has changed the methods used to set an application as default
266 * and the new methods are incompatible with removing the ddeexec registry key.
267 * See bug 353089.
269 * OS DDE Sequence:
270 * 1. OS checks if the dde name is registered.
271 * 2. If it is registered the OS sends a DDE request with the WWW_OpenURL topic
272 * and the params as specified in the default value of the ddeexec registry
273 * key for the verb (e.g. open).
274 * 3. If it isn't registered the OS launches the executable defined in the
275 * verb's (e.g. open) command registry key.
276 * 4. If the ifexec registry key is not present the OS sends a DDE request with
277 * the WWW_OpenURL topic and the params as specified in the default value of
278 * the ddeexec registry key for the verb (e.g. open).
279 * 5. If the ifexec registry key is present the OS sends a DDE request with the
280 * WWW_OpenURL topic and the params as specified in the ifexec registry key
281 * for the verb (e.g. open).
283 * Application DDE Sequence:
284 * 1. If the application is running a DDE request is received with the
285 * WWW_OpenURL topic and the params as specified in the default value of the
286 * ddeexec registry key (e.g. "%1",,0,0,,,, where '%1' is the url to open)
287 * for the verb (e.g. open).
288 * 2. If the application is not running it is launched with the -requestPending
289 * and the -url argument.
290 * 2.1 If the application does not need to restart and the -requestPending
291 * argument is present the accompanying url will not be used. Instead the
292 * application will wait for the DDE message to open the url.
293 * 2.2 If the application needs to restart the -requestPending argument is
294 * removed from the arguments used to restart the application and the url
295 * will be handled normally.
297 * Note: Due to a bug in IE the ifexec key should not be used (see bug 355650).
300 class nsNativeAppSupportWin : public nsNativeAppSupportBase,
301 public nsIObserver
303 public:
304 NS_DECL_NSIOBSERVER
305 NS_DECL_ISUPPORTS_INHERITED
307 // Overrides of base implementation.
308 NS_IMETHOD Start( PRBool *aResult );
309 NS_IMETHOD Stop( PRBool *aResult );
310 NS_IMETHOD Quit();
311 NS_IMETHOD Enable();
313 // The "old" Start method (renamed).
314 NS_IMETHOD StartDDE();
316 // Utility function to handle a Win32-specific command line
317 // option: "-console", which dynamically creates a Windows
318 // console.
319 void CheckConsole();
321 private:
322 static HDDEDATA CALLBACK HandleDDENotification( UINT uType,
323 UINT uFmt,
324 HCONV hconv,
325 HSZ hsz1,
326 HSZ hsz2,
327 HDDEDATA hdata,
328 ULONG dwData1,
329 ULONG dwData2 );
330 static void HandleCommandLine(const char* aCmdLineString, nsIFile* aWorkingDir, PRUint32 aState);
331 static void ParseDDEArg( HSZ args, int index, nsString& string);
332 static void ParseDDEArg( const WCHAR* args, int index, nsString& aString);
333 static void ActivateLastWindow();
334 static HDDEDATA CreateDDEData( DWORD value );
335 static HDDEDATA CreateDDEData( LPBYTE value, DWORD len );
336 static PRBool InitTopicStrings();
337 static int FindTopic( HSZ topic );
338 static nsresult OpenWindow( const char *urlstr, const char *args );
339 static nsresult OpenBrowserWindow();
340 static nsresult ReParent( nsISupports *window, HWND newParent );
341 static void SetupSysTrayIcon();
342 static void RemoveSysTrayIcon();
344 static int mConversations;
345 enum {
346 topicOpenURL,
347 topicActivate,
348 topicCancelProgress,
349 topicVersion,
350 topicRegisterViewer,
351 topicUnRegisterViewer,
352 topicGetWindowInfo,
353 // Note: Insert new values above this line!!!!!
354 topicCount // Count of the number of real topics
357 static HSZ mApplication, mTopics[ topicCount ];
358 static DWORD mInstance;
359 static PRBool mCanHandleRequests;
360 static char mMutexName[];
361 friend struct MessageWindow;
362 }; // nsNativeAppSupportWin
364 NS_INTERFACE_MAP_BEGIN(nsNativeAppSupportWin)
365 NS_INTERFACE_MAP_ENTRY(nsIObserver)
366 NS_INTERFACE_MAP_END_INHERITING(nsNativeAppSupportBase)
368 NS_IMPL_ADDREF_INHERITED(nsNativeAppSupportWin, nsNativeAppSupportBase)
369 NS_IMPL_RELEASE_INHERITED(nsNativeAppSupportWin, nsNativeAppSupportBase)
371 void
372 nsNativeAppSupportWin::CheckConsole() {
373 for ( int i = 1; i < gArgc; i++ ) {
374 if ( strcmp( "-console", gArgv[i] ) == 0
376 strcmp( "/console", gArgv[i] ) == 0 ) {
377 // Users wants to make sure we have a console.
378 // Try to allocate one.
379 BOOL rc = ::AllocConsole();
380 if ( rc ) {
381 // Console allocated. Fix it up so that output works in
382 // all cases. See http://support.microsoft.com/support/kb/articles/q105/3/05.asp.
384 // stdout
385 int hCrt = ::_open_osfhandle( (long)GetStdHandle( STD_OUTPUT_HANDLE ),
386 _O_TEXT );
387 if ( hCrt != -1 ) {
388 FILE *hf = ::_fdopen( hCrt, "w" );
389 if ( hf ) {
390 *stdout = *hf;
391 #ifdef DEBUG
392 ::fprintf( stdout, "stdout directed to dynamic console\n" );
393 #endif
397 // stderr
398 hCrt = ::_open_osfhandle( (long)::GetStdHandle( STD_ERROR_HANDLE ),
399 _O_TEXT );
400 if ( hCrt != -1 ) {
401 FILE *hf = ::_fdopen( hCrt, "w" );
402 if ( hf ) {
403 *stderr = *hf;
404 #ifdef DEBUG
405 ::fprintf( stderr, "stderr directed to dynamic console\n" );
406 #endif
410 // stdin?
411 /* Don't bother for now.
412 hCrt = ::_open_osfhandle( (long)::GetStdHandle( STD_INPUT_HANDLE ),
413 _O_TEXT );
414 if ( hCrt != -1 ) {
415 FILE *hf = ::_fdopen( hCrt, "r" );
416 if ( hf ) {
417 *stdin = *hf;
421 } else {
422 // Failed. Probably because there already is one.
423 // There's little we can do, in any case.
426 // Remove the console argument from the command line.
427 do {
428 gArgv[i] = gArgv[i + 1];
429 ++i;
430 } while (gArgv[i]);
432 --gArgc;
434 // Don't bother doing this more than once.
435 break;
439 return;
443 // Create and return an instance of class nsNativeAppSupportWin.
444 nsresult
445 NS_CreateNativeAppSupport( nsINativeAppSupport **aResult ) {
446 nsNativeAppSupportWin *pNative = new nsNativeAppSupportWin;
447 if (!pNative) return NS_ERROR_OUT_OF_MEMORY;
449 // Check for dynamic console creation request.
450 pNative->CheckConsole();
452 *aResult = pNative;
453 NS_ADDREF( *aResult );
455 return NS_OK;
458 // Constants
459 #define MOZ_DDE_APPLICATION "Mozilla"
460 #define MOZ_MUTEX_NAMESPACE "Local\\"
461 #define MOZ_STARTUP_MUTEX_NAME "StartupMutex"
462 #define MOZ_DDE_START_TIMEOUT 30000
463 #define MOZ_DDE_STOP_TIMEOUT 15000
464 #define MOZ_DDE_EXEC_TIMEOUT 15000
466 // The array entries must match the enum ordering!
467 const char * const topicNames[] = { "WWW_OpenURL",
468 "WWW_Activate",
469 "WWW_CancelProgress",
470 "WWW_Version",
471 "WWW_RegisterViewer",
472 "WWW_UnRegisterViewer",
473 "WWW_GetWindowInfo" };
475 // Static member definitions.
476 int nsNativeAppSupportWin::mConversations = 0;
477 HSZ nsNativeAppSupportWin::mApplication = 0;
478 HSZ nsNativeAppSupportWin::mTopics[nsNativeAppSupportWin::topicCount] = { 0 };
479 DWORD nsNativeAppSupportWin::mInstance = 0;
480 PRBool nsNativeAppSupportWin::mCanHandleRequests = PR_FALSE;
482 char nsNativeAppSupportWin::mMutexName[ 128 ] = { 0 };
485 // Message window encapsulation.
486 struct MessageWindow {
487 // ctor/dtor are simplistic
488 MessageWindow() {
489 // Try to find window.
490 mHandle = ::FindWindowA( className(), 0 );
493 // Act like an HWND.
494 operator HWND() {
495 return mHandle;
498 // Class name: appName + "MessageWindow"
499 static const char *className() {
500 static char classNameBuffer[128];
501 static char *mClassName = 0;
502 if ( !mClassName ) {
503 ::_snprintf( classNameBuffer,
504 sizeof classNameBuffer,
505 "%s%s",
506 gAppData->name,
507 "MessageWindow" );
508 mClassName = classNameBuffer;
510 return mClassName;
513 // Create: Register class and create window.
514 NS_IMETHOD Create() {
515 WNDCLASSA classStruct = { 0, // style
516 &MessageWindow::WindowProc, // lpfnWndProc
517 0, // cbClsExtra
518 0, // cbWndExtra
519 0, // hInstance
520 0, // hIcon
521 0, // hCursor
522 0, // hbrBackground
523 0, // lpszMenuName
524 className() }; // lpszClassName
526 // Register the window class.
527 NS_ENSURE_TRUE( ::RegisterClassA( &classStruct ), NS_ERROR_FAILURE );
529 // Create the window.
530 NS_ENSURE_TRUE( ( mHandle = ::CreateWindowA(className(),
531 0, // title
532 WS_CAPTION, // style
533 0,0,0,0, // x, y, cx, cy
534 0, // parent
535 0, // menu
536 0, // instance
537 0 ) ), // create struct
538 NS_ERROR_FAILURE );
540 #if MOZ_DEBUG_DDE
541 printf( "Message window = 0x%08X\n", (int)mHandle );
542 #endif
544 return NS_OK;
547 // Destory: Get rid of window and reset mHandle.
548 NS_IMETHOD Destroy() {
549 nsresult retval = NS_OK;
551 if ( mHandle ) {
552 // DestroyWindow can only destroy windows created from
553 // the same thread.
554 BOOL desRes = DestroyWindow( mHandle );
555 if ( FALSE != desRes ) {
556 mHandle = NULL;
558 else {
559 retval = NS_ERROR_FAILURE;
563 return retval;
566 // SendRequest: Pass the command line via WM_COPYDATA to message window.
567 NS_IMETHOD SendRequest() {
568 WCHAR *cmd = ::GetCommandLineW();
569 WCHAR cwd[MAX_PATH];
570 _wgetcwd(cwd, MAX_PATH);
572 // Construct a narrow UTF8 buffer <commandline>\0<workingdir>\0
573 NS_ConvertUTF16toUTF8 utf8buffer(cmd);
574 utf8buffer.Append('\0');
575 AppendUTF16toUTF8(cwd, utf8buffer);
576 utf8buffer.Append('\0');
578 // We used to set dwData to zero, when we didn't send the working dir.
579 // Now we're using it as a version number.
580 COPYDATASTRUCT cds = {
582 utf8buffer.Length(),
583 (void*) utf8buffer.get()
585 // Bring the already running Mozilla process to the foreground.
586 // nsWindow will restore the window (if minimized) and raise it.
587 ::SetForegroundWindow( mHandle );
588 ::SendMessage( mHandle, WM_COPYDATA, 0, (LPARAM)&cds );
589 return NS_OK;
592 // Window proc.
593 static long CALLBACK WindowProc( HWND msgWindow, UINT msg, WPARAM wp, LPARAM lp ) {
594 if ( msg == WM_COPYDATA ) {
595 if (!nsNativeAppSupportWin::mCanHandleRequests)
596 return FALSE;
598 // This is an incoming request.
599 COPYDATASTRUCT *cds = (COPYDATASTRUCT*)lp;
600 #if MOZ_DEBUG_DDE
601 printf( "Incoming request: %s\n", (const char*)cds->lpData );
602 #endif
603 nsCOMPtr<nsILocalFile> workingDir;
605 if (1 >= cds->dwData) {
606 char* wdpath = (char*) cds->lpData;
607 // skip the command line, and get the working dir of the
608 // other process, which is after the first null char
609 while (*wdpath)
610 ++wdpath;
612 ++wdpath;
614 #ifdef MOZ_DEBUG_DDE
615 printf( "Working dir: %s\n", wdpath);
616 #endif
618 NS_NewLocalFile(NS_ConvertUTF8toUTF16(wdpath),
619 PR_FALSE,
620 getter_AddRefs(workingDir));
622 (void)nsNativeAppSupportWin::HandleCommandLine((char*)cds->lpData, workingDir, nsICommandLine::STATE_REMOTE_AUTO);
624 // Get current window and return its window handle.
625 nsCOMPtr<nsIDOMWindowInternal> win;
626 GetMostRecentWindow( 0, getter_AddRefs( win ) );
627 return win ? (long)hwndForDOMWindow( win ) : 0;
629 return DefWindowProc( msgWindow, msg, wp, lp );
632 private:
633 HWND mHandle;
634 }; // struct MessageWindow
636 /* Start: Tries to find the "message window" to determine if it
637 * exists. If so, then Mozilla is already running. In that
638 * case, we use the handle to the "message" window and send
639 * a request corresponding to this process's command line
640 * options.
642 * If not, then this is the first instance of Mozilla. In
643 * that case, we create and set up the message window.
645 * The checking for existance of the message window must
646 * be protected by use of a mutex semaphore.
648 NS_IMETHODIMP
649 nsNativeAppSupportWin::Start( PRBool *aResult ) {
650 NS_ENSURE_ARG( aResult );
651 NS_ENSURE_TRUE( mInstance == 0, NS_ERROR_NOT_INITIALIZED );
652 NS_ENSURE_STATE( gAppData );
654 if (getenv("MOZ_NO_REMOTE"))
656 *aResult = PR_TRUE;
657 return NS_OK;
660 nsresult rv = NS_ERROR_FAILURE;
661 *aResult = PR_FALSE;
663 // Grab mutex first.
665 // Build mutex name from app name.
666 ::_snprintf( mMutexName, sizeof mMutexName, "%s%s%s", MOZ_MUTEX_NAMESPACE,
667 gAppData->name, MOZ_STARTUP_MUTEX_NAME );
668 Mutex startupLock = Mutex( mMutexName );
670 NS_ENSURE_TRUE( startupLock.Lock( MOZ_DDE_START_TIMEOUT ), NS_ERROR_FAILURE );
672 // Search for existing message window.
673 MessageWindow msgWindow;
674 if ( (HWND)msgWindow ) {
675 // We are a client process. Pass request to message window.
676 rv = msgWindow.SendRequest();
677 } else {
678 // We will be server.
679 rv = msgWindow.Create();
680 if ( NS_SUCCEEDED( rv ) ) {
681 // Start up DDE server.
682 this->StartDDE();
683 // Tell caller to spin message loop.
684 *aResult = PR_TRUE;
688 startupLock.Unlock();
690 return rv;
693 PRBool
694 nsNativeAppSupportWin::InitTopicStrings() {
695 for ( int i = 0; i < topicCount; i++ ) {
696 if ( !( mTopics[ i ] = DdeCreateStringHandleA( mInstance, const_cast<char *>(topicNames[ i ]), CP_WINANSI ) ) ) {
697 return PR_FALSE;
700 return PR_TRUE;
704 nsNativeAppSupportWin::FindTopic( HSZ topic ) {
705 for ( int i = 0; i < topicCount; i++ ) {
706 if ( DdeCmpStringHandles( topic, mTopics[i] ) == 0 ) {
707 return i;
710 return -1;
714 // Start DDE server.
716 // This used to be the Start() method when we were using DDE as the
717 // primary IPC mechanism between secondary Mozilla processes and the
718 // initial "server" process.
720 // Now, it simply initializes the DDE server. The caller must check
721 // that this process is to be the server, and, must acquire the DDE
722 // startup mutex semaphore prior to calling this routine. See ::Start(),
723 // above.
724 NS_IMETHODIMP
725 nsNativeAppSupportWin::StartDDE() {
726 NS_ENSURE_TRUE( mInstance == 0, NS_ERROR_NOT_INITIALIZED );
728 // Initialize DDE.
729 NS_ENSURE_TRUE( DMLERR_NO_ERROR == DdeInitialize( &mInstance,
730 nsNativeAppSupportWin::HandleDDENotification,
731 APPCLASS_STANDARD,
732 0 ),
733 NS_ERROR_FAILURE );
735 // Allocate DDE strings.
736 NS_ENSURE_TRUE( ( mApplication = DdeCreateStringHandleA( mInstance, (char*) gAppData->name, CP_WINANSI ) ) && InitTopicStrings(),
737 NS_ERROR_FAILURE );
739 // Next step is to register a DDE service.
740 NS_ENSURE_TRUE( DdeNameService( mInstance, mApplication, 0, DNS_REGISTER ), NS_ERROR_FAILURE );
742 #if MOZ_DEBUG_DDE
743 printf( "DDE server started\n" );
744 #endif
746 return NS_OK;
749 // If no DDE conversations are pending, terminate DDE.
750 NS_IMETHODIMP
751 nsNativeAppSupportWin::Stop( PRBool *aResult ) {
752 NS_ENSURE_ARG( aResult );
753 NS_ENSURE_TRUE( mInstance, NS_ERROR_NOT_INITIALIZED );
755 nsresult rv = NS_OK;
756 *aResult = PR_TRUE;
758 Mutex ddeLock( mMutexName );
760 if ( ddeLock.Lock( MOZ_DDE_STOP_TIMEOUT ) ) {
761 if ( mConversations == 0 ) {
762 this->Quit();
763 } else {
764 *aResult = PR_FALSE;
767 ddeLock.Unlock();
769 else {
770 // No DDE application name specified, but that's OK. Just
771 // forge ahead.
772 *aResult = PR_TRUE;
775 return rv;
778 NS_IMETHODIMP
779 nsNativeAppSupportWin::Observe(nsISupports* aSubject, const char* aTopic,
780 const PRUnichar* aData)
782 if (strcmp(aTopic, "quit-application") == 0) {
783 Quit();
784 } else {
785 NS_ERROR("Unexpected observer topic.");
788 return NS_OK;
791 // Terminate DDE regardless.
792 NS_IMETHODIMP
793 nsNativeAppSupportWin::Quit() {
794 // If another process wants to look for the message window, they need
795 // to wait to hold the lock, in which case they will not find the
796 // window as we will destroy ours under our lock.
797 // When the mutex goes off the stack, it is unlocked via destructor.
798 Mutex mutexLock(mMutexName);
799 NS_ENSURE_TRUE(mutexLock.Lock(MOZ_DDE_START_TIMEOUT), NS_ERROR_FAILURE);
801 // If we've got a message window to receive IPC or new window requests,
802 // get rid of it as we are shutting down.
803 // Note: Destroy calls DestroyWindow, which will only work on a window
804 // created by the same thread.
805 MessageWindow mw;
806 mw.Destroy();
808 if ( mInstance ) {
809 // Unregister application name.
810 DdeNameService( mInstance, mApplication, 0, DNS_UNREGISTER );
811 // Clean up strings.
812 if ( mApplication ) {
813 DdeFreeStringHandle( mInstance, mApplication );
814 mApplication = 0;
816 for ( int i = 0; i < topicCount; i++ ) {
817 if ( mTopics[i] ) {
818 DdeFreeStringHandle( mInstance, mTopics[i] );
819 mTopics[i] = 0;
822 DdeUninitialize( mInstance );
823 mInstance = 0;
824 #if MOZ_DEBUG_DDE
825 printf( "DDE server stopped\n" );
826 #endif
829 return NS_OK;
832 NS_IMETHODIMP
833 nsNativeAppSupportWin::Enable()
835 mCanHandleRequests = PR_TRUE;
837 nsCOMPtr<nsIObserverService> obs
838 (do_GetService("@mozilla.org/observer-service;1"));
839 if (obs) {
840 obs->AddObserver(this, "quit-application", PR_FALSE);
841 } else {
842 NS_ERROR("No observer service?");
845 return NS_OK;
848 #if MOZ_DEBUG_DDE
849 // Macro to generate case statement for a given XTYP value.
850 #define XTYP_CASE(t) \
851 case t: result = #t; break
853 static nsCString uTypeDesc( UINT uType ) {
854 nsCString result;
855 switch ( uType ) {
856 XTYP_CASE(XTYP_ADVSTART);
857 XTYP_CASE(XTYP_CONNECT);
858 XTYP_CASE(XTYP_ADVREQ);
859 XTYP_CASE(XTYP_REQUEST);
860 XTYP_CASE(XTYP_WILDCONNECT);
861 XTYP_CASE(XTYP_ADVDATA);
862 XTYP_CASE(XTYP_EXECUTE);
863 XTYP_CASE(XTYP_POKE);
864 XTYP_CASE(XTYP_ADVSTOP);
865 XTYP_CASE(XTYP_CONNECT_CONFIRM);
866 XTYP_CASE(XTYP_DISCONNECT);
867 XTYP_CASE(XTYP_ERROR);
868 XTYP_CASE(XTYP_MONITOR);
869 XTYP_CASE(XTYP_REGISTER);
870 XTYP_CASE(XTYP_XACT_COMPLETE);
871 XTYP_CASE(XTYP_UNREGISTER);
872 default: result = "XTYP_?????";
874 return result;
877 static nsCString hszValue( DWORD instance, HSZ hsz ) {
878 // Extract string from HSZ.
879 nsCString result("[");
880 DWORD len = DdeQueryString( instance, hsz, NULL, NULL, CP_WINANSI );
881 if ( len ) {
882 char buffer[ 256 ];
883 DdeQueryString( instance, hsz, buffer, sizeof buffer, CP_WINANSI );
884 result += buffer;
886 result += "]";
887 return result;
889 #else
890 // These are purely a safety measure to avoid the infamous "won't
891 // build non-debug" type Tinderbox flames.
892 static nsCString uTypeDesc( UINT ) {
893 return nsCString( "?" );
895 static nsCString hszValue( DWORD, HSZ ) {
896 return nsCString( "?" );
898 #endif
901 // Utility function to escape double-quotes within a string.
902 static void escapeQuotes( nsAString &aString ) {
903 PRInt32 offset = -1;
904 while( 1 ) {
905 // Find next '"'.
906 offset = aString.FindChar( '"', ++offset );
907 if ( offset == kNotFound ) {
908 // No more quotes, exit.
909 break;
910 } else {
911 // Insert back-slash ahead of the '"'.
912 aString.Insert( PRUnichar('\\'), offset );
913 // Increment offset because we just inserted a slash
914 offset++;
917 return;
920 HDDEDATA CALLBACK
921 nsNativeAppSupportWin::HandleDDENotification( UINT uType, // transaction type
922 UINT uFmt, // clipboard data format
923 HCONV hconv, // handle to the conversation
924 HSZ hsz1, // handle to a string
925 HSZ hsz2, // handle to a string
926 HDDEDATA hdata, // handle to a global memory object
927 ULONG dwData1, // transaction-specific data
928 ULONG dwData2 ) { // transaction-specific data
930 if (!mCanHandleRequests)
931 return 0;
934 #if MOZ_DEBUG_DDE
935 printf( "DDE: uType =%s\n", uTypeDesc( uType ).get() );
936 printf( " uFmt =%u\n", (unsigned)uFmt );
937 printf( " hconv =%08x\n", (int)hconv );
938 printf( " hsz1 =%08x:%s\n", (int)hsz1, hszValue( mInstance, hsz1 ).get() );
939 printf( " hsz2 =%08x:%s\n", (int)hsz2, hszValue( mInstance, hsz2 ).get() );
940 printf( " hdata =%08x\n", (int)hdata );
941 printf( " dwData1=%08x\n", (int)dwData1 );
942 printf( " dwData2=%08x\n", (int)dwData2 );
943 #endif
945 HDDEDATA result = 0;
946 if ( uType & XCLASS_BOOL ) {
947 switch ( uType ) {
948 case XTYP_CONNECT:
949 // Make sure its for our service/topic.
950 if ( FindTopic( hsz1 ) != -1 ) {
951 // We support this connection.
952 result = (HDDEDATA)1;
954 break;
955 case XTYP_CONNECT_CONFIRM:
956 // We don't care about the conversation handle, at this point.
957 result = (HDDEDATA)1;
958 break;
960 } else if ( uType & XCLASS_DATA ) {
961 if ( uType == XTYP_REQUEST ) {
962 switch ( FindTopic( hsz1 ) ) {
963 case topicOpenURL: {
964 // Open a given URL...
966 // Get the URL from the first argument in the command.
967 nsAutoString url;
968 ParseDDEArg(hsz2, 0, url);
970 // Read the 3rd argument in the command to determine if a
971 // new window is to be used.
972 nsAutoString windowID;
973 ParseDDEArg(hsz2, 2, windowID);
974 // "" means to open the URL in a new window.
975 if ( windowID.IsEmpty() ) {
976 url.Insert(NS_LITERAL_STRING("mozilla -new-window "), 0);
978 else {
979 url.Insert(NS_LITERAL_STRING("mozilla -url "), 0);
982 #if MOZ_DEBUG_DDE
983 printf( "Handling dde XTYP_REQUEST request: [%s]...\n", NS_ConvertUTF16toUTF8(url).get() );
984 #endif
985 // Now handle it.
986 HandleCommandLine(NS_ConvertUTF16toUTF8(url).get(), nsnull, nsICommandLine::STATE_REMOTE_EXPLICIT);
988 // Return pseudo window ID.
989 result = CreateDDEData( 1 );
990 break;
992 case topicGetWindowInfo: {
993 // This topic has to get the current URL, get the current
994 // page title and then format the output into the DDE
995 // return string. The return value is "URL","Page Title",
996 // "Window ID" however the window ID is not used for this
997 // command, therefore it is returned as a null string
999 // This isn't really a loop. We just use "break"
1000 // statements to bypass the remaining steps when
1001 // something goes wrong.
1002 do {
1003 // Get most recently used Nav window.
1004 nsCOMPtr<nsIDOMWindowInternal> navWin;
1005 GetMostRecentWindow( NS_LITERAL_STRING( "navigator:browser" ).get(),
1006 getter_AddRefs( navWin ) );
1007 if ( !navWin ) {
1008 // There is not a window open
1009 break;
1011 // Get content window.
1012 nsCOMPtr<nsIDOMWindow> content;
1013 navWin->GetContent( getter_AddRefs( content ) );
1014 if ( !content ) {
1015 break;
1017 // Convert that to internal interface.
1018 nsCOMPtr<nsPIDOMWindow> internalContent( do_QueryInterface( content ) );
1019 if ( !internalContent ) {
1020 break;
1022 // Get location.
1023 nsCOMPtr<nsIDOMLocation> location;
1024 internalContent->GetLocation( getter_AddRefs( location ) );
1025 if ( !location ) {
1026 break;
1028 // Get href for URL.
1029 nsAutoString url;
1030 if ( NS_FAILED( location->GetHref( url ) ) ) {
1031 break;
1033 // Escape any double-quotes.
1034 escapeQuotes( url );
1036 // Now for the title...
1038 // Get the base window from the doc shell...
1039 nsCOMPtr<nsIBaseWindow> baseWindow =
1040 do_QueryInterface( internalContent->GetDocShell() );
1041 if ( !baseWindow ) {
1042 break;
1044 // And from the base window we can get the title.
1045 nsXPIDLString title;
1046 if(!baseWindow) {
1047 break;
1049 baseWindow->GetTitle(getter_Copies(title));
1050 // Escape any double-quotes in the title.
1051 escapeQuotes( title );
1053 // Use a string buffer for the output data, first
1054 // save a quote.
1055 nsCAutoString outpt( NS_LITERAL_CSTRING("\"") );
1056 // Now copy the URL converting the Unicode string
1057 // to a single-byte ASCII string
1058 nsCAutoString tmpNativeStr;
1059 NS_CopyUnicodeToNative( url, tmpNativeStr );
1060 outpt.Append( tmpNativeStr );
1061 // Add the "," used to separate the URL and the page
1062 // title
1063 outpt.Append( NS_LITERAL_CSTRING("\",\"") );
1064 // Now copy the current page title to the return string
1065 NS_CopyUnicodeToNative( title, tmpNativeStr );
1066 outpt.Append( tmpNativeStr );
1067 // Fill out the return string with the remainin ",""
1068 outpt.Append( NS_LITERAL_CSTRING( "\",\"\"" ));
1070 // Create a DDE handle to a char string for the data
1071 // being returned, this copies and creates a "shared"
1072 // copy of the DDE response until the calling APP
1073 // reads it and says it can be freed.
1074 result = CreateDDEData( (LPBYTE)(const char*)outpt.get(),
1075 outpt.Length() + 1 );
1076 #if MOZ_DEBUG_DDE
1077 printf( "WWW_GetWindowInfo->%s\n", outpt.get() );
1078 #endif
1079 } while ( PR_FALSE );
1080 break;
1082 case topicActivate: {
1083 // Activate a Nav window...
1084 nsAutoString windowID;
1085 ParseDDEArg(hsz2, 0, windowID);
1086 // 4294967295 is decimal for 0xFFFFFFFF which is also a
1087 // correct value to do that Activate last window stuff
1088 if ( windowID.EqualsLiteral( "-1" ) ||
1089 windowID.EqualsLiteral( "4294967295" ) ) {
1090 // We only support activating the most recent window (or a new one).
1091 ActivateLastWindow();
1092 // Return pseudo window ID.
1093 result = CreateDDEData( 1 );
1095 break;
1097 case topicVersion: {
1098 // Return version. We're restarting at 1.0!
1099 DWORD version = 1 << 16; // "1.0"
1100 result = CreateDDEData( version );
1101 break;
1103 case topicRegisterViewer: {
1104 // Register new viewer (not implemented).
1105 result = CreateDDEData( PR_FALSE );
1106 break;
1108 case topicUnRegisterViewer: {
1109 // Unregister new viewer (not implemented).
1110 result = CreateDDEData( PR_FALSE );
1111 break;
1113 default:
1114 break;
1116 } else if ( uType & XTYP_POKE ) {
1117 switch ( FindTopic( hsz1 ) ) {
1118 case topicCancelProgress: {
1119 // "Handle" progress cancel (actually, pretty much ignored).
1120 result = (HDDEDATA)DDE_FACK;
1121 break;
1123 default:
1124 break;
1127 } else if ( uType & XCLASS_FLAGS ) {
1128 if ( uType == XTYP_EXECUTE ) {
1129 // Prove that we received the request.
1130 DWORD bytes;
1131 LPBYTE request = DdeAccessData( hdata, &bytes );
1132 #if MOZ_DEBUG_DDE
1133 printf( "Handling dde request: [%s]...\n", (char*)request );
1134 #endif
1135 // Default is to open in current window.
1136 PRBool new_window = PR_FALSE;
1138 nsAutoString url;
1139 ParseDDEArg((const WCHAR*) request, 0, url);
1141 // Read the 3rd argument in the command to determine if a
1142 // new window is to be used.
1143 nsAutoString windowID;
1144 ParseDDEArg((const WCHAR*) request, 2, windowID);
1146 // "" means to open the URL in a new window.
1147 if ( windowID.IsEmpty() ) {
1148 url.Insert(NS_LITERAL_STRING("mozilla -new-window "), 0);
1150 else {
1151 url.Insert(NS_LITERAL_STRING("mozilla -url "), 0);
1153 #if MOZ_DEBUG_DDE
1154 printf( "Handling dde XTYP_REQUEST request: [%s]...\n", NS_ConvertUTF16toUTF8(url).get() );
1155 #endif
1156 // Now handle it.
1157 HandleCommandLine(NS_ConvertUTF16toUTF8(url).get(), nsnull, nsICommandLine::STATE_REMOTE_EXPLICIT);
1159 // Release the data.
1160 DdeUnaccessData( hdata );
1161 result = (HDDEDATA)DDE_FACK;
1162 } else {
1163 result = (HDDEDATA)DDE_FNOTPROCESSED;
1165 } else if ( uType & XCLASS_NOTIFICATION ) {
1167 #if MOZ_DEBUG_DDE
1168 printf( "DDE result=%d (0x%08X)\n", (int)result, (int)result );
1169 #endif
1170 return result;
1173 // Utility function to advance to end of quoted string.
1174 // p+offset must point to the comma preceding the arg on entry.
1175 // On return, p+result points to the closing '"' (or end of the string
1176 // if the closing '"' is missing) if the arg is quoted. If the arg
1177 // is not quoted, then p+result will point to the first character
1178 // of the arg.
1179 static PRInt32 advanceToEndOfQuotedArg( const WCHAR *p, PRInt32 offset, PRInt32 len ) {
1180 // Check whether the current arg is quoted.
1181 if ( p[++offset] == '"' ) {
1182 // Advance past the closing quote.
1183 while ( offset < len && p[++offset] != '"' ) {
1184 // If the current character is a backslash, then the
1185 // next character can't be a *real* '"', so skip it.
1186 if ( p[offset] == '\\' ) {
1187 offset++;
1191 return offset;
1194 void nsNativeAppSupportWin::ParseDDEArg( const WCHAR* args, int index, nsString& aString) {
1195 if ( args ) {
1196 nsDependentString temp(args);
1198 // offset points to the comma preceding the desired arg.
1199 PRInt32 offset = -1;
1200 // Skip commas till we get to the arg we want.
1201 while( index-- ) {
1202 // If this arg is quoted, then go to closing quote.
1203 offset = advanceToEndOfQuotedArg( args, offset, temp.Length());
1204 // Find next comma.
1205 offset = temp.FindChar( ',', offset );
1206 if ( offset == kNotFound ) {
1207 // No more commas, give up.
1208 aString = args;
1209 return;
1212 // The desired argument starts just past the preceding comma,
1213 // which offset points to, and extends until the following
1214 // comma (or the end of the string).
1216 // Since the argument might be enclosed in quotes, we need to
1217 // deal with that before searching for the terminating comma.
1218 // We advance offset so it ends up pointing to the start of
1219 // the argument we want.
1220 PRInt32 end = advanceToEndOfQuotedArg( args, offset++, temp.Length() );
1221 // Find next comma (or end of string).
1222 end = temp.FindChar( ',', end );
1223 if ( end == kNotFound ) {
1224 // Arg is the rest of the string.
1225 end = temp.Length();
1227 // Extract result.
1228 aString.Assign( args + offset, end - offset );
1230 return;
1233 // Utility to parse out argument from a DDE item string.
1234 void nsNativeAppSupportWin::ParseDDEArg( HSZ args, int index, nsString& aString) {
1235 DWORD argLen = DdeQueryStringW( mInstance, args, NULL, NULL, CP_WINUNICODE );
1236 // there wasn't any string, so return empty string
1237 if ( !argLen ) return;
1238 nsAutoString temp;
1239 // Ensure result's buffer is sufficiently big.
1240 temp.SetLength( argLen );
1241 // Now get the string contents.
1242 DdeQueryString( mInstance, args, temp.BeginWriting(), temp.Length(), CP_WINUNICODE );
1243 // Parse out the given arg.
1244 ParseDDEArg(temp.get(), index, aString);
1245 return;
1248 void nsNativeAppSupportWin::ActivateLastWindow() {
1249 nsCOMPtr<nsIDOMWindowInternal> navWin;
1250 GetMostRecentWindow( NS_LITERAL_STRING("navigator:browser").get(), getter_AddRefs( navWin ) );
1251 if ( navWin ) {
1252 // Activate that window.
1253 activateWindow( navWin );
1254 } else {
1255 // Need to create a Navigator window, then.
1256 OpenBrowserWindow();
1260 HDDEDATA nsNativeAppSupportWin::CreateDDEData( DWORD value ) {
1261 return CreateDDEData( (LPBYTE)&value, sizeof value );
1264 HDDEDATA nsNativeAppSupportWin::CreateDDEData( LPBYTE value, DWORD len ) {
1265 HDDEDATA result = DdeCreateDataHandle( mInstance,
1266 value,
1267 len,
1269 mApplication,
1270 CF_TEXT,
1271 0 );
1272 return result;
1275 void
1276 nsNativeAppSupportWin::HandleCommandLine(const char* aCmdLineString,
1277 nsIFile* aWorkingDir,
1278 PRUint32 aState)
1280 nsresult rv;
1282 int justCounting = 1;
1283 char **argv = 0;
1284 // Flags, etc.
1285 int init = 1;
1286 int between, quoted, bSlashCount;
1287 int argc;
1288 const char *p;
1289 nsCAutoString arg;
1291 nsCOMPtr<nsICommandLineRunner> cmdLine
1292 (do_CreateInstance("@mozilla.org/toolkit/command-line;1"));
1293 if (!cmdLine) {
1294 NS_ERROR("Couldn't create command line!");
1295 return;
1298 // Parse command line args according to MS spec
1299 // (see "Parsing C++ Command-Line Arguments" at
1300 // http://msdn.microsoft.com/library/devprods/vs6/visualc/vclang/_pluslang_parsing_c.2b2b_.command.2d.line_arguments.htm).
1301 // We loop if we've not finished the second pass through.
1302 while ( 1 ) {
1303 // Initialize if required.
1304 if ( init ) {
1305 p = aCmdLineString;
1306 between = 1;
1307 argc = quoted = bSlashCount = 0;
1309 init = 0;
1311 if ( between ) {
1312 // We are traversing whitespace between args.
1313 // Check for start of next arg.
1314 if ( *p != 0 && !isspace( *p ) ) {
1315 // Start of another arg.
1316 between = 0;
1317 arg = "";
1318 switch ( *p ) {
1319 case '\\':
1320 // Count the backslash.
1321 bSlashCount = 1;
1322 break;
1323 case '"':
1324 // Remember we're inside quotes.
1325 quoted = 1;
1326 break;
1327 default:
1328 // Add character to arg.
1329 arg += *p;
1330 break;
1332 } else {
1333 // Another space between args, ignore it.
1335 } else {
1336 // We are processing the contents of an argument.
1337 // Check for whitespace or end.
1338 if ( *p == 0 || ( !quoted && isspace( *p ) ) ) {
1339 // Process pending backslashes (interpret them
1340 // literally since they're not followed by a ").
1341 while( bSlashCount ) {
1342 arg += '\\';
1343 bSlashCount--;
1345 // End current arg.
1346 if ( !justCounting ) {
1347 argv[argc] = new char[ arg.Length() + 1 ];
1348 strcpy( argv[argc], arg.get() );
1350 argc++;
1351 // We're now between args.
1352 between = 1;
1353 } else {
1354 // Still inside argument, process the character.
1355 switch ( *p ) {
1356 case '"':
1357 // First, digest preceding backslashes (if any).
1358 while ( bSlashCount > 1 ) {
1359 // Put one backsplash in arg for each pair.
1360 arg += '\\';
1361 bSlashCount -= 2;
1363 if ( bSlashCount ) {
1364 // Quote is literal.
1365 arg += '"';
1366 bSlashCount = 0;
1367 } else {
1368 // Quote starts or ends a quoted section.
1369 if ( quoted ) {
1370 // Check for special case of consecutive double
1371 // quotes inside a quoted section.
1372 if ( *(p+1) == '"' ) {
1373 // This implies a literal double-quote. Fake that
1374 // out by causing next double-quote to look as
1375 // if it was preceded by a backslash.
1376 bSlashCount = 1;
1377 } else {
1378 quoted = 0;
1380 } else {
1381 quoted = 1;
1384 break;
1385 case '\\':
1386 // Add to count.
1387 bSlashCount++;
1388 break;
1389 default:
1390 // Accept any preceding backslashes literally.
1391 while ( bSlashCount ) {
1392 arg += '\\';
1393 bSlashCount--;
1395 // Just add next char to the current arg.
1396 arg += *p;
1397 break;
1401 // Check for end of input.
1402 if ( *p ) {
1403 // Go to next character.
1404 p++;
1405 } else {
1406 // If on first pass, go on to second.
1407 if ( justCounting ) {
1408 // Allocate argv array.
1409 argv = new char*[ argc ];
1411 // Start second pass
1412 justCounting = 0;
1413 init = 1;
1414 } else {
1415 // Quit.
1416 break;
1421 rv = cmdLine->Init(argc, argv, aWorkingDir, aState);
1423 // Cleanup.
1424 while ( argc ) {
1425 delete [] argv[ --argc ];
1427 delete [] argv;
1429 if (NS_FAILED(rv)) {
1430 NS_ERROR("Error initializing command line.");
1431 return;
1434 cmdLine->Run();
1437 nsresult
1438 nsNativeAppSupportWin::OpenWindow( const char*urlstr, const char *args ) {
1440 nsresult rv = NS_ERROR_FAILURE;
1442 nsCOMPtr<nsIWindowWatcher> wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID));
1443 nsCOMPtr<nsISupportsCString> sarg(do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID));
1444 if (sarg)
1445 sarg->SetData(nsDependentCString(args));
1447 if (wwatch && sarg) {
1448 nsCOMPtr<nsIDOMWindow> newWindow;
1449 rv = wwatch->OpenWindow(0, urlstr, "_blank", "chrome,dialog=no,all",
1450 sarg, getter_AddRefs(newWindow));
1451 #if MOZ_DEBUG_DDE
1452 } else {
1453 printf("Get WindowWatcher (or create string) failed\n");
1454 #endif
1456 return rv;
1459 HWND hwndForDOMWindow( nsISupports *window ) {
1460 nsCOMPtr<nsPIDOMWindow> pidomwindow( do_QueryInterface(window) );
1461 if ( !pidomwindow ) {
1462 return 0;
1465 nsCOMPtr<nsIBaseWindow> ppBaseWindow =
1466 do_QueryInterface( pidomwindow->GetDocShell() );
1467 if ( !ppBaseWindow ) {
1468 return 0;
1471 nsCOMPtr<nsIWidget> ppWidget;
1472 ppBaseWindow->GetMainWidget( getter_AddRefs( ppWidget ) );
1474 return (HWND)( ppWidget->GetNativeData( NS_NATIVE_WIDGET ) );
1477 static const char sJSStackContractID[] = "@mozilla.org/js/xpc/ContextStack;1";
1479 class SafeJSContext {
1480 public:
1481 SafeJSContext();
1482 ~SafeJSContext();
1484 nsresult Push();
1485 JSContext *get() { return mContext; }
1487 protected:
1488 nsCOMPtr<nsIThreadJSContextStack> mService;
1489 JSContext *mContext;
1492 SafeJSContext::SafeJSContext() : mContext(nsnull) {
1495 SafeJSContext::~SafeJSContext() {
1496 JSContext *cx;
1497 nsresult rv;
1499 if(mContext) {
1500 rv = mService->Pop(&cx);
1501 NS_ASSERTION(NS_SUCCEEDED(rv) && cx == mContext, "JSContext push/pop mismatch");
1505 nsresult SafeJSContext::Push() {
1506 if (mContext) // only once
1507 return NS_ERROR_FAILURE;
1509 mService = do_GetService(sJSStackContractID);
1510 if(mService) {
1511 JSContext *cx;
1512 if (NS_SUCCEEDED(mService->GetSafeJSContext(&cx)) &&
1513 cx &&
1514 NS_SUCCEEDED(mService->Push(cx))) {
1515 // Save cx in mContext to indicate need to pop.
1516 mContext = cx;
1519 return mContext ? NS_OK : NS_ERROR_FAILURE;
1523 nsresult
1524 nsNativeAppSupportWin::OpenBrowserWindow()
1526 nsresult rv = NS_OK;
1528 // Open the argument URL in the most recently used Navigator window.
1529 // If there is no Nav window, open a new one.
1531 // If at all possible, hand the request off to the most recent
1532 // browser window.
1534 nsCOMPtr<nsIDOMWindowInternal> navWin;
1535 GetMostRecentWindow( NS_LITERAL_STRING( "navigator:browser" ).get(), getter_AddRefs( navWin ) );
1537 // This isn't really a loop. We just use "break" statements to fall
1538 // out to the OpenWindow call when things go awry.
1539 do {
1540 // If caller requires a new window, then don't use an existing one.
1541 if ( !navWin ) {
1542 // Have to open a new one.
1543 break;
1546 nsCOMPtr<nsIBrowserDOMWindow> bwin;
1547 { // scope a bunch of temporary cruft used to generate bwin
1548 nsCOMPtr<nsIWebNavigation> navNav( do_GetInterface( navWin ) );
1549 nsCOMPtr<nsIDocShellTreeItem> navItem( do_QueryInterface( navNav ) );
1550 if ( navItem ) {
1551 nsCOMPtr<nsIDocShellTreeItem> rootItem;
1552 navItem->GetRootTreeItem( getter_AddRefs( rootItem ) );
1553 nsCOMPtr<nsIDOMWindow> rootWin( do_GetInterface( rootItem ) );
1554 nsCOMPtr<nsIDOMChromeWindow> chromeWin(do_QueryInterface(rootWin));
1555 if ( chromeWin )
1556 chromeWin->GetBrowserDOMWindow( getter_AddRefs ( bwin ) );
1559 if ( bwin ) {
1560 nsCOMPtr<nsIURI> uri;
1561 NS_NewURI( getter_AddRefs( uri ), NS_LITERAL_CSTRING("about:blank"), 0, 0 );
1562 if ( uri ) {
1563 nsCOMPtr<nsIDOMWindow> container;
1564 rv = bwin->OpenURI( uri, 0,
1565 nsIBrowserDOMWindow::OPEN_DEFAULTWINDOW,
1566 nsIBrowserDOMWindow::OPEN_EXTERNAL,
1567 getter_AddRefs( container ) );
1568 if ( NS_SUCCEEDED( rv ) )
1569 return NS_OK;
1573 NS_ERROR("failed to hand off external URL to extant window\n");
1574 } while ( PR_FALSE );
1576 // open a new window if caller requested it or if anything above failed
1578 char* argv[] = { 0 };
1579 nsCOMPtr<nsICommandLineRunner> cmdLine
1580 (do_CreateInstance("@mozilla.org/toolkit/command-line;1"));
1581 NS_ENSURE_TRUE(cmdLine, NS_ERROR_FAILURE);
1583 rv = cmdLine->Init(0, argv, nsnull, nsICommandLine::STATE_REMOTE_EXPLICIT);
1584 NS_ENSURE_SUCCESS(rv, rv);
1586 return cmdLine->Run();