PageRenderTime 127ms CodeModel.GetById 14ms app.highlight 101ms RepoModel.GetById 1ms app.codeStats 0ms

/toolkit/xre/nsNativeAppSupportWin.cpp

http://github.com/zpao/v8monkey
C++ | 1582 lines | 1217 code | 65 blank | 300 comment | 89 complexity | 996938c5a2f8c975f5020365875ba7af MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   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
   4 *
   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/
   9 *
  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.
  14 *
  15 * The Original Code is Mozilla Communicator client code.
  16 *
  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.
  21 *
  22 * Contributor(s):
  23 *   Bill Law       law@netscape.com
  24 *   Robert Strong  robert.bugzilla@gmail.com
  25 *
  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.
  37 *
  38 * ***** END LICENSE BLOCK ***** */
  39
  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"
  76
  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>
  85
  86static HWND hwndForDOMWindow( nsISupports * );
  87
  88static
  89nsresult
  90GetMostRecentWindow(const PRUnichar* aType, nsIDOMWindow** aWindow) {
  91    nsresult rv;
  92    nsCOMPtr<nsIWindowMediator> med( do_GetService( NS_WINDOWMEDIATOR_CONTRACTID, &rv ) );
  93    if ( NS_FAILED( rv ) )
  94        return rv;
  95
  96    if ( med )
  97        return med->GetMostRecentWindow( aType, aWindow );
  98
  99    return NS_ERROR_FAILURE;
 100}
 101
 102static
 103void
 104activateWindow( nsIDOMWindow *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 );
 111        }
 112        // Use the OS call, if possible.
 113        ::SetForegroundWindow( hwnd );
 114    } else {
 115        // Use internal method.
 116        win->Focus();
 117    }
 118}
 119
 120
 121#ifdef DEBUG_law
 122#undef MOZ_DEBUG_DDE
 123#define MOZ_DEBUG_DDE 1
 124#endif
 125
 126// Simple Win32 mutex wrapper.
 127struct Mutex {
 128    Mutex( const PRUnichar *name )
 129        : mName( name ),
 130          mHandle( 0 ),
 131          mState( -1 ) {
 132        mHandle = CreateMutexW( 0, FALSE, mName.get() );
 133#if MOZ_DEBUG_DDE
 134        printf( "CreateMutex error = 0x%08X\n", (int)GetLastError() );
 135#endif
 136    }
 137    ~Mutex() {
 138        if ( mHandle ) {
 139            // Make sure we release it if we own it.
 140            Unlock();
 141
 142            BOOL rc = CloseHandle( mHandle );
 143#if MOZ_DEBUG_DDE
 144            if ( !rc ) {
 145                printf( "CloseHandle error = 0x%08X\n", (int)GetLastError() );
 146            }
 147#endif
 148        }
 149    }
 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;
 162        }
 163    }
 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;
 171        }
 172    }
 173private:
 174    nsString  mName;
 175    HANDLE    mHandle;
 176    DWORD     mState;
 177};
 178
 179/* DDE Notes
 180 *
 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.
 185 *
 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.
 190 *
 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.
 197 *
 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).
 203 *
 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.
 209 *
 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.
 213 *
 214 * There are a couple of subtleties that one should be aware of:
 215 *
 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.
 225 *
 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).
 228 *
 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).
 242 */
 243
 244/* Update 2001 March
 245 *
 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).
 249 *
 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().
 255 *
 256 * We still use the mutex semaphore to protect the code that detects
 257 * whether Mozilla is already running.
 258 */
 259
 260/* Update 2007 January
 261 *
 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.
 268 *
 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).
 282 *
 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.
 296 *
 297 * Note: Due to a bug in IE the ifexec key should not be used (see bug 355650).
 298 */
 299
 300class nsNativeAppSupportWin : public nsNativeAppSupportBase,
 301                              public nsIObserver
 302{
 303public:
 304    NS_DECL_NSIOBSERVER
 305    NS_DECL_ISUPPORTS_INHERITED
 306
 307    // Overrides of base implementation.
 308    NS_IMETHOD Start( bool *aResult );
 309    NS_IMETHOD Stop( bool *aResult );
 310    NS_IMETHOD Quit();
 311    NS_IMETHOD Enable();
 312    // The "old" Start method (renamed).
 313    NS_IMETHOD StartDDE();
 314    // Utility function to handle a Win32-specific command line
 315    // option: "-console", which dynamically creates a Windows
 316    // console.
 317    void CheckConsole();
 318
 319private:
 320    static void HandleCommandLine(const char* aCmdLineString, nsIFile* aWorkingDir, PRUint32 aState);
 321    static HDDEDATA CALLBACK HandleDDENotification( UINT     uType,
 322                                                    UINT     uFmt,
 323                                                    HCONV    hconv,
 324                                                    HSZ      hsz1,
 325                                                    HSZ      hsz2,
 326                                                    HDDEDATA hdata,
 327                                                    ULONG_PTR dwData1,
 328                                                    ULONG_PTR dwData2 );
 329    static void ParseDDEArg( HSZ args, int index, nsString& string);
 330    static void ParseDDEArg( const WCHAR* args, int index, nsString& aString);
 331    static HDDEDATA CreateDDEData( DWORD value );
 332    static HDDEDATA CreateDDEData( LPBYTE value, DWORD len );
 333    static bool     InitTopicStrings();
 334    static int      FindTopic( HSZ topic );
 335    static void ActivateLastWindow();
 336    static nsresult OpenWindow( const char *urlstr, const char *args );
 337    static nsresult OpenBrowserWindow();
 338    static void     SetupSysTrayIcon();
 339    static void     RemoveSysTrayIcon();
 340
 341    static int   mConversations;
 342    enum {
 343        topicOpenURL,
 344        topicActivate,
 345        topicCancelProgress,
 346        topicVersion,
 347        topicRegisterViewer,
 348        topicUnRegisterViewer,
 349        topicGetWindowInfo,
 350        // Note: Insert new values above this line!!!!!
 351        topicCount // Count of the number of real topics
 352    };
 353    static HSZ   mApplication, mTopics[ topicCount ];
 354    static DWORD mInstance;
 355    static bool mCanHandleRequests;
 356    static PRUnichar mMutexName[];
 357    friend struct MessageWindow;
 358}; // nsNativeAppSupportWin
 359
 360NS_INTERFACE_MAP_BEGIN(nsNativeAppSupportWin)
 361    NS_INTERFACE_MAP_ENTRY(nsIObserver)
 362NS_INTERFACE_MAP_END_INHERITING(nsNativeAppSupportBase)
 363
 364NS_IMPL_ADDREF_INHERITED(nsNativeAppSupportWin, nsNativeAppSupportBase)
 365NS_IMPL_RELEASE_INHERITED(nsNativeAppSupportWin, nsNativeAppSupportBase)
 366
 367void
 368nsNativeAppSupportWin::CheckConsole() {
 369    for ( int i = 1; i < gArgc; i++ ) {
 370        if ( strcmp( "-console", gArgv[i] ) == 0
 371             ||
 372             strcmp( "/console", gArgv[i] ) == 0 ) {
 373            // Users wants to make sure we have a console.
 374            // Try to allocate one.
 375            BOOL rc = ::AllocConsole();
 376            if ( rc ) {
 377                // Console allocated.  Fix it up so that output works in
 378                // all cases.  See http://support.microsoft.com/support/kb/articles/q105/3/05.asp.
 379
 380                // stdout
 381                int hCrt = ::_open_osfhandle( (intptr_t)GetStdHandle( STD_OUTPUT_HANDLE ),
 382                                            _O_TEXT );
 383                if ( hCrt != -1 ) {
 384                    FILE *hf = ::_fdopen( hCrt, "w" );
 385                    if ( hf ) {
 386                        *stdout = *hf;
 387#ifdef DEBUG
 388                        ::fprintf( stdout, "stdout directed to dynamic console\n" );
 389#endif
 390                    }
 391                }
 392
 393                // stderr
 394                hCrt = ::_open_osfhandle( (intptr_t)::GetStdHandle( STD_ERROR_HANDLE ),
 395                                          _O_TEXT );
 396                if ( hCrt != -1 ) {
 397                    FILE *hf = ::_fdopen( hCrt, "w" );
 398                    if ( hf ) {
 399                        *stderr = *hf;
 400#ifdef DEBUG
 401                        ::fprintf( stderr, "stderr directed to dynamic console\n" );
 402#endif
 403                    }
 404                }
 405
 406                // stdin?
 407                /* Don't bother for now.
 408                hCrt = ::_open_osfhandle( (long)::GetStdHandle( STD_INPUT_HANDLE ),
 409                                          _O_TEXT );
 410                if ( hCrt != -1 ) {
 411                    FILE *hf = ::_fdopen( hCrt, "r" );
 412                    if ( hf ) {
 413                        *stdin = *hf;
 414                    }
 415                }
 416                */
 417            } else {
 418                // Failed.  Probably because there already is one.
 419                // There's little we can do, in any case.
 420            }
 421            // Remove the console argument from the command line.
 422            do {
 423                gArgv[i] = gArgv[i + 1];
 424                ++i;
 425            } while (gArgv[i]);
 426
 427            --gArgc;
 428
 429            // Don't bother doing this more than once.
 430            break;
 431        }
 432    }
 433
 434    return;
 435}
 436
 437
 438// Create and return an instance of class nsNativeAppSupportWin.
 439nsresult
 440NS_CreateNativeAppSupport( nsINativeAppSupport **aResult ) {
 441    nsNativeAppSupportWin *pNative = new nsNativeAppSupportWin;
 442    if (!pNative) return NS_ERROR_OUT_OF_MEMORY;
 443
 444    // Check for dynamic console creation request.
 445    pNative->CheckConsole();
 446
 447    *aResult = pNative;
 448    NS_ADDREF( *aResult );
 449
 450    return NS_OK;
 451}
 452
 453// Constants
 454#define MOZ_DDE_APPLICATION    "Mozilla"
 455#define MOZ_MUTEX_NAMESPACE    L"Local\\"
 456#define MOZ_STARTUP_MUTEX_NAME L"StartupMutex"
 457#define MOZ_DDE_START_TIMEOUT 30000
 458#define MOZ_DDE_STOP_TIMEOUT  15000
 459#define MOZ_DDE_EXEC_TIMEOUT  15000
 460
 461// The array entries must match the enum ordering!
 462const char * const topicNames[] = { "WWW_OpenURL",
 463                                    "WWW_Activate",
 464                                    "WWW_CancelProgress",
 465                                    "WWW_Version",
 466                                    "WWW_RegisterViewer",
 467                                    "WWW_UnRegisterViewer",
 468                                    "WWW_GetWindowInfo" };
 469
 470// Static member definitions.
 471int   nsNativeAppSupportWin::mConversations = 0;
 472HSZ   nsNativeAppSupportWin::mApplication   = 0;
 473HSZ   nsNativeAppSupportWin::mTopics[nsNativeAppSupportWin::topicCount] = { 0 };
 474DWORD nsNativeAppSupportWin::mInstance      = 0;
 475bool nsNativeAppSupportWin::mCanHandleRequests   = false;
 476
 477PRUnichar nsNativeAppSupportWin::mMutexName[ 128 ] = { 0 };
 478
 479
 480// Message window encapsulation.
 481struct MessageWindow {
 482    // ctor/dtor are simplistic
 483    MessageWindow() {
 484        // Try to find window.
 485        mHandle = ::FindWindowW( className(), 0 );
 486    }
 487
 488    // Act like an HWND.
 489    operator HWND() {
 490        return mHandle;
 491    }
 492
 493    // Class name: appName + "MessageWindow"
 494    static const PRUnichar *className() {
 495        static PRUnichar classNameBuffer[128];
 496        static PRUnichar *mClassName = 0;
 497        if ( !mClassName ) {
 498            ::_snwprintf(classNameBuffer,
 499                         128,   // size of classNameBuffer in PRUnichars
 500                         L"%s%s",
 501                         NS_ConvertUTF8toUTF16(gAppData->name).get(),
 502                         L"MessageWindow" );
 503            mClassName = classNameBuffer;
 504        }
 505        return mClassName;
 506    }
 507
 508    // Create: Register class and create window.
 509    NS_IMETHOD Create() {
 510        WNDCLASSW classStruct = { 0,                          // style
 511                                 &MessageWindow::WindowProc, // lpfnWndProc
 512                                 0,                          // cbClsExtra
 513                                 0,                          // cbWndExtra
 514                                 0,                          // hInstance
 515                                 0,                          // hIcon
 516                                 0,                          // hCursor
 517                                 0,                          // hbrBackground
 518                                 0,                          // lpszMenuName
 519                                 className() };              // lpszClassName
 520
 521        // Register the window class.
 522        NS_ENSURE_TRUE( ::RegisterClassW( &classStruct ), NS_ERROR_FAILURE );
 523
 524        // Create the window.
 525        NS_ENSURE_TRUE( ( mHandle = ::CreateWindowW(className(),
 526                                                    0,          // title
 527                                                    WS_CAPTION, // style
 528                                                    0,0,0,0,    // x, y, cx, cy
 529                                                    0,          // parent
 530                                                    0,          // menu
 531                                                    0,          // instance
 532                                                    0 ) ),      // create struct
 533                        NS_ERROR_FAILURE );
 534
 535#if MOZ_DEBUG_DDE
 536        printf( "Message window = 0x%08X\n", (int)mHandle );
 537#endif
 538
 539        return NS_OK;
 540    }
 541
 542    // Destory:  Get rid of window and reset mHandle.
 543    NS_IMETHOD Destroy() {
 544        nsresult retval = NS_OK;
 545
 546        if ( mHandle ) {
 547            // DestroyWindow can only destroy windows created from
 548            //  the same thread.
 549            BOOL desRes = DestroyWindow( mHandle );
 550            if ( FALSE != desRes ) {
 551                mHandle = NULL;
 552            }
 553            else {
 554                retval = NS_ERROR_FAILURE;
 555            }
 556        }
 557
 558        return retval;
 559    }
 560
 561    // SendRequest: Pass the command line via WM_COPYDATA to message window.
 562    NS_IMETHOD SendRequest() {
 563        WCHAR *cmd = ::GetCommandLineW();
 564        WCHAR cwd[MAX_PATH];
 565        _wgetcwd(cwd, MAX_PATH);
 566
 567        // Construct a narrow UTF8 buffer <commandline>\0<workingdir>\0
 568        NS_ConvertUTF16toUTF8 utf8buffer(cmd);
 569        utf8buffer.Append('\0');
 570        AppendUTF16toUTF8(cwd, utf8buffer);
 571        utf8buffer.Append('\0');
 572
 573        // We used to set dwData to zero, when we didn't send the working dir.
 574        // Now we're using it as a version number.
 575        COPYDATASTRUCT cds = {
 576            1,
 577            utf8buffer.Length(),
 578            (void*) utf8buffer.get()
 579        };
 580        // Bring the already running Mozilla process to the foreground.
 581        // nsWindow will restore the window (if minimized) and raise it.
 582        ::SetForegroundWindow( mHandle );
 583        ::SendMessage( mHandle, WM_COPYDATA, 0, (LPARAM)&cds );
 584        return NS_OK;
 585    }
 586
 587    // Window proc.
 588    static LRESULT CALLBACK WindowProc( HWND msgWindow, UINT msg, WPARAM wp, LPARAM lp ) {
 589        if ( msg == WM_COPYDATA ) {
 590            if (!nsNativeAppSupportWin::mCanHandleRequests)
 591                return FALSE;
 592
 593            // This is an incoming request.
 594            COPYDATASTRUCT *cds = (COPYDATASTRUCT*)lp;
 595#if MOZ_DEBUG_DDE
 596            printf( "Incoming request: %s\n", (const char*)cds->lpData );
 597#endif
 598            nsCOMPtr<nsILocalFile> workingDir;
 599
 600            if (1 >= cds->dwData) {
 601                char* wdpath = (char*) cds->lpData;
 602                // skip the command line, and get the working dir of the
 603                // other process, which is after the first null char
 604                while (*wdpath)
 605                    ++wdpath;
 606
 607                ++wdpath;
 608
 609#ifdef MOZ_DEBUG_DDE
 610                printf( "Working dir: %s\n", wdpath);
 611#endif
 612
 613                NS_NewLocalFile(NS_ConvertUTF8toUTF16(wdpath),
 614                                false,
 615                                getter_AddRefs(workingDir));
 616            }
 617            (void)nsNativeAppSupportWin::HandleCommandLine((char*)cds->lpData, workingDir, nsICommandLine::STATE_REMOTE_AUTO);
 618
 619            // Get current window and return its window handle.
 620            nsCOMPtr<nsIDOMWindow> win;
 621            GetMostRecentWindow( 0, getter_AddRefs( win ) );
 622            return win ? (LRESULT)hwndForDOMWindow( win ) : 0;
 623        }
 624        return DefWindowProc( msgWindow, msg, wp, lp );
 625    }
 626
 627private:
 628    HWND mHandle;
 629}; // struct MessageWindow
 630
 631/* Start: Tries to find the "message window" to determine if it
 632 *        exists.  If so, then Mozilla is already running.  In that
 633 *        case, we use the handle to the "message" window and send
 634 *        a request corresponding to this process's command line
 635 *        options.
 636 *
 637 *        If not, then this is the first instance of Mozilla.  In
 638 *        that case, we create and set up the message window.
 639 *
 640 *        The checking for existence of the message window must
 641 *        be protected by use of a mutex semaphore.
 642 */
 643NS_IMETHODIMP
 644nsNativeAppSupportWin::Start( bool *aResult ) {
 645    NS_ENSURE_ARG( aResult );
 646    NS_ENSURE_TRUE( mInstance == 0, NS_ERROR_NOT_INITIALIZED );
 647    NS_ENSURE_STATE( gAppData );
 648
 649    if (getenv("MOZ_NO_REMOTE"))
 650    {
 651        *aResult = true;
 652        return NS_OK;
 653    }
 654
 655    nsresult rv = NS_ERROR_FAILURE;
 656    *aResult = false;
 657
 658    // Grab mutex first.
 659
 660    // Build mutex name from app name.
 661    ::_snwprintf(mMutexName, sizeof mMutexName / sizeof(PRUnichar), L"%s%s%s", 
 662                 MOZ_MUTEX_NAMESPACE,
 663                 NS_ConvertUTF8toUTF16(gAppData->name).get(),
 664                 MOZ_STARTUP_MUTEX_NAME );
 665    Mutex startupLock = Mutex( mMutexName );
 666
 667    NS_ENSURE_TRUE( startupLock.Lock( MOZ_DDE_START_TIMEOUT ), NS_ERROR_FAILURE );
 668
 669    // Search for existing message window.
 670    MessageWindow msgWindow;
 671    if ( (HWND)msgWindow ) {
 672        // We are a client process.  Pass request to message window.
 673        rv = msgWindow.SendRequest();
 674    } else {
 675        // We will be server.
 676        rv = msgWindow.Create();
 677        if ( NS_SUCCEEDED( rv ) ) {
 678            // Start up DDE server.
 679            this->StartDDE();
 680            // Tell caller to spin message loop.
 681            *aResult = true;
 682        }
 683    }
 684
 685    startupLock.Unlock();
 686
 687    return rv;
 688}
 689
 690bool
 691nsNativeAppSupportWin::InitTopicStrings() {
 692    for ( int i = 0; i < topicCount; i++ ) {
 693        if ( !( mTopics[ i ] = DdeCreateStringHandleA( mInstance, const_cast<char *>(topicNames[ i ]), CP_WINANSI ) ) ) {
 694            return false;
 695        }
 696    }
 697    return true;
 698}
 699
 700int
 701nsNativeAppSupportWin::FindTopic( HSZ topic ) {
 702    for ( int i = 0; i < topicCount; i++ ) {
 703        if ( DdeCmpStringHandles( topic, mTopics[i] ) == 0 ) {
 704            return i;
 705        }
 706    }
 707    return -1;
 708}
 709
 710
 711// Start DDE server.
 712//
 713// This used to be the Start() method when we were using DDE as the
 714// primary IPC mechanism between secondary Mozilla processes and the
 715// initial "server" process.
 716//
 717// Now, it simply initializes the DDE server.  The caller must check
 718// that this process is to be the server, and, must acquire the DDE
 719// startup mutex semaphore prior to calling this routine.  See ::Start(),
 720// above.
 721NS_IMETHODIMP
 722nsNativeAppSupportWin::StartDDE() {
 723    NS_ENSURE_TRUE( mInstance == 0, NS_ERROR_NOT_INITIALIZED );
 724
 725    // Initialize DDE.
 726    NS_ENSURE_TRUE( DMLERR_NO_ERROR == DdeInitialize( &mInstance,
 727                                                      nsNativeAppSupportWin::HandleDDENotification,
 728                                                      APPCLASS_STANDARD,
 729                                                      0 ),
 730                    NS_ERROR_FAILURE );
 731
 732    // Allocate DDE strings.
 733    NS_ENSURE_TRUE( ( mApplication = DdeCreateStringHandleA( mInstance, (char*) gAppData->name, CP_WINANSI ) ) && InitTopicStrings(),
 734                    NS_ERROR_FAILURE );
 735
 736    // Next step is to register a DDE service.
 737    NS_ENSURE_TRUE( DdeNameService( mInstance, mApplication, 0, DNS_REGISTER ), NS_ERROR_FAILURE );
 738
 739#if MOZ_DEBUG_DDE
 740    printf( "DDE server started\n" );
 741#endif
 742
 743    return NS_OK;
 744}
 745
 746// If no DDE conversations are pending, terminate DDE.
 747NS_IMETHODIMP
 748nsNativeAppSupportWin::Stop( bool *aResult ) {
 749    NS_ENSURE_ARG( aResult );
 750    NS_ENSURE_TRUE( mInstance, NS_ERROR_NOT_INITIALIZED );
 751
 752    nsresult rv = NS_OK;
 753    *aResult = true;
 754
 755    Mutex ddeLock( mMutexName );
 756
 757    if ( ddeLock.Lock( MOZ_DDE_STOP_TIMEOUT ) ) {
 758        if ( mConversations == 0 ) {
 759            this->Quit();
 760        } else {
 761            *aResult = false;
 762        }
 763
 764        ddeLock.Unlock();
 765    }
 766    else {
 767        // No DDE application name specified, but that's OK.  Just
 768        // forge ahead.
 769        *aResult = true;
 770    }
 771
 772    return rv;
 773}
 774
 775NS_IMETHODIMP
 776nsNativeAppSupportWin::Observe(nsISupports* aSubject, const char* aTopic,
 777                               const PRUnichar* aData)
 778{
 779    if (strcmp(aTopic, "quit-application") == 0) {
 780        Quit();
 781    } else {
 782        NS_ERROR("Unexpected observer topic.");
 783    }
 784
 785    return NS_OK;
 786}
 787
 788// Terminate DDE regardless.
 789NS_IMETHODIMP
 790nsNativeAppSupportWin::Quit() {
 791    // If another process wants to look for the message window, they need
 792    // to wait to hold the lock, in which case they will not find the
 793    // window as we will destroy ours under our lock.
 794    // When the mutex goes off the stack, it is unlocked via destructor.
 795    Mutex mutexLock(mMutexName);
 796    NS_ENSURE_TRUE(mutexLock.Lock(MOZ_DDE_START_TIMEOUT), NS_ERROR_FAILURE);
 797
 798    // If we've got a message window to receive IPC or new window requests,
 799    // get rid of it as we are shutting down.
 800    // Note:  Destroy calls DestroyWindow, which will only work on a window
 801    //  created by the same thread.
 802    MessageWindow mw;
 803    mw.Destroy();
 804
 805    if ( mInstance ) {
 806        // Unregister application name.
 807        DdeNameService( mInstance, mApplication, 0, DNS_UNREGISTER );
 808        // Clean up strings.
 809        if ( mApplication ) {
 810            DdeFreeStringHandle( mInstance, mApplication );
 811            mApplication = 0;
 812        }
 813        for ( int i = 0; i < topicCount; i++ ) {
 814            if ( mTopics[i] ) {
 815                DdeFreeStringHandle( mInstance, mTopics[i] );
 816                mTopics[i] = 0;
 817            }
 818        }
 819        DdeUninitialize( mInstance );
 820        mInstance = 0;
 821#if MOZ_DEBUG_DDE
 822    printf( "DDE server stopped\n" );
 823#endif
 824    }
 825
 826    return NS_OK;
 827}
 828
 829NS_IMETHODIMP
 830nsNativeAppSupportWin::Enable()
 831{
 832    mCanHandleRequests = true;
 833
 834    nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
 835    if (obs) {
 836        obs->AddObserver(this, "quit-application", false);
 837    } else {
 838        NS_ERROR("No observer service?");
 839    }
 840
 841    return NS_OK;
 842}
 843
 844#if MOZ_DEBUG_DDE
 845// Macro to generate case statement for a given XTYP value.
 846#define XTYP_CASE(t) \
 847    case t: result = #t; break
 848
 849static nsCString uTypeDesc( UINT uType ) {
 850    nsCString result;
 851    switch ( uType ) {
 852    XTYP_CASE(XTYP_ADVSTART);
 853    XTYP_CASE(XTYP_CONNECT);
 854    XTYP_CASE(XTYP_ADVREQ);
 855    XTYP_CASE(XTYP_REQUEST);
 856    XTYP_CASE(XTYP_WILDCONNECT);
 857    XTYP_CASE(XTYP_ADVDATA);
 858    XTYP_CASE(XTYP_EXECUTE);
 859    XTYP_CASE(XTYP_POKE);
 860    XTYP_CASE(XTYP_ADVSTOP);
 861    XTYP_CASE(XTYP_CONNECT_CONFIRM);
 862    XTYP_CASE(XTYP_DISCONNECT);
 863    XTYP_CASE(XTYP_ERROR);
 864    XTYP_CASE(XTYP_MONITOR);
 865    XTYP_CASE(XTYP_REGISTER);
 866    XTYP_CASE(XTYP_XACT_COMPLETE);
 867    XTYP_CASE(XTYP_UNREGISTER);
 868    default: result = "XTYP_?????";
 869    }
 870    return result;
 871}
 872
 873static nsCString hszValue( DWORD instance, HSZ hsz ) {
 874    // Extract string from HSZ.
 875    nsCString result("[");
 876    DWORD len = DdeQueryString( instance, hsz, NULL, NULL, CP_WINANSI );
 877    if ( len ) {
 878        char buffer[ 256 ];
 879        DdeQueryString( instance, hsz, buffer, sizeof buffer, CP_WINANSI );
 880        result += buffer;
 881    }
 882    result += "]";
 883    return result;
 884}
 885#else
 886// These are purely a safety measure to avoid the infamous "won't
 887// build non-debug" type Tinderbox flames.
 888static nsCString uTypeDesc( UINT ) {
 889    return nsCString( "?" );
 890}
 891static nsCString hszValue( DWORD, HSZ ) {
 892    return nsCString( "?" );
 893}
 894#endif
 895
 896
 897// Utility function to escape double-quotes within a string.
 898static void escapeQuotes( nsAString &aString ) {
 899    PRInt32 offset = -1;
 900    while( 1 ) {
 901       // Find next '"'.
 902       offset = aString.FindChar( '"', ++offset );
 903       if ( offset == kNotFound ) {
 904           // No more quotes, exit.
 905           break;
 906       } else {
 907           // Insert back-slash ahead of the '"'.
 908           aString.Insert( PRUnichar('\\'), offset );
 909           // Increment offset because we just inserted a slash
 910           offset++;
 911       }
 912    }
 913    return;
 914}
 915
 916HDDEDATA CALLBACK
 917nsNativeAppSupportWin::HandleDDENotification( UINT uType,       // transaction type
 918                                              UINT uFmt,        // clipboard data format
 919                                              HCONV hconv,      // handle to the conversation
 920                                              HSZ hsz1,         // handle to a string
 921                                              HSZ hsz2,         // handle to a string
 922                                              HDDEDATA hdata,   // handle to a global memory object
 923                                              ULONG_PTR dwData1,    // transaction-specific data
 924                                              ULONG_PTR dwData2 ) { // transaction-specific data
 925
 926    if (!mCanHandleRequests)
 927        return 0;
 928
 929
 930#if MOZ_DEBUG_DDE
 931    printf( "DDE: uType  =%s\n",      uTypeDesc( uType ).get() );
 932    printf( "     uFmt   =%u\n",      (unsigned)uFmt );
 933    printf( "     hconv  =%08x\n",    (int)hconv );
 934    printf( "     hsz1   =%08x:%s\n", (int)hsz1, hszValue( mInstance, hsz1 ).get() );
 935    printf( "     hsz2   =%08x:%s\n", (int)hsz2, hszValue( mInstance, hsz2 ).get() );
 936    printf( "     hdata  =%08x\n",    (int)hdata );
 937    printf( "     dwData1=%08x\n",    (int)dwData1 );
 938    printf( "     dwData2=%08x\n",    (int)dwData2 );
 939#endif
 940
 941    HDDEDATA result = 0;
 942    if ( uType & XCLASS_BOOL ) {
 943        switch ( uType ) {
 944            case XTYP_CONNECT:
 945                // Make sure its for our service/topic.
 946                if ( FindTopic( hsz1 ) != -1 ) {
 947                    // We support this connection.
 948                    result = (HDDEDATA)1;
 949                }
 950                break;
 951            case XTYP_CONNECT_CONFIRM:
 952                // We don't care about the conversation handle, at this point.
 953                result = (HDDEDATA)1;
 954                break;
 955        }
 956    } else if ( uType & XCLASS_DATA ) {
 957        if ( uType == XTYP_REQUEST ) {
 958            switch ( FindTopic( hsz1 ) ) {
 959                case topicOpenURL: {
 960                    // Open a given URL...
 961
 962                    // Get the URL from the first argument in the command.
 963                    nsAutoString url;
 964                    ParseDDEArg(hsz2, 0, url);
 965
 966                    // Read the 3rd argument in the command to determine if a
 967                    // new window is to be used.
 968                    nsAutoString windowID;
 969                    ParseDDEArg(hsz2, 2, windowID);
 970                    // "" means to open the URL in a new window.
 971                    if ( windowID.IsEmpty() ) {
 972                        url.Insert(NS_LITERAL_STRING("mozilla -new-window "), 0);
 973                    }
 974                    else {
 975                        url.Insert(NS_LITERAL_STRING("mozilla -url "), 0);
 976                    }
 977
 978#if MOZ_DEBUG_DDE
 979                    printf( "Handling dde XTYP_REQUEST request: [%s]...\n", NS_ConvertUTF16toUTF8(url).get() );
 980#endif
 981                    // Now handle it.
 982                    HandleCommandLine(NS_ConvertUTF16toUTF8(url).get(), nsnull, nsICommandLine::STATE_REMOTE_EXPLICIT);
 983
 984                    // Return pseudo window ID.
 985                    result = CreateDDEData( 1 );
 986                    break;
 987                }
 988                case topicGetWindowInfo: {
 989                    // This topic has to get the current URL, get the current
 990                    // page title and then format the output into the DDE
 991                    // return string.  The return value is "URL","Page Title",
 992                    // "Window ID" however the window ID is not used for this
 993                    // command, therefore it is returned as a null string
 994
 995                    // This isn't really a loop.  We just use "break"
 996                    // statements to bypass the remaining steps when
 997                    // something goes wrong.
 998                    do {
 999                        // Get most recently used Nav window.
1000                        nsCOMPtr<nsIDOMWindow> navWin;
1001                        GetMostRecentWindow( NS_LITERAL_STRING( "navigator:browser" ).get(),
1002                                             getter_AddRefs( navWin ) );
1003                        if ( !navWin ) {
1004                            // There is not a window open
1005                            break;
1006                        }
1007                        // Get content window.
1008                        nsCOMPtr<nsIDOMWindow> content;
1009                        navWin->GetContent( getter_AddRefs( content ) );
1010                        if ( !content ) {
1011                            break;
1012                        }
1013                        // Convert that to internal interface.
1014                        nsCOMPtr<nsPIDOMWindow> internalContent( do_QueryInterface( content ) );
1015                        if ( !internalContent ) {
1016                            break;
1017                        }
1018                        // Get location.
1019                        nsCOMPtr<nsIDOMLocation> location;
1020                        internalContent->GetLocation( getter_AddRefs( location ) );
1021                        if ( !location ) {
1022                            break;
1023                        }
1024                        // Get href for URL.
1025                        nsAutoString url;
1026                        if ( NS_FAILED( location->GetHref( url ) ) ) {
1027                            break;
1028                        }
1029                        // Escape any double-quotes.
1030                        escapeQuotes( url );
1031
1032                        // Now for the title...
1033
1034                        // Get the base window from the doc shell...
1035                        nsCOMPtr<nsIBaseWindow> baseWindow =
1036                          do_QueryInterface( internalContent->GetDocShell() );
1037                        if ( !baseWindow ) {
1038                            break;
1039                        }
1040                        // And from the base window we can get the title.
1041                        nsXPIDLString title;
1042                        if(!baseWindow) {
1043                            break;
1044                        }
1045                        baseWindow->GetTitle(getter_Copies(title));
1046                        // Escape any double-quotes in the title.
1047                        escapeQuotes( title );
1048
1049                        // Use a string buffer for the output data, first
1050                        // save a quote.
1051                        nsCAutoString   outpt( NS_LITERAL_CSTRING("\"") );
1052                        // Now copy the URL converting the Unicode string
1053                        // to a single-byte ASCII string
1054                        nsCAutoString tmpNativeStr;
1055                        NS_CopyUnicodeToNative( url, tmpNativeStr );
1056                        outpt.Append( tmpNativeStr );
1057                        // Add the "," used to separate the URL and the page
1058                        // title
1059                        outpt.Append( NS_LITERAL_CSTRING("\",\"") );
1060                        // Now copy the current page title to the return string
1061                        NS_CopyUnicodeToNative( title, tmpNativeStr );
1062                        outpt.Append( tmpNativeStr );
1063                        // Fill out the return string with the remainin ",""
1064                        outpt.Append( NS_LITERAL_CSTRING( "\",\"\"" ));
1065
1066                        // Create a DDE handle to a char string for the data
1067                        // being returned, this copies and creates a "shared"
1068                        // copy of the DDE response until the calling APP
1069                        // reads it and says it can be freed.
1070                        result = CreateDDEData( (LPBYTE)(const char*)outpt.get(),
1071                                                outpt.Length() + 1 );
1072#if MOZ_DEBUG_DDE
1073                        printf( "WWW_GetWindowInfo->%s\n", outpt.get() );
1074#endif
1075                    } while ( false );
1076                    break;
1077                }
1078                case topicActivate: {
1079                    // Activate a Nav window...
1080                    nsAutoString windowID;
1081                    ParseDDEArg(hsz2, 0, windowID);
1082                    // 4294967295 is decimal for 0xFFFFFFFF which is also a
1083                    //   correct value to do that Activate last window stuff
1084                    if ( windowID.EqualsLiteral( "-1" ) ||
1085                         windowID.EqualsLiteral( "4294967295" ) ) {
1086                        // We only support activating the most recent window (or a new one).
1087                        ActivateLastWindow();
1088                        // Return pseudo window ID.
1089                        result = CreateDDEData( 1 );
1090                    }
1091                    break;
1092                }
1093                case topicVersion: {
1094                    // Return version.  We're restarting at 1.0!
1095                    DWORD version = 1 << 16; // "1.0"
1096                    result = CreateDDEData( version );
1097                    break;
1098                }
1099                case topicRegisterViewer: {
1100                    // Register new viewer (not implemented).
1101                    result = CreateDDEData( false );
1102                    break;
1103                }
1104                case topicUnRegisterViewer: {
1105                    // Unregister new viewer (not implemented).
1106                    result = CreateDDEData( false );
1107                    break;
1108                }
1109                default:
1110                    break;
1111            }
1112        } else if ( uType & XTYP_POKE ) {
1113            switch ( FindTopic( hsz1 ) ) {
1114                case topicCancelProgress: {
1115                    // "Handle" progress cancel (actually, pretty much ignored).
1116                    result = (HDDEDATA)DDE_FACK;
1117                    break;
1118                }
1119                default:
1120                    break;
1121            }
1122        }
1123    } else if ( uType & XCLASS_FLAGS ) {
1124        if ( uType == XTYP_EXECUTE ) {
1125            // Prove that we received the request.
1126            DWORD bytes;
1127            LPBYTE request = DdeAccessData( hdata, &bytes );
1128#if MOZ_DEBUG_DDE
1129            printf( "Handling dde request: [%s]...\n", (char*)request );
1130#endif
1131
1132            nsAutoString url;
1133            ParseDDEArg((const WCHAR*) request, 0, url);
1134
1135            // Read the 3rd argument in the command to determine if a
1136            // new window is to be used.
1137            nsAutoString windowID;
1138            ParseDDEArg((const WCHAR*) request, 2, windowID);
1139
1140            // "" means to open the URL in a new window.
1141            if ( windowID.IsEmpty() ) {
1142                url.Insert(NS_LITERAL_STRING("mozilla -new-window "), 0);
1143            }
1144            else {
1145                url.Insert(NS_LITERAL_STRING("mozilla -url "), 0);
1146            }
1147#if MOZ_DEBUG_DDE
1148            printf( "Handling dde XTYP_REQUEST request: [%s]...\n", NS_ConvertUTF16toUTF8(url).get() );
1149#endif
1150            // Now handle it.
1151            HandleCommandLine(NS_ConvertUTF16toUTF8(url).get(), nsnull, nsICommandLine::STATE_REMOTE_EXPLICIT);
1152
1153            // Release the data.
1154            DdeUnaccessData( hdata );
1155            result = (HDDEDATA)DDE_FACK;
1156        } else {
1157            result = (HDDEDATA)DDE_FNOTPROCESSED;
1158        }
1159    } else if ( uType & XCLASS_NOTIFICATION ) {
1160    }
1161#if MOZ_DEBUG_DDE
1162    printf( "DDE result=%d (0x%08X)\n", (int)result, (int)result );
1163#endif
1164    return result;
1165}
1166
1167// Utility function to advance to end of quoted string.
1168// p+offset must point to the comma preceding the arg on entry.
1169// On return, p+result points to the closing '"' (or end of the string
1170// if the closing '"' is missing) if the arg is quoted.  If the arg
1171// is not quoted, then p+result will point to the first character
1172// of the arg.
1173static PRInt32 advanceToEndOfQuotedArg( const WCHAR *p, PRInt32 offset, PRInt32 len ) {
1174    // Check whether the current arg is quoted.
1175    if ( p[++offset] == '"' ) {
1176        // Advance past the closing quote.
1177        while ( offset < len && p[++offset] != '"' ) {
1178            // If the current character is a backslash, then the
1179            // next character can't be a *real* '"', so skip it.
1180            if ( p[offset] == '\\' ) {
1181                offset++;
1182            }
1183        }
1184    }
1185    return offset;
1186}
1187
1188void nsNativeAppSupportWin::ParseDDEArg( const WCHAR* args, int index, nsString& aString) {
1189    if ( args ) {
1190        nsDependentString temp(args);
1191
1192        // offset points to the comma preceding the desired arg.
1193        PRInt32 offset = -1;
1194        // Skip commas till we get to the arg we want.
1195        while( index-- ) {
1196            // If this arg is quoted, then go to closing quote.
1197            offset = advanceToEndOfQuotedArg( args, offset, temp.Length());
1198            // Find next comma.
1199            offset = temp.FindChar( ',', offset );
1200            if ( offset == kNotFound ) {
1201                // No more commas, give up.
1202                aString = args;
1203                return;
1204            }
1205        }
1206        // The desired argument starts just past the preceding comma,
1207        // which offset points to, and extends until the following
1208        // comma (or the end of the string).
1209        //
1210        // Since the argument might be enclosed in quotes, we need to
1211        // deal with that before searching for the terminating comma.
1212        // We advance offset so it ends up pointing to the start of
1213        // the argument we want.
1214        PRInt32 end = advanceToEndOfQuotedArg( args, offset++, temp.Length() );
1215        // Find next comma (or end of string).
1216        end = temp.FindChar( ',', end );
1217        if ( end == kNotFound ) {
1218            // Arg is the rest of the string.
1219            end = temp.Length();
1220        }
1221        // Extract result.
1222        aString.Assign( args + offset, end - offset );
1223    }
1224    return;
1225}
1226
1227// Utility to parse out argument from a DDE item string.
1228void nsNativeAppSupportWin::ParseDDEArg( HSZ args, int index, nsString& aString) {
1229    DWORD argLen = DdeQueryStringW( mInstance, args, NULL, 0, CP_WINUNICODE );
1230    // there wasn't any string, so return empty string
1231    if ( !argLen ) return;
1232    nsAutoString temp;
1233    // Ensure result's buffer is sufficiently big.
1234    temp.SetLength( argLen );
1235    // Now get the string contents.
1236    DdeQueryString( mInstance, args, temp.BeginWriting(), temp.Length(), CP_WINUNICODE );
1237    // Parse out the given arg.
1238    ParseDDEArg(temp.get(), index, aString);
1239    return;
1240}
1241
1242HDDEDATA nsNativeAppSupportWin::CreateDDEData( DWORD value ) {
1243    return CreateDDEData( (LPBYTE)&value, sizeof value );
1244}
1245
1246HDDEDATA nsNativeAppSupportWin::CreateDDEData( LPBYTE value, DWORD len ) {
1247    HDDEDATA result = DdeCreateDataHandle( mInstance,
1248                                           value,
1249                                           len,
1250                                           0,
1251                                           mApplication,
1252                                           CF_TEXT,
1253                                           0 );
1254    return result;
1255}
1256
1257void nsNativeAppSupportWin::ActivateLastWindow() {
1258    nsCOMPtr<nsIDOMWindow> navWin;
1259    GetMostRecentWindow( NS_LITERAL_STRING("navigator:browser").get(), getter_AddRefs( navWin ) );
1260    if ( navWin ) {
1261        // Activate that window.
1262        activateWindow( navWin );
1263    } else {
1264        // Need to create a Navigator window, then.
1265        OpenBrowserWindow();
1266    }
1267}
1268
1269void
1270nsNativeAppSupportWin::HandleCommandLine(const char* aCmdLineString,
1271                                         nsIFile* aWorkingDir,
1272                                         PRUint32 aState)
1273{
1274    nsresult rv;
1275
1276    int justCounting = 1;
1277    char **argv = 0;
1278    // Flags, etc.
1279    int init = 1;
1280    int between, quoted, bSlashCount;
1281    int argc;
1282    const char *p;
1283    nsCAutoString arg;
1284
1285    nsCOMPtr<nsICommandLineRunner> cmdLine
1286        (do_CreateInstance("@mozilla.org/toolkit/command-line;1"));
1287    if (!cmdLine) {
1288        NS_ERROR("Couldn't create command line!");
1289        return;
1290    }
1291
1292    // Parse command line args according to MS spec
1293    // (see "Parsing C++ Command-Line Arguments" at
1294    // http://msdn.microsoft.com/library/devprods/vs6/visualc/vclang/_pluslang_parsing_c.2b2b_.command.2d.line_arguments.htm).
1295    // We loop if we've not finished the second pass through.
1296    while ( 1 ) {
1297        // Initialize if required.
1298        if ( init ) {
1299            p = aCmdLineString;
1300            between = 1;
1301            argc = quoted = bSlashCount = 0;
1302
1303            init = 0;
1304        }
1305        if ( between ) {
1306            // We are traversing whitespace between args.
1307            // Check for start of next arg.
1308            if (  *p != 0 && !isspace( *p ) ) {
1309                // Start of another arg.
1310                between = 0;
1311                arg = "";
1312                switch ( *p ) {
1313                    case '\\':
1314                        // Count the backslash.
1315                        bSlashCount = 1;
1316                        break;
1317                    case '"':
1318                        // Remember we're inside quotes.
1319                        quoted = 1;
1320                        break;
1321                    default:
1322                        // Add character to arg.
1323                        arg += *p;
1324                        break;
1325                }
1326            } else {
1327                // Another space between args, ignore it.
1328            }
1329        } else {
1330            // We are processing the contents of an argument.
1331            // Check for whitespace or end.
1332            if ( *p == 0 || ( !quoted && isspace( *p ) ) ) {
1333                // Process pending backslashes (interpret them
1334                // literally since they're not followed by a ").
1335                while( bSlashCount ) {
1336                    arg += '\\';
1337                    bSlashCount--;
1338                }
1339                // End current arg.
1340                if ( !justCounting ) {
1341                    argv[argc] = new char[ arg.Length() + 1 ];
1342                    strcpy( argv[argc], arg.get() );
1343                }
1344                argc++;
1345                // We're now between args.
1346                between = 1;
1347            } else {
1348                

Large files files are truncated, but you can click here to view the full file