Bug 1530506 - Add a generic writable property bag wrapper for Rust code. r=nika
authorLina Cambridge <lina@yakshaving.ninja>
Tue, 26 Feb 2019 21:53:32 +0000
changeset 519138 f67d5891bef8d40fdce3c1ad266ff787cd19a4d2
parent 519137 0b6224482132b58b0984a94a5733f7c85542e178
child 519139 70eb46335a2ca23516774624f7ce1cbe34de56ea
push id10862
push userffxbld-merge
push dateMon, 11 Mar 2019 13:01:11 +0000
treeherdermozilla-beta@a2e7f5c935da [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnika
bugs1530506
milestone67.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1530506 - Add a generic writable property bag wrapper for Rust code. r=nika This commit adds a `storage_variant::HashPropertyBag` type that exposes an idiomatic Rust interface for `nsIWritablePropertyBag`. Differential Revision: https://phabricator.services.mozilla.com/D21062
storage/variant/src/bag.rs
storage/variant/src/lib.rs
xpcom/ds/nsHashPropertyBag.cpp
new file mode 100644
--- /dev/null
+++ b/storage/variant/src/bag.rs
@@ -0,0 +1,94 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use nserror::{nsresult, NS_OK};
+use nsstring::nsString;
+use xpcom::{getter_addrefs, interfaces::nsIWritablePropertyBag, RefPtr};
+
+use crate::VariantType;
+
+extern "C" {
+    fn NS_NewHashPropertyBag(bag: *mut *const nsIWritablePropertyBag) -> libc::c_void;
+}
+
+/// A hash property bag backed by storage variant values.
+pub struct HashPropertyBag(RefPtr<nsIWritablePropertyBag>);
+
+impl Default for HashPropertyBag {
+    fn default() -> HashPropertyBag {
+        // This is safe to unwrap because `NS_NewHashPropertyBag` is infallible.
+        let bag = getter_addrefs(|p| {
+            unsafe { NS_NewHashPropertyBag(p) };
+            NS_OK
+        }).unwrap();
+        HashPropertyBag(bag)
+    }
+}
+
+impl HashPropertyBag {
+    /// Creates an empty property bag.
+    #[inline]
+    pub fn new() -> HashPropertyBag {
+        HashPropertyBag::default()
+    }
+
+    /// Returns the value for a property name. Fails with `NS_ERROR_FAILURE`
+    /// if the property doesn't exist, or `NS_ERROR_CANNOT_CONVERT_DATA` if the
+    /// property exists, but is not of the value type `V`.
+    pub fn get<K, V>(&self, name: K) -> Result<V, nsresult>
+    where
+        K: AsRef<str>,
+        V: VariantType,
+    {
+        getter_addrefs(|p| unsafe { self.0.GetProperty(&*nsString::from(name.as_ref()), p) })
+            .and_then(|v| V::from_variant(v.coerce()))
+    }
+
+    /// Returns the value for a property name, or the default if not set or
+    /// not of the value type `V`.
+    #[inline]
+    pub fn get_or_default<K, V>(&self, name: K) -> V
+    where
+        K: AsRef<str>,
+        V: VariantType + Default,
+    {
+        self.get(name).unwrap_or_default()
+    }
+
+    /// Sets a property with the name to the value, overwriting any previous
+    /// value.
+    pub fn set<K, V>(&mut self, name: K, value: V)
+    where
+        K: AsRef<str>,
+        V: VariantType,
+    {
+        let v = value.into_variant();
+        unsafe {
+            // This is safe to unwrap because
+            // `nsHashPropertyBagBase::SetProperty` only returns an error if `v`
+            // is a null pointer.
+            self.0
+                .SetProperty(&*nsString::from(name.as_ref()), v.coerce())
+                .to_result()
+                .unwrap()
+        }
+    }
+
+    /// Deletes a property with the name. Returns `true` if the property
+    /// was previously in the bag, `false` if not.
+    pub fn delete(&mut self, name: impl AsRef<str>) -> bool {
+        unsafe {
+            self.0
+                .DeleteProperty(&*nsString::from(name.as_ref()))
+                .to_result()
+                .is_ok()
+        }
+    }
+
+    /// Returns a reference to the backing `nsIWritablePropertyBag`.
+    #[inline]
+    pub fn bag(&self) -> &nsIWritablePropertyBag {
+        &self.0
+    }
+}
--- a/storage/variant/src/lib.rs
+++ b/storage/variant/src/lib.rs
@@ -2,21 +2,25 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 extern crate libc;
 extern crate nserror;
 extern crate nsstring;
 extern crate xpcom;
 
+mod bag;
+
 use libc::{c_double, int64_t, uint16_t};
 use nserror::{nsresult, NS_OK};
 use nsstring::{nsACString, nsAString, nsCString, nsString};
 use xpcom::{getter_addrefs, interfaces::nsIVariant, RefPtr};
 
+pub use crate::bag::HashPropertyBag;
+
 extern "C" {
     fn NS_GetDataType(variant: *const nsIVariant) -> uint16_t;
     fn NS_NewStorageNullVariant(result: *mut *const nsIVariant);
     fn NS_NewStorageBooleanVariant(value: bool, result: *mut *const nsIVariant);
     fn NS_NewStorageIntegerVariant(value: int64_t, result: *mut *const nsIVariant);
     fn NS_NewStorageFloatVariant(value: c_double, result: *mut *const nsIVariant);
     fn NS_NewStorageTextVariant(value: *const nsAString, result: *mut *const nsIVariant);
     fn NS_NewStorageUTF8TextVariant(value: *const nsACString, result: *mut *const nsIVariant);
--- a/xpcom/ds/nsHashPropertyBag.cpp
+++ b/xpcom/ds/nsHashPropertyBag.cpp
@@ -9,16 +9,26 @@
 #include "nsArrayEnumerator.h"
 #include "nsIVariant.h"
 #include "nsIProperty.h"
 #include "nsThreadUtils.h"
 #include "nsVariant.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/Move.h"
 
+extern "C" {
+
+// This function uses C linkage because it's exposed to Rust to support the
+// `HashPropertyBag` wrapper in the `storage_variant` crate.
+void NS_NewHashPropertyBag(nsIWritablePropertyBag** aBag) {
+  MakeRefPtr<nsHashPropertyBag>().forget(aBag);
+}
+
+}  // extern "C"
+
 /*
  * nsHashPropertyBagBase implementation.
  */
 
 NS_IMETHODIMP
 nsHashPropertyBagBase::HasKey(const nsAString& aName, bool* aResult) {
   *aResult = mPropertyHash.Get(aName, nullptr);
   return NS_OK;