Bug 808991. Named getters should get called for indexed properties on objects that don't support indexed properties. r=peterv
authorBoris Zbarsky <bzbarsky@mit.edu>
Thu, 08 Nov 2012 23:48:42 -0800
changeset 121436 1c1fa8af5978ed6bf0b038839750228a63d40a70
parent 121435 f0ca8ef31bef6a53718c9b8f047de6d5e60d8916
child 121437 ed1c4f642584e18d5dc57d496d3fc33bb9e5714e
push id273
push userlsblakk@mozilla.com
push dateThu, 14 Feb 2013 23:19:38 +0000
treeherdermozilla-release@c5e807a3f8b8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspeterv
bugs808991
milestone19.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 808991. Named getters should get called for indexed properties on objects that don't support indexed properties. r=peterv
dom/bindings/Codegen.py
dom/bindings/DOMJSProxyHandler.h
dom/bindings/test/Makefile.in
dom/bindings/test/test_namedNoIndexed.html
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -5074,92 +5074,100 @@ class CGProxyIndexedOperation(CGProxySpe
 
 class CGProxyIndexedGetter(CGProxyIndexedOperation):
     """
     Class to generate a call to an indexed getter. If templateValues is not None
     the returned value will be wrapped with wrapForType using templateValues.
     """
     def __init__(self, descriptor, templateValues=None):
         self.templateValues = templateValues
-        CGProxySpecialOperation.__init__(self, descriptor, 'IndexedGetter')
+        CGProxyIndexedOperation.__init__(self, descriptor, 'IndexedGetter')
 
 class CGProxyIndexedPresenceChecker(CGProxyIndexedGetter):
     """
     Class to generate a call that checks whether an indexed property exists.
 
     For now, we just delegate to CGProxyIndexedGetter
     """
     def __init__(self, descriptor):
         CGProxyIndexedGetter.__init__(self, descriptor)
 
 class CGProxyIndexedSetter(CGProxyIndexedOperation):
     """
     Class to generate a call to an indexed setter.
     """
     def __init__(self, descriptor):
-        CGProxySpecialOperation.__init__(self, descriptor, 'IndexedSetter')
+        CGProxyIndexedOperation.__init__(self, descriptor, 'IndexedSetter')
 
 class CGProxyNamedOperation(CGProxySpecialOperation):
     """
     Class to generate a call to a named operation.
-    """
-    def __init__(self, descriptor, name):
+
+    'value' is the jsval to use for the name; None indicates that it should be
+    gotten from the property id.
+    """
+    def __init__(self, descriptor, name, value=None):
         CGProxySpecialOperation.__init__(self, descriptor, name)
+        if value is None:
+            value = "js::IdToValue(id)"
+        self.value = value
     def define(self):
         # Our first argument is the id we're getting.
         argName = self.arguments[0].identifier.name
-        return (("JS::Value nameVal = STRING_TO_JSVAL(JSID_TO_STRING(id));\n"
+        return (("JS::Value nameVal = %s;\n"
                  "FakeDependentString %s;\n"
                  "if (!ConvertJSValueToString(cx, nameVal, &nameVal,\n"
                  "                            eStringify, eStringify, %s)) {\n"
                  "  return false;\n"
                  "}\n"
                  "\n"
                  "%s* self = UnwrapProxy(proxy);\n" %
-                 (argName, argName, self.descriptor.nativeType)) +
+                 (self.value, argName, argName, self.descriptor.nativeType)) +
                 CGProxySpecialOperation.define(self))
 
 class CGProxyNamedGetter(CGProxyNamedOperation):
     """
     Class to generate a call to an named getter. If templateValues is not None
     the returned value will be wrapped with wrapForType using templateValues.
-    """
-    def __init__(self, descriptor, templateValues=None):
+    'value' is the jsval to use for the name; None indicates that it should be
+    gotten from the property id.
+    """
+    def __init__(self, descriptor, templateValues=None, value=None):
         self.templateValues = templateValues
-        CGProxySpecialOperation.__init__(self, descriptor, 'NamedGetter')
+        CGProxyNamedOperation.__init__(self, descriptor, 'NamedGetter', value)
 
 class CGProxyNamedPresenceChecker(CGProxyNamedGetter):
     """
     Class to generate a call that checks whether a named property exists.
 
     For now, we just delegate to CGProxyNamedGetter
     """
     def __init__(self, descriptor):
         CGProxyNamedGetter.__init__(self, descriptor)
 
 class CGProxyNamedSetter(CGProxyNamedOperation):
     """
     Class to generate a call to a named setter.
     """
     def __init__(self, descriptor):
-        CGProxySpecialOperation.__init__(self, descriptor, 'NamedSetter')
+        CGProxyNamedOperation.__init__(self, descriptor, 'NamedSetter')
 
 class CGProxyIndexedDeleter(CGProxyIndexedOperation):
     """
     Class to generate a call to an indexed deleter.
     """
     def __init__(self, descriptor):
-        CGProxySpecialOperation.__init__(self, descriptor, 'IndexedDeleter')
+        CGProxyIndexedOperation.__init__(self, descriptor, 'IndexedDeleter')
 
 class CGProxyNamedDeleter(CGProxyNamedOperation):
     """
     Class to generate a call to a named deleter.
     """
     def __init__(self, descriptor):
-        CGProxySpecialOperation.__init__(self, descriptor, 'NamedDeleter')
+        CGProxyNamedOperation.__init__(self, descriptor, 'NamedDeleter')
 
 class CGProxyIsProxy(CGAbstractMethod):
     def __init__(self, descriptor):
         args = [Argument('JSObject*', 'obj')]
         CGAbstractMethod.__init__(self, descriptor, "IsProxy", "bool", args, alwaysInline=True)
     def declare(self):
         return ""
     def definition_body(self):
@@ -5209,38 +5217,44 @@ class CGDOMJSProxyHandler_getOwnProperty
 
         setOrIndexedGet = ""
         if self.descriptor.supportsIndexedProperties():
             setOrIndexedGet += "int32_t index = GetArrayIndexFromId(cx, id);\n"
             readonly = toStringBool(indexedSetter is None)
             fillDescriptor = "FillPropertyDescriptor(desc, proxy, %s);\nreturn true;" % readonly
             templateValues = {'jsvalRef': 'desc->value', 'jsvalPtr': '&desc->value',
                               'obj': 'proxy', 'successCode': fillDescriptor}
-            get = ("if (index >= 0) {\n" +
+            get = ("if (IsArrayIndex(index)) {\n" +
                    CGIndenter(CGProxyIndexedGetter(self.descriptor, templateValues)).define() + "\n" +
                    "}\n") % (self.descriptor.nativeType)
 
         if indexedSetter or self.descriptor.operations['NamedSetter']:
             setOrIndexedGet += "if (set) {\n"
             if indexedSetter:
-                setOrIndexedGet += ("  if (index >= 0) {\n")
+                setOrIndexedGet += ("  if (IsArrayIndex(index)) {\n")
                 if not 'IndexedCreator' in self.descriptor.operations:
-                    # FIXME need to check that this is a 'supported property index'
+                    # FIXME need to check that this is a 'supported property
+                    # index'.  But if that happens, watch out for the assumption
+                    # below that the name setter always returns for
+                    # IsArrayIndex(index).
                     assert False
                 setOrIndexedGet += ("    FillPropertyDescriptor(desc, proxy, JSVAL_VOID, false);\n" +
                                     "    return true;\n" +
                                     "  }\n")
             if self.descriptor.operations['NamedSetter']:
-                setOrIndexedGet += "  if (JSID_IS_STRING(id)) {\n"
                 if not 'NamedCreator' in self.descriptor.operations:
                     # FIXME need to check that this is a 'supported property name'
                     assert False
-                setOrIndexedGet += ("    FillPropertyDescriptor(desc, proxy, JSVAL_VOID, false);\n" +
-                                    "    return true;\n" +
-                                    "  }\n")
+                create = CGGeneric("FillPropertyDescriptor(desc, proxy, JSVAL_VOID, false);\n"
+                                   "return true;")
+                # If we have an indexed setter we've already returned
+                if (self.descriptor.supportsIndexedProperties() and
+                    not indexedSetter):
+                    create = CGIfWrapper(create, "!IsArrayIndex(index)")
+                setOrIndexedGet += CGIndenter(create).define() + "\n"
             setOrIndexedGet += "}"
             if indexedGetter:
                 setOrIndexedGet += (" else {\n" +
                                     CGIndenter(CGGeneric(get)).define() +
                                     "}")
             setOrIndexedGet += "\n\n"
         elif indexedGetter:
             setOrIndexedGet += ("if (!set) {\n" +
@@ -5250,20 +5264,23 @@ class CGDOMJSProxyHandler_getOwnProperty
         if self.descriptor.supportsNamedProperties():
             readonly = toStringBool(self.descriptor.operations['NamedSetter'] is None)
             fillDescriptor = "FillPropertyDescriptor(desc, proxy, %s);\nreturn true;" % readonly
             templateValues = {'jsvalRef': 'desc->value', 'jsvalPtr': '&desc->value',
                               'obj': 'proxy', 'successCode': fillDescriptor}
             # Once we start supporting OverrideBuiltins we need to make
             # ResolveOwnProperty or EnumerateOwnProperties filter out named
             # properties that shadow prototype properties.
+            condition = "!set && !HasPropertyOnPrototype(cx, proxy, this, id)"
+            if self.descriptor.supportsIndexedProperties():
+                condition = "!IsArrayIndex(index) && " + condition
             namedGet = ("\n" +
-                        "if (!set && JSID_IS_STRING(id) && !HasPropertyOnPrototype(cx, proxy, this, id)) {\n" +
-                        CGIndenter(CGProxyNamedGetter(self.descriptor, templateValues)).define() + "\n" +
-                        "}\n")
+                        CGIfWrapper(CGProxyNamedGetter(self.descriptor, templateValues),
+                                    condition).define() +
+                        "\n")
         else:
             namedGet = ""
 
         return setOrIndexedGet + """JSObject* expando;
 if (!xpc::WrapperFactory::IsXrayWrapper(proxy) && (expando = GetExpandoObject(proxy))) {
   unsigned flags = (set ? JSRESOLVE_ASSIGNING : 0) | JSRESOLVE_QUALIFIED;
   if (!JS_GetPropertyDescriptorById(cx, expando, id, flags, desc)) {
     return false;
@@ -5288,41 +5305,50 @@ class CGDOMJSProxyHandler_defineProperty
     def getBody(self):
         set = ""
 
         indexedSetter = self.descriptor.operations['IndexedSetter']
         if indexedSetter:
             if not (self.descriptor.operations['IndexedCreator'] is indexedSetter):
                 raise TypeError("Can't handle creator that's different from the setter")
             set += ("int32_t index = GetArrayIndexFromId(cx, id);\n" +
-                    "if (index >= 0) {\n" +
+                    "if (IsArrayIndex(index)) {\n" +
                     CGIndenter(CGProxyIndexedSetter(self.descriptor)).define() +
                     "  return true;\n" +
                     "}\n") % (self.descriptor.nativeType)
         elif self.descriptor.supportsIndexedProperties():
-            set += ("if (GetArrayIndexFromId(cx, id) >= 0) {\n" +
+            # XXXbz Once this is fixed to only throw in strict mode, update the
+            # code that decides whether to do a
+            # CGDOMJSProxyHandler_defineProperty at all.
+            set += ("if (IsArrayIndex(GetArrayIndexFromId(cx, id))) {\n" +
                     "  return ThrowErrorMessage(cx, MSG_NO_PROPERTY_SETTER, \"%s\");\n" +
                     "}\n") % self.descriptor.name
 
         namedSetter = self.descriptor.operations['NamedSetter']
         if namedSetter:
             if not self.descriptor.operations['NamedCreator'] is namedSetter:
                 raise TypeError("Can't handle creator that's different from the setter")
-            set += ("if (JSID_IS_STRING(id)) {\n" +
-                    CGIndenter(CGProxyNamedSetter(self.descriptor)).define() + "\n" +
-                    "  return true;\n" +
-                    "}\n")
-        elif self.descriptor.supportsNamedProperties():
-            set += ("if (JSID_IS_STRING(id)) {\n" +
-                    CGIndenter(CGProxyNamedPresenceChecker(self.descriptor)).define() +
-                    "  if (found) {\n"
-                    "    return ThrowErrorMessage(cx, MSG_NO_PROPERTY_SETTER, \"%s\");\n" +
-                    "  }\n" +
-                    "}\n") % (self.descriptor.name)
-        return set + """return mozilla::dom::DOMProxyHandler::defineProperty(%s);""" % ", ".join(a.name for a in self.args)
+            # If we support indexed properties, we won't get down here for
+            # indices, so we can just do our setter unconditionally here.
+            set += (CGProxyNamedSetter(self.descriptor).define() + "\n" +
+                    "return true;\n")
+        else:
+            if self.descriptor.supportsNamedProperties():
+                # XXXbz Once this is fixed to only throw in strict mode, update
+                # the code that decides whether to do a
+                # CGDOMJSProxyHandler_defineProperty at all.  If we support
+                # indexed properties, we won't get down here for indices, so we
+                # can just do our setter unconditionally here.
+                set += (CGProxyNamedPresenceChecker(self.descriptor).define() + "\n" +
+                        "if (found) {\n"
+                        "  return ThrowErrorMessage(cx, MSG_NO_PROPERTY_SETTER, \"%s\");\n"
+                        "}" % self.descriptor.name)
+            set += ("return mozilla::dom::DOMProxyHandler::defineProperty(%s);" %
+                    ", ".join(a.name for a in self.args))
+        return set
 
 class CGDOMJSProxyHandler_delete(ClassMethod):
     def __init__(self, descriptor):
         args = [Argument('JSContext*', 'cx'), Argument('JSObject*', 'proxy'),
                 Argument('jsid', 'id'),
                 Argument('bool*', 'bp')]
         ClassMethod.__init__(self, "delete_", "bool", args)
         self.descriptor = descriptor
@@ -5360,25 +5386,28 @@ class CGDOMJSProxyHandler_delete(ClassMe
                 body = None
             return body
 
         delete = ""
 
         indexedBody = getDeleterBody("Indexed")
         if indexedBody is not None:
             delete += ("int32_t index = GetArrayIndexFromId(cx, id);\n" +
-                       "if (index >= 0) {\n" +
+                       "if (IsArrayIndex(index)) {\n" +
                        CGIndenter(CGGeneric(indexedBody)).define() + "\n"
                        "  // We always return here, even if the property was not found\n"
                        "  return true;\n" +
                        "}\n") % self.descriptor.nativeType
 
         namedBody = getDeleterBody("Named")
         if namedBody is not None:
-            delete += ("if (JSID_IS_STRING(id) && !HasPropertyOnPrototype(cx, proxy, this, id)) {\n" +
+            # We always return above for an index id in the case when we support
+            # indexed properties, so we can just treat the id as a name
+            # unconditionally here.
+            delete += ("if (!HasPropertyOnPrototype(cx, proxy, this, id)) {\n" +
                        CGIndenter(CGGeneric(namedBody)).define() + "\n"
                        "  if (found) {\n"
                        "    return true;\n"
                        "  }\n"
                        "}\n")
 
         delete += "return dom::DOMProxyHandler::delete_(cx, proxy, id, bp);";
 
@@ -5428,26 +5457,28 @@ class CGDOMJSProxyHandler_hasOwn(ClassMe
     def __init__(self, descriptor):
         args = [Argument('JSContext*', 'cx'), Argument('JSObject*', 'proxy'),
                 Argument('jsid', 'id'), Argument('bool*', 'bp')]
         ClassMethod.__init__(self, "hasOwn", "bool", args)
         self.descriptor = descriptor
     def getBody(self):
         if self.descriptor.supportsIndexedProperties():
             indexed = ("int32_t index = GetArrayIndexFromId(cx, id);\n" + 
-                       "if (index >= 0) {\n" +
+                       "if (IsArrayIndex(index)) {\n" +
                        CGIndenter(CGProxyIndexedPresenceChecker(self.descriptor)).define() + "\n" +
                        "  *bp = found;\n" +
                        "  return true;\n" +
                        "}\n\n") % (self.descriptor.nativeType)
         else:
             indexed = ""
 
         if self.descriptor.supportsNamedProperties():
-            named = ("if (JSID_IS_STRING(id) && !HasPropertyOnPrototype(cx, proxy, this, id)) {\n" +
+            # If we support indexed properties we always return above for index
+            # property names, so no need to check for those here.
+            named = ("if (!HasPropertyOnPrototype(cx, proxy, this, id)) {\n" +
                      CGIndenter(CGProxyNamedPresenceChecker(self.descriptor)).define() + "\n" +
                      "  *bp = found;\n"
                      "  return true;\n"
                      "}\n" +
                      "\n")
         else:
             named = ""
 
@@ -5483,46 +5514,49 @@ if (expando) {
     return JS_GetPropertyById(cx, expando, id, vp);
   }
 }"""
 
         templateValues = {'jsvalRef': '*vp', 'jsvalPtr': 'vp', 'obj': 'proxy'}
 
         if self.descriptor.supportsIndexedProperties():
             getIndexedOrExpando = ("int32_t index = GetArrayIndexFromId(cx, id);\n" +
-                                   "if (index >= 0) {\n" +
+                                   "if (IsArrayIndex(index)) {\n" +
                                    CGIndenter(CGProxyIndexedGetter(self.descriptor, templateValues)).define()) % (self.descriptor.nativeType)
             getIndexedOrExpando += """
   // Even if we don't have this index, we don't forward the
   // get on to our expando object.
 } else {
   %s
 }
 """ % (stripTrailingWhitespace(getFromExpando.replace('\n', '\n  ')))
         else:
             getIndexedOrExpando = getFromExpando + "\n"
 
         if self.descriptor.supportsNamedProperties():
-            getNamed = ("if (JSID_IS_STRING(id)) {\n" +
-                        CGIndenter(CGProxyNamedGetter(self.descriptor, templateValues)).define() +
-                        "}\n")
+            getNamed = CGProxyNamedGetter(self.descriptor, templateValues)
+            if self.descriptor.supportsIndexedProperties():
+                getNamed = CGIfWrapper(getNamed, "!IsArrayIndex(index)")
+            getNamed = getNamed.define() + "\n"
         else:
             getNamed = ""
 
         return """MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy),
             "Should not have a XrayWrapper here");
 
 %s
-bool found;
-if (!GetPropertyOnPrototype(cx, proxy, id, &found, vp)) {
-  return false;
-}
-
-if (found) {
-  return true;
+{  // Scope for this "found" so it doesn't leak to things below
+  bool found;
+  if (!GetPropertyOnPrototype(cx, proxy, id, &found, vp)) {
+    return false;
+  }
+
+  if (found) {
+    return true;
+  }
 }
 %s
 vp->setUndefined();
 return true;""" % (getIndexedOrExpando, getNamed)
 
 class CGDOMJSProxyHandler_obj_toString(ClassMethod):
     def __init__(self, descriptor):
         args = [Argument('JSContext*', 'cx'), Argument('JSObject*', 'proxy')]
@@ -5544,26 +5578,30 @@ class CGDOMJSProxyHandler_getElementIfPr
     def __init__(self, descriptor):
         args = [Argument('JSContext*', 'cx'), Argument('JSObject*', 'proxy'),
                 Argument('JSObject*', 'receiver'),
                 Argument('uint32_t', 'index'),
                 Argument('JS::Value*', 'vp'), Argument('bool*', 'present')]
         ClassMethod.__init__(self, "getElementIfPresent", "bool", args)
         self.descriptor = descriptor
     def getBody(self):
+        successCode = ("*present = found;\n"
+                       "return true;")
+        templateValues = {'jsvalRef': '*vp', 'jsvalPtr': 'vp',
+                          'obj': 'proxy', 'successCode': successCode}
         if self.descriptor.supportsIndexedProperties():
-            successCode = """*present = found;
-return true;"""
-            templateValues = {'jsvalRef': '*vp', 'jsvalPtr': 'vp',
-                              'obj': 'proxy', 'successCode': successCode}
             get = (CGProxyIndexedGetter(self.descriptor, templateValues).define() + "\n"
-                   "// We skip the expando object if there is an indexed getter.\n" +
+                   "// We skip the expando object and any named getters if\n"
+                   "// there is an indexed getter.\n" +
                    "\n") % (self.descriptor.nativeType)
         else:
-            get = """
+            if self.descriptor.supportsNamedProperties():
+                get = CGProxyNamedGetter(self.descriptor, templateValues,
+                                         "UINT_TO_JSVAL(index)").define()
+            get += """
 
 JSObject* expando = GetExpandoObject(proxy);
 if (expando) {
   JSBool isPresent;
   if (!JS_GetElementIfPresent(cx, expando, index, expando, vp, &isPresent)) {
     return false;
   }
   if (isPresent) {
@@ -5572,18 +5610,16 @@ if (expando) {
   }
 }
 """
 
         return """MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy),
              "Should not have a XrayWrapper here");
 
 """ + get + """
-// No need to worry about name getters here, so just check the proto.
-
 JSObject *proto;
 if (!js::GetObjectProto(cx, proxy, &proto)) {
   return false;
 }
 if (proto) {
   JSBool isPresent;
   if (!JS_GetElementIfPresent(cx, proto, index, proxy, vp, &isPresent)) {
     return false;
@@ -5602,16 +5638,19 @@ class CGDOMJSProxyHandler_getInstance(Cl
     def getBody(self):
         return """static DOMProxyHandler instance;
 return &instance;"""
 
 class CGDOMJSProxyHandler(CGClass):
     def __init__(self, descriptor):
         constructors = [CGDOMJSProxyHandler_CGDOMJSProxyHandler()]
         methods = [CGDOMJSProxyHandler_getOwnPropertyDescriptor(descriptor)]
+        # XXXbz This should really just test supportsIndexedProperties() and
+        # supportsNamedProperties(), but that would make us throw in all cases
+        # because we don't know whether we're in strict mode.
         if descriptor.operations['IndexedSetter'] or descriptor.operations['NamedSetter']:
             methods.append(CGDOMJSProxyHandler_defineProperty(descriptor))
         methods.extend([CGDOMJSProxyHandler_getOwnPropertyNames(descriptor),
                         CGDOMJSProxyHandler_hasOwn(descriptor),
                         CGDOMJSProxyHandler_get(descriptor),
                         CGDOMJSProxyHandler_obj_toString(descriptor),
                         CGDOMJSProxyHandler_finalize(descriptor),
                         CGDOMJSProxyHandler_getElementIfPresent(descriptor),
--- a/dom/bindings/DOMJSProxyHandler.h
+++ b/dom/bindings/DOMJSProxyHandler.h
@@ -63,16 +63,18 @@ protected:
                               nsTArray<nsString>& names,
                               JS::AutoIdVector& props);
 };
 
 extern jsid s_length_id;
 
 int32_t IdToInt32(JSContext* cx, jsid id);
 
+// XXXbz this should really return uint32_t, with the maximum value
+// meaning "not an index"...
 inline int32_t
 GetArrayIndexFromId(JSContext* cx, jsid id)
 {
   if (MOZ_LIKELY(JSID_IS_INT(id))) {
     return JSID_TO_INT(id);
   }
   if (MOZ_LIKELY(id == s_length_id)) {
     return -1;
@@ -85,16 +87,22 @@ GetArrayIndexFromId(JSContext* cx, jsid 
 
     uint32_t i;
     JSLinearString* str = js::AtomToLinearString(JSID_TO_ATOM(id));
     return js::StringIsArrayIndex(str, &i) ? i : -1;
   }
   return IdToInt32(cx, id);
 }
 
+inline bool
+IsArrayIndex(int32_t index)
+{
+  return index >= 0;
+}
+
 inline void
 FillPropertyDescriptor(JSPropertyDescriptor* desc, JSObject* obj, bool readonly)
 {
   desc->obj = obj;
   desc->attrs = (readonly ? JSPROP_READONLY : 0) | JSPROP_ENUMERATE;
   desc->getter = NULL;
   desc->setter = NULL;
   desc->shortid = 0;
--- a/dom/bindings/test/Makefile.in
+++ b/dom/bindings/test/Makefile.in
@@ -58,16 +58,17 @@ MOCHITEST_FILES := \
   test_InstanceOf.html \
   test_traceProtos.html \
   test_forOf.html \
   forOf_iframe.html \
   test_sequence_wrapping.html \
   file_bug775543.html \
   test_bug788369.html \
   test_bug742191.html \
+  test_namedNoIndexed.html \
   $(NULL)
 
 MOCHITEST_CHROME_FILES = \
   test_bug775543.html \
   $(NULL)
 
 # Include rules.mk before any of our targets so our first target is coming from
 # rules.mk and running make with no target in this dir does the right thing.
new file mode 100644
--- /dev/null
+++ b/dom/bindings/test/test_namedNoIndexed.html
@@ -0,0 +1,36 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=808991
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 808991</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=808991">Mozilla Bug 808991</a>
+<p id="display"></p>
+<div id="content" style="display: none" data-1="foo" data-bar="baz">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 808991 **/
+is($("content").dataset[1], "foo",
+   "Indexed props should work like named on dataset");
+is($("content").dataset["1"], "foo",
+   "Indexed props as strings should work like named on dataset");
+is($("content").dataset.bar, "baz",
+   "Named props should work on dataset");
+is($("content").dataset['bar'], "baz",
+   "Named props as strings should work on dataset");
+
+
+
+</script>
+</pre>
+</body>
+</html>