|
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
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 "mozilla/ArrayUtils.h" |
|
7 |
|
8 #include "nsGSettingsService.h" |
|
9 #include "nsStringAPI.h" |
|
10 #include "nsCOMPtr.h" |
|
11 #include "nsMemory.h" |
|
12 #include "prlink.h" |
|
13 #include "nsComponentManagerUtils.h" |
|
14 #include "nsIMutableArray.h" |
|
15 #include "nsISupportsPrimitives.h" |
|
16 |
|
17 #include <glib.h> |
|
18 #include <glib-object.h> |
|
19 |
|
20 using namespace mozilla; |
|
21 |
|
22 typedef struct _GSettings GSettings; |
|
23 typedef struct _GVariantType GVariantType; |
|
24 typedef struct _GVariant GVariant; |
|
25 |
|
26 #ifndef G_VARIANT_TYPE_INT32 |
|
27 # define G_VARIANT_TYPE_INT32 ((const GVariantType *) "i") |
|
28 # define G_VARIANT_TYPE_BOOLEAN ((const GVariantType *) "b") |
|
29 # define G_VARIANT_TYPE_STRING ((const GVariantType *) "s") |
|
30 # define G_VARIANT_TYPE_OBJECT_PATH ((const GVariantType *) "o") |
|
31 # define G_VARIANT_TYPE_SIGNATURE ((const GVariantType *) "g") |
|
32 #endif |
|
33 #ifndef G_VARIANT_TYPE_STRING_ARRAY |
|
34 # define G_VARIANT_TYPE_STRING_ARRAY ((const GVariantType *) "as") |
|
35 #endif |
|
36 |
|
37 #define GSETTINGS_FUNCTIONS \ |
|
38 FUNC(g_settings_new, GSettings *, (const char* schema)) \ |
|
39 FUNC(g_settings_list_schemas, const char * const *, (void)) \ |
|
40 FUNC(g_settings_list_keys, char **, (GSettings* settings)) \ |
|
41 FUNC(g_settings_get_value, GVariant *, (GSettings* settings, const char* key)) \ |
|
42 FUNC(g_settings_set_value, gboolean, (GSettings* settings, const char* key, GVariant* value)) \ |
|
43 FUNC(g_settings_range_check, gboolean, (GSettings* settings, const char* key, GVariant* value)) \ |
|
44 FUNC(g_variant_get_int32, gint32, (GVariant* variant)) \ |
|
45 FUNC(g_variant_get_boolean, gboolean, (GVariant* variant)) \ |
|
46 FUNC(g_variant_get_string, const char *, (GVariant* value, gsize* length)) \ |
|
47 FUNC(g_variant_get_strv, const char **, (GVariant* value, gsize* length)) \ |
|
48 FUNC(g_variant_is_of_type, gboolean, (GVariant* value, const GVariantType* type)) \ |
|
49 FUNC(g_variant_new_int32, GVariant *, (gint32 value)) \ |
|
50 FUNC(g_variant_new_boolean, GVariant *, (gboolean value)) \ |
|
51 FUNC(g_variant_new_string, GVariant *, (const char* string)) \ |
|
52 FUNC(g_variant_unref, void, (GVariant* value)) |
|
53 |
|
54 #define FUNC(name, type, params) \ |
|
55 typedef type (*_##name##_fn) params; \ |
|
56 static _##name##_fn _##name; |
|
57 |
|
58 GSETTINGS_FUNCTIONS |
|
59 |
|
60 #undef FUNC |
|
61 |
|
62 #define g_settings_new _g_settings_new |
|
63 #define g_settings_list_schemas _g_settings_list_schemas |
|
64 #define g_settings_list_keys _g_settings_list_keys |
|
65 #define g_settings_get_value _g_settings_get_value |
|
66 #define g_settings_set_value _g_settings_set_value |
|
67 #define g_settings_range_check _g_settings_range_check |
|
68 #define g_variant_get_int32 _g_variant_get_int32 |
|
69 #define g_variant_get_boolean _g_variant_get_boolean |
|
70 #define g_variant_get_string _g_variant_get_string |
|
71 #define g_variant_get_strv _g_variant_get_strv |
|
72 #define g_variant_is_of_type _g_variant_is_of_type |
|
73 #define g_variant_new_int32 _g_variant_new_int32 |
|
74 #define g_variant_new_boolean _g_variant_new_boolean |
|
75 #define g_variant_new_string _g_variant_new_string |
|
76 #define g_variant_unref _g_variant_unref |
|
77 |
|
78 static PRLibrary *gioLib = nullptr; |
|
79 |
|
80 class nsGSettingsCollection MOZ_FINAL : public nsIGSettingsCollection |
|
81 { |
|
82 public: |
|
83 NS_DECL_ISUPPORTS |
|
84 NS_DECL_NSIGSETTINGSCOLLECTION |
|
85 |
|
86 nsGSettingsCollection(GSettings* aSettings) : mSettings(aSettings), |
|
87 mKeys(nullptr) {} |
|
88 ~nsGSettingsCollection(); |
|
89 |
|
90 private: |
|
91 bool KeyExists(const nsACString& aKey); |
|
92 bool SetValue(const nsACString& aKey, |
|
93 GVariant *aValue); |
|
94 |
|
95 GSettings *mSettings; |
|
96 char **mKeys; |
|
97 }; |
|
98 |
|
99 nsGSettingsCollection::~nsGSettingsCollection() |
|
100 { |
|
101 g_strfreev(mKeys); |
|
102 g_object_unref(mSettings); |
|
103 } |
|
104 |
|
105 bool |
|
106 nsGSettingsCollection::KeyExists(const nsACString& aKey) |
|
107 { |
|
108 if (!mKeys) |
|
109 mKeys = g_settings_list_keys(mSettings); |
|
110 |
|
111 for (uint32_t i = 0; mKeys[i] != nullptr; i++) { |
|
112 if (aKey.Equals(mKeys[i])) |
|
113 return true; |
|
114 } |
|
115 |
|
116 return false; |
|
117 } |
|
118 |
|
119 bool |
|
120 nsGSettingsCollection::SetValue(const nsACString& aKey, |
|
121 GVariant *aValue) |
|
122 { |
|
123 if (!KeyExists(aKey) || |
|
124 !g_settings_range_check(mSettings, |
|
125 PromiseFlatCString(aKey).get(), |
|
126 aValue)) { |
|
127 g_variant_unref(aValue); |
|
128 return false; |
|
129 } |
|
130 |
|
131 return g_settings_set_value(mSettings, |
|
132 PromiseFlatCString(aKey).get(), |
|
133 aValue); |
|
134 } |
|
135 |
|
136 NS_IMPL_ISUPPORTS(nsGSettingsCollection, nsIGSettingsCollection) |
|
137 |
|
138 NS_IMETHODIMP |
|
139 nsGSettingsCollection::SetString(const nsACString& aKey, |
|
140 const nsACString& aValue) |
|
141 { |
|
142 GVariant *value = g_variant_new_string(PromiseFlatCString(aValue).get()); |
|
143 if (!value) |
|
144 return NS_ERROR_OUT_OF_MEMORY; |
|
145 |
|
146 bool res = SetValue(aKey, value); |
|
147 |
|
148 return res ? NS_OK : NS_ERROR_FAILURE; |
|
149 } |
|
150 |
|
151 NS_IMETHODIMP |
|
152 nsGSettingsCollection::SetBoolean(const nsACString& aKey, |
|
153 bool aValue) |
|
154 { |
|
155 GVariant *value = g_variant_new_boolean(aValue); |
|
156 if (!value) |
|
157 return NS_ERROR_OUT_OF_MEMORY; |
|
158 |
|
159 bool res = SetValue(aKey, value); |
|
160 |
|
161 return res ? NS_OK : NS_ERROR_FAILURE; |
|
162 } |
|
163 |
|
164 NS_IMETHODIMP |
|
165 nsGSettingsCollection::SetInt(const nsACString& aKey, |
|
166 int32_t aValue) |
|
167 { |
|
168 GVariant *value = g_variant_new_int32(aValue); |
|
169 if (!value) |
|
170 return NS_ERROR_OUT_OF_MEMORY; |
|
171 |
|
172 bool res = SetValue(aKey, value); |
|
173 |
|
174 return res ? NS_OK : NS_ERROR_FAILURE; |
|
175 } |
|
176 |
|
177 NS_IMETHODIMP |
|
178 nsGSettingsCollection::GetString(const nsACString& aKey, |
|
179 nsACString& aResult) |
|
180 { |
|
181 if (!KeyExists(aKey)) |
|
182 return NS_ERROR_INVALID_ARG; |
|
183 |
|
184 GVariant *value = g_settings_get_value(mSettings, |
|
185 PromiseFlatCString(aKey).get()); |
|
186 if (!g_variant_is_of_type(value, G_VARIANT_TYPE_STRING) && |
|
187 !g_variant_is_of_type(value, G_VARIANT_TYPE_OBJECT_PATH) && |
|
188 !g_variant_is_of_type(value, G_VARIANT_TYPE_SIGNATURE)) { |
|
189 g_variant_unref(value); |
|
190 return NS_ERROR_FAILURE; |
|
191 } |
|
192 |
|
193 aResult.Assign(g_variant_get_string(value, nullptr)); |
|
194 g_variant_unref(value); |
|
195 |
|
196 return NS_OK; |
|
197 } |
|
198 |
|
199 NS_IMETHODIMP |
|
200 nsGSettingsCollection::GetBoolean(const nsACString& aKey, |
|
201 bool* aResult) |
|
202 { |
|
203 NS_ENSURE_ARG_POINTER(aResult); |
|
204 |
|
205 if (!KeyExists(aKey)) |
|
206 return NS_ERROR_INVALID_ARG; |
|
207 |
|
208 GVariant *value = g_settings_get_value(mSettings, |
|
209 PromiseFlatCString(aKey).get()); |
|
210 if (!g_variant_is_of_type(value, G_VARIANT_TYPE_BOOLEAN)) { |
|
211 g_variant_unref(value); |
|
212 return NS_ERROR_FAILURE; |
|
213 } |
|
214 |
|
215 gboolean res = g_variant_get_boolean(value); |
|
216 *aResult = res ? true : false; |
|
217 g_variant_unref(value); |
|
218 |
|
219 return NS_OK; |
|
220 } |
|
221 |
|
222 NS_IMETHODIMP |
|
223 nsGSettingsCollection::GetInt(const nsACString& aKey, |
|
224 int32_t* aResult) |
|
225 { |
|
226 NS_ENSURE_ARG_POINTER(aResult); |
|
227 |
|
228 if (!KeyExists(aKey)) |
|
229 return NS_ERROR_INVALID_ARG; |
|
230 |
|
231 GVariant *value = g_settings_get_value(mSettings, |
|
232 PromiseFlatCString(aKey).get()); |
|
233 if (!g_variant_is_of_type(value, G_VARIANT_TYPE_INT32)) { |
|
234 g_variant_unref(value); |
|
235 return NS_ERROR_FAILURE; |
|
236 } |
|
237 |
|
238 *aResult = g_variant_get_int32(value); |
|
239 g_variant_unref(value); |
|
240 |
|
241 return NS_OK; |
|
242 } |
|
243 |
|
244 // These types are local to nsGSettingsService::Init, but ISO C++98 doesn't |
|
245 // allow a template (ArrayLength) to be instantiated based on a local type. |
|
246 // Boo-urns! |
|
247 typedef void (*nsGSettingsFunc)(); |
|
248 struct nsGSettingsDynamicFunction { |
|
249 const char *functionName; |
|
250 nsGSettingsFunc *function; |
|
251 }; |
|
252 |
|
253 NS_IMETHODIMP |
|
254 nsGSettingsCollection::GetStringList(const nsACString& aKey, nsIArray** aResult) |
|
255 { |
|
256 if (!KeyExists(aKey)) |
|
257 return NS_ERROR_INVALID_ARG; |
|
258 |
|
259 nsCOMPtr<nsIMutableArray> items(do_CreateInstance(NS_ARRAY_CONTRACTID)); |
|
260 if (!items) { |
|
261 return NS_ERROR_OUT_OF_MEMORY; |
|
262 } |
|
263 |
|
264 GVariant *value = g_settings_get_value(mSettings, |
|
265 PromiseFlatCString(aKey).get()); |
|
266 |
|
267 if (!g_variant_is_of_type(value, G_VARIANT_TYPE_STRING_ARRAY)) { |
|
268 g_variant_unref(value); |
|
269 return NS_ERROR_FAILURE; |
|
270 } |
|
271 |
|
272 const gchar ** gs_strings = g_variant_get_strv(value, nullptr); |
|
273 if (!gs_strings) { |
|
274 // empty array |
|
275 NS_ADDREF(*aResult = items); |
|
276 g_variant_unref(value); |
|
277 return NS_OK; |
|
278 } |
|
279 |
|
280 const gchar** p_gs_strings = gs_strings; |
|
281 while (*p_gs_strings != nullptr) |
|
282 { |
|
283 nsCOMPtr<nsISupportsCString> obj(do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID)); |
|
284 if (obj) { |
|
285 obj->SetData(nsDependentCString(*p_gs_strings)); |
|
286 items->AppendElement(obj, false); |
|
287 } |
|
288 p_gs_strings++; |
|
289 } |
|
290 g_free(gs_strings); |
|
291 NS_ADDREF(*aResult = items); |
|
292 g_variant_unref(value); |
|
293 return NS_OK; |
|
294 } |
|
295 |
|
296 nsresult |
|
297 nsGSettingsService::Init() |
|
298 { |
|
299 #define FUNC(name, type, params) { #name, (nsGSettingsFunc *)&_##name }, |
|
300 static const nsGSettingsDynamicFunction kGSettingsSymbols[] = { |
|
301 GSETTINGS_FUNCTIONS |
|
302 }; |
|
303 #undef FUNC |
|
304 |
|
305 if (!gioLib) { |
|
306 gioLib = PR_LoadLibrary("libgio-2.0.so.0"); |
|
307 if (!gioLib) |
|
308 return NS_ERROR_FAILURE; |
|
309 } |
|
310 |
|
311 for (uint32_t i = 0; i < ArrayLength(kGSettingsSymbols); i++) { |
|
312 *kGSettingsSymbols[i].function = |
|
313 PR_FindFunctionSymbol(gioLib, kGSettingsSymbols[i].functionName); |
|
314 if (!*kGSettingsSymbols[i].function) { |
|
315 return NS_ERROR_FAILURE; |
|
316 } |
|
317 } |
|
318 |
|
319 return NS_OK; |
|
320 } |
|
321 |
|
322 NS_IMPL_ISUPPORTS(nsGSettingsService, nsIGSettingsService) |
|
323 |
|
324 nsGSettingsService::~nsGSettingsService() |
|
325 { |
|
326 if (gioLib) { |
|
327 PR_UnloadLibrary(gioLib); |
|
328 gioLib = nullptr; |
|
329 } |
|
330 } |
|
331 |
|
332 NS_IMETHODIMP |
|
333 nsGSettingsService::GetCollectionForSchema(const nsACString& schema, |
|
334 nsIGSettingsCollection** collection) |
|
335 { |
|
336 NS_ENSURE_ARG_POINTER(collection); |
|
337 |
|
338 const char * const *schemas = g_settings_list_schemas(); |
|
339 |
|
340 for (uint32_t i = 0; schemas[i] != nullptr; i++) { |
|
341 if (schema.Equals(schemas[i])) { |
|
342 GSettings *settings = g_settings_new(PromiseFlatCString(schema).get()); |
|
343 nsGSettingsCollection *mozGSettings = new nsGSettingsCollection(settings); |
|
344 NS_ADDREF(*collection = mozGSettings); |
|
345 return NS_OK; |
|
346 } |
|
347 } |
|
348 |
|
349 return NS_ERROR_FAILURE; |
|
350 } |