nsNativeAppSupportWin.cpp 56 KB

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