diff options
author | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
---|---|---|
committer | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
commit | 5f8de423f190bbb79a62f804151bc24824fa32d8 (patch) | |
tree | 10027f336435511475e392454359edea8e25895d /toolkit/xre/nsNativeAppSupportWin.cpp | |
parent | 49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff) | |
download | UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip |
Add m-esr52 at 52.6.0
Diffstat (limited to 'toolkit/xre/nsNativeAppSupportWin.cpp')
-rw-r--r-- | toolkit/xre/nsNativeAppSupportWin.cpp | 1541 |
1 files changed, 1541 insertions, 0 deletions
diff --git a/toolkit/xre/nsNativeAppSupportWin.cpp b/toolkit/xre/nsNativeAppSupportWin.cpp new file mode 100644 index 000000000..0605ccb5a --- /dev/null +++ b/toolkit/xre/nsNativeAppSupportWin.cpp @@ -0,0 +1,1541 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsNativeAppSupportBase.h" +#include "nsNativeAppSupportWin.h" +#include "nsAppRunner.h" +#include "nsXULAppAPI.h" +#include "nsString.h" +#include "nsIBrowserDOMWindow.h" +#include "nsICommandLineRunner.h" +#include "nsCOMPtr.h" +#include "nsXPIDLString.h" +#include "nsIComponentManager.h" +#include "nsIServiceManager.h" +#include "nsIDOMChromeWindow.h" +#include "nsXPCOM.h" +#include "nsISupportsPrimitives.h" +#include "nsIWindowWatcher.h" +#include "nsPIDOMWindow.h" +#include "nsGlobalWindow.h" +#include "nsIDocShell.h" +#include "nsIDocShellTreeItem.h" +#include "nsIBaseWindow.h" +#include "nsIWidget.h" +#include "nsIAppShellService.h" +#include "nsIXULWindow.h" +#include "nsIInterfaceRequestor.h" +#include "nsIInterfaceRequestorUtils.h" +#include "nsIPromptService.h" +#include "nsNetCID.h" +#include "nsNetUtil.h" +#include "mozilla/Services.h" +#include "nsIFile.h" +#include "nsIObserver.h" +#include "nsIObserverService.h" +#include "nsIDOMLocation.h" +#include "nsIWebNavigation.h" +#include "nsIWindowMediator.h" +#include "nsNativeCharsetUtils.h" +#include "nsIAppStartup.h" + +#include <windows.h> +#include <shellapi.h> +#include <ddeml.h> +#include <stdlib.h> +#include <stdio.h> +#include <io.h> +#include <direct.h> +#include <fcntl.h> + +using namespace mozilla; + +static HWND hwndForDOMWindow( mozIDOMWindowProxy * ); + +static +nsresult +GetMostRecentWindow(const char16_t* aType, mozIDOMWindowProxy** aWindow) { + nsresult rv; + nsCOMPtr<nsIWindowMediator> med( do_GetService( NS_WINDOWMEDIATOR_CONTRACTID, &rv ) ); + if ( NS_FAILED( rv ) ) + return rv; + + if ( med ) + return med->GetMostRecentWindow( aType, aWindow ); + + return NS_ERROR_FAILURE; +} + +static +void +activateWindow( mozIDOMWindowProxy *win ) { + // Try to get native window handle. + HWND hwnd = hwndForDOMWindow( win ); + if ( hwnd ) { + // Restore the window if it is minimized. + if ( ::IsIconic( hwnd ) ) { + ::ShowWindow( hwnd, SW_RESTORE ); + } + // Use the OS call, if possible. + ::SetForegroundWindow( hwnd ); + } else { + // Use internal method. + nsCOMPtr<nsPIDOMWindowOuter> piWin = nsPIDOMWindowOuter::From(win); + piWin->Focus(); + } +} + + +#ifdef DEBUG_law +#undef MOZ_DEBUG_DDE +#define MOZ_DEBUG_DDE 1 +#endif + +// Simple Win32 mutex wrapper. +struct Win32Mutex { + Win32Mutex( const char16_t *name ) + : mName( name ), + mHandle( 0 ), + mState( -1 ) { + mHandle = CreateMutexW( 0, FALSE, mName.get() ); +#if MOZ_DEBUG_DDE + printf( "CreateMutex error = 0x%08X\n", (int)GetLastError() ); +#endif + } + ~Win32Mutex() { + if ( mHandle ) { + // Make sure we release it if we own it. + Unlock(); + + BOOL rc = CloseHandle( mHandle ); +#if MOZ_DEBUG_DDE + if ( !rc ) { + printf( "CloseHandle error = 0x%08X\n", (int)GetLastError() ); + } +#endif + } + } + BOOL Lock( DWORD timeout ) { + if ( mHandle ) { +#if MOZ_DEBUG_DDE + printf( "Waiting (%d msec) for DDE mutex...\n", (int)timeout ); +#endif + mState = WaitForSingleObject( mHandle, timeout ); +#if MOZ_DEBUG_DDE + printf( "...wait complete, result = 0x%08X, GetLastError=0x%08X\n", (int)mState, (int)::GetLastError() ); +#endif + return mState == WAIT_OBJECT_0 || mState == WAIT_ABANDONED; + } else { + return FALSE; + } + } + void Unlock() { + if ( mHandle && mState == WAIT_OBJECT_0 ) { +#if MOZ_DEBUG_DDE + printf( "Releasing DDE mutex\n" ); +#endif + ReleaseMutex( mHandle ); + mState = -1; + } + } +private: + nsString mName; + HANDLE mHandle; + DWORD mState; +}; + +/* DDE Notes + * + * This section describes the Win32 DDE service implementation for + * Mozilla. DDE is used on Win32 platforms to communicate between + * separate instances of mozilla.exe (or other Mozilla-based + * executables), or, between the Win32 desktop shell and Mozilla. + * + * The first instance of Mozilla will become the "server" and + * subsequent executables (and the shell) will use DDE to send + * requests to that process. The requests are DDE "execute" requests + * that pass the command line arguments. + * + * Mozilla registers the DDE application "Mozilla" and currently + * supports only the "WWW_OpenURL" topic. This should be reasonably + * compatible with applications that interfaced with Netscape + * Communicator (and its predecessors?). Note that even that topic + * may not be supported in a compatible fashion as the command-line + * options for Mozilla are different than for Communiator. + * + * It is imperative that at most one instance of Mozilla execute in + * "server mode" at any one time. The "native app support" in Mozilla + * on Win32 ensures that only the server process performs XPCOM + * initialization (that is not required for subsequent client processes + * to communicate with the server process). + * + * To guarantee that only one server starts up, a Win32 "mutex" is used + * to ensure only one process executes the server-detection code. That + * code consists of initializing DDE and doing a DdeConnect to Mozilla's + * application/topic. If that connection succeeds, then a server process + * must be running already. + * + * Otherwise, no server has started. In that case, the current process + * calls DdeNameService to register that application/topic. Only at that + * point does the mutex get released. + * + * There are a couple of subtleties that one should be aware of: + * + * 1. It is imperative that DdeInitialize be called only after the mutex + * lock has been obtained. The reason is that at shutdown, DDE + * notifications go out to all initialized DDE processes. Thus, if + * the mutex is owned by a terminating intance of Mozilla, then + * calling DdeInitialize and then WaitForSingleObject will cause the + * DdeUninitialize from the terminating process to "hang" until the + * process waiting for the mutex times out (and can then service the + * notification that the DDE server is terminating). So, don't mess + * with the sequence of things in the startup/shutdown logic. + * + * 2. All mutex requests are made with a reasonably long timeout value and + * are designed to "fail safe" (i.e., a timeout is treated as failure). + * + * 3. An attempt has been made to minimize the degree to which the main + * Mozilla application logic needs to be aware of the DDE mechanisms + * implemented herein. As a result, this module surfaces a very + * large-grained interface, consisting of simple start/stop methods. + * As a consequence, details of certain scenarios can be "lost." + * Particularly, incoming DDE requests can arrive after this module + * initiates the DDE server, but before Mozilla is initialized to the + * point where those requests can be serviced (e.g., open a browser + * window to a particular URL). Since the client process sends the + * request early on, it may not be prepared to respond to that error. + * Thus, such situations may fail silently. The design goal is that + * they fail harmlessly. Refinements on this point will be made as + * details emerge (and time permits). + */ + +/* Update 2001 March + * + * A significant DDE bug in Windows is causing Mozilla to get wedged at + * startup. This is detailed in Bugzill bug 53952 + * (http://bugzilla.mozilla.org/show_bug.cgi?id=53952). + * + * To resolve this, we are using a new strategy: + * o Use a "message window" to detect that Mozilla is already running and + * to pass requests from a second instance back to the first; + * o Run only as a "DDE server" (not as DDE client); this avoids the + * problematic call to DDEConnect(). + * + * We still use the mutex semaphore to protect the code that detects + * whether Mozilla is already running. + */ + +/* Update 2007 January + * + * A change in behavior was implemented in July 2004 which made the + * application on launch to add and on quit to remove the ddexec registry key. + * See bug 246078. + * Windows Vista has changed the methods used to set an application as default + * and the new methods are incompatible with removing the ddeexec registry key. + * See bug 353089. + * + * OS DDE Sequence: + * 1. OS checks if the dde name is registered. + * 2. If it is registered the OS sends a DDE request with the WWW_OpenURL topic + * and the params as specified in the default value of the ddeexec registry + * key for the verb (e.g. open). + * 3. If it isn't registered the OS launches the executable defined in the + * verb's (e.g. open) command registry key. + * 4. If the ifexec registry key is not present the OS sends a DDE request with + * the WWW_OpenURL topic and the params as specified in the default value of + * the ddeexec registry key for the verb (e.g. open). + * 5. If the ifexec registry key is present the OS sends a DDE request with the + * WWW_OpenURL topic and the params as specified in the ifexec registry key + * for the verb (e.g. open). + * + * Application DDE Sequence: + * 1. If the application is running a DDE request is received with the + * WWW_OpenURL topic and the params as specified in the default value of the + * ddeexec registry key (e.g. "%1",,0,0,,,, where '%1' is the url to open) + * for the verb (e.g. open). + * 2. If the application is not running it is launched with the --requestPending + * and the --url argument. + * 2.1 If the application does not need to restart and the --requestPending + * argument is present the accompanying url will not be used. Instead the + * application will wait for the DDE message to open the url. + * 2.2 If the application needs to restart the --requestPending argument is + * removed from the arguments used to restart the application and the url + * will be handled normally. + * + * Note: Due to a bug in IE the ifexec key should not be used (see bug 355650). + */ + +class nsNativeAppSupportWin : public nsNativeAppSupportBase, + public nsIObserver +{ +public: + NS_DECL_NSIOBSERVER + NS_DECL_ISUPPORTS_INHERITED + + // Overrides of base implementation. + NS_IMETHOD Start( bool *aResult ); + NS_IMETHOD Stop( bool *aResult ); + NS_IMETHOD Quit(); + NS_IMETHOD Enable(); + // The "old" Start method (renamed). + NS_IMETHOD StartDDE(); + // Utility function to handle a Win32-specific command line + // option: "--console", which dynamically creates a Windows + // console. + void CheckConsole(); + +private: + ~nsNativeAppSupportWin() {} + static void HandleCommandLine(const char* aCmdLineString, nsIFile* aWorkingDir, uint32_t aState); + static HDDEDATA CALLBACK HandleDDENotification( UINT uType, + UINT uFmt, + HCONV hconv, + HSZ hsz1, + HSZ hsz2, + HDDEDATA hdata, + ULONG_PTR dwData1, + ULONG_PTR dwData2 ); + static void ParseDDEArg( HSZ args, int index, nsString& string); + static void ParseDDEArg( const WCHAR* args, int index, nsString& aString); + static HDDEDATA CreateDDEData( DWORD value ); + static HDDEDATA CreateDDEData( LPBYTE value, DWORD len ); + static bool InitTopicStrings(); + static int FindTopic( HSZ topic ); + static void ActivateLastWindow(); + static nsresult OpenWindow( const char *urlstr, const char *args ); + static nsresult OpenBrowserWindow(); + static void SetupSysTrayIcon(); + static void RemoveSysTrayIcon(); + + static int mConversations; + enum { + topicOpenURL, + topicActivate, + topicCancelProgress, + topicVersion, + topicRegisterViewer, + topicUnRegisterViewer, + topicGetWindowInfo, + // Note: Insert new values above this line!!!!! + topicCount // Count of the number of real topics + }; + static HSZ mApplication, mTopics[ topicCount ]; + static DWORD mInstance; + static bool mCanHandleRequests; + static char16_t mMutexName[]; + friend struct MessageWindow; +}; // nsNativeAppSupportWin + +NS_INTERFACE_MAP_BEGIN(nsNativeAppSupportWin) + NS_INTERFACE_MAP_ENTRY(nsIObserver) +NS_INTERFACE_MAP_END_INHERITING(nsNativeAppSupportBase) + +NS_IMPL_ADDREF_INHERITED(nsNativeAppSupportWin, nsNativeAppSupportBase) +NS_IMPL_RELEASE_INHERITED(nsNativeAppSupportWin, nsNativeAppSupportBase) + +void +UseParentConsole() +{ + // Try to attach console to the parent process. + // It will succeed when the parent process is a command line, + // so that stdio will be displayed in it. + if (AttachConsole(ATTACH_PARENT_PROCESS)) { + // Change std handles to refer to new console handles. + // Before doing so, ensure that stdout/stderr haven't been + // redirected to a valid file. + // The return value for _fileno(<a std handle>) for GUI apps was changed over. + // Until VC7, it was -1. Starting from VC8, it was changed to -2. + // http://msdn.microsoft.com/en-us/library/zs6wbdhx%28v=vs.80%29.aspx + // Starting from VC11, the return value was cahnged to 0 for stdin, + // 1 for stdout, 2 for stdout. Accroding to Microsoft, this is a bug + // which will be fixed in VC14. + // https://connect.microsoft.com/VisualStudio/feedback/details/785119/ + // Although the document does not make it explicit, it looks like + // the return value from _get_osfhandle(_fileno(<a std handle>)) also + // changed to -2 and VC11 and 12 do not have a bug about _get_osfhandle(). + // We support VC10 or later, so it's sufficient to compare the return + // value with -2. + if (_fileno(stdout) == -2 || + _get_osfhandle(fileno(stdout)) == -2) + freopen("CONOUT$", "w", stdout); + // Merge stderr into CONOUT$ since there isn't any `CONERR$`. + // http://msdn.microsoft.com/en-us/library/windows/desktop/ms683231%28v=vs.85%29.aspx + if (_fileno(stderr) == -2 || + _get_osfhandle(fileno(stderr)) == -2) + freopen("CONOUT$", "w", stderr); + if (_fileno(stdin) == -2 || _get_osfhandle(fileno(stdin)) == -2) + freopen("CONIN$", "r", stdin); + } +} + +void +nsNativeAppSupportWin::CheckConsole() { + for ( int i = 1; i < gArgc; i++ ) { + if ( strcmp( "-console", gArgv[i] ) == 0 || + strcmp( "--console", gArgv[i] ) == 0 || + strcmp( "/console", gArgv[i] ) == 0 ) { + // Users wants to make sure we have a console. + // Try to allocate one. + BOOL rc = ::AllocConsole(); + if ( rc ) { + // Console allocated. Fix it up so that output works in + // all cases. See http://support.microsoft.com/support/kb/articles/q105/3/05.asp. + + // stdout + int hCrt = ::_open_osfhandle( (intptr_t)GetStdHandle( STD_OUTPUT_HANDLE ), + _O_TEXT ); + if ( hCrt != -1 ) { + FILE *hf = ::_fdopen( hCrt, "w" ); + if ( hf ) { + *stdout = *hf; +#ifdef DEBUG + ::fprintf( stdout, "stdout directed to dynamic console\n" ); +#endif + } + } + + // stderr + hCrt = ::_open_osfhandle( (intptr_t)::GetStdHandle( STD_ERROR_HANDLE ), + _O_TEXT ); + if ( hCrt != -1 ) { + FILE *hf = ::_fdopen( hCrt, "w" ); + if ( hf ) { + *stderr = *hf; +#ifdef DEBUG + ::fprintf( stderr, "stderr directed to dynamic console\n" ); +#endif + } + } + + // stdin? + /* Don't bother for now. + hCrt = ::_open_osfhandle( (long)::GetStdHandle( STD_INPUT_HANDLE ), + _O_TEXT ); + if ( hCrt != -1 ) { + FILE *hf = ::_fdopen( hCrt, "r" ); + if ( hf ) { + *stdin = *hf; + } + } + */ + } else { + // Failed. Probably because there already is one. + // There's little we can do, in any case. + } + // Remove the console argument from the command line. + do { + gArgv[i] = gArgv[i + 1]; + ++i; + } while (gArgv[i]); + + --gArgc; + + } else if ( strcmp( "-attach-console", gArgv[i] ) == 0 + || + strcmp( "/attach-console", gArgv[i] ) == 0 ) { + UseParentConsole(); + } + } + + return; +} + + +// Create and return an instance of class nsNativeAppSupportWin. +nsresult +NS_CreateNativeAppSupport( nsINativeAppSupport **aResult ) { + nsNativeAppSupportWin *pNative = new nsNativeAppSupportWin; + if (!pNative) return NS_ERROR_OUT_OF_MEMORY; + + // Check for dynamic console creation request. + pNative->CheckConsole(); + + *aResult = pNative; + NS_ADDREF( *aResult ); + + return NS_OK; +} + +// Constants +#define MOZ_DDE_APPLICATION "Mozilla" +#define MOZ_MUTEX_NAMESPACE L"Local\\" +#define MOZ_STARTUP_MUTEX_NAME L"StartupMutex" +#define MOZ_DDE_START_TIMEOUT 30000 +#define MOZ_DDE_STOP_TIMEOUT 15000 +#define MOZ_DDE_EXEC_TIMEOUT 15000 + +// The array entries must match the enum ordering! +const char * const topicNames[] = { "WWW_OpenURL", + "WWW_Activate", + "WWW_CancelProgress", + "WWW_Version", + "WWW_RegisterViewer", + "WWW_UnRegisterViewer", + "WWW_GetWindowInfo" }; + +// Static member definitions. +int nsNativeAppSupportWin::mConversations = 0; +HSZ nsNativeAppSupportWin::mApplication = 0; +HSZ nsNativeAppSupportWin::mTopics[nsNativeAppSupportWin::topicCount] = { 0 }; +DWORD nsNativeAppSupportWin::mInstance = 0; +bool nsNativeAppSupportWin::mCanHandleRequests = false; + +char16_t nsNativeAppSupportWin::mMutexName[ 128 ] = { 0 }; + + +// Message window encapsulation. +struct MessageWindow { + // ctor/dtor are simplistic + MessageWindow() { + // Try to find window. + mHandle = ::FindWindowW( className(), 0 ); + } + + // Act like an HWND. + operator HWND() { + return mHandle; + } + + // Class name: appName + "MessageWindow" + static const wchar_t *className() { + static wchar_t classNameBuffer[128]; + static wchar_t *mClassName = 0; + if ( !mClassName ) { + ::_snwprintf(classNameBuffer, + 128, // size of classNameBuffer in PRUnichars + L"%s%s", + static_cast<const wchar_t*>(NS_ConvertUTF8toUTF16(gAppData->remotingName).get()), + L"MessageWindow" ); + mClassName = classNameBuffer; + } + return mClassName; + } + + // Create: Register class and create window. + NS_IMETHOD Create() { + WNDCLASSW classStruct = { 0, // style + &MessageWindow::WindowProc, // lpfnWndProc + 0, // cbClsExtra + 0, // cbWndExtra + 0, // hInstance + 0, // hIcon + 0, // hCursor + 0, // hbrBackground + 0, // lpszMenuName + className() }; // lpszClassName + + // Register the window class. + NS_ENSURE_TRUE( ::RegisterClassW( &classStruct ), NS_ERROR_FAILURE ); + + // Create the window. + NS_ENSURE_TRUE( ( mHandle = ::CreateWindowW(className(), + 0, // title + WS_CAPTION, // style + 0,0,0,0, // x, y, cx, cy + 0, // parent + 0, // menu + 0, // instance + 0 ) ), // create struct + NS_ERROR_FAILURE ); + +#if MOZ_DEBUG_DDE + printf( "Message window = 0x%08X\n", (int)mHandle ); +#endif + + return NS_OK; + } + + // Destory: Get rid of window and reset mHandle. + NS_IMETHOD Destroy() { + nsresult retval = NS_OK; + + if ( mHandle ) { + // DestroyWindow can only destroy windows created from + // the same thread. + BOOL desRes = DestroyWindow( mHandle ); + if ( FALSE != desRes ) { + mHandle = nullptr; + } + else { + retval = NS_ERROR_FAILURE; + } + } + + return retval; + } + + // SendRequest: Pass the command line via WM_COPYDATA to message window. + NS_IMETHOD SendRequest() { + WCHAR *cmd = ::GetCommandLineW(); + WCHAR cwd[MAX_PATH]; + _wgetcwd(cwd, MAX_PATH); + + // Construct a narrow UTF8 buffer <commandline>\0<workingdir>\0 + NS_ConvertUTF16toUTF8 utf8buffer(cmd); + utf8buffer.Append('\0'); + AppendUTF16toUTF8(cwd, utf8buffer); + utf8buffer.Append('\0'); + + // We used to set dwData to zero, when we didn't send the working dir. + // Now we're using it as a version number. + COPYDATASTRUCT cds = { + 1, + utf8buffer.Length(), + (void*) utf8buffer.get() + }; + // Bring the already running Mozilla process to the foreground. + // nsWindow will restore the window (if minimized) and raise it. + ::SetForegroundWindow( mHandle ); + ::SendMessage( mHandle, WM_COPYDATA, 0, (LPARAM)&cds ); + return NS_OK; + } + + // Window proc. + static LRESULT CALLBACK WindowProc( HWND msgWindow, UINT msg, WPARAM wp, LPARAM lp ) { + if ( msg == WM_COPYDATA ) { + if (!nsNativeAppSupportWin::mCanHandleRequests) + return FALSE; + + // This is an incoming request. + COPYDATASTRUCT *cds = (COPYDATASTRUCT*)lp; +#if MOZ_DEBUG_DDE + printf( "Incoming request: %s\n", (const char*)cds->lpData ); +#endif + nsCOMPtr<nsIFile> workingDir; + + if (1 >= cds->dwData) { + char* wdpath = (char*) cds->lpData; + // skip the command line, and get the working dir of the + // other process, which is after the first null char + while (*wdpath) + ++wdpath; + + ++wdpath; + +#ifdef MOZ_DEBUG_DDE + printf( "Working dir: %s\n", wdpath); +#endif + + NS_NewLocalFile(NS_ConvertUTF8toUTF16(wdpath), + false, + getter_AddRefs(workingDir)); + } + (void)nsNativeAppSupportWin::HandleCommandLine((char*)cds->lpData, workingDir, nsICommandLine::STATE_REMOTE_AUTO); + + // Get current window and return its window handle. + nsCOMPtr<mozIDOMWindowProxy> win; + GetMostRecentWindow( 0, getter_AddRefs( win ) ); + return win ? (LRESULT)hwndForDOMWindow( win ) : 0; + } + return DefWindowProc( msgWindow, msg, wp, lp ); + } + +private: + HWND mHandle; +}; // struct MessageWindow + +/* Start: Tries to find the "message window" to determine if it + * exists. If so, then Mozilla is already running. In that + * case, we use the handle to the "message" window and send + * a request corresponding to this process's command line + * options. + * + * If not, then this is the first instance of Mozilla. In + * that case, we create and set up the message window. + * + * The checking for existence of the message window must + * be protected by use of a mutex semaphore. + */ +NS_IMETHODIMP +nsNativeAppSupportWin::Start( bool *aResult ) { + NS_ENSURE_ARG( aResult ); + NS_ENSURE_TRUE( mInstance == 0, NS_ERROR_NOT_INITIALIZED ); + NS_ENSURE_STATE( gAppData ); + + if (getenv("MOZ_NO_REMOTE")) + { + *aResult = true; + return NS_OK; + } + + nsresult rv = NS_ERROR_FAILURE; + *aResult = false; + + // Grab mutex first. + + // Build mutex name from app name. + ::_snwprintf(reinterpret_cast<wchar_t*>(mMutexName), + sizeof mMutexName / sizeof(char16_t), L"%s%s%s", + MOZ_MUTEX_NAMESPACE, + static_cast<const wchar_t*>(NS_ConvertUTF8toUTF16(gAppData->name).get()), + MOZ_STARTUP_MUTEX_NAME ); + Win32Mutex startupLock = Win32Mutex( mMutexName ); + + NS_ENSURE_TRUE( startupLock.Lock( MOZ_DDE_START_TIMEOUT ), NS_ERROR_FAILURE ); + + // Search for existing message window. + MessageWindow msgWindow; + if ( (HWND)msgWindow ) { + // We are a client process. Pass request to message window. + rv = msgWindow.SendRequest(); + } else { + // We will be server. + rv = msgWindow.Create(); + if ( NS_SUCCEEDED( rv ) ) { + // Start up DDE server. + this->StartDDE(); + // Tell caller to spin message loop. + *aResult = true; + } + } + + startupLock.Unlock(); + + return rv; +} + +bool +nsNativeAppSupportWin::InitTopicStrings() { + for ( int i = 0; i < topicCount; i++ ) { + if ( !( mTopics[ i ] = DdeCreateStringHandleA( mInstance, const_cast<char *>(topicNames[ i ]), CP_WINANSI ) ) ) { + return false; + } + } + return true; +} + +int +nsNativeAppSupportWin::FindTopic( HSZ topic ) { + for ( int i = 0; i < topicCount; i++ ) { + if ( DdeCmpStringHandles( topic, mTopics[i] ) == 0 ) { + return i; + } + } + return -1; +} + + +// Start DDE server. +// +// This used to be the Start() method when we were using DDE as the +// primary IPC mechanism between secondary Mozilla processes and the +// initial "server" process. +// +// Now, it simply initializes the DDE server. The caller must check +// that this process is to be the server, and, must acquire the DDE +// startup mutex semaphore prior to calling this routine. See ::Start(), +// above. +NS_IMETHODIMP +nsNativeAppSupportWin::StartDDE() { + NS_ENSURE_TRUE( mInstance == 0, NS_ERROR_NOT_INITIALIZED ); + + // Initialize DDE. + NS_ENSURE_TRUE( DMLERR_NO_ERROR == DdeInitialize( &mInstance, + nsNativeAppSupportWin::HandleDDENotification, + APPCLASS_STANDARD, + 0 ), + NS_ERROR_FAILURE ); + + // Allocate DDE strings. + NS_ENSURE_TRUE( ( mApplication = DdeCreateStringHandleA( mInstance, (char*) gAppData->name, CP_WINANSI ) ) && InitTopicStrings(), + NS_ERROR_FAILURE ); + + // Next step is to register a DDE service. + NS_ENSURE_TRUE( DdeNameService( mInstance, mApplication, 0, DNS_REGISTER ), NS_ERROR_FAILURE ); + +#if MOZ_DEBUG_DDE + printf( "DDE server started\n" ); +#endif + + return NS_OK; +} + +// If no DDE conversations are pending, terminate DDE. +NS_IMETHODIMP +nsNativeAppSupportWin::Stop( bool *aResult ) { + NS_ENSURE_ARG( aResult ); + NS_ENSURE_TRUE( mInstance, NS_ERROR_NOT_INITIALIZED ); + + nsresult rv = NS_OK; + *aResult = true; + + Win32Mutex ddeLock( mMutexName ); + + if ( ddeLock.Lock( MOZ_DDE_STOP_TIMEOUT ) ) { + if ( mConversations == 0 ) { + this->Quit(); + } else { + *aResult = false; + } + + ddeLock.Unlock(); + } + else { + // No DDE application name specified, but that's OK. Just + // forge ahead. + *aResult = true; + } + + return rv; +} + +NS_IMETHODIMP +nsNativeAppSupportWin::Observe(nsISupports* aSubject, const char* aTopic, + const char16_t* aData) +{ + if (strcmp(aTopic, "quit-application") == 0) { + Quit(); + } else { + NS_ERROR("Unexpected observer topic."); + } + + return NS_OK; +} + +// Terminate DDE regardless. +NS_IMETHODIMP +nsNativeAppSupportWin::Quit() { + // If another process wants to look for the message window, they need + // to wait to hold the lock, in which case they will not find the + // window as we will destroy ours under our lock. + // When the mutex goes off the stack, it is unlocked via destructor. + Win32Mutex mutexLock(mMutexName); + NS_ENSURE_TRUE(mutexLock.Lock(MOZ_DDE_START_TIMEOUT), NS_ERROR_FAILURE); + + // If we've got a message window to receive IPC or new window requests, + // get rid of it as we are shutting down. + // Note: Destroy calls DestroyWindow, which will only work on a window + // created by the same thread. + MessageWindow mw; + mw.Destroy(); + + if ( mInstance ) { + // Unregister application name. + DdeNameService( mInstance, mApplication, 0, DNS_UNREGISTER ); + // Clean up strings. + if ( mApplication ) { + DdeFreeStringHandle( mInstance, mApplication ); + mApplication = 0; + } + for ( int i = 0; i < topicCount; i++ ) { + if ( mTopics[i] ) { + DdeFreeStringHandle( mInstance, mTopics[i] ); + mTopics[i] = 0; + } + } + DdeUninitialize( mInstance ); + mInstance = 0; +#if MOZ_DEBUG_DDE + printf( "DDE server stopped\n" ); +#endif + } + + return NS_OK; +} + +NS_IMETHODIMP +nsNativeAppSupportWin::Enable() +{ + mCanHandleRequests = true; + + nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); + if (obs) { + obs->AddObserver(this, "quit-application", false); + } else { + NS_ERROR("No observer service?"); + } + + return NS_OK; +} + +#if MOZ_DEBUG_DDE +// Macro to generate case statement for a given XTYP value. +#define XTYP_CASE(t) \ + case t: result = #t; break + +static nsCString uTypeDesc( UINT uType ) { + nsCString result; + switch ( uType ) { + XTYP_CASE(XTYP_ADVSTART); + XTYP_CASE(XTYP_CONNECT); + XTYP_CASE(XTYP_ADVREQ); + XTYP_CASE(XTYP_REQUEST); + XTYP_CASE(XTYP_WILDCONNECT); + XTYP_CASE(XTYP_ADVDATA); + XTYP_CASE(XTYP_EXECUTE); + XTYP_CASE(XTYP_POKE); + XTYP_CASE(XTYP_ADVSTOP); + XTYP_CASE(XTYP_CONNECT_CONFIRM); + XTYP_CASE(XTYP_DISCONNECT); + XTYP_CASE(XTYP_ERROR); + XTYP_CASE(XTYP_MONITOR); + XTYP_CASE(XTYP_REGISTER); + XTYP_CASE(XTYP_XACT_COMPLETE); + XTYP_CASE(XTYP_UNREGISTER); + default: result = "XTYP_?????"; + } + return result; +} + +static nsCString hszValue( DWORD instance, HSZ hsz ) { + // Extract string from HSZ. + nsCString result("["); + DWORD len = DdeQueryString( instance, hsz, nullptr, nullptr, CP_WINANSI ); + if ( len ) { + char buffer[ 256 ]; + DdeQueryString( instance, hsz, buffer, sizeof buffer, CP_WINANSI ); + result += buffer; + } + result += "]"; + return result; +} +#else +// These are purely a safety measure to avoid the infamous "won't +// build non-debug" type Tinderbox flames. +static nsCString uTypeDesc( UINT ) { + return nsCString( "?" ); +} +static nsCString hszValue( DWORD, HSZ ) { + return nsCString( "?" ); +} +#endif + + +// Utility function to escape double-quotes within a string. +static void escapeQuotes( nsAString &aString ) { + int32_t offset = -1; + while( 1 ) { + // Find next '"'. + offset = aString.FindChar( '"', ++offset ); + if ( offset == kNotFound ) { + // No more quotes, exit. + break; + } else { + // Insert back-slash ahead of the '"'. + aString.Insert( char16_t('\\'), offset ); + // Increment offset because we just inserted a slash + offset++; + } + } + return; +} + +HDDEDATA CALLBACK +nsNativeAppSupportWin::HandleDDENotification( UINT uType, // transaction type + UINT uFmt, // clipboard data format + HCONV hconv, // handle to the conversation + HSZ hsz1, // handle to a string + HSZ hsz2, // handle to a string + HDDEDATA hdata, // handle to a global memory object + ULONG_PTR dwData1, // transaction-specific data + ULONG_PTR dwData2 ) { // transaction-specific data + + if (!mCanHandleRequests) + return 0; + + +#if MOZ_DEBUG_DDE + printf( "DDE: uType =%s\n", uTypeDesc( uType ).get() ); + printf( " uFmt =%u\n", (unsigned)uFmt ); + printf( " hconv =%08x\n", (int)hconv ); + printf( " hsz1 =%08x:%s\n", (int)hsz1, hszValue( mInstance, hsz1 ).get() ); + printf( " hsz2 =%08x:%s\n", (int)hsz2, hszValue( mInstance, hsz2 ).get() ); + printf( " hdata =%08x\n", (int)hdata ); + printf( " dwData1=%08x\n", (int)dwData1 ); + printf( " dwData2=%08x\n", (int)dwData2 ); +#endif + + HDDEDATA result = 0; + if ( uType & XCLASS_BOOL ) { + switch ( uType ) { + case XTYP_CONNECT: + // Make sure its for our service/topic. + if ( FindTopic( hsz1 ) != -1 ) { + // We support this connection. + result = (HDDEDATA)1; + } + break; + case XTYP_CONNECT_CONFIRM: + // We don't care about the conversation handle, at this point. + result = (HDDEDATA)1; + break; + } + } else if ( uType & XCLASS_DATA ) { + if ( uType == XTYP_REQUEST ) { + switch ( FindTopic( hsz1 ) ) { + case topicOpenURL: { + // Open a given URL... + + // Get the URL from the first argument in the command. + nsAutoString url; + ParseDDEArg(hsz2, 0, url); + + // Read the 3rd argument in the command to determine if a + // new window is to be used. + nsAutoString windowID; + ParseDDEArg(hsz2, 2, windowID); + // "" means to open the URL in a new window. + if ( windowID.IsEmpty() ) { + url.Insert(NS_LITERAL_STRING("mozilla -new-window "), 0); + } + else { + url.Insert(NS_LITERAL_STRING("mozilla -url "), 0); + } + +#if MOZ_DEBUG_DDE + printf( "Handling dde XTYP_REQUEST request: [%s]...\n", NS_ConvertUTF16toUTF8(url).get() ); +#endif + // Now handle it. + HandleCommandLine(NS_ConvertUTF16toUTF8(url).get(), nullptr, nsICommandLine::STATE_REMOTE_EXPLICIT); + + // Return pseudo window ID. + result = CreateDDEData( 1 ); + break; + } + case topicGetWindowInfo: { + // This topic has to get the current URL, get the current + // page title and then format the output into the DDE + // return string. The return value is "URL","Page Title", + // "Window ID" however the window ID is not used for this + // command, therefore it is returned as a null string + + // This isn't really a loop. We just use "break" + // statements to bypass the remaining steps when + // something goes wrong. + do { + // Get most recently used Nav window. + nsCOMPtr<mozIDOMWindowProxy> navWin; + GetMostRecentWindow( NS_LITERAL_STRING( "navigator:browser" ).get(), + getter_AddRefs( navWin ) ); + nsCOMPtr<nsPIDOMWindowOuter> piNavWin = do_QueryInterface(navWin); + if ( !piNavWin ) { + // There is not a window open + break; + } + + // Get content window. + nsCOMPtr<nsPIDOMWindowOuter> internalContent = nsGlobalWindow::Cast(piNavWin)->GetContent(); + if ( !internalContent ) { + break; + } + // Get location. + nsCOMPtr<nsIDOMLocation> location = internalContent->GetLocation(); + if ( !location ) { + break; + } + // Get href for URL. + nsAutoString url; + if ( NS_FAILED( location->GetHref( url ) ) ) { + break; + } + // Escape any double-quotes. + escapeQuotes( url ); + + // Now for the title... + + // Get the base window from the doc shell... + nsCOMPtr<nsIBaseWindow> baseWindow = + do_QueryInterface( internalContent->GetDocShell() ); + if ( !baseWindow ) { + break; + } + // And from the base window we can get the title. + nsXPIDLString title; + if(!baseWindow) { + break; + } + baseWindow->GetTitle(getter_Copies(title)); + // Escape any double-quotes in the title. + escapeQuotes( title ); + + // Use a string buffer for the output data, first + // save a quote. + nsAutoCString outpt( NS_LITERAL_CSTRING("\"") ); + // Now copy the URL converting the Unicode string + // to a single-byte ASCII string + nsAutoCString tmpNativeStr; + NS_CopyUnicodeToNative( url, tmpNativeStr ); + outpt.Append( tmpNativeStr ); + // Add the "," used to separate the URL and the page + // title + outpt.Append( NS_LITERAL_CSTRING("\",\"") ); + // Now copy the current page title to the return string + NS_CopyUnicodeToNative( title, tmpNativeStr ); + outpt.Append( tmpNativeStr ); + // Fill out the return string with the remainin ","" + outpt.Append( NS_LITERAL_CSTRING( "\",\"\"" )); + + // Create a DDE handle to a char string for the data + // being returned, this copies and creates a "shared" + // copy of the DDE response until the calling APP + // reads it and says it can be freed. + result = CreateDDEData( (LPBYTE)(const char*)outpt.get(), + outpt.Length() + 1 ); +#if MOZ_DEBUG_DDE + printf( "WWW_GetWindowInfo->%s\n", outpt.get() ); +#endif + } while ( false ); + break; + } + case topicActivate: { + // Activate a Nav window... + nsAutoString windowID; + ParseDDEArg(hsz2, 0, windowID); + // 4294967295 is decimal for 0xFFFFFFFF which is also a + // correct value to do that Activate last window stuff + if ( windowID.EqualsLiteral( "-1" ) || + windowID.EqualsLiteral( "4294967295" ) ) { + // We only support activating the most recent window (or a new one). + ActivateLastWindow(); + // Return pseudo window ID. + result = CreateDDEData( 1 ); + } + break; + } + case topicVersion: { + // Return version. We're restarting at 1.0! + DWORD version = 1 << 16; // "1.0" + result = CreateDDEData( version ); + break; + } + case topicRegisterViewer: { + // Register new viewer (not implemented). + result = CreateDDEData( false ); + break; + } + case topicUnRegisterViewer: { + // Unregister new viewer (not implemented). + result = CreateDDEData( false ); + break; + } + default: + break; + } + } else if ( uType & XTYP_POKE ) { + switch ( FindTopic( hsz1 ) ) { + case topicCancelProgress: { + // "Handle" progress cancel (actually, pretty much ignored). + result = (HDDEDATA)DDE_FACK; + break; + } + default: + break; + } + } + } else if ( uType & XCLASS_FLAGS ) { + if ( uType == XTYP_EXECUTE ) { + // Prove that we received the request. + DWORD bytes; + LPBYTE request = DdeAccessData( hdata, &bytes ); +#if MOZ_DEBUG_DDE + printf( "Handling dde request: [%s]...\n", (char*)request ); +#endif + + nsAutoString url; + ParseDDEArg((const WCHAR*) request, 0, url); + + // Read the 3rd argument in the command to determine if a + // new window is to be used. + nsAutoString windowID; + ParseDDEArg((const WCHAR*) request, 2, windowID); + + // "" means to open the URL in a new window. + if ( windowID.IsEmpty() ) { + url.Insert(NS_LITERAL_STRING("mozilla -new-window "), 0); + } + else { + url.Insert(NS_LITERAL_STRING("mozilla -url "), 0); + } +#if MOZ_DEBUG_DDE + printf( "Handling dde XTYP_REQUEST request: [%s]...\n", NS_ConvertUTF16toUTF8(url).get() ); +#endif + // Now handle it. + HandleCommandLine(NS_ConvertUTF16toUTF8(url).get(), nullptr, nsICommandLine::STATE_REMOTE_EXPLICIT); + + // Release the data. + DdeUnaccessData( hdata ); + result = (HDDEDATA)DDE_FACK; + } else { + result = (HDDEDATA)DDE_FNOTPROCESSED; + } + } else if ( uType & XCLASS_NOTIFICATION ) { + } +#if MOZ_DEBUG_DDE + printf( "DDE result=%d (0x%08X)\n", (int)result, (int)result ); +#endif + return result; +} + +// Utility function to advance to end of quoted string. +// p+offset must point to the comma preceding the arg on entry. +// On return, p+result points to the closing '"' (or end of the string +// if the closing '"' is missing) if the arg is quoted. If the arg +// is not quoted, then p+result will point to the first character +// of the arg. +static int32_t advanceToEndOfQuotedArg( const WCHAR *p, int32_t offset, int32_t len ) { + // Check whether the current arg is quoted. + if ( p[++offset] == '"' ) { + // Advance past the closing quote. + while ( offset < len && p[++offset] != '"' ) { + // If the current character is a backslash, then the + // next character can't be a *real* '"', so skip it. + if ( p[offset] == '\\' ) { + offset++; + } + } + } + return offset; +} + +void nsNativeAppSupportWin::ParseDDEArg( const WCHAR* args, int index, nsString& aString) { + if ( args ) { + nsDependentString temp(args); + + // offset points to the comma preceding the desired arg. + int32_t offset = -1; + // Skip commas till we get to the arg we want. + while( index-- ) { + // If this arg is quoted, then go to closing quote. + offset = advanceToEndOfQuotedArg( args, offset, temp.Length()); + // Find next comma. + offset = temp.FindChar( ',', offset ); + if ( offset == kNotFound ) { + // No more commas, give up. + aString = args; + return; + } + } + // The desired argument starts just past the preceding comma, + // which offset points to, and extends until the following + // comma (or the end of the string). + // + // Since the argument might be enclosed in quotes, we need to + // deal with that before searching for the terminating comma. + // We advance offset so it ends up pointing to the start of + // the argument we want. + int32_t end = advanceToEndOfQuotedArg( args, offset++, temp.Length() ); + // Find next comma (or end of string). + end = temp.FindChar( ',', end ); + if ( end == kNotFound ) { + // Arg is the rest of the string. + end = temp.Length(); + } + // Extract result. + aString.Assign( args + offset, end - offset ); + } + return; +} + +// Utility to parse out argument from a DDE item string. +void nsNativeAppSupportWin::ParseDDEArg( HSZ args, int index, nsString& aString) { + DWORD argLen = DdeQueryStringW( mInstance, args, nullptr, 0, CP_WINUNICODE ); + // there wasn't any string, so return empty string + if ( !argLen ) return; + nsAutoString temp; + // Ensure result's buffer is sufficiently big. + temp.SetLength( argLen ); + // Now get the string contents. + DdeQueryString( mInstance, args, reinterpret_cast<wchar_t*>(temp.BeginWriting()), temp.Length(), CP_WINUNICODE ); + // Parse out the given arg. + ParseDDEArg(temp.get(), index, aString); + return; +} + +HDDEDATA nsNativeAppSupportWin::CreateDDEData( DWORD value ) { + return CreateDDEData( (LPBYTE)&value, sizeof value ); +} + +HDDEDATA nsNativeAppSupportWin::CreateDDEData( LPBYTE value, DWORD len ) { + HDDEDATA result = DdeCreateDataHandle( mInstance, + value, + len, + 0, + mApplication, + CF_TEXT, + 0 ); + return result; +} + +void nsNativeAppSupportWin::ActivateLastWindow() { + nsCOMPtr<mozIDOMWindowProxy> navWin; + GetMostRecentWindow( u"navigator:browser", getter_AddRefs( navWin ) ); + if ( navWin ) { + // Activate that window. + activateWindow( navWin ); + } else { + // Need to create a Navigator window, then. + OpenBrowserWindow(); + } +} + +void +nsNativeAppSupportWin::HandleCommandLine(const char* aCmdLineString, + nsIFile* aWorkingDir, + uint32_t aState) +{ + nsresult rv; + + int justCounting = 1; + char **argv = 0; + // Flags, etc. + int init = 1; + int between, quoted, bSlashCount; + int argc; + const char *p; + nsAutoCString arg; + + nsCOMPtr<nsICommandLineRunner> cmdLine + (do_CreateInstance("@mozilla.org/toolkit/command-line;1")); + if (!cmdLine) { + NS_ERROR("Couldn't create command line!"); + return; + } + + // Parse command line args according to MS spec + // (see "Parsing C++ Command-Line Arguments" at + // http://msdn.microsoft.com/library/devprods/vs6/visualc/vclang/_pluslang_parsing_c.2b2b_.command.2d.line_arguments.htm). + // We loop if we've not finished the second pass through. + while ( 1 ) { + // Initialize if required. + if ( init ) { + p = aCmdLineString; + between = 1; + argc = quoted = bSlashCount = 0; + + init = 0; + } + if ( between ) { + // We are traversing whitespace between args. + // Check for start of next arg. + if ( *p != 0 && !isspace( *p ) ) { + // Start of another arg. + between = 0; + arg = ""; + switch ( *p ) { + case '\\': + // Count the backslash. + bSlashCount = 1; + break; + case '"': + // Remember we're inside quotes. + quoted = 1; + break; + default: + // Add character to arg. + arg += *p; + break; + } + } else { + // Another space between args, ignore it. + } + } else { + // We are processing the contents of an argument. + // Check for whitespace or end. + if ( *p == 0 || ( !quoted && isspace( *p ) ) ) { + // Process pending backslashes (interpret them + // literally since they're not followed by a "). + while( bSlashCount ) { + arg += '\\'; + bSlashCount--; + } + // End current arg. + if ( !justCounting ) { + argv[argc] = new char[ arg.Length() + 1 ]; + strcpy( argv[argc], arg.get() ); + } + argc++; + // We're now between args. + between = 1; + } else { + // Still inside argument, process the character. + switch ( *p ) { + case '"': + // First, digest preceding backslashes (if any). + while ( bSlashCount > 1 ) { + // Put one backsplash in arg for each pair. + arg += '\\'; + bSlashCount -= 2; + } + if ( bSlashCount ) { + // Quote is literal. + arg += '"'; + bSlashCount = 0; + } else { + // Quote starts or ends a quoted section. + if ( quoted ) { + // Check for special case of consecutive double + // quotes inside a quoted section. + if ( *(p+1) == '"' ) { + // This implies a literal double-quote. Fake that + // out by causing next double-quote to look as + // if it was preceded by a backslash. + bSlashCount = 1; + } else { + quoted = 0; + } + } else { + quoted = 1; + } + } + break; + case '\\': + // Add to count. + bSlashCount++; + break; + default: + // Accept any preceding backslashes literally. + while ( bSlashCount ) { + arg += '\\'; + bSlashCount--; + } + // Just add next char to the current arg. + arg += *p; + break; + } + } + } + // Check for end of input. + if ( *p ) { + // Go to next character. + p++; + } else { + // If on first pass, go on to second. + if ( justCounting ) { + // Allocate argv array. + argv = new char*[ argc ]; + + // Start second pass + justCounting = 0; + init = 1; + } else { + // Quit. + break; + } + } + } + + rv = cmdLine->Init(argc, argv, aWorkingDir, aState); + + // Cleanup. + while ( argc ) { + delete [] argv[ --argc ]; + } + delete [] argv; + + if (NS_FAILED(rv)) { + NS_ERROR("Error initializing command line."); + return; + } + + cmdLine->Run(); +} + +nsresult +nsNativeAppSupportWin::OpenWindow( const char*urlstr, const char *args ) { + + nsresult rv = NS_ERROR_FAILURE; + + nsCOMPtr<nsIWindowWatcher> wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID)); + nsCOMPtr<nsISupportsCString> sarg(do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID)); + if (sarg) + sarg->SetData(nsDependentCString(args)); + + if (wwatch && sarg) { + nsCOMPtr<mozIDOMWindowProxy> newWindow; + rv = wwatch->OpenWindow(0, urlstr, "_blank", "chrome,dialog=no,all", + sarg, getter_AddRefs(newWindow)); +#if MOZ_DEBUG_DDE + } else { + printf("Get WindowWatcher (or create string) failed\n"); +#endif + } + return rv; +} + +HWND hwndForDOMWindow(mozIDOMWindowProxy *window ) { + if ( !window ) { + return 0; + } + nsCOMPtr<nsPIDOMWindowOuter > pidomwindow = nsPIDOMWindowOuter::From(window); + + nsCOMPtr<nsIBaseWindow> ppBaseWindow = + do_QueryInterface( pidomwindow->GetDocShell() ); + if ( !ppBaseWindow ) { + return 0; + } + + nsCOMPtr<nsIWidget> ppWidget; + ppBaseWindow->GetMainWidget( getter_AddRefs( ppWidget ) ); + + return (HWND)( ppWidget->GetNativeData( NS_NATIVE_WIDGET ) ); +} + +nsresult +nsNativeAppSupportWin::OpenBrowserWindow() +{ + nsresult rv = NS_OK; + + // Open the argument URL in the most recently used Navigator window. + // If there is no Nav window, open a new one. + + // If at all possible, hand the request off to the most recent + // browser window. + + nsCOMPtr<mozIDOMWindowProxy> navWin; + GetMostRecentWindow( NS_LITERAL_STRING( "navigator:browser" ).get(), getter_AddRefs( navWin ) ); + + // This isn't really a loop. We just use "break" statements to fall + // out to the OpenWindow call when things go awry. + do { + // If caller requires a new window, then don't use an existing one. + if ( !navWin ) { + // Have to open a new one. + break; + } + + nsCOMPtr<nsIBrowserDOMWindow> bwin; + { // scope a bunch of temporary cruft used to generate bwin + nsCOMPtr<nsIWebNavigation> navNav( do_GetInterface( navWin ) ); + nsCOMPtr<nsIDocShellTreeItem> navItem( do_QueryInterface( navNav ) ); + if ( navItem ) { + nsCOMPtr<nsIDocShellTreeItem> rootItem; + navItem->GetRootTreeItem( getter_AddRefs( rootItem ) ); + nsCOMPtr<nsPIDOMWindowOuter> rootWin = + rootItem ? rootItem->GetWindow() : nullptr; + nsCOMPtr<nsIDOMChromeWindow> chromeWin(do_QueryInterface(rootWin)); + if ( chromeWin ) + chromeWin->GetBrowserDOMWindow( getter_AddRefs ( bwin ) ); + } + } + if ( bwin ) { + nsCOMPtr<nsIURI> uri; + NS_NewURI( getter_AddRefs( uri ), NS_LITERAL_CSTRING("about:blank"), 0, 0 ); + if ( uri ) { + nsCOMPtr<mozIDOMWindowProxy> container; + rv = bwin->OpenURI( uri, 0, + nsIBrowserDOMWindow::OPEN_DEFAULTWINDOW, + nsIBrowserDOMWindow::OPEN_EXTERNAL, + getter_AddRefs( container ) ); + if ( NS_SUCCEEDED( rv ) ) + return NS_OK; + } + } + + NS_ERROR("failed to hand off external URL to extant window"); + } while ( false ); + + // open a new window if caller requested it or if anything above failed + + char* argv[] = { 0 }; + nsCOMPtr<nsICommandLineRunner> cmdLine + (do_CreateInstance("@mozilla.org/toolkit/command-line;1")); + NS_ENSURE_TRUE(cmdLine, NS_ERROR_FAILURE); + + rv = cmdLine->Init(0, argv, nullptr, nsICommandLine::STATE_REMOTE_EXPLICIT); + NS_ENSURE_SUCCESS(rv, rv); + + return cmdLine->Run(); +} + |