Bug 1038399 - Add a ToJSON method to Web IDL dictionaries. r=bholley, a=lmandel
authorBoris Zbarsky <bzbarsky@mit.edu>
Thu, 31 Jul 2014 23:50:19 -0400
changeset 217605 2479de9ab541f7b13c9a3f86136e48680423481a
parent 217604 ea25dee8ad3fc5eec2349390779cc365197d1157
child 217606 49d46c29fc9da3d2282e7dcba7ad5239a6398a08
push id515
push userraliiev@mozilla.com
push dateMon, 06 Oct 2014 12:51:51 +0000
treeherdermozilla-release@267c7a481bef [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbholley, lmandel
bugs1038399
milestone33.0a2
Bug 1038399 - Add a ToJSON method to Web IDL dictionaries. r=bholley, a=lmandel
dom/bindings/BindingDeclarations.h
dom/bindings/BindingUtils.cpp
dom/bindings/Codegen.py
dom/crypto/WebCryptoTask.cpp
--- a/dom/bindings/BindingDeclarations.h
+++ b/dom/bindings/BindingDeclarations.h
@@ -30,16 +30,25 @@ namespace dom {
 
 // Struct that serves as a base class for all dictionaries.  Particularly useful
 // so we can use IsBaseOf to detect dictionary template arguments.
 struct DictionaryBase
 {
 protected:
   bool ParseJSON(JSContext* aCx, const nsAString& aJSON,
                  JS::MutableHandle<JS::Value> aVal);
+
+  bool StringifyToJSON(JSContext* aCx,
+                       JS::MutableHandle<JS::Value> aValue,
+                       nsAString& aJSON);
+private:
+  // aString is expected to actually be an nsAString*.  Should only be
+  // called from StringifyToJSON.
+  static bool AppendJSONToString(const jschar* aJSONData, uint32_t aDataLength,
+                                 void* aString);
 };
 
 // Struct that serves as a base class for all typed arrays and array buffers and
 // array buffer views.  Particularly useful so we can use IsBaseOf to detect
 // typed array/buffer/view template arguments.
 struct AllTypedArraysBase {
 };
 
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -1603,16 +1603,39 @@ DictionaryBase::ParseJSON(JSContext* aCx
   if (aJSON.IsEmpty()) {
     return true;
   }
   return JS_ParseJSON(aCx,
                       static_cast<const jschar*>(PromiseFlatString(aJSON).get()),
                       aJSON.Length(), aVal);
 }
 
+bool
+DictionaryBase::StringifyToJSON(JSContext* aCx,
+                                JS::MutableHandle<JS::Value> aValue,
+                                nsAString& aJSON)
+{
+  return JS_Stringify(aCx, aValue, JS::NullPtr(), JS::NullHandleValue,
+                      AppendJSONToString, &aJSON);
+}
+
+/* static */
+bool
+DictionaryBase::AppendJSONToString(const jschar* aJSONData,
+                                   uint32_t aDataLength,
+                                   void* aString)
+{
+  nsAString* string = static_cast<nsAString*>(aString);
+  string->Append(static_cast<const char16_t*>(aJSONData),
+                 aDataLength);
+  return true;
+}
+
+
+
 static JSString*
 ConcatJSString(JSContext* cx, const char* pre, JS::Handle<JSString*> str, const char* post)
 {
   if (!str) {
     return nullptr;
   }
 
   JS::Rooted<JSString*> preString(cx, JS_NewStringCopyN(cx, pre, strlen(pre)));
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -10853,16 +10853,30 @@ class CGDictionary(CGThing):
                 MOZ_ASSERT(NS_IsMainThread());
                 AutoSafeJSContext cx;
                 JS::Rooted<JS::Value> json(cx);
                 bool ok = ParseJSON(cx, aJSON, &json);
                 NS_ENSURE_TRUE(ok, false);
                 return Init(cx, json);
                 """))
 
+    def toJSONMethod(self):
+        return ClassMethod(
+            "ToJSON", "bool",
+            [Argument('nsAString&', 'aJSON')],
+            body=dedent("""
+                MOZ_ASSERT(NS_IsMainThread());
+                AutoJSAPI jsapi;
+                jsapi.Init();
+                JSContext *cx = jsapi.cx();
+                JSAutoCompartment ac(cx, xpc::GetSafeJSContextGlobal()); // Usage approved by bholley
+                JS::Rooted<JS::Value> obj(cx);
+                return ToObjectInternal(cx, &obj) && StringifyToJSON(cx, &obj, aJSON);
+            """))
+
     def toObjectInternalMethod(self):
         body = ""
         if self.needToInitIds:
             body += fill(
                 """
                 ${dictName}Atoms* atomsCache = GetAtomCache<${dictName}Atoms>(cx);
                 if (!*reinterpret_cast<jsid**>(atomsCache) && !InitIds(cx, atomsCache)) {
                   return false;
@@ -10980,20 +10994,22 @@ class CGDictionary(CGThing):
 
         if self.needToInitIds:
             methods.append(self.initIdsMethod())
 
         methods.append(self.initMethod())
         methods.append(self.initFromJSONMethod())
         try:
             methods.append(self.toObjectInternalMethod())
+            methods.append(self.toJSONMethod())
         except MethodNotNewObjectError:
             # If we can't have a ToObjectInternal() because one of our members
             # can only be returned from [NewObject] methods, then just skip
-            # generating ToObjectInternal().
+            # generating ToObjectInternal() and ToJSON (since the latter depens
+            # on the former).
             pass
         methods.append(self.traceDictionaryMethod())
 
         if CGDictionary.isDictionaryCopyConstructible(d):
             disallowCopyConstruction = False
             # Note: no base constructors because our operator= will
             # deal with that.
             ctors.append(ClassConstructor([Argument("const %s&" % selfName,
@@ -11577,16 +11593,18 @@ class CGBindingRoot(CGThing):
                    mainCallbacks or workerCallbacks)
         bindingHeaders["mozilla/dom/BindingUtils.h"] = hasCode
         bindingHeaders["mozilla/dom/OwningNonNull.h"] = hasCode
         bindingHeaders["mozilla/dom/BindingDeclarations.h"] = (
             not hasCode and enums)
 
         bindingHeaders["WrapperFactory.h"] = descriptors
         bindingHeaders["mozilla/dom/DOMJSClass.h"] = descriptors
+        bindingHeaders["mozilla/dom/ScriptSettings.h"] = dictionaries # AutoJSAPI
+        bindingHeaders["xpcpublic.h"] = dictionaries ## xpc::GetSafeJSContextGlobal
 
         # Do codegen for all the dictionaries.  We have to be a bit careful
         # here, because we have to generate these in order from least derived
         # to most derived so that class inheritance works out.  We also have to
         # generate members before the dictionary that contains them.
         cgthings.extend([CGDictionary(d, config.getDescriptorProvider(False))
                          for d in
                          dependencySortObjects(dictionaries,
--- a/dom/crypto/WebCryptoTask.cpp
+++ b/dom/crypto/WebCryptoTask.cpp
@@ -2242,25 +2242,16 @@ private:
   {
     if (mTask && !mResolved) {
       mTask->Skip();
     }
     mTask = nullptr;
   }
 };
 
-static bool
-JSONCreator(const jschar* aBuf, uint32_t aLen, void* aData)
-{
-  nsAString* result = static_cast<nsAString*>(aData);
-  result->Append(static_cast<const char16_t*>(aBuf),
-                 static_cast<uint32_t>(aLen));
-  return true;
-}
-
 template<class KeyEncryptTask>
 class WrapKeyTask : public ExportKeyTask
 {
 public:
   WrapKeyTask(JSContext* aCx,
               const nsAString& aFormat,
               CryptoKey& aKey,
               CryptoKey& aWrappingKey,
@@ -2274,40 +2265,21 @@ public:
 
     mTask = new KeyEncryptTask(aCx, aWrapAlgorithm, aWrappingKey, true);
   }
 
 private:
   nsRefPtr<KeyEncryptTask> mTask;
   bool mResolved;
 
-  static bool StringifyJWK(const JsonWebKey& aJwk, nsAString& aRetVal)
-  {
-    // XXX: This should move into DictionaryBase and Codegen.py,
-    //      in the same way as ParseJSON is split out. (Bug 1038399)
-    // We use AutoSafeJSContext even though the exact compartment
-    // doesn't matter (since we're making an XPCOM string)
-    MOZ_ASSERT(NS_IsMainThread());
-    AutoSafeJSContext cx;
-    JS::Rooted<JS::Value> obj(cx);
-    bool ok = ToJSValue(cx, aJwk, &obj);
-    if (!ok) {
-      JS_ClearPendingException(cx);
-      return false;
-    }
-
-    return JS_Stringify(cx, &obj, JS::NullPtr(), JS::NullHandleValue,
-                        JSONCreator, &aRetVal);
-  }
-
   virtual nsresult AfterCrypto() MOZ_OVERRIDE {
     // If wrapping JWK, stringify the JSON
     if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
       nsAutoString json;
-      if (!StringifyJWK(mJwk, json)) {
+      if (!mJwk.ToJSON(json)) {
         return NS_ERROR_DOM_OPERATION_ERR;
       }
 
       NS_ConvertUTF16toUTF8 utf8(json);
       mResult.Assign((const uint8_t*) utf8.BeginReading(), utf8.Length());
     }
 
     return NS_OK;