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