PageRenderTime 55ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/toolkit/xre/nsNativeAppSupportWin.cpp

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