toolkit/xre/nsNativeAppSupportWin.cpp

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

mercurial