Backed out changesets ea25dee8ad3f and 57cdea2c224d (bug 1036214) for Android debug failures.
authorRyan VanderMeulen <ryanvm@gmail.com>
Thu, 21 Aug 2014 14:55:56 -0400
changeset 217612 947dd9a0f12be223626e373bcb3f20ac0b743f56
parent 217611 f830a26234295a680901f5caec348bbee82553bb
child 217613 912f3087ec0c9bf1abb58fe8930fdae3fe5b0128
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)
bugs1036214
milestone33.0a2
backs outea25dee8ad3fc5eec2349390779cc365197d1157
57cdea2c224d33d72cd3e2c5501f9cbf4db8dcb3
Backed out changesets ea25dee8ad3f and 57cdea2c224d (bug 1036214) for Android debug failures.
dom/bindings/BindingUtils.cpp
dom/bindings/BindingUtils.h
dom/bindings/Codegen.py
dom/bindings/Errors.msg
dom/bindings/test/TestInterfaceJS.js
dom/bindings/test/mochitest.ini
dom/bindings/test/test_bug1036214.html
dom/bindings/test/test_bug923904.html
dom/webidl/TestInterfaceJS.webidl
dom/webidl/TestInterfaceJSDictionaries.webidl
dom/webidl/moz.build
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -2610,17 +2610,10 @@ AssertReturnTypeMatchesJitinfo(const JSJ
   default:
     // Someone messed up their jitinfo type.
     MOZ_ASSERT(false, "Unexpected JSValueType stored in jitinfo");
     break;
   }
 }
 #endif
 
-bool
-CallerSubsumes(JSObject *aObject)
-{
-  nsIPrincipal* objPrin = nsContentUtils::ObjectPrincipal(js::UncheckedUnwrap(aObject));
-  return nsContentUtils::SubjectPrincipal()->Subsumes(objPrin);
-}
-
 } // namespace dom
 } // namespace mozilla
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -2905,24 +2905,12 @@ AssertReturnTypeMatchesJitinfo(const JSJ
                                JS::Handle<JS::Value> aValue);
 #endif
 
 // Returns true if aObj's global has any of the permissions named in aPermissions
 // set to nsIPermissionManager::ALLOW_ACTION. aPermissions must be null-terminated.
 bool
 CheckPermissions(JSContext* aCx, JSObject* aObj, const char* const aPermissions[]);
 
-bool
-CallerSubsumes(JSObject* aObject);
-
-MOZ_ALWAYS_INLINE bool
-CallerSubsumes(JS::Handle<JS::Value> aValue)
-{
-  if (!aValue.isObject()) {
-    return true;
-  }
-  return CallerSubsumes(&aValue.toObject());
-}
-
 } // namespace dom
 } // namespace mozilla
 
 #endif /* mozilla_dom_BindingUtils_h__ */
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -3450,19 +3450,16 @@ class JSToNativeConversionInfo():
 
           ${val} is a handle to the JS::Value in question
           ${holderName} replaced by the holder's name, if any
           ${declName} replaced by the declaration's name
           ${haveValue} replaced by an expression that evaluates to a boolean
                        for whether we have a JS::Value.  Only used when
                        defaultValue is not None or when True is passed for
                        checkForValue to instantiateJSToNativeConversion.
-          ${passedToJSImpl} replaced by an expression that evaluates to a boolean
-                            for whether this value is being passed to a JS-
-                            implemented interface.
 
         declType: A CGThing representing the native C++ type we're converting
                   to.  This is allowed to be None if the conversion code is
                   supposed to be used as-is.
 
         holderType: A CGThing representing the type of a "holder" which will
                     hold a possible reference to the C++ thing whose type we
                     returned in declType, or  None if no such holder is needed.
@@ -3708,45 +3705,32 @@ def getJSToNativeConversionInfo(type, de
                 # Our caller will handle it
                 pass
             else:
                 assert defaultValue is None
 
         return templateBody
 
     # A helper function for converting things that look like a JSObject*.
-    def handleJSObjectType(type, isMember, failureCode, exceptionCode, sourceDescription):
+    def handleJSObjectType(type, isMember, failureCode):
         if not isMember:
             if isOptional:
                 # We have a specialization of Optional that will use a
                 # Rooted for the storage here.
                 declType = CGGeneric("JS::Handle<JSObject*>")
             else:
                 declType = CGGeneric("JS::Rooted<JSObject*>")
             declArgs = "cx"
         else:
             assert (isMember in
                     ("Sequence", "Variadic", "Dictionary", "OwningUnion", "MozMap"))
             # We'll get traced by the sequence or dictionary or union tracer
             declType = CGGeneric("JSObject*")
             declArgs = None
         templateBody = "${declName} = &${val}.toObject();\n"
-
-        # For JS-implemented APIs, we refuse to allow passing objects that the
-        # API consumer does not subsume.
-        if not isinstance(descriptorProvider, Descriptor) or descriptorProvider.interface.isJSImplemented():
-            templateBody = fill("""
-                            if ($${passedToJSImpl} && !CallerSubsumes($${val})) {
-                              ThrowErrorMessage(cx, MSG_PERMISSION_DENIED_TO_PASS_ARG, "${sourceDescription}");
-                              $*{exceptionCode}
-                            }
-                            """,
-                            sourceDescription=sourceDescription,
-                            exceptionCode=exceptionCode) + templateBody
-
         setToNullCode = "${declName} = nullptr;\n"
         template = wrapObjectTemplate(templateBody, type, setToNullCode,
                                       failureCode)
         return JSToNativeConversionInfo(template, declType=declType,
                                         dealWithOptional=isOptional,
                                         declArgs=declArgs)
 
     assert not (isEnforceRange and isClamp)  # These are mutually exclusive
@@ -3811,18 +3795,17 @@ def getJSToNativeConversionInfo(type, de
             arrayRef = "${declName}"
 
         elementConversion = string.Template(elementInfo.template).substitute({
                 "val": "temp",
                 "declName": "slot",
                 # We only need holderName here to handle isExternal()
                 # interfaces, which use an internal holder for the
                 # conversion even when forceOwningType ends up true.
-                "holderName": "tempHolder",
-                "passedToJSImpl": "${passedToJSImpl}"
+                "holderName": "tempHolder"
             })
 
         # NOTE: Keep this in sync with variadic conversions as needed
         templateBody = fill(
             """
             JS::ForOfIterator iter(cx);
             if (!iter.init($${val}, JS::ForOfIterator::AllowNonIterable)) {
               $*{exceptionCode}
@@ -3916,18 +3899,17 @@ def getJSToNativeConversionInfo(type, de
             mozMapRef = "${declName}"
 
         valueConversion = string.Template(valueInfo.template).substitute({
                 "val": "temp",
                 "declName": "slot",
                 # We only need holderName here to handle isExternal()
                 # interfaces, which use an internal holder for the
                 # conversion even when forceOwningType ends up true.
-                "holderName": "tempHolder",
-                "passedToJSImpl": "${passedToJSImpl}"
+                "holderName": "tempHolder"
             })
 
         templateBody = fill(
             """
             ${mozMapType} &mozMap = ${mozMapRef};
 
             JS::Rooted<JSObject*> mozMapObj(cx, &$${val}.toObject());
             JS::AutoIdArray ids(cx, JS_Enumerate(cx, mozMapObj));
@@ -4007,30 +3989,30 @@ def getJSToNativeConversionInfo(type, de
         names = []
 
         interfaceMemberTypes = filter(lambda t: t.isNonCallbackInterface(), memberTypes)
         if len(interfaceMemberTypes) > 0:
             interfaceObject = []
             for memberType in interfaceMemberTypes:
                 name = getUnionMemberName(memberType)
                 interfaceObject.append(
-                    CGGeneric("(failed = !%s.TrySetTo%s(cx, ${val}, tryNext, ${passedToJSImpl})) || !tryNext" %
+                    CGGeneric("(failed = !%s.TrySetTo%s(cx, ${val}, tryNext)) || !tryNext" %
                               (unionArgumentObj, name)))
                 names.append(name)
             interfaceObject = CGWrapper(CGList(interfaceObject, " ||\n"),
                                         pre="done = ", post=";\n\n", reindent=True)
         else:
             interfaceObject = None
 
         arrayObjectMemberTypes = filter(lambda t: t.isArray() or t.isSequence(), memberTypes)
         if len(arrayObjectMemberTypes) > 0:
             assert len(arrayObjectMemberTypes) == 1
             name = getUnionMemberName(arrayObjectMemberTypes[0])
             arrayObject = CGGeneric(
-                "done = (failed = !%s.TrySetTo%s(cx, ${val}, tryNext, ${passedToJSImpl})) || !tryNext;\n" %
+                "done = (failed = !%s.TrySetTo%s(cx, ${val}, tryNext)) || !tryNext;\n" %
                 (unionArgumentObj, name))
             names.append(name)
         else:
             arrayObject = None
 
         dateObjectMemberTypes = filter(lambda t: t.isDate(), memberTypes)
         if len(dateObjectMemberTypes) > 0:
             assert len(dateObjectMemberTypes) == 1
@@ -4044,54 +4026,52 @@ def getJSToNativeConversionInfo(type, de
             dateObject = None
 
         callbackMemberTypes = filter(lambda t: t.isCallback() or t.isCallbackInterface(), memberTypes)
         if len(callbackMemberTypes) > 0:
             assert len(callbackMemberTypes) == 1
             memberType = callbackMemberTypes[0]
             name = getUnionMemberName(memberType)
             callbackObject = CGGeneric(
-                "done = (failed = !%s.TrySetTo%s(cx, ${val}, tryNext, ${passedToJSImpl})) || !tryNext;\n" %
+                "done = (failed = !%s.TrySetTo%s(cx, ${val}, tryNext)) || !tryNext;\n" %
                 (unionArgumentObj, name))
             names.append(name)
         else:
             callbackObject = None
 
         dictionaryMemberTypes = filter(lambda t: t.isDictionary(), memberTypes)
         if len(dictionaryMemberTypes) > 0:
             assert len(dictionaryMemberTypes) == 1
             name = getUnionMemberName(dictionaryMemberTypes[0])
             setDictionary = CGGeneric(
-                "done = (failed = !%s.TrySetTo%s(cx, ${val}, tryNext, ${passedToJSImpl})) || !tryNext;\n" %
+                "done = (failed = !%s.TrySetTo%s(cx, ${val}, tryNext)) || !tryNext;\n" %
                 (unionArgumentObj, name))
             names.append(name)
         else:
             setDictionary = None
 
         mozMapMemberTypes = filter(lambda t: t.isMozMap(), memberTypes)
         if len(mozMapMemberTypes) > 0:
             assert len(mozMapMemberTypes) == 1
             name = getUnionMemberName(mozMapMemberTypes[0])
             mozMapObject = CGGeneric(
-                "done = (failed = !%s.TrySetTo%s(cx, ${val}, tryNext, ${passedToJSImpl})) || !tryNext;\n" %
+                "done = (failed = !%s.TrySetTo%s(cx, ${val}, tryNext)) || !tryNext;\n" %
                 (unionArgumentObj, name))
             names.append(name)
         else:
             mozMapObject = None
 
         objectMemberTypes = filter(lambda t: t.isObject(), memberTypes)
         if len(objectMemberTypes) > 0:
             assert len(objectMemberTypes) == 1
             # Very important to NOT construct a temporary Rooted here, since the
             # SetToObject call can call a Rooted constructor and we need to keep
             # stack discipline for Rooted.
-            object = CGGeneric("if (!%s.SetToObject(cx, &${val}.toObject(), ${passedToJSImpl})) {\n"
-                               "%s"
-                               "}\n"
-                               "done = true;\n" % (unionArgumentObj, indent(exceptionCode)))
+            object = CGGeneric("%s.SetToObject(cx, &${val}.toObject());\n"
+                               "done = true;\n" % unionArgumentObj)
             names.append(objectMemberTypes[0].name)
         else:
             object = None
 
         hasObjectTypes = interfaceObject or arrayObject or dateObject or callbackObject or object or mozMapObject
         if hasObjectTypes:
             # "object" is not distinguishable from other types
             assert not object or not (interfaceObject or arrayObject or dateObject or callbackObject or mozMapObject)
@@ -4313,17 +4293,17 @@ def getJSToNativeConversionInfo(type, de
         assert not isEnforceRange and not isClamp
 
         descriptor = descriptorProvider.getDescriptor(
             type.unroll().inner.identifier.name)
 
         if descriptor.nativeType == 'JSObject':
             # XXXbz Workers code does this sometimes
             assert descriptor.workers
-            return handleJSObjectType(type, isMember, failureCode, exceptionCode, sourceDescription)
+            return handleJSObjectType(type, isMember, failureCode)
 
         if descriptor.interface.isCallback():
             name = descriptor.interface.identifier.name
             if type.nullable() or isCallbackReturnValue:
                 declType = CGGeneric("nsRefPtr<%s>" % name)
             else:
                 declType = CGGeneric("OwningNonNull<%s>" % name)
             conversion = indent(CGCallbackTempRoot(name).define())
@@ -4395,17 +4375,17 @@ def getJSToNativeConversionInfo(type, de
                 templateBody += str(FailureFatalCastableObjectUnwrapper(
                     descriptor,
                     "&${val}.toObject()",
                     "${declName}",
                     exceptionCode,
                     isCallbackReturnValue,
                     firstCap(sourceDescription)))
         elif descriptor.workers:
-            return handleJSObjectType(type, isMember, failureCode, exceptionCode, sourceDescription)
+            return handleJSObjectType(type, isMember, failureCode)
         else:
             # Either external, or new-binding non-castable.  We always have a
             # holder for these, because we don't actually know whether we have
             # to addref when unwrapping or not.  So we just pass an
             # getter_AddRefs(nsRefPtr) to XPConnect and if we'll need a release
             # it'll put a non-null pointer in there.
             if forceOwningType:
                 # Don't return a holderType in this case; our declName
@@ -4708,45 +4688,32 @@ def getJSToNativeConversionInfo(type, de
             declType = "JS::Value"
         else:
             assert not isMember
             declType = "JS::Rooted<JS::Value>"
             declArgs = "cx"
 
         assert not isOptional
         templateBody = "${declName} = ${val};\n"
-
-        # For JS-implemented APIs, we refuse to allow passing objects that the
-        # API consumer does not subsume.
-        if not isinstance(descriptorProvider, Descriptor) or descriptorProvider.interface.isJSImplemented():
-            templateBody = fill("""
-                            if ($${passedToJSImpl} && !CallerSubsumes($${val})) {
-                              ThrowErrorMessage(cx, MSG_PERMISSION_DENIED_TO_PASS_ARG, "${sourceDescription}");
-                              $*{exceptionCode}
-                            }
-                            """,
-                            sourceDescription=sourceDescription,
-                            exceptionCode=exceptionCode) + templateBody
-
         # We may not have a default value if we're being converted for
         # a setter, say.
         if defaultValue:
             if isinstance(defaultValue, IDLNullValue):
                 defaultHandling = "${declName} = JS::NullValue();\n"
             else:
                 assert isinstance(defaultValue, IDLUndefinedValue)
                 defaultHandling = "${declName} = JS::UndefinedValue();\n"
             templateBody = handleDefault(templateBody, defaultHandling)
         return JSToNativeConversionInfo(templateBody,
                                         declType=CGGeneric(declType),
                                         declArgs=declArgs)
 
     if type.isObject():
         assert not isEnforceRange and not isClamp
-        return handleJSObjectType(type, isMember, failureCode, exceptionCode, sourceDescription)
+        return handleJSObjectType(type, isMember, failureCode)
 
     if type.isDictionary():
         # There are no nullable dictionaries
         assert not type.nullable() or isCallbackReturnValue
         # All optional dictionaries always have default values, so we
         # should be able to assume not isOptional here.
         assert not isOptional
         # In the callback return value case we never have to worry
@@ -4786,17 +4753,17 @@ def getJSToNativeConversionInfo(type, de
                 "!%s(cx, ${val})" % dictionaryTest).define() + "\n"
         else:
             template = ""
 
         dictLoc = "${declName}"
         if type.nullable():
             dictLoc += ".SetValue()"
 
-        template += ('if (!%s.Init(cx, %s, "%s", ${passedToJSImpl})) {\n'
+        template += ('if (!%s.Init(cx, %s, "%s")) {\n'
                      "%s"
                      "}\n" % (dictLoc, val, firstCap(sourceDescription),
                               exceptionCodeIndented.define()))
 
         if type.nullable():
             declType = CGTemplatedType("Nullable", declType)
             template = CGIfElseWrapper("${val}.isNullOrUndefined()",
                                        CGGeneric("${declName}.SetNull();\n"),
@@ -5062,18 +5029,17 @@ class CGArgumentConverter(CGThing):
 
         replacer = {
             "index": index,
             "argc": "args.length()"
         }
         self.replacementVariables = {
             "declName": "arg%d" % index,
             "holderName": ("arg%d" % index) + "_holder",
-            "obj": "obj",
-            "passedToJSImpl": toStringBool(isJSImplementedDescriptor(descriptorProvider))
+            "obj": "obj"
         }
         self.replacementVariables["val"] = string.Template(
             "args[${index}]").substitute(replacer)
         haveValueCheck = string.Template(
             "args.hasDefined(${index})").substitute(replacer)
         self.replacementVariables["haveValue"] = haveValueCheck
         self.descriptorProvider = descriptorProvider
         if self.argument.optional and not self.argument.defaultValue:
@@ -5141,18 +5107,17 @@ class CGArgumentConverter(CGThing):
             string.Template(typeConversion.template).substitute({
                 "val": val,
                 "declName": "slot",
                 # We only need holderName here to handle isExternal()
                 # interfaces, which use an internal holder for the
                 # conversion even when forceOwningType ends up true.
                 "holderName": "tempHolder",
                 # Use the same ${obj} as for the variadic arg itself
-                "obj": replacer["obj"],
-                "passedToJSImpl": toStringBool(isJSImplementedDescriptor(self.descriptorProvider))
+                "obj": replacer["obj"]
             }), 4)
 
         variadicConversion += ("  }\n"
                                "}\n")
         return variadicConversion
 
 
 def getMaybeWrapValueFuncForType(type):
@@ -6616,18 +6581,17 @@ class CGMethodCall(CGThing):
                                                 isNullOrUndefined=isNullOrUndefined,
                                                 isOptional=argIsOptional,
                                                 sourceDescription=(argDesc % (distinguishingIndex + 1))),
                     {
                         "declName": "arg%d" % distinguishingIndex,
                         "holderName": ("arg%d" % distinguishingIndex) + "_holder",
                         "val": distinguishingArg,
                         "obj": "obj",
-                        "haveValue": "args.hasDefined(%d)" % distinguishingIndex,
-                        "passedToJSImpl": toStringBool(isJSImplementedDescriptor(descriptor))
+                        "haveValue": "args.hasDefined(%d)" % distinguishingIndex
                     },
                     checkForValue=argIsOptional)
                 caseBody.append(CGIndenter(testCode, indent))
 
                 # If we got this far, we know we unwrapped to the right
                 # C++ type, so just do the call.  Start conversion with
                 # distinguishingIndex + 1, since we already converted
                 # distinguishingIndex.
@@ -8188,32 +8152,19 @@ def getUnionTypeTemplateVars(unionType, 
                 mType = eObject;
                 """)
         else:
             body = dedent("""
                 MOZ_ASSERT(mUnion.mType == mUnion.eUninitialized);
                 mUnion.mValue.mObject.SetValue(cx, obj);
                 mUnion.mType = mUnion.eObject;
                 """)
-
-        # It's a bit sketchy to do the security check after setting the value,
-        # but it keeps the code cleaner and lets us avoid rooting |obj| over the
-        # call to CallerSubsumes().
-        body = body + dedent("""
-            if (passedToJSImpl && !CallerSubsumes(obj)) {
-              ThrowErrorMessage(cx, MSG_PERMISSION_DENIED_TO_PASS_ARG, "%s");
-              return false;
-            }
-            return true;
-            """)
-
-        setter = ClassMethod("SetToObject", "bool",
+        setter = ClassMethod("SetToObject", "void",
                              [Argument("JSContext*", "cx"),
-                              Argument("JSObject*", "obj"),
-                              Argument("bool", "passedToJSImpl", default="false")],
+                              Argument("JSObject*", "obj")],
                              inline=True, bodyInHeader=True,
                              body=body)
 
     else:
         # Important: we need to not have our declName involve
         # maybe-GCing operations.
         if conversionInfo.holderType is not None:
             holderArgs = conversionInfo.holderArgs
@@ -8223,18 +8174,17 @@ def getUnionTypeTemplateVars(unionType, 
         else:
             initHolder = ""
 
         jsConversion = fill(
             initHolder + conversionInfo.template,
             val="value",
             declName="memberSlot",
             holderName=(holderName if ownsMembers else "%s.ref()" % holderName),
-            destroyHolder=destroyHolder,
-            passedToJSImpl="passedToJSImpl")
+            destroyHolder=destroyHolder)
 
         jsConversion = fill(
             """
             tryNext = false;
             { // scope for memberSlot
               ${structType}& memberSlot = RawSetAs${name}(${ctorArgs});
               $*{jsConversion}
             }
@@ -8243,18 +8193,17 @@ def getUnionTypeTemplateVars(unionType, 
             structType=structType,
             name=name,
             ctorArgs=ctorArgs,
             jsConversion=jsConversion)
 
         setter = ClassMethod("TrySetTo" + name, "bool",
                              [Argument("JSContext*", "cx"),
                               Argument("JS::Handle<JS::Value>", "value"),
-                              Argument("bool&", "tryNext"),
-                              Argument("bool", "passedToJSImpl", default="false")],
+                              Argument("bool&", "tryNext")],
                              inline=not ownsMembers,
                              bodyInHeader=not ownsMembers,
                              body=jsConversion)
 
     return {
         "name": name,
         "structType": structType,
         "externalType": externalType,
@@ -9336,18 +9285,17 @@ class CGProxySpecialOperation(CGPerSigna
                 sourceDescription=("value being assigned to %s setter" %
                                    descriptor.interface.identifier.name))
             if argumentMutableValue is None:
                 argumentMutableValue = "desc.value()"
             templateValues = {
                 "declName": argument.identifier.name,
                 "holderName": argument.identifier.name + "_holder",
                 "val": argumentMutableValue,
-                "obj": "obj",
-                "passedToJSImpl": "false"
+                "obj": "obj"
             }
             self.cgRoot.prepend(instantiateJSToNativeConversion(info, templateValues))
         elif operation.isGetter() or operation.isDeleter():
             self.cgRoot.prepend(CGGeneric("bool found;\n"))
 
     def getArguments(self):
         args = [(a, a.identifier.name) for a in self.arguments]
         if self.idlNode.isGetter() or self.idlNode.isDeleter():
@@ -10836,18 +10784,17 @@ class CGDictionary(CGThing):
                 """,
                 memberInits="\n".join(memberInits))
 
         body += "return true;\n"
 
         return ClassMethod("Init", "bool", [
             Argument('JSContext*', 'cx'),
             Argument('JS::Handle<JS::Value>', 'val'),
-            Argument('const char*', 'sourceDescription', default='"Value"'),
-            Argument('bool', 'passedToJSImpl', default='false')
+            Argument('const char*', 'sourceDescription', default='"Value"')
         ], body=body)
 
     def initFromJSONMethod(self):
         return ClassMethod(
             "Init", "bool",
             [Argument('const nsAString&', 'aJSON')],
             body=dedent("""
                 MOZ_ASSERT(NS_IsMainThread());
@@ -11072,18 +11019,17 @@ class CGDictionary(CGThing):
     def getMemberConversion(self, memberInfo):
         member, conversionInfo = memberInfo
         replacements = {
             "val": "temp.ref()",
             "declName": self.makeMemberName(member.identifier.name),
             # We need a holder name for external interfaces, but
             # it's scoped down to the conversion so we can just use
             # anything we want.
-            "holderName": "holder",
-            "passedToJSImpl": "passedToJSImpl"
+            "holderName": "holder"
         }
         # We can't handle having a holderType here
         assert conversionInfo.holderType is None
         if conversionInfo.dealWithOptional:
             replacements["declName"] = "(" + replacements["declName"] + ".Value())"
         if member.defaultValue:
             replacements["haveValue"] = "!isNull && !temp.ref().isUndefined()"
 
@@ -11209,21 +11155,16 @@ class CGDictionary(CGThing):
                 trace = CGGeneric('%s.TraceDictionary(trc);\n' % memberData)
             elif type.isUnion():
                 trace = CGGeneric('%s.TraceUnion(trc);\n' % memberData)
             else:
                 assert type.isSpiderMonkeyInterface()
                 trace = CGGeneric('%s.TraceSelf(trc);\n' % memberData)
             if type.nullable():
                 trace = CGIfWrapper(trace, "!%s.IsNull()" % memberNullable)
-        elif type.isMozMap():
-            # If you implement this, add a MozMap<object> to
-            # TestInterfaceJSDictionary and test it in test_bug1036214.html
-            # to make sure we end up with the correct security properties.
-            assert False
         else:
             assert False  # unknown type
 
         if not member.defaultValue:
             trace = CGIfWrapper(trace, "%s.WasPassed()" % memberLoc)
 
         return trace.define()
 
@@ -13089,18 +13030,17 @@ class CallbackMember(CGNativeMember):
     def getResultConversion(self):
         replacements = {
             "val": "rval",
             "holderName": "rvalHolder",
             "declName": "rvalDecl",
             # We actually want to pass in a null scope object here, because
             # wrapping things into our current compartment (that of mCallback)
             # is what we want.
-            "obj": "nullptr",
-            "passedToJSImpl": "false"
+            "obj": "nullptr"
         }
 
         if isJSImplementedDescriptor(self.descriptorProvider):
             isCallbackReturnValue = "JSImpl"
         else:
             isCallbackReturnValue = "Callback"
         sourceDescription = "return value of %s" % self.getPrettyName()
         convertType = instantiateJSToNativeConversion(
--- a/dom/bindings/Errors.msg
+++ b/dom/bindings/Errors.msg
@@ -49,9 +49,8 @@ MSG_DEF(MSG_INVALID_VERSION, 0, "0 (Zero
 MSG_DEF(MSG_INVALID_BYTESTRING, 2, "Cannot convert string to ByteString because the character"
         " at index {0} has value {1} which is greater than 255.")
 MSG_DEF(MSG_NOT_DATE, 1, "{0} is not a date.")
 MSG_DEF(MSG_INVALID_ADVANCE_COUNT, 0, "0 (Zero) is not a valid advance count.")
 MSG_DEF(MSG_DEFINEPROPERTY_ON_GSP, 0, "Not allowed to define a property on the named properties object.")
 MSG_DEF(MSG_INVALID_URL, 1, "{0} is not a valid URL.")
 MSG_DEF(MSG_METADATA_NOT_CONFIGURED, 0, "Either size or lastModified should be true.")
 MSG_DEF(MSG_INVALID_READ_SIZE, 0, "0 (Zero) is not a valid read size.")
-MSG_DEF(MSG_PERMISSION_DENIED_TO_PASS_ARG, 1, "Permission denied to pass cross-origin object as {0}.")
--- a/dom/bindings/test/TestInterfaceJS.js
+++ b/dom/bindings/test/TestInterfaceJS.js
@@ -4,46 +4,48 @@
 
 const Cu = Components.utils;
 const Ci = Components.interfaces;
 
 "use strict";
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 
+var gGlobal = this;
+function checkGlobal(obj) {
+  if (Object(obj) === obj && Cu.getGlobalForObject(obj) != gGlobal) {
+    // This message may not make it to the caller in a useful form, so dump
+    // as well.
+    var msg = "TestInterfaceJS received an object from a different scope!";
+    dump(msg + "\n");
+    throw new Error(msg);
+  }
+}
+
 function TestInterfaceJS(anyArg, objectArg) {}
 
 TestInterfaceJS.prototype = {
   classID: Components.ID("{2ac4e026-cf25-47d5-b067-78d553c3cad8}"),
   contractID: "@mozilla.org/dom/test-interface-js;1",
   QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports]),
 
-  __init: function (anyArg, objectArg, dictionaryArg) {
+  __init: function (anyArg, objectArg) {
     this._anyAttr = undefined;
     this._objectAttr = null;
     this._anyArg = anyArg;
     this._objectArg = objectArg;
-    this._dictionaryArg = dictionaryArg;
+    checkGlobal(anyArg);
+    checkGlobal(objectArg);
   },
 
   get anyArg() { return this._anyArg; },
   get objectArg() { return this._objectArg; },
-  get dictionaryArg() { return this._dictionaryArg; },
   get anyAttr() { return this._anyAttr; },
-  set anyAttr(val) { this._anyAttr = val; },
+  set anyAttr(val) { checkGlobal(val); this._anyAttr = val; },
   get objectAttr() { return this._objectAttr; },
-  set objectAttr(val) { this._objectAttr = val; },
-  get dictionaryAttr() { return this._dictionaryAttr; },
-  set dictionaryAttr(val) { this._dictionaryAttr = val; },
-  pingPongAny: function(any) { return any; },
-  pingPongObject: function(obj) { return obj; },
-  pingPongObjectOrString: function(objectOrString) { return objectOrString; },
-  pingPongDictionary: function(dict) { return dict; },
-  pingPongDictionaryOrLong: function(dictOrLong) { return dictOrLong.anyMember || dictOrLong; },
-  pingPongMap: function(map) { return JSON.stringify(map); },
-  objectSequenceLength: function(seq) { return seq.length; },
-  anySequenceLength: function(seq) { return seq.length; },
-
+  set objectAttr(val) { checkGlobal(val); this._objectAttr = val; },
+  pingPongAny: function(any) { checkGlobal(any); return any; },
+  pingPongObject: function(obj) { checkGlobal(obj); return obj; },
 
   getCallerPrincipal: function() { return Cu.getWebIDLCallerPrincipal().origin; }
 };
 
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([TestInterfaceJS])
--- a/dom/bindings/test/mochitest.ini
+++ b/dom/bindings/test/mochitest.ini
@@ -11,18 +11,20 @@ support-files =
 [test_bug560072.html]
 [test_bug707564.html]
 [test_bug742191.html]
 [test_bug759621.html]
 [test_bug773326.html]
 [test_bug788369.html]
 [test_bug852846.html]
 [test_bug862092.html]
-[test_bug1036214.html]
-skip-if = debug == false
+# When bug 923904 lands, this test can be turned on, but only for debug builds
+# where we have our test component. So this should become skip-if = debug == false.
+[test_bug923904.html]
+skip-if = true
 [test_bug1041646.html]
 [test_barewordGetsWindow.html]
 [test_callback_default_thisval.html]
 [test_cloneAndImportNode.html]
 [test_defineProperty.html]
 [test_enums.html]
 [test_exceptionThrowing.html]
 [test_exception_messages.html]
deleted file mode 100644
--- a/dom/bindings/test/test_bug1036214.html
+++ /dev/null
@@ -1,123 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=1036214
--->
-<head>
-  <meta charset="utf-8">
-  <title>Test for Bug 1036214</title>
-  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
-  <script type="application/javascript">
-
-  /** Test for subsumes-checking |any| and |object| for js-implemented WebIDL. **/
-  SimpleTest.waitForExplicitFinish();
-  var xoObjects = [];
-  function setup() {
-    xoObjects.push(window[0]);
-    xoObjects.push(window[0].location);
-    xoObjects.push(SpecialPowers.unwrap(SpecialPowers.wrap(window[0]).document));
-    xoObjects.push(SpecialPowers);
-    xoObjects.push(SpecialPowers.wrap);
-    SpecialPowers.pushPrefEnv({set: [['dom.expose_test_interfaces', true]]}, go);
-  }
-
-  function checkThrows(f, msg) {
-    try {
-      f();
-      ok(false, "Should have thrown: " + msg);
-    } catch (e) {
-      ok(true, "Threw correctly: " + msg);
-      ok(/denied|insecure/.test(e), "Threw security exception: " + e);
-    }
-  }
-
-  function go() {
-
-    //
-    // Test the basics of the test interface.
-    //
-
-    var any = { a: 11 };
-    var obj = { b: 22, c: "str" };
-    var obj2 = { foo: "baz" };
-    var myDict = { anyMember: 42, objectMember: { answer: 42 }, objectOrStringMember: { answer: "anobject" },
-                   anySequenceMember: [{}, 1, "thirdinsequence"],
-                   innerDictionary: { innerObject: { answer: "rabbithole" } } };
-    var t = new TestInterfaceJS(any, obj, myDict);
-    is(Object.getPrototypeOf(t), TestInterfaceJS.prototype, "Prototype setup works correctly");
-    is(t.anyArg, any, "anyArg is correct");
-    is(t.objectArg, obj, "objectArg is correct");
-    is(t.dictionaryArg.anyMember, 42, "dictionaryArg looks correct");
-    is(t.dictionaryArg.objectMember.answer, 42, "dictionaryArg looks correct");
-    t.anyAttr = 2;
-    is(t.anyAttr, 2, "ping-pong any attribute works");
-    t.objAttr = obj2;
-    is(t.objAttr, obj2, "ping-pong object attribute works");
-    t.dictionaryAttr = myDict;
-    is(t.dictionaryAttr.anyMember, 42, "ping-pong dictionary attribute works");
-    is(t.dictionaryAttr.objectMember.answer, 42, "ping-pong dictionary attribute works");
-
-    is(any, t.pingPongAny(any), "ping-pong works with any");
-    is(obj, t.pingPongObject(obj), "ping-pong works with obj");
-    is(obj, t.pingPongObjectOrString(obj), "ping-pong works with obj or string");
-    is("foo", t.pingPongObjectOrString("foo"), "ping-pong works with obj or string");
-    is(t.pingPongDictionary(myDict).anyMember, 42, "ping pong works with dict");
-    is(t.pingPongDictionary(myDict).objectMember.answer, 42, "ping pong works with dict");
-    is(t.pingPongDictionary(myDict).objectOrStringMember.answer, "anobject", "ping pong works with dict");
-    is(t.pingPongDictionary(myDict).anySequenceMember[2], "thirdinsequence", "ping pong works with dict");
-    is(t.pingPongDictionary(myDict).innerDictionary.innerObject.answer, "rabbithole", "ping pong works with layered dicts");
-    is(t.pingPongDictionaryOrLong({anyMember: 42}), 42, "ping pong (dict or long) works with dict");
-    is(t.pingPongDictionaryOrLong(42), 42, "ping pong (dict or long) works with long");
-    ok(/canary/.test(t.pingPongMap({ someVal: 42, someOtherVal: "canary" })), "ping pong works with mozmap");
-    is(t.objectSequenceLength([{}, {}, {}]), 3, "ping pong works with object sequence");
-    is(t.anySequenceLength([42, 'string', {}, undefined]), 4, "ping pong works with any sequence");
-
-    //
-    // Test that we throw in the cross-origin cases.
-    //
-
-    xoObjects.forEach(function(xoObj) {
-      var blank = new TestInterfaceJS();
-      checkThrows(() => new TestInterfaceJS(xoObj, undefined), "any param for constructor");
-      checkThrows(() => new TestInterfaceJS(undefined, xoObj), "obj param for constructor");
-      checkThrows(() => new TestInterfaceJS(undefined, undefined, { anyMember: xoObj }), "any dict param for constructor");
-      checkThrows(() => new TestInterfaceJS(undefined, undefined, { objectMember: xoObj }), "object dict param for constructor");
-      checkThrows(() => new TestInterfaceJS(undefined, undefined, { objectOrStringMember: xoObj }), "union dict param for constructor");
-      checkThrows(() => new TestInterfaceJS(undefined, undefined, { anySequenceMember: [0, xoObj, 'hi' ] }), "sequence dict param for constructor");
-      checkThrows(() => new TestInterfaceJS(undefined, undefined, { innerDictionary: { innerObject: xoObj } }), "inner dict param for constructor");
-      checkThrows(() => t.anyAttr = xoObj, "anyAttr");
-      checkThrows(() => t.objectAttr = xoObj, "objAttr");
-      checkThrows(() => t.dictionaryAttr = { anyMember: xoObj }, "dictionaryAttr any");
-      checkThrows(() => t.dictionaryAttr = { objectMember: xoObj }, "dictionaryAttr object");
-      checkThrows(() => t.pingPongAny(xoObj), "pingpong any");
-      checkThrows(() => t.pingPongObject(xoObj), "pingpong obj");
-      checkThrows(() => t.pingPongObjectOrString(xoObj), "pingpong union");
-      checkThrows(() => t.pingPongDictionary({ anyMember: xoObj }), "dictionary pingpong any");
-      checkThrows(() => t.pingPongDictionary({ objectMember: xoObj }), "dictionary pingpong object");
-      checkThrows(() => t.pingPongDictionary({ anyMember: xoObj, objectMember: xoObj }), "dictionary pingpong both");
-      checkThrows(() => t.pingPongDictionary({ objectOrStringMember: xoObj }), "dictionary pingpong objectorstring");
-      checkThrows(() => t.pingPongDictionaryOrLong({ objectMember: xoObj }), "unionable dictionary");
-      checkThrows(() => t.pingPongDictionaryOrLong({ anyMember: xoObj }), "unionable dictionary");
-      checkThrows(() => t.pingPongMap({ someMember: 42, someOtherMember: {}, crossOriginMember: xoObj }), "mozmap");
-      checkThrows(() => t.objectSequenceLength([{}, {}, xoObj, {}]), "object sequence");
-      checkThrows(() => t.anySequenceLength([42, 'someString', xoObj, {}]), "any sequence");
-    });
-
-
-    SimpleTest.finish();
-  }
-
-  </script>
-</head>
-<body>
-<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1036214">Mozilla Bug 1036214</a>
-<p id="display"></p>
-<div id="content" style="display: none">
-
-</div>
-<pre id="test">
-</pre>
-<iframe id="ifr" onload="setup();" src="http://example.org/tests/js/xpconnect/tests/mochitest/file_empty.html"></iframe>
-</body>
-</html>
new file mode 100644
--- /dev/null
+++ b/dom/bindings/test/test_bug923904.html
@@ -0,0 +1,66 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=923904
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 923904</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript">
+
+  /** Test for cloning of |any| and |object| for JS-Implemented WebIDL. **/
+  SimpleTest.waitForExplicitFinish();
+  SpecialPowers.pushPrefEnv({set: [['dom.expose_test_interfaces', true]]}, go);
+
+  function go() {
+    var someAny = { a: 11 };
+    var someObj = { b: 22, c: "str" };
+    var t = new TestInterfaceJS(someAny, someObj);
+    is(Object.getPrototypeOf(t), TestInterfaceJS.prototype, "Prototype setup works correctly");
+    is(t.anyArg.toSource(), someAny.toSource(), "anyArg comes back looking like what we sent");
+    is(t.objectArg.toSource(), someObj.toSource(), "objectArg comes back looking like what we sent");
+    isnot(t.anyArg, t.anyArg, "get a new anyArg each time");
+    isnot(t.objectArg, t.objectArg, "get a new objectArg each time");
+
+    t.anyAttr = 2;
+    is(t.anyAttr, 2, "ping-pong works");
+    testObjectCloned(t, 'anyAttr');
+    testObjectCloned(t, 'objectAttr');
+
+    is(someAny.toSource(), t.pingPongAny(someAny).toSource(), "ping-pong works with any");
+    is(someObj.toSource(), t.pingPongObject(someObj).toSource(), "ping-pong works with obj");
+    isnot(someAny, t.pingPongAny(someAny), "Clone works for ping-pong any");
+    isnot(someObj, t.pingPongObject(someObj), "Clone works for ping-pong obj");
+
+    SimpleTest.finish();
+  }
+
+  function testObjectCloned(iface, propname) {
+    var obj = { prop: 42 };
+    iface[propname] = obj;
+    is(iface[propname].prop, 42, "objects come back as well");
+    is(iface[propname].__proto__, Object.prototype, "vanilla object");
+    isnot(iface[propname], obj, "Should not be the original object");
+    isnot(iface[propname], iface[propname], "Should get cloned each time");
+    try {
+      iface[propname] = { stringProp: "hi", reflectorProp: document };
+      ok(false, "Should throw when trying to clone reflector");
+    } catch (e) {
+      ok(/cloned/.test(e), "Should throw clone error: " + e);
+    }
+  }
+
+  </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=923904">Mozilla Bug 923904</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
--- a/dom/webidl/TestInterfaceJS.webidl
+++ b/dom/webidl/TestInterfaceJS.webidl
@@ -1,33 +1,20 @@
 /* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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/.
  */
 
-dictionary TestInterfaceJSUnionableDictionary {
-  object objectMember;
-  any anyMember;
-};
-
 [JSImplementation="@mozilla.org/dom/test-interface-js;1",
  Pref="dom.expose_test_interfaces",
- Constructor(optional any anyArg, optional object objectArg, optional TestInterfaceJSDictionary dictionaryArg)]
+ Constructor(optional any anyArg, optional object objectArg)]
 interface TestInterfaceJS {
   readonly attribute any anyArg;
   readonly attribute object objectArg;
-  [Cached, Pure] readonly attribute TestInterfaceJSDictionary dictionaryArg;
   attribute any anyAttr;
   attribute object objectAttr;
-  [Cached, Pure] attribute TestInterfaceJSDictionary dictionaryAttr;
   any pingPongAny(any arg);
-  object pingPongObject(object obj);
-  any pingPongObjectOrString((object or DOMString) objOrString);
-  TestInterfaceJSDictionary pingPongDictionary(optional TestInterfaceJSDictionary dict);
-  long pingPongDictionaryOrLong(optional (TestInterfaceJSUnionableDictionary or long) dictOrLong);
-  DOMString pingPongMap(MozMap<any> map);
-  long objectSequenceLength(sequence<object> seq);
-  long anySequenceLength(sequence<any> seq);
+  object pingPongObject(any obj);
 
   // For testing bug 968335.
   DOMString getCallerPrincipal();
 };
deleted file mode 100644
--- a/dom/webidl/TestInterfaceJSDictionaries.webidl
+++ /dev/null
@@ -1,27 +0,0 @@
-/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* 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/.
- */
-
-//
-// These dictionaries are in a separate WebIDL file to avoid circular include
-// problems. One of the dictionary includes a union as a member, so that
-// dictionary's header needs to include UnionTypes.h. But the API in
-// TestInterfaceJS also declares a union of dictionaries, so _that_
-// dictionary's header needs to be included _by_ UnionTypes.h. The solution
-// is to separate those two dictionaries into separate header files.
-//
-
-dictionary TestInterfaceJSDictionary2 {
-  object innerObject;
-};
-
-dictionary TestInterfaceJSDictionary {
-  TestInterfaceJSDictionary2 innerDictionary;
-  object objectMember;
-  any anyMember;
-  (object or DOMString) objectOrStringMember;
-  sequence<any> anySequenceMember;
-};
-
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -555,17 +555,17 @@ WEBIDL_FILES += [
     'StyleRuleChangeEvent.webidl',
     'StyleSheetApplicableStateChangeEvent.webidl',
     'StyleSheetChangeEvent.webidl',
 ]
 
 # We only expose our prefable test interfaces in debug builds, just to be on
 # the safe side.
 if CONFIG['MOZ_DEBUG']:
-    WEBIDL_FILES += ['TestInterfaceJS.webidl', 'TestInterfaceJSDictionaries.webidl']
+    WEBIDL_FILES += ['TestInterfaceJS.webidl']
 
 if CONFIG['MOZ_B2G_BT']:
     if CONFIG['MOZ_B2G_BT_API_V2']:
         WEBIDL_FILES += [
             'BluetoothAdapter2.webidl',
             'BluetoothClassOfDevice.webidl',
             'BluetoothDevice2.webidl',
             'BluetoothDiscoveryHandle.webidl',