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 }