gnome-shell-3.6.3.1/browser-plugin/browser-plugin.c

No issues found

   1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
   2 /*
   3  * Copyright (C) 2011 Red Hat
   4  *
   5  * This program is free software; you can redistribute it and/or
   6  * modify it under the terms of the GNU General Public License as
   7  * published by the Free Software Foundation; either version 2 of the
   8  * License, or (at your option) any later version.
   9  *
  10  * This program is distributed in the hope that it will be useful, but
  11  * WITHOUT ANY WARRANTY; without even the implied warranty of
  12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  13  * General Public License for more details.
  14  *
  15  * You should have received a copy of the GNU General Public License
  16  * along with this program; if not, write to the Free Software
  17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
  18  * 02111-1307, USA.
  19  *
  20  * Authors:
  21  *      Jasper St. Pierre <jstpierre@mecheye.net>
  22  *      Giovanni Campagna <scampa.giovanni@gmail.com>
  23  */
  24 
  25 #include <string.h>
  26 
  27 #define XP_UNIX 1
  28 
  29 #include "npapi/npapi.h"
  30 #include "npapi/npruntime.h"
  31 #include "npapi/npfunctions.h"
  32 
  33 #include <glib.h>
  34 #include <gio/gio.h>
  35 #include <json-glib/json-glib.h>
  36 
  37 #define ORIGIN "extensions.gnome.org"
  38 #define PLUGIN_NAME "Gnome Shell Integration"
  39 #define PLUGIN_DESCRIPTION "This plugin provides integration with Gnome Shell " \
  40       "for live extension enabling and disabling. " \
  41       "It can be used only by extensions.gnome.org"
  42 #define PLUGIN_MIME_STRING "application/x-gnome-shell-integration::Gnome Shell Integration Dummy Content-Type";
  43 
  44 #define PLUGIN_API_VERSION 5
  45 
  46 typedef struct {
  47   GDBusProxy *proxy;
  48 } PluginData;
  49 
  50 static NPNetscapeFuncs funcs;
  51 
  52 static inline gchar *
  53 get_string_property (NPP         instance,
  54                      NPObject   *obj,
  55                      const char *name)
  56 {
  57   NPVariant result = { NPVariantType_Void };
  58   NPString result_str;
  59   gchar *result_copy;
  60 
  61   result_copy = NULL;
  62 
  63   if (!funcs.getproperty (instance, obj,
  64                           funcs.getstringidentifier (name),
  65                           &result))
  66     goto out;
  67 
  68   if (!NPVARIANT_IS_STRING (result))
  69     goto out;
  70 
  71   result_str = NPVARIANT_TO_STRING (result);
  72   result_copy = g_strndup (result_str.UTF8Characters, result_str.UTF8Length);
  73 
  74  out:
  75   funcs.releasevariantvalue (&result);
  76   return result_copy;
  77 }
  78 
  79 static gboolean
  80 check_origin_and_protocol (NPP instance)
  81 {
  82   gboolean ret = FALSE;
  83   NPError error;
  84   NPObject *window = NULL;
  85   NPVariant document = { NPVariantType_Void };
  86   NPVariant location = { NPVariantType_Void };
  87   gchar *hostname = NULL;
  88   gchar *protocol = NULL;
  89 
  90   error = funcs.getvalue (instance, NPNVWindowNPObject, &window);
  91   if (error != NPERR_NO_ERROR)
  92     goto out;
  93 
  94   if (!funcs.getproperty (instance, window,
  95                           funcs.getstringidentifier ("document"),
  96                           &document))
  97     goto out;
  98 
  99   if (!NPVARIANT_IS_OBJECT (document))
 100     goto out;
 101 
 102   if (!funcs.getproperty (instance, NPVARIANT_TO_OBJECT (document),
 103                           funcs.getstringidentifier ("location"),
 104                           &location))
 105     goto out;
 106 
 107   if (!NPVARIANT_IS_OBJECT (location))
 108     goto out;
 109 
 110   hostname = get_string_property (instance,
 111                                   NPVARIANT_TO_OBJECT (location),
 112                                   "hostname");
 113 
 114   if (g_strcmp0 (hostname, ORIGIN))
 115     {
 116       g_debug ("origin does not match, is %s",
 117                hostname);
 118 
 119       goto out;
 120     }
 121 
 122   protocol = get_string_property (instance,
 123                                   NPVARIANT_TO_OBJECT (location),
 124                                   "protocol");
 125 
 126   if (g_strcmp0 (protocol, "https:") != 0)
 127     {
 128       g_debug ("protocol does not match, is %s",
 129                protocol);
 130 
 131       goto out;
 132     }
 133 
 134   ret = TRUE;
 135 
 136  out:
 137   g_free (protocol);
 138   g_free (hostname);
 139 
 140   funcs.releasevariantvalue (&location);
 141   funcs.releasevariantvalue (&document);
 142 
 143   if (window != NULL)
 144     funcs.releaseobject (window);
 145   return ret;
 146 }
 147 
 148 /* =============== public entry points =================== */
 149 
 150 NPError
 151 NP_Initialize(NPNetscapeFuncs *pfuncs, NPPluginFuncs *plugin)
 152 {
 153   /* global initialization routine, called once when plugin
 154      is loaded */
 155 
 156   g_type_init ();
 157 
 158   g_debug ("plugin loaded");
 159 
 160   memcpy (&funcs, pfuncs, sizeof (funcs));
 161 
 162   plugin->size = sizeof(NPPluginFuncs);
 163   plugin->newp = NPP_New;
 164   plugin->destroy = NPP_Destroy;
 165   plugin->getvalue = NPP_GetValue;
 166   plugin->setwindow = NPP_SetWindow;
 167 
 168   return NPERR_NO_ERROR;
 169 }
 170 
 171 NPError
 172 NP_Shutdown(void)
 173 {
 174   return NPERR_NO_ERROR;
 175 }
 176 
 177 const char*
 178 NP_GetMIMEDescription(void)
 179 {
 180   return PLUGIN_MIME_STRING;
 181 }
 182 
 183 NPError
 184 NP_GetValue(void         *instance,
 185             NPPVariable   variable,
 186             void         *value)
 187 {
 188   switch (variable) {
 189   case NPPVpluginNameString:
 190     *(char**)value = PLUGIN_NAME;
 191     break;
 192   case NPPVpluginDescriptionString:
 193     *(char**)value = PLUGIN_DESCRIPTION;
 194     break;
 195   default:
 196     ;
 197   }
 198 
 199   return NPERR_NO_ERROR;
 200 }
 201 
 202 NPError
 203 NPP_New(NPMIMEType    mimetype,
 204         NPP           instance,
 205         uint16_t      mode,
 206         int16_t       argc,
 207         char        **argn,
 208         char        **argv,
 209         NPSavedData  *saved)
 210 {
 211   /* instance initialization function */
 212   PluginData *data;
 213   GError *error = NULL;
 214 
 215   g_debug ("plugin created");
 216 
 217   if (!check_origin_and_protocol (instance))
 218     return NPERR_GENERIC_ERROR;
 219 
 220   data = g_slice_new (PluginData);
 221   instance->pdata = data;
 222 
 223   data->proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION,
 224                                                G_DBUS_PROXY_FLAGS_NONE,
 225                                                NULL, /* interface info */
 226                                                "org.gnome.Shell",
 227                                                "/org/gnome/Shell",
 228                                                "org.gnome.Shell.Extensions",
 229                                                NULL, /* GCancellable */
 230                                                &error);
 231   if (!data->proxy)
 232     {
 233       /* ignore error if the shell is not running, otherwise warn */
 234       if (error->domain != G_DBUS_ERROR ||
 235           error->code != G_DBUS_ERROR_NAME_HAS_NO_OWNER)
 236         {
 237           g_warning ("Failed to set up Shell proxy: %s", error->message);
 238         }
 239       g_clear_error (&error);
 240       return NPERR_GENERIC_ERROR;
 241     }
 242 
 243   g_debug ("plugin created successfully");
 244 
 245   return NPERR_NO_ERROR;
 246 }
 247 
 248 NPError
 249 NPP_Destroy(NPP           instance,
 250 	    NPSavedData **saved)
 251 {
 252   /* instance finalization function */
 253 
 254   PluginData *data = instance->pdata;
 255 
 256   g_debug ("plugin destroyed");
 257 
 258   g_object_unref (data->proxy);
 259 
 260   g_slice_free (PluginData, data);
 261 
 262   return NPERR_NO_ERROR;
 263 }
 264 
 265 /* =================== scripting interface =================== */
 266 
 267 typedef struct {
 268   NPObject     parent;
 269   NPP          instance;
 270   GDBusProxy  *proxy;
 271   GSettings   *settings;
 272   NPObject    *listener;
 273   NPObject    *restart_listener;
 274   gint         signal_id;
 275   guint        watch_name_id;
 276 } PluginObject;
 277 
 278 static void
 279 on_shell_signal (GDBusProxy *proxy,
 280                  gchar      *sender_name,
 281                  gchar      *signal_name,
 282                  GVariant   *parameters,
 283                  gpointer    user_data)
 284 {
 285   PluginObject *obj = user_data;
 286 
 287   if (strcmp (signal_name, "ExtensionStatusChanged") == 0)
 288     {
 289       gchar *uuid;
 290       gint32 status;
 291       gchar *error;
 292       NPVariant args[3];
 293       NPVariant result = { NPVariantType_Void };
 294 
 295       g_variant_get (parameters, "(sis)", &uuid, &status, &error);
 296       STRINGZ_TO_NPVARIANT (uuid, args[0]);
 297       INT32_TO_NPVARIANT (status, args[1]);
 298       STRINGZ_TO_NPVARIANT (error, args[2]);
 299 
 300       funcs.invokeDefault (obj->instance, obj->listener,
 301 			   args, 3, &result);
 302 
 303       funcs.releasevariantvalue (&result);
 304       g_free (uuid);
 305       g_free (error);
 306     }
 307 }
 308 
 309 static void
 310 on_shell_appeared (GDBusConnection *connection,
 311                    const gchar     *name,
 312                    const gchar     *name_owner,
 313                    gpointer         user_data)
 314 {
 315   PluginObject *obj = (PluginObject*) user_data;
 316 
 317   if (obj->restart_listener)
 318     {
 319       NPVariant result = { NPVariantType_Void };
 320 
 321       funcs.invokeDefault (obj->instance, obj->restart_listener,
 322                            NULL, 0, &result);
 323 
 324       funcs.releasevariantvalue (&result);
 325     }
 326 }
 327 
 328 #define SHELL_SCHEMA "org.gnome.shell"
 329 #define ENABLED_EXTENSIONS_KEY "enabled-extensions"
 330 
 331 static NPObject *
 332 plugin_object_allocate (NPP      instance,
 333                         NPClass *klass)
 334 {
 335   PluginData *data = instance->pdata;
 336   PluginObject *obj = g_slice_new0 (PluginObject);
 337 
 338   obj->instance = instance;
 339   obj->proxy = g_object_ref (data->proxy);
 340   obj->settings = g_settings_new (SHELL_SCHEMA);
 341   obj->signal_id = g_signal_connect (obj->proxy, "g-signal",
 342                                      G_CALLBACK (on_shell_signal), obj);
 343 
 344   obj->watch_name_id = g_bus_watch_name (G_BUS_TYPE_SESSION,
 345                                          "org.gnome.Shell",
 346                                          G_BUS_NAME_WATCHER_FLAGS_NONE,
 347                                          on_shell_appeared,
 348                                          NULL,
 349                                          obj,
 350                                          NULL);
 351 
 352   g_debug ("plugin object created");
 353 
 354   return (NPObject*)obj;
 355 }
 356 
 357 static void
 358 plugin_object_deallocate (NPObject *npobj)
 359 {
 360   PluginObject *obj = (PluginObject*)npobj;
 361 
 362   g_signal_handler_disconnect (obj->proxy, obj->signal_id);
 363   g_object_unref (obj->proxy);
 364 
 365   if (obj->listener)
 366     funcs.releaseobject (obj->listener);
 367 
 368   if (obj->watch_name_id)
 369     g_bus_unwatch_name (obj->watch_name_id);
 370 
 371   g_debug ("plugin object destroyed");
 372 
 373   g_slice_free (PluginObject, obj);
 374 }
 375 
 376 static inline gboolean
 377 uuid_is_valid (NPString string)
 378 {
 379   gsize i;
 380 
 381   for (i = 0; i < string.UTF8Length; i++)
 382     {
 383       gchar c = string.UTF8Characters[i];
 384       if (c < 32 || c >= 127)
 385         return FALSE;
 386 
 387       switch (c)
 388         {
 389         case '&':
 390         case '<':
 391         case '>':
 392         case '/':
 393         case '\\':
 394           return FALSE;
 395         default:
 396           break;
 397         }
 398     }
 399   return TRUE;
 400 }
 401 
 402 static gboolean
 403 jsonify_variant (GVariant  *variant,
 404                  NPVariant *result)
 405 {
 406   gboolean ret;
 407   GVariant *real_value;
 408   JsonNode *root;
 409   JsonGenerator *generator;
 410   gsize json_length;
 411   gchar *json;
 412   gchar *buffer;
 413 
 414   ret = TRUE;
 415 
 416   /* DBus methods can return multiple values,
 417    * but we're only interested in the first. */
 418   g_variant_get (variant, "(@*)", &real_value);
 419 
 420   root = json_gvariant_serialize (real_value);
 421 
 422   generator = json_generator_new ();
 423   json_generator_set_root (generator, root);
 424   json = json_generator_to_data (generator, &json_length);
 425 
 426   buffer = funcs.memalloc (json_length + 1);
 427   if (!buffer)
 428     {
 429       ret = FALSE;
 430       goto out;
 431     }
 432 
 433   strcpy (buffer, json);
 434 
 435   STRINGN_TO_NPVARIANT (buffer, json_length, *result);
 436 
 437  out:
 438   g_variant_unref (variant);
 439   g_variant_unref (real_value);
 440   json_node_free (root);
 441   g_free (json);
 442 
 443   return ret;
 444 }
 445 
 446 static gboolean
 447 parse_args (const gchar     *format_str,
 448             uint32_t         argc,
 449             const NPVariant *argv,
 450             ...)
 451 {
 452   va_list args;
 453   gsize i;
 454   gboolean ret = FALSE;
 455 
 456   if (strlen (format_str) != argc)
 457     return FALSE;
 458 
 459   va_start (args, argv);
 460 
 461   for (i = 0; format_str[i]; i++)
 462     {
 463       gpointer arg_location;
 464       const NPVariant arg = argv[i];
 465 
 466       arg_location = va_arg (args, gpointer);
 467 
 468       switch (format_str[i])
 469         {
 470         case 'u':
 471           {
 472             NPString string;
 473 
 474             if (!NPVARIANT_IS_STRING (arg))
 475               goto out;
 476 
 477             string = NPVARIANT_TO_STRING (arg);
 478 
 479             if (!uuid_is_valid (string))
 480               goto out;
 481 
 482             *(gchar **) arg_location = g_strndup (string.UTF8Characters, string.UTF8Length);
 483           }
 484           break;
 485 
 486         case 'b':
 487           if (!NPVARIANT_IS_BOOLEAN (arg))
 488             goto out;
 489 
 490           *(gboolean *) arg_location = NPVARIANT_TO_BOOLEAN (arg);
 491           break;
 492 
 493         case 'o':
 494           if (!NPVARIANT_IS_OBJECT (arg))
 495             goto out;
 496 
 497           *(NPObject **) arg_location = NPVARIANT_TO_OBJECT (arg);
 498         }
 499     }
 500 
 501   ret = TRUE;
 502 
 503  out:
 504   va_end (args);
 505 
 506   return ret;
 507 }
 508 
 509 static gboolean
 510 plugin_list_extensions (PluginObject    *obj,
 511                         uint32_t         argc,
 512                         const NPVariant *args,
 513                         NPVariant       *result)
 514 {
 515   GError *error = NULL;
 516   GVariant *res;
 517 
 518   res = g_dbus_proxy_call_sync (obj->proxy,
 519                                 "ListExtensions",
 520                                 NULL, /* parameters */
 521                                 G_DBUS_CALL_FLAGS_NONE,
 522                                 -1, /* timeout */
 523                                 NULL, /* cancellable */
 524                                 &error);
 525 
 526   if (!res)
 527     {
 528       g_warning ("Failed to retrieve extension list: %s", error->message);
 529       g_error_free (error);
 530       return FALSE;
 531     }
 532 
 533   return jsonify_variant (res, result);
 534 }
 535 
 536 static gboolean
 537 plugin_enable_extension (PluginObject    *obj,
 538                          uint32_t         argc,
 539                          const NPVariant *argv,
 540                          NPVariant       *result)
 541 {
 542   gboolean ret;
 543   gchar *uuid;
 544   gboolean enabled;
 545   gsize length;
 546   gchar **uuids;
 547   const gchar **new_uuids;
 548 
 549   if (!parse_args ("ub", argc, argv, &uuid, &enabled))
 550     return FALSE;
 551 
 552   uuids = g_settings_get_strv (obj->settings, ENABLED_EXTENSIONS_KEY);
 553   length = g_strv_length (uuids);
 554 
 555   if (enabled)
 556     {
 557       new_uuids = g_new (const gchar *, length + 2); /* New key, NULL */
 558       memcpy (new_uuids, uuids, length * sizeof (*new_uuids));
 559       new_uuids[length] = uuid;
 560       new_uuids[length + 1] = NULL;
 561     }
 562   else
 563     {
 564       gsize i = 0, j = 0;
 565       new_uuids = g_new (const gchar *, length);
 566       for (i = 0; i < length; i ++)
 567         {
 568           if (g_str_equal (uuids[i], uuid))
 569             continue;
 570 
 571           new_uuids[j] = uuids[i];
 572           j++;
 573         }
 574 
 575       new_uuids[j] = NULL;
 576     }
 577 
 578   ret = g_settings_set_strv (obj->settings,
 579                              ENABLED_EXTENSIONS_KEY,
 580                              new_uuids);
 581 
 582   g_strfreev (uuids);
 583   g_free (new_uuids);
 584   g_free (uuid);
 585 
 586   return ret;
 587 }
 588 
 589 typedef struct _AsyncClosure AsyncClosure;
 590 
 591 struct _AsyncClosure {
 592   PluginObject *obj;
 593   NPObject *callback;
 594   NPObject *errback;
 595 };
 596 
 597 static void
 598 install_extension_cb (GObject      *proxy,
 599                       GAsyncResult *async_res,
 600                       gpointer      user_data)
 601 {
 602   AsyncClosure *async_closure = (AsyncClosure *) user_data;
 603   GError *error = NULL;
 604   GVariant *res;
 605   NPVariant args[1];
 606   NPVariant result = { NPVariantType_Void };
 607   NPObject *callback;
 608 
 609   res = g_dbus_proxy_call_finish (G_DBUS_PROXY (proxy), async_res, &error);
 610 
 611   if (res == NULL)
 612     {
 613       if (g_dbus_error_is_remote_error (error))
 614         g_dbus_error_strip_remote_error (error);
 615       STRINGZ_TO_NPVARIANT (error->message, args[0]);
 616       callback = async_closure->errback;
 617     }
 618   else
 619     {
 620       char *string_result;
 621       g_variant_get (res, "(&s)", &string_result);
 622       STRINGZ_TO_NPVARIANT (string_result, args[0]);
 623       callback = async_closure->callback;
 624     }
 625 
 626   funcs.invokeDefault (async_closure->obj->instance,
 627                        callback, args, 1, &result);
 628 
 629   funcs.releasevariantvalue (&result);
 630 
 631   funcs.releaseobject (async_closure->callback);
 632   funcs.releaseobject (async_closure->errback);
 633   g_slice_free (AsyncClosure, async_closure);
 634 }
 635 
 636 static gboolean
 637 plugin_install_extension (PluginObject    *obj,
 638                           uint32_t         argc,
 639                           const NPVariant *argv,
 640                           NPVariant       *result)
 641 {
 642   gchar *uuid;
 643   NPObject *callback, *errback;
 644   AsyncClosure *async_closure;
 645 
 646   if (!parse_args ("uoo", argc, argv, &uuid, &callback, &errback))
 647     return FALSE;
 648 
 649   async_closure = g_slice_new (AsyncClosure);
 650   async_closure->obj = obj;
 651   async_closure->callback = funcs.retainobject (callback);
 652   async_closure->errback = funcs.retainobject (errback);
 653 
 654   g_dbus_proxy_call (obj->proxy,
 655                      "InstallRemoteExtension",
 656                      g_variant_new ("(s)", uuid),
 657                      G_DBUS_CALL_FLAGS_NONE,
 658                      -1, /* timeout */
 659                      NULL, /* cancellable */
 660                      install_extension_cb,
 661                      async_closure);
 662 
 663   g_free (uuid);
 664 
 665   return TRUE;
 666 }
 667 
 668 static gboolean
 669 plugin_uninstall_extension (PluginObject    *obj,
 670                             uint32_t         argc,
 671                             const NPVariant *argv,
 672                             NPVariant       *result)
 673 {
 674   GError *error = NULL;
 675   GVariant *res;
 676   gchar *uuid;
 677 
 678   if (!parse_args ("u", argc, argv, &uuid))
 679     return FALSE;
 680 
 681   res = g_dbus_proxy_call_sync (obj->proxy,
 682                                 "UninstallExtension",
 683                                 g_variant_new ("(s)", uuid),
 684                                 G_DBUS_CALL_FLAGS_NONE,
 685                                 -1, /* timeout */
 686                                 NULL, /* cancellable */
 687                                 &error);
 688 
 689   g_free (uuid);
 690 
 691   if (!res)
 692     {
 693       g_warning ("Failed to uninstall extension: %s", error->message);
 694       g_error_free (error);
 695       return FALSE;
 696     }
 697 
 698   return jsonify_variant (res, result);
 699 }
 700 
 701 static gboolean
 702 plugin_get_info (PluginObject    *obj,
 703                  uint32_t         argc,
 704                  const NPVariant *argv,
 705                  NPVariant       *result)
 706 {
 707   GError *error = NULL;
 708   GVariant *res;
 709   gchar *uuid;
 710 
 711   if (!parse_args ("u", argc, argv, &uuid))
 712     return FALSE;
 713 
 714   res = g_dbus_proxy_call_sync (obj->proxy,
 715                                 "GetExtensionInfo",
 716                                 g_variant_new ("(s)", uuid),
 717                                 G_DBUS_CALL_FLAGS_NONE,
 718                                 -1, /* timeout */
 719                                 NULL, /* cancellable */
 720                                 &error);
 721 
 722   g_free (uuid);
 723 
 724   if (!res)
 725     {
 726       g_warning ("Failed to retrieve extension metadata: %s", error->message);
 727       g_error_free (error);
 728       return FALSE;
 729     }
 730 
 731   return jsonify_variant (res, result);
 732 }
 733 
 734 static gboolean
 735 plugin_get_errors (PluginObject    *obj,
 736                    uint32_t         argc,
 737                    const NPVariant *argv,
 738                    NPVariant       *result)
 739 {
 740   GError *error = NULL;
 741   GVariant *res;
 742   gchar *uuid;
 743 
 744   if (!parse_args ("u", argc, argv, &uuid))
 745     return FALSE;
 746 
 747   res = g_dbus_proxy_call_sync (obj->proxy,
 748                                 "GetExtensionErrors",
 749                                 g_variant_new ("(s)", uuid),
 750                                 G_DBUS_CALL_FLAGS_NONE,
 751                                 -1, /* timeout */
 752                                 NULL, /* cancellable */
 753                                 &error);
 754 
 755   if (!res)
 756     {
 757       g_warning ("Failed to retrieve errors: %s", error->message);
 758       g_error_free (error);
 759       return FALSE;
 760     }
 761 
 762   return jsonify_variant (res, result);
 763 }
 764 
 765 static gboolean
 766 plugin_launch_extension_prefs (PluginObject    *obj,
 767                                uint32_t         argc,
 768                                const NPVariant *argv,
 769                                NPVariant       *result)
 770 {
 771   gchar *uuid;
 772 
 773   if (!parse_args ("u", argc, argv, &uuid))
 774     return FALSE;
 775 
 776   g_dbus_proxy_call (obj->proxy,
 777                      "LaunchExtensionPrefs",
 778                      g_variant_new ("(s)", uuid),
 779                      G_DBUS_CALL_FLAGS_NONE,
 780                      -1, /* timeout */
 781                      NULL, /* cancellable */
 782                      NULL, /* callback */
 783                      NULL /* user_data */);
 784 
 785   return TRUE;
 786 }
 787 
 788 static int
 789 plugin_get_api_version (PluginObject  *obj,
 790                         NPVariant     *result)
 791 {
 792   INT32_TO_NPVARIANT (PLUGIN_API_VERSION, *result);
 793   return TRUE;
 794 }
 795 
 796 static gboolean
 797 plugin_get_shell_version (PluginObject  *obj,
 798                           NPVariant     *result)
 799 {
 800   GVariant *res;
 801   const gchar *version;
 802   gsize length;
 803   gchar *buffer;
 804   gboolean ret;
 805 
 806   ret = TRUE;
 807 
 808   res = g_dbus_proxy_get_cached_property (obj->proxy,
 809                                           "ShellVersion");
 810 
 811   if (res == NULL)
 812     {
 813       g_warning ("Failed to grab shell version.");
 814       version = "-1";
 815     }
 816   else
 817     {
 818       g_variant_get (res, "&s", &version);
 819     }
 820 
 821   length = strlen (version);
 822   buffer = funcs.memalloc (length + 1);
 823   if (!buffer)
 824     {
 825       ret = FALSE;
 826       goto out;
 827     }
 828   strcpy (buffer, version);
 829 
 830   STRINGN_TO_NPVARIANT (buffer, length, *result);
 831 
 832  out:
 833   if (res)
 834     g_variant_unref (res);
 835   return ret;
 836 }
 837 
 838 #define METHODS                                 \
 839   METHOD (list_extensions)                      \
 840   METHOD (get_info)                             \
 841   METHOD (enable_extension)                     \
 842   METHOD (install_extension)                    \
 843   METHOD (uninstall_extension)                  \
 844   METHOD (get_errors)                           \
 845   METHOD (launch_extension_prefs)               \
 846   /* */
 847 
 848 #define METHOD(x)                               \
 849   static NPIdentifier x##_id;
 850 METHODS
 851 #undef METHOD
 852 
 853 static NPIdentifier api_version_id;
 854 static NPIdentifier shell_version_id;
 855 static NPIdentifier onextension_changed_id;
 856 static NPIdentifier onrestart_id;
 857 
 858 static bool
 859 plugin_object_has_method (NPObject     *npobj,
 860                           NPIdentifier  name)
 861 {
 862 #define METHOD(x) (name == (x##_id)) ||
 863   /* expands to (name == list_extensions_id) || FALSE; */
 864   return METHODS FALSE;
 865 #undef METHOD
 866 }
 867 
 868 static bool
 869 plugin_object_invoke (NPObject        *npobj,
 870                       NPIdentifier     name,
 871                       const NPVariant *argv,
 872                       uint32_t         argc,
 873                       NPVariant       *result)
 874 {
 875   PluginObject *obj;
 876 
 877   g_debug ("invoking plugin object method");
 878 
 879   obj = (PluginObject*) npobj;
 880 
 881   VOID_TO_NPVARIANT (*result);
 882 
 883 #define METHOD(x)                                       \
 884   if (name == x##_id)                                   \
 885     return plugin_##x (obj, argc, argv, result);
 886 METHODS
 887 #undef METHOD
 888 
 889   return FALSE;
 890 }
 891 
 892 static bool
 893 plugin_object_has_property (NPObject     *npobj,
 894                             NPIdentifier  name)
 895 {
 896   return (name == onextension_changed_id ||
 897           name == onrestart_id ||
 898           name == api_version_id ||
 899           name == shell_version_id);
 900 }
 901 
 902 static bool
 903 plugin_object_get_property (NPObject     *npobj,
 904                             NPIdentifier  name,
 905                             NPVariant    *result)
 906 {
 907   PluginObject *obj;
 908 
 909   if (!plugin_object_has_property (npobj, name))
 910     return FALSE;
 911 
 912   obj = (PluginObject*) npobj;
 913   if (name == api_version_id)
 914     return plugin_get_api_version (obj, result);
 915   else if (name == shell_version_id)
 916     return plugin_get_shell_version (obj, result);
 917   else if (name == onextension_changed_id)
 918     {
 919       if (obj->listener)
 920         OBJECT_TO_NPVARIANT (obj->listener, *result);
 921       else
 922         NULL_TO_NPVARIANT (*result);
 923     }
 924   else if (name == onrestart_id)
 925     {
 926       if (obj->restart_listener)
 927         OBJECT_TO_NPVARIANT (obj->restart_listener, *result);
 928       else
 929         NULL_TO_NPVARIANT (*result);
 930     }
 931 
 932   return TRUE;
 933 }
 934 
 935 static bool
 936 plugin_object_set_callback (NPObject        **listener,
 937                             const NPVariant  *value)
 938 {
 939   if (!NPVARIANT_IS_OBJECT (*value) && !NPVARIANT_IS_NULL (*value))
 940     return FALSE;
 941 
 942   if (*listener)
 943     funcs.releaseobject (*listener);
 944   *listener = NULL;
 945 
 946   if (NPVARIANT_IS_OBJECT (*value))
 947     {
 948       *listener = NPVARIANT_TO_OBJECT (*value);
 949       funcs.retainobject (*listener);
 950     }
 951 
 952   return TRUE;
 953 }
 954 
 955 static bool
 956 plugin_object_set_property (NPObject        *npobj,
 957                             NPIdentifier     name,
 958                             const NPVariant *value)
 959 {
 960   PluginObject *obj;
 961 
 962   obj = (PluginObject *)npobj;
 963 
 964   if (name == onextension_changed_id)
 965     return plugin_object_set_callback (&obj->listener, value);
 966 
 967   if (name == onrestart_id)
 968     return plugin_object_set_callback (&obj->restart_listener, value);
 969 
 970   return FALSE;
 971 }
 972 
 973 static NPClass plugin_class = {
 974   NP_CLASS_STRUCT_VERSION,
 975   plugin_object_allocate,
 976   plugin_object_deallocate,
 977   NULL, /* invalidate */
 978   plugin_object_has_method,
 979   plugin_object_invoke,
 980   NULL, /* invoke default */
 981   plugin_object_has_property,
 982   plugin_object_get_property,
 983   plugin_object_set_property,
 984   NULL, /* remove property */
 985   NULL, /* enumerate */
 986   NULL, /* construct */
 987 };
 988 
 989 static void
 990 init_methods_and_properties (void)
 991 {
 992   /* this is the JS public API; it is manipulated through NPIdentifiers for speed */
 993   api_version_id = funcs.getstringidentifier ("apiVersion");
 994   shell_version_id = funcs.getstringidentifier ("shellVersion");
 995 
 996   get_info_id = funcs.getstringidentifier ("getExtensionInfo");
 997   list_extensions_id = funcs.getstringidentifier ("listExtensions");
 998   enable_extension_id = funcs.getstringidentifier ("setExtensionEnabled");
 999   install_extension_id = funcs.getstringidentifier ("installExtension");
1000   uninstall_extension_id = funcs.getstringidentifier ("uninstallExtension");
1001   get_errors_id = funcs.getstringidentifier ("getExtensionErrors");
1002   launch_extension_prefs_id = funcs.getstringidentifier ("launchExtensionPrefs");
1003 
1004   onrestart_id = funcs.getstringidentifier ("onshellrestart");
1005   onextension_changed_id = funcs.getstringidentifier ("onchange");
1006 }
1007 
1008 NPError
1009 NPP_GetValue(NPP          instance,
1010 	     NPPVariable  variable,
1011 	     void        *value)
1012 {
1013   g_debug ("NPP_GetValue called");
1014 
1015   switch (variable) {
1016   case NPPVpluginScriptableNPObject:
1017     g_debug ("creating scriptable object");
1018     init_methods_and_properties ();
1019 
1020     *(NPObject**)value = funcs.createobject (instance, &plugin_class);
1021     break;
1022 
1023   case NPPVpluginNeedsXEmbed:
1024     *(bool *)value = TRUE;
1025     break;
1026 
1027   default:
1028     ;
1029   }
1030 
1031   return NPERR_NO_ERROR;
1032 }
1033 
1034 /* Opera tries to call NPP_SetWindow without checking the
1035  * NULL pointer beforehand. */
1036 NPError
1037 NPP_SetWindow(NPP          instance,
1038               NPWindow    *window)
1039 {
1040   return NPERR_NO_ERROR;
1041 }