toolkit/xre/nsNativeAppSupportWin.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

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

mercurial