Bug 452317 - FeedConverter.js: QueryInterface should throw NS_ERROR_NO_INTERFACE...
[wine-gecko.git] / toolkit / components / remote / nsGTKRemoteService.cpp
blob887d3e9f746aa400737bfa3f1508601abb724150
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:expandtab:shiftwidth=2:tabstop=8:
3 */
4 /* ***** BEGIN LICENSE BLOCK *****
5 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
7 * The contents of this file are subject to the Mozilla Public License Version
8 * 1.1 (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
10 * http://www.mozilla.org/MPL/
12 * Software distributed under the License is distributed on an "AS IS" basis,
13 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 * for the specific language governing rights and limitations under the
15 * License.
17 * The Original Code is mozilla.org code.
19 * The Initial Developer of the Original Code is
20 * Christopher Blizzard.
21 * Portions created by the Initial Developer are Copyright (C) 2001
22 * the Initial Developer. All Rights Reserved.
24 * Contributor(s):
25 * Christopher Blizzard <blizzard@mozilla.org>
26 * Benjamin Smedberg <benjamin@smedbergs.us>
28 * Alternatively, the contents of this file may be used under the terms of
29 * either the GNU General Public License Version 2 or later (the "GPL"), or
30 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
31 * in which case the provisions of the GPL or the LGPL are applicable instead
32 * of those above. If you wish to allow use of your version of this file only
33 * under the terms of either the GPL or the LGPL, and not to allow others to
34 * use your version of this file under the terms of the MPL, indicate your
35 * decision by deleting the provisions above and replace them with the notice
36 * and other provisions required by the GPL or the LGPL. If you do not delete
37 * the provisions above, a recipient may use your version of this file under
38 * the terms of any one of the MPL, the GPL or the LGPL.
40 * ***** END LICENSE BLOCK ***** */
42 #include "nsGTKRemoteService.h"
44 #include <X11/Xatom.h> // for XA_STRING
45 #include <stdlib.h>
46 #include <gtk/gtkinvisible.h> // For some reason GTK+ doesn't include this file
47 // automatically from gtk.h
48 #include <gdk/gdk.h>
49 #include <gdk/gdkx.h>
51 #include "nsIBaseWindow.h"
52 #include "nsIDocShell.h"
53 #include "nsPIDOMWindow.h"
54 #include "nsIGenericFactory.h"
55 #include "nsILocalFile.h"
56 #include "nsIObserverService.h"
57 #include "nsIServiceManager.h"
58 #include "nsIWeakReference.h"
59 #include "nsIWidget.h"
60 #include "nsIAppShellService.h"
61 #include "nsAppShellCID.h"
63 #include "nsCOMPtr.h"
64 #include "nsString.h"
65 #include "prprf.h"
66 #include "prenv.h"
67 #include "nsCRT.h"
69 #ifdef MOZ_WIDGET_GTK2
70 #include "nsGTKToolkit.h"
71 #endif
73 #include "nsICommandLineRunner.h"
74 #include "nsXULAppAPI.h"
76 #define MOZILLA_VERSION_PROP "_MOZILLA_VERSION"
77 #define MOZILLA_LOCK_PROP "_MOZILLA_LOCK"
78 #define MOZILLA_COMMAND_PROP "_MOZILLA_COMMAND"
79 #define MOZILLA_RESPONSE_PROP "_MOZILLA_RESPONSE"
80 #define MOZILLA_USER_PROP "_MOZILLA_USER"
81 #define MOZILLA_PROFILE_PROP "_MOZILLA_PROFILE"
82 #define MOZILLA_PROGRAM_PROP "_MOZILLA_PROGRAM"
83 #define MOZILLA_COMMANDLINE_PROP "_MOZILLA_COMMANDLINE"
85 #ifdef IS_BIG_ENDIAN
86 #define TO_LITTLE_ENDIAN32(x) \
87 ((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >> 8) | \
88 (((x) & 0x0000ff00) << 8) | (((x) & 0x000000ff) << 24))
89 #else
90 #define TO_LITTLE_ENDIAN32(x) (x)
91 #endif
93 const unsigned char kRemoteVersion[] = "5.1";
95 NS_IMPL_ISUPPORTS2(nsGTKRemoteService,
96 nsIRemoteService,
97 nsIObserver)
99 NS_IMETHODIMP
100 nsGTKRemoteService::Startup(const char* aAppName, const char* aProfileName)
102 NS_ASSERTION(aAppName, "Don't pass a null appname!");
104 EnsureAtoms();
105 if (mServerWindow) return NS_ERROR_ALREADY_INITIALIZED;
107 mAppName = aAppName;
108 ToLowerCase(mAppName);
110 mProfileName = aProfileName;
112 mServerWindow = gtk_invisible_new();
113 gtk_widget_realize(mServerWindow);
114 HandleCommandsFor(mServerWindow, nsnull);
116 if (!mWindows.IsInitialized())
117 mWindows.Init();
119 mWindows.EnumerateRead(StartupHandler, this);
121 nsCOMPtr<nsIObserverService> obs
122 (do_GetService("@mozilla.org/observer-service;1"));
123 if (obs) {
124 obs->AddObserver(this, "xpcom-shutdown", PR_FALSE);
125 obs->AddObserver(this, "quit-application", PR_FALSE);
128 return NS_OK;
131 PLDHashOperator
132 nsGTKRemoteService::StartupHandler(const void* aKey,
133 nsIWeakReference* aData,
134 void* aClosure)
136 GtkWidget* widget = (GtkWidget*) aKey;
137 nsGTKRemoteService* aThis = (nsGTKRemoteService*) aClosure;
139 aThis->HandleCommandsFor(widget, aData);
140 return PL_DHASH_NEXT;
143 static nsIWidget* GetMainWidget(nsIDOMWindow* aWindow)
145 // get the native window for this instance
146 nsCOMPtr<nsPIDOMWindow> window(do_QueryInterface(aWindow));
147 NS_ENSURE_TRUE(window, nsnull);
149 nsCOMPtr<nsIBaseWindow> baseWindow
150 (do_QueryInterface(window->GetDocShell()));
151 NS_ENSURE_TRUE(baseWindow, nsnull);
153 nsCOMPtr<nsIWidget> mainWidget;
154 baseWindow->GetMainWidget(getter_AddRefs(mainWidget));
155 return mainWidget;
158 #ifdef MOZ_WIDGET_GTK2
159 static nsGTKToolkit* GetGTKToolkit()
161 nsCOMPtr<nsIAppShellService> svc = do_GetService(NS_APPSHELLSERVICE_CONTRACTID);
162 if (!svc)
163 return nsnull;
164 nsCOMPtr<nsIDOMWindowInternal> window;
165 svc->GetHiddenDOMWindow(getter_AddRefs(window));
166 if (!window)
167 return nsnull;
168 nsIWidget* widget = GetMainWidget(window);
169 if (!widget)
170 return nsnull;
171 nsIToolkit* toolkit = widget->GetToolkit();
172 if (!toolkit)
173 return nsnull;
174 return static_cast<nsGTKToolkit*>(toolkit);
176 #endif
178 NS_IMETHODIMP
179 nsGTKRemoteService::RegisterWindow(nsIDOMWindow* aWindow)
181 nsIWidget* mainWidget = GetMainWidget(aWindow);
182 NS_ENSURE_TRUE(mainWidget, NS_ERROR_FAILURE);
184 // walk up the widget tree and find the toplevel window in the
185 // hierarchy
187 nsIWidget* tempWidget = mainWidget->GetParent();
189 while (tempWidget) {
190 tempWidget = tempWidget->GetParent();
191 if (tempWidget)
192 mainWidget = tempWidget;
195 GtkWidget* widget =
196 (GtkWidget*) mainWidget->GetNativeData(NS_NATIVE_SHELLWIDGET);
197 NS_ENSURE_TRUE(widget, NS_ERROR_FAILURE);
199 nsCOMPtr<nsIWeakReference> weak = do_GetWeakReference(aWindow);
200 NS_ENSURE_TRUE(weak, NS_ERROR_FAILURE);
202 if (!mWindows.IsInitialized())
203 mWindows.Init();
205 mWindows.Put(widget, weak);
207 // If Startup() has already been called, immediately register this window.
208 if (mServerWindow) {
209 HandleCommandsFor(widget, weak);
212 return NS_OK;
215 NS_IMETHODIMP
216 nsGTKRemoteService::Shutdown()
218 if (!mServerWindow)
219 return NS_ERROR_NOT_INITIALIZED;
221 gtk_widget_destroy(mServerWindow);
222 mServerWindow = nsnull;
223 return NS_OK;
226 NS_IMETHODIMP
227 nsGTKRemoteService::Observe(nsISupports* aSubject,
228 const char *aTopic,
229 const PRUnichar *aData)
231 // This can be xpcom-shutdown or quit-application, but it's the same either
232 // way.
233 Shutdown();
234 return NS_OK;
237 // Minimize the roundtrips to the X server by getting all the atoms at once
238 static char *XAtomNames[] = {
239 MOZILLA_VERSION_PROP,
240 MOZILLA_LOCK_PROP,
241 MOZILLA_COMMAND_PROP,
242 MOZILLA_RESPONSE_PROP,
243 MOZILLA_USER_PROP,
244 MOZILLA_PROFILE_PROP,
245 MOZILLA_PROGRAM_PROP,
246 MOZILLA_COMMANDLINE_PROP
248 static Atom XAtoms[NS_ARRAY_LENGTH(XAtomNames)];
250 void
251 nsGTKRemoteService::EnsureAtoms(void)
253 if (sMozVersionAtom)
254 return;
256 XInternAtoms(GDK_DISPLAY(), XAtomNames, NS_ARRAY_LENGTH(XAtomNames),
257 False, XAtoms);
258 int i = 0;
259 sMozVersionAtom = XAtoms[i++];
260 sMozLockAtom = XAtoms[i++];
261 sMozCommandAtom = XAtoms[i++];
262 sMozResponseAtom = XAtoms[i++];
263 sMozUserAtom = XAtoms[i++];
264 sMozProfileAtom = XAtoms[i++];
265 sMozProgramAtom = XAtoms[i++];
266 sMozCommandLineAtom = XAtoms[i++];
269 // Set desktop startup ID to the passed ID, if there is one, so that any created
270 // windows get created with the right window manager metadata, and any windows
271 // that get new tabs and are activated also get the right WM metadata.
272 // If there is no desktop startup ID, then use the X event's timestamp
273 // for _NET_ACTIVE_WINDOW when the window gets focused or shown.
274 static void
275 SetDesktopStartupIDOrTimestamp(const nsACString& aDesktopStartupID,
276 PRUint32 aTimestamp) {
277 #ifdef MOZ_WIDGET_GTK2
278 nsGTKToolkit* toolkit = GetGTKToolkit();
279 if (!toolkit)
280 return;
281 if (!aDesktopStartupID.IsEmpty()) {
282 toolkit->SetDesktopStartupID(aDesktopStartupID);
283 } else {
284 toolkit->SetFocusTimestamp(aTimestamp);
286 #endif
289 static PRBool
290 FindExtensionParameterInCommand(const char* aParameterName,
291 const nsACString& aCommand,
292 char aSeparator,
293 nsACString* aValue)
295 nsCAutoString searchFor;
296 searchFor.Append(aSeparator);
297 searchFor.Append(aParameterName);
298 searchFor.Append('=');
300 nsACString::const_iterator start, end;
301 aCommand.BeginReading(start);
302 aCommand.EndReading(end);
303 if (!FindInReadable(searchFor, start, end))
304 return PR_FALSE;
306 nsACString::const_iterator charStart, charEnd;
307 charStart = end;
308 aCommand.EndReading(charEnd);
309 nsACString::const_iterator idStart = charStart, idEnd;
310 if (FindCharInReadable(aSeparator, charStart, charEnd)) {
311 idEnd = charStart;
312 } else {
313 idEnd = charEnd;
315 *aValue = nsDependentCSubstring(idStart, idEnd);
316 return PR_TRUE;
319 const char*
320 nsGTKRemoteService::HandleCommand(char* aCommand, nsIDOMWindow* aWindow,
321 PRUint32 aTimestamp)
323 nsresult rv;
325 nsCOMPtr<nsICommandLineRunner> cmdline
326 (do_CreateInstance("@mozilla.org/toolkit/command-line;1", &rv));
327 if (NS_FAILED(rv))
328 return "509 internal error";
330 // 1) Make sure that it looks remotely valid with parens
331 // 2) Treat ping() immediately and specially
333 nsCAutoString command(aCommand);
334 PRInt32 p1, p2;
335 p1 = command.FindChar('(');
336 p2 = command.FindChar(')');
338 if (p1 == kNotFound || p2 == kNotFound || p1 == 0 || p2 < p1) {
339 return "500 command not parseable";
342 command.Truncate(p1);
343 command.Trim(" ", PR_TRUE, PR_TRUE);
344 ToLowerCase(command);
346 #ifdef DEBUG_bsmedberg
347 printf("Processing xremote command: %s\n", command.get());
348 #endif
350 if (!command.EqualsLiteral("ping")) {
351 nsCAutoString desktopStartupID;
352 nsDependentCString cmd(aCommand);
353 FindExtensionParameterInCommand("DESKTOP_STARTUP_ID",
354 cmd, '\n',
355 &desktopStartupID);
357 char* argv[3] = {"dummyappname", "-remote", aCommand};
358 rv = cmdline->Init(3, argv, nsnull, nsICommandLine::STATE_REMOTE_EXPLICIT);
359 if (NS_FAILED(rv))
360 return "509 internal error";
362 if (aWindow)
363 cmdline->SetWindowContext(aWindow);
365 SetDesktopStartupIDOrTimestamp(desktopStartupID, aTimestamp);
367 rv = cmdline->Run();
368 if (NS_ERROR_ABORT == rv)
369 return "500 command not parseable";
370 if (NS_FAILED(rv))
371 return "509 internal error";
374 return "200 executed command";
377 const char*
378 nsGTKRemoteService::HandleCommandLine(char* aBuffer, nsIDOMWindow* aWindow,
379 PRUint32 aTimestamp)
381 nsresult rv;
383 nsCOMPtr<nsICommandLineRunner> cmdline
384 (do_CreateInstance("@mozilla.org/toolkit/command-line;1", &rv));
385 if (NS_FAILED(rv))
386 return "509 internal error";
388 // the commandline property is constructed as an array of PRInt32
389 // followed by a series of null-terminated strings:
391 // [argc][offsetargv0][offsetargv1...]<workingdir>\0<argv[0]>\0argv[1]...\0
392 // (offset is from the beginning of the buffer)
394 PRInt32 argc = TO_LITTLE_ENDIAN32(*reinterpret_cast<PRInt32*>(aBuffer));
395 char *wd = aBuffer + ((argc + 1) * sizeof(PRInt32));
397 #ifdef DEBUG_bsmedberg
398 printf("Receiving command line:\n"
399 " wd:\t%s\n"
400 " argc:\t%i\n",
401 wd, argc);
402 #endif
404 nsCOMPtr<nsILocalFile> lf;
405 rv = NS_NewNativeLocalFile(nsDependentCString(wd), PR_TRUE,
406 getter_AddRefs(lf));
407 if (NS_FAILED(rv))
408 return "509 internal error";
410 nsCAutoString desktopStartupID;
412 char **argv = (char**) malloc(sizeof(char*) * argc);
413 if (!argv) return "509 internal error";
415 PRInt32 *offset = reinterpret_cast<PRInt32*>(aBuffer) + 1;
417 for (int i = 0; i < argc; ++i) {
418 argv[i] = aBuffer + TO_LITTLE_ENDIAN32(offset[i]);
420 if (i == 0) {
421 nsDependentCString cmd(argv[0]);
422 FindExtensionParameterInCommand("DESKTOP_STARTUP_ID",
423 cmd, ' ',
424 &desktopStartupID);
426 #ifdef DEBUG_bsmedberg
427 printf(" argv[%i]:\t%s\n", i, argv[i]);
428 #endif
431 rv = cmdline->Init(argc, argv, lf, nsICommandLine::STATE_REMOTE_AUTO);
432 free (argv);
433 if (NS_FAILED(rv)) {
434 return "509 internal error";
437 if (aWindow)
438 cmdline->SetWindowContext(aWindow);
440 SetDesktopStartupIDOrTimestamp(desktopStartupID, aTimestamp);
442 rv = cmdline->Run();
444 if (NS_ERROR_ABORT == rv)
445 return "500 command not parseable";
447 if (NS_FAILED(rv))
448 return "509 internal error";
450 return "200 executed command";
453 void
454 nsGTKRemoteService::HandleCommandsFor(GtkWidget* widget,
455 nsIWeakReference* aWindow)
457 #ifdef MOZ_WIDGET_GTK2
458 g_signal_connect(G_OBJECT(widget), "property_notify_event",
459 G_CALLBACK(HandlePropertyChange), aWindow);
460 #else // GTK+
461 gtk_signal_connect(GTK_OBJECT(widget), "property_notify_event",
462 GTK_SIGNAL_FUNC(HandlePropertyChange), aWindow);
463 #endif
465 gtk_widget_add_events(widget, GDK_PROPERTY_CHANGE_MASK);
467 Window window = GDK_WINDOW_XWINDOW(widget->window);
469 // set our version
470 XChangeProperty(GDK_DISPLAY(), window, sMozVersionAtom, XA_STRING,
471 8, PropModeReplace, kRemoteVersion, sizeof(kRemoteVersion) - 1);
473 // get our username
474 unsigned char *logname;
475 logname = (unsigned char*) PR_GetEnv("LOGNAME");
476 if (logname) {
477 // set the property on the window if it's available
478 XChangeProperty(GDK_DISPLAY(), window, sMozUserAtom, XA_STRING,
479 8, PropModeReplace, logname, strlen((char*) logname));
482 XChangeProperty(GDK_DISPLAY(), window, sMozProgramAtom, XA_STRING,
483 8, PropModeReplace, (unsigned char*) mAppName.get(), mAppName.Length());
485 if (!mProfileName.IsEmpty()) {
486 XChangeProperty(GDK_DISPLAY(), window, sMozProfileAtom, XA_STRING,
487 8, PropModeReplace, (unsigned char*) mProfileName.get(), mProfileName.Length());
491 #ifdef MOZ_WIDGET_GTK2
492 #define CMP_GATOM_XATOM(gatom,xatom) (gatom == gdk_x11_xatom_to_atom(xatom))
493 #else
494 #define CMP_GATOM_XATOM(gatom,xatom) (gatom == xatom)
495 #endif
497 gboolean
498 nsGTKRemoteService::HandlePropertyChange(GtkWidget *aWidget,
499 GdkEventProperty *pevent,
500 nsIWeakReference* aThis)
502 nsCOMPtr<nsIDOMWindow> window (do_QueryReferent(aThis));
504 if (pevent->state == GDK_PROPERTY_NEW_VALUE &&
505 CMP_GATOM_XATOM(pevent->atom, sMozCommandAtom)) {
507 // We got a new command atom.
508 int result;
509 Atom actual_type;
510 int actual_format;
511 unsigned long nitems, bytes_after;
512 char *data = 0;
514 result = XGetWindowProperty (GDK_DISPLAY(),
515 GDK_WINDOW_XWINDOW(pevent->window),
516 sMozCommandAtom,
517 0, /* long_offset */
518 (65536 / sizeof (long)), /* long_length */
519 True, /* atomic delete after */
520 XA_STRING, /* req_type */
521 &actual_type, /* actual_type return */
522 &actual_format, /* actual_format_return */
523 &nitems, /* nitems_return */
524 &bytes_after, /* bytes_after_return */
525 (unsigned char **)&data); /* prop_return
526 (we only care
527 about the first ) */
529 #ifdef DEBUG_bsmedberg
530 printf("Handling command: %s\n", data);
531 #endif
533 // Failed to get property off the window?
534 if (result != Success)
535 return FALSE;
537 // Failed to get the data off the window or it was the wrong type?
538 if (!data || !TO_LITTLE_ENDIAN32(*reinterpret_cast<PRInt32*>(data)))
539 return FALSE;
541 // cool, we got the property data.
542 const char *response = HandleCommand(data, window, pevent->time);
544 // put the property onto the window as the response
545 XChangeProperty (GDK_DISPLAY(), GDK_WINDOW_XWINDOW(pevent->window),
546 sMozResponseAtom, XA_STRING,
547 8, PropModeReplace, (const unsigned char *)response, strlen (response));
548 XFree(data);
549 return TRUE;
552 if (pevent->state == GDK_PROPERTY_NEW_VALUE &&
553 CMP_GATOM_XATOM(pevent->atom, sMozCommandLineAtom)) {
555 // We got a new commandline atom.
556 int result;
557 Atom actual_type;
558 int actual_format;
559 unsigned long nitems, bytes_after;
560 char *data = 0;
562 result = XGetWindowProperty (GDK_DISPLAY(),
563 GDK_WINDOW_XWINDOW(pevent->window),
564 sMozCommandLineAtom,
565 0, /* long_offset */
566 (65536 / sizeof (long)), /* long_length */
567 True, /* atomic delete after */
568 XA_STRING, /* req_type */
569 &actual_type, /* actual_type return */
570 &actual_format, /* actual_format_return */
571 &nitems, /* nitems_return */
572 &bytes_after, /* bytes_after_return */
573 (unsigned char **)&data); /* prop_return
574 (we only care
575 about the first ) */
577 // Failed to get property off the window?
578 if (result != Success)
579 return FALSE;
581 // Failed to get the data off the window or it was the wrong type?
582 if (!data || !TO_LITTLE_ENDIAN32(*reinterpret_cast<PRInt32*>(data)))
583 return FALSE;
585 // cool, we got the property data.
586 const char *response = HandleCommandLine(data, window, pevent->time);
588 // put the property onto the window as the response
589 XChangeProperty (GDK_DISPLAY(), GDK_WINDOW_XWINDOW(pevent->window),
590 sMozResponseAtom, XA_STRING,
591 8, PropModeReplace, (const unsigned char *)response, strlen (response));
592 XFree(data);
593 return TRUE;
596 if (pevent->state == GDK_PROPERTY_NEW_VALUE &&
597 CMP_GATOM_XATOM(pevent->atom, sMozResponseAtom)) {
598 // client accepted the response. party on wayne.
599 return TRUE;
602 if (pevent->state == GDK_PROPERTY_NEW_VALUE &&
603 CMP_GATOM_XATOM(pevent->atom, sMozLockAtom)) {
604 // someone locked the window
605 return TRUE;
608 return FALSE;
611 Atom nsGTKRemoteService::sMozVersionAtom;
612 Atom nsGTKRemoteService::sMozLockAtom;
613 Atom nsGTKRemoteService::sMozCommandAtom;
614 Atom nsGTKRemoteService::sMozResponseAtom;
615 Atom nsGTKRemoteService::sMozUserAtom;
616 Atom nsGTKRemoteService::sMozProfileAtom;
617 Atom nsGTKRemoteService::sMozProgramAtom;
618 Atom nsGTKRemoteService::sMozCommandLineAtom;
620 // {C0773E90-5799-4eff-AD03-3EBCD85624AC}
621 #define NS_REMOTESERVICE_CID \
622 { 0xc0773e90, 0x5799, 0x4eff, { 0xad, 0x3, 0x3e, 0xbc, 0xd8, 0x56, 0x24, 0xac } }
624 NS_GENERIC_FACTORY_CONSTRUCTOR(nsGTKRemoteService)
626 static const nsModuleComponentInfo components[] =
628 { "Remote Service",
629 NS_REMOTESERVICE_CID,
630 "@mozilla.org/toolkit/remote-service;1",
631 nsGTKRemoteServiceConstructor
635 NS_IMPL_NSGETMODULE(RemoteServiceModule, components)