Bug 1210099 - Fix structured clone of expanded principal. r=bholley, a=lizzard, a=sylvestre
authorBill McCloskey <billm@mozilla.com>
Thu, 04 Feb 2016 15:12:52 -0800
changeset 311188 e70d3cebecb26412c230b90982dcfad0d3bc24c0
parent 311187 1ffddd8379168592b87448c82a4c1fed1b7ff764
child 311189 dfdbdf2f2a8c8d91588ce45a0b664beee929a942
push id5598
push usercbook@mozilla.com
push dateThu, 11 Feb 2016 14:52:18 +0000
treeherdermozilla-beta@46bf92e380db [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbholley, lizzard, sylvestre
bugs1210099
milestone45.0
Bug 1210099 - Fix structured clone of expanded principal. r=bholley, a=lizzard, a=sylvestre
caps/nsJSPrincipals.cpp
dom/base/StructuredCloneHolder.cpp
dom/base/StructuredCloneTags.h
dom/base/test/test_messagemanager_send_principal.html
--- a/caps/nsJSPrincipals.cpp
+++ b/caps/nsJSPrincipals.cpp
@@ -109,45 +109,57 @@ nsJSPrincipals::ReadPrincipals(JSContext
     uint32_t tag;
     uint32_t unused;
     if (!JS_ReadUint32Pair(aReader, &tag, &unused)) {
         return false;
     }
 
     if (!(tag == SCTAG_DOM_NULL_PRINCIPAL ||
           tag == SCTAG_DOM_SYSTEM_PRINCIPAL ||
-          tag == SCTAG_DOM_CONTENT_PRINCIPAL)) {
+          tag == SCTAG_DOM_CONTENT_PRINCIPAL ||
+          tag == SCTAG_DOM_EXPANDED_PRINCIPAL)) {
         xpc::Throw(aCx, NS_ERROR_DOM_DATA_CLONE_ERR);
         return false;
     }
 
     return ReadKnownPrincipalType(aCx, aReader, tag, aOutPrincipals);
 }
 
-/* static */ bool
-nsJSPrincipals::ReadKnownPrincipalType(JSContext* aCx,
-                                       JSStructuredCloneReader* aReader,
-                                       uint32_t aTag,
-                                       JSPrincipals** aOutPrincipals)
+static bool
+ReadPrincipalInfo(JSStructuredCloneReader* aReader,
+                  uint32_t aTag,
+                  PrincipalInfo& aInfo)
 {
-    MOZ_ASSERT(aTag == SCTAG_DOM_NULL_PRINCIPAL ||
-               aTag == SCTAG_DOM_SYSTEM_PRINCIPAL ||
-               aTag == SCTAG_DOM_CONTENT_PRINCIPAL);
+    if (aTag == SCTAG_DOM_SYSTEM_PRINCIPAL) {
+        aInfo = SystemPrincipalInfo();
+    } else if (aTag == SCTAG_DOM_NULL_PRINCIPAL) {
+        aInfo = NullPrincipalInfo();
+    } else if (aTag == SCTAG_DOM_EXPANDED_PRINCIPAL) {
+        uint32_t length, unused;
+        if (!JS_ReadUint32Pair(aReader, &length, &unused)) {
+            return false;
+        }
 
-    if (NS_WARN_IF(!NS_IsMainThread())) {
-        xpc::Throw(aCx, NS_ERROR_UNCATCHABLE_EXCEPTION);
-        return false;
-    }
+        ExpandedPrincipalInfo expanded;
+
+        for (uint32_t i = 0; i < length; i++) {
+            uint32_t tag;
+            if (!JS_ReadUint32Pair(aReader, &tag, &unused)) {
+                return false;
+            }
 
-    PrincipalInfo info;
-    if (aTag == SCTAG_DOM_SYSTEM_PRINCIPAL) {
-        info = SystemPrincipalInfo();
-    } else if (aTag == SCTAG_DOM_NULL_PRINCIPAL) {
-        info = NullPrincipalInfo();
-    } else {
+            PrincipalInfo sub;
+            if (!ReadPrincipalInfo(aReader, tag, sub)) {
+                return false;
+            }
+            expanded.whitelist().AppendElement(sub);
+        }
+
+        aInfo = expanded;
+    } else if (aTag == SCTAG_DOM_CONTENT_PRINCIPAL) {
         uint32_t suffixLength, specLength;
         if (!JS_ReadUint32Pair(aReader, &suffixLength, &specLength)) {
             return false;
         }
 
         nsAutoCString suffix;
         suffix.SetLength(suffixLength);
         if (!JS_ReadBytes(aReader, suffix.BeginWriting(), suffixLength)) {
@@ -157,47 +169,93 @@ nsJSPrincipals::ReadKnownPrincipalType(J
         nsAutoCString spec;
         spec.SetLength(specLength);
         if (!JS_ReadBytes(aReader, spec.BeginWriting(), specLength)) {
             return false;
         }
 
         PrincipalOriginAttributes attrs;
         attrs.PopulateFromSuffix(suffix);
-        info = ContentPrincipalInfo(attrs, spec);
+        aInfo = ContentPrincipalInfo(attrs, spec);
+    } else {
+        MOZ_CRASH("unexpected principal structured clone tag");
+    }
+
+    return true;
+}
+
+/* static */ bool
+nsJSPrincipals::ReadKnownPrincipalType(JSContext* aCx,
+                                       JSStructuredCloneReader* aReader,
+                                       uint32_t aTag,
+                                       JSPrincipals** aOutPrincipals)
+{
+    MOZ_ASSERT(aTag == SCTAG_DOM_NULL_PRINCIPAL ||
+               aTag == SCTAG_DOM_SYSTEM_PRINCIPAL ||
+               aTag == SCTAG_DOM_CONTENT_PRINCIPAL ||
+               aTag == SCTAG_DOM_EXPANDED_PRINCIPAL);
+
+    if (NS_WARN_IF(!NS_IsMainThread())) {
+        xpc::Throw(aCx, NS_ERROR_UNCATCHABLE_EXCEPTION);
+        return false;
+    }
+
+    PrincipalInfo info;
+    if (!ReadPrincipalInfo(aReader, aTag, info)) {
+        return false;
     }
 
     nsresult rv;
     nsCOMPtr<nsIPrincipal> prin = PrincipalInfoToPrincipal(info, &rv);
     if (NS_WARN_IF(NS_FAILED(rv))) {
         xpc::Throw(aCx, NS_ERROR_DOM_DATA_CLONE_ERR);
         return false;
     }
 
     *aOutPrincipals = get(prin.forget().take());
     return true;
 }
 
+static bool
+WritePrincipalInfo(JSStructuredCloneWriter* aWriter, const PrincipalInfo& aInfo)
+{
+    if (aInfo.type() == PrincipalInfo::TNullPrincipalInfo) {
+        return JS_WriteUint32Pair(aWriter, SCTAG_DOM_NULL_PRINCIPAL, 0);
+    }
+    if (aInfo.type() == PrincipalInfo::TSystemPrincipalInfo) {
+        return JS_WriteUint32Pair(aWriter, SCTAG_DOM_SYSTEM_PRINCIPAL, 0);
+    }
+    if (aInfo.type() == PrincipalInfo::TExpandedPrincipalInfo) {
+        const ExpandedPrincipalInfo& expanded = aInfo;
+        if (!JS_WriteUint32Pair(aWriter, SCTAG_DOM_EXPANDED_PRINCIPAL, 0) ||
+            !JS_WriteUint32Pair(aWriter, expanded.whitelist().Length(), 0)) {
+            return false;
+        }
+
+        for (uint32_t i = 0; i < expanded.whitelist().Length(); i++) {
+            if (!WritePrincipalInfo(aWriter, expanded.whitelist()[i])) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    MOZ_ASSERT(aInfo.type() == PrincipalInfo::TContentPrincipalInfo);
+    const ContentPrincipalInfo& cInfo = aInfo;
+    nsAutoCString suffix;
+    cInfo.attrs().CreateSuffix(suffix);
+    return JS_WriteUint32Pair(aWriter, SCTAG_DOM_CONTENT_PRINCIPAL, 0) &&
+           JS_WriteUint32Pair(aWriter, suffix.Length(), cInfo.spec().Length()) &&
+           JS_WriteBytes(aWriter, suffix.get(), suffix.Length()) &&
+           JS_WriteBytes(aWriter, cInfo.spec().get(), cInfo.spec().Length());
+}
+
 bool
 nsJSPrincipals::write(JSContext* aCx, JSStructuredCloneWriter* aWriter)
 {
     PrincipalInfo info;
     if (NS_WARN_IF(NS_FAILED(PrincipalToPrincipalInfo(this, &info)))) {
         xpc::Throw(aCx, NS_ERROR_DOM_DATA_CLONE_ERR);
         return false;
     }
 
-    if (info.type() == PrincipalInfo::TNullPrincipalInfo) {
-        return JS_WriteUint32Pair(aWriter, SCTAG_DOM_NULL_PRINCIPAL, 0);
-    }
-    if (info.type() == PrincipalInfo::TSystemPrincipalInfo) {
-        return JS_WriteUint32Pair(aWriter, SCTAG_DOM_SYSTEM_PRINCIPAL, 0);
-    }
-
-    MOZ_ASSERT(info.type() == PrincipalInfo::TContentPrincipalInfo);
-    const ContentPrincipalInfo& cInfo = info;
-    nsAutoCString suffix;
-    cInfo.attrs().CreateSuffix(suffix);
-    return JS_WriteUint32Pair(aWriter, SCTAG_DOM_CONTENT_PRINCIPAL, 0) &&
-           JS_WriteUint32Pair(aWriter, suffix.Length(), cInfo.spec().Length()) &&
-           JS_WriteBytes(aWriter, suffix.get(), suffix.Length()) &&
-           JS_WriteBytes(aWriter, cInfo.spec().get(), cInfo.spec().Length());
+    return WritePrincipalInfo(aWriter, info);
 }
--- a/dom/base/StructuredCloneHolder.cpp
+++ b/dom/base/StructuredCloneHolder.cpp
@@ -411,17 +411,18 @@ StructuredCloneHolder::ReadFullySerializ
         result = key->WrapObject(aCx, nullptr);
       }
     }
     return result;
   }
 
   if (aTag == SCTAG_DOM_NULL_PRINCIPAL ||
       aTag == SCTAG_DOM_SYSTEM_PRINCIPAL ||
-      aTag == SCTAG_DOM_CONTENT_PRINCIPAL) {
+      aTag == SCTAG_DOM_CONTENT_PRINCIPAL ||
+      aTag == SCTAG_DOM_EXPANDED_PRINCIPAL) {
     JSPrincipals* prin;
     if (!nsJSPrincipals::ReadKnownPrincipalType(aCx, aReader, aTag, &prin)) {
       return nullptr;
     }
     // nsJSPrincipals::ReadKnownPrincipalType addrefs for us, but because of the
     // casting between JSPrincipals* and nsIPrincipal* we can't use
     // getter_AddRefs above and have to already_AddRefed here.
     nsCOMPtr<nsIPrincipal> principal = already_AddRefed<nsIPrincipal>(nsJSPrincipals::get(prin));
--- a/dom/base/StructuredCloneTags.h
+++ b/dom/base/StructuredCloneTags.h
@@ -46,15 +46,17 @@ enum StructuredCloneTags {
 
   SCTAG_DOM_RTC_CERTIFICATE,
 
   SCTAG_DOM_FORMDATA,
 
   // This tag is for OffscreenCanvas.
   SCTAG_DOM_CANVAS,
 
+  SCTAG_DOM_EXPANDED_PRINCIPAL,
+
   SCTAG_DOM_MAX
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // StructuredCloneTags_h__
--- a/dom/base/test/test_messagemanager_send_principal.html
+++ b/dom/base/test/test_messagemanager_send_principal.html
@@ -42,16 +42,23 @@
                          ("isInBrowserElement" in message.data ? "OK" : "KO"));
       });
 
       addMessageListener("test:system", function(message) {
         sendAsyncMessage("test:result", "isSystemPrincipal: " +
                          (secMan.isSystemPrincipal(message.data) ? "OK" : "KO"));
       });
 
+      addMessageListener("test:ep", function(message) {
+        sendAsyncMessage("test:result", "expanded principal: " +
+                         (message.data.isExpandedPrincipal ? "OK" : "KO"));
+        sendAsyncMessage("test:result", "correct origin: " +
+                         (message.data.origin == "[Expanded Principal [http://bar.example.com, http://foo.example.com]]" ? "OK" : "KO"));
+      });
+
       addMessageListener("test:null", function(message) {
         sendAsyncMessage("test:result", "is nsIPrincipal: " +
                          (message.data instanceof Ci.nsIPrincipal ? "OK" : "KO"));
 
         sendAsyncMessage("test:result", "isNullPrincipal: " +
                          (message.data.isNullPrincipal ? "OK" : "KO"));
         sendAsyncMessage("test:result", "DONE");
       });
@@ -60,16 +67,19 @@
     function runTests() {
       ok("Browser prefs set.");
 
       let iframe = document.createElement("iframe");
       SpecialPowers.wrap(iframe).mozbrowser = true;
       iframe.id = "iframe";
       iframe.src = childFrameURL;
 
+      let sb = new Cu.Sandbox(['http://foo.example.com', 'http://bar.example.com']);
+      let ep = Components.utils.getObjectPrincipal(sb);
+
       iframe.addEventListener("mozbrowserloadend", function() {
         ok(true, "Got iframe load event.");
 
         let mm = SpecialPowers.getBrowserFrameMessageManager(iframe);
         mm.addMessageListener("test:result", function(message) {
           // We need to wrap to access message.json, and unwrap to do the
           // identity check.
           var msg = SpecialPowers.unwrap(SpecialPowers.wrap(message).data);
@@ -87,16 +97,18 @@
                            false);
 
         mm.sendAsyncMessage("test:content", window.document.nodePrincipal);
 
         let system = Cc["@mozilla.org/systemprincipal;1"].
           createInstance(Ci.nsIPrincipal);
         mm.sendAsyncMessage("test:system", system);
 
+        mm.sendAsyncMessage("test:ep", ep);
+
         let nullP = Cc["@mozilla.org/nullprincipal;1"].
           createInstance(Ci.nsIPrincipal);
         mm.sendAsyncMessage("test:null", nullP);
       });
 
       document.body.appendChild(iframe);
     }