Bug 832014 part 1. Add parser support for an [Unforgeable] annotation on WebIDL interfaces. r=peterv
authorBoris Zbarsky <bzbarsky@mit.edu>
Fri, 11 Jul 2014 19:30:26 -0400
changeset 202595 e437d7fa90a5dc06a399b432eadc79a02877b0ae
parent 202594 67c13008762f79f95e6f8d51f5b74386e075822b
child 202596 d52ad359c13246173c00f3c085442f96d004805b
push id3857
push userraliiev@mozilla.com
push dateTue, 02 Sep 2014 16:39:23 +0000
treeherdermozilla-esr52@5638b907b505 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspeterv
bugs832014
milestone33.0a1
Bug 832014 part 1. Add parser support for an [Unforgeable] annotation on WebIDL interfaces. r=peterv
dom/bindings/parser/WebIDL.py
dom/bindings/parser/tests/test_unforgeable.py
dom/webidl/Location.webidl
--- a/dom/bindings/parser/WebIDL.py
+++ b/dom/bindings/parser/WebIDL.py
@@ -664,18 +664,46 @@ class IDLInterface(IDLObjectWithScope):
             self.members.extend(additionalMembers)
             iface.interfacesImplementingSelf.add(self)
 
         for ancestor in self.getInheritedInterfaces():
             ancestor.interfacesBasedOnSelf.add(self)
             for ancestorConsequential in ancestor.getConsequentialInterfaces():
                 ancestorConsequential.interfacesBasedOnSelf.add(self)
 
+        # Deal with interfaces marked [Unforgeable], now that we have our full
+        # member list, except unforgeables pulled in from parents.  We want to
+        # do this before we set "originatingInterface" on our unforgeable
+        # members.
+        if self.getExtendedAttribute("Unforgeable"):
+            # Check that the interface already has all the things the
+            # spec would otherwise require us to synthesize and is
+            # missing the ones we plan to synthesize.
+            if not any(m.isMethod() and m.isStringifier() for m in self.members):
+                raise WebIDLError("Unforgeable interface %s does not have a "
+                                  "stringifier" % self.identifier.name,
+                                  [self.location])
+
+            for m in self.members:
+                if ((m.isMethod() and m.isJsonifier()) or
+                    m.identifier.name == "toJSON"):
+                    raise WebIDLError("Unforgeable interface %s has a "
+                                      "jsonifier so we won't be able to add "
+                                      "one ourselves" % self.identifier.name,
+                                      [self.location, m.location])
+
+                if m.identifier.name == "valueOf" and not m.isStatic():
+                    raise WebIDLError("Unforgeable interface %s has a valueOf "
+                                      "member so we won't be able to add one "
+                                      "ourselves" % self.identifier.name,
+                                      [self.location, m.location])
+
         for member in self.members:
-            if (member.isAttr() and member.isUnforgeable() and
+            if ((member.isAttr() or member.isMethod()) and
+                member.isUnforgeable() and
                 not hasattr(member, "originatingInterface")):
                 member.originatingInterface = self
 
         # Compute slot indices for our members before we pull in
         # unforgeable members from our parent.
         for member in self.members:
             if (member.isAttr() and
                 (member.getExtendedAttribute("StoreInSlot") or
@@ -688,38 +716,38 @@ class IDLInterface(IDLObjectWithScope):
         if self.parent:
             # Make sure we don't shadow any of the [Unforgeable] attributes on
             # our ancestor interfaces.  We don't have to worry about
             # consequential interfaces here, because those have already been
             # imported into the relevant .members lists.  And we don't have to
             # worry about anything other than our parent, because it has already
             # imported its ancestors unforgeable attributes into its member
             # list.
-            for unforgeableAttr in (attr for attr in self.parent.members if
-                                    attr.isAttr() and not attr.isStatic() and
-                                    attr.isUnforgeable()):
+            for unforgeableMember in (member for member in self.parent.members if
+                                      (member.isAttr() or member.isMethod()) and
+                                      member.isUnforgeable()):
                 shadows = [ m for m in self.members if
                             (m.isAttr() or m.isMethod()) and
                             not m.isStatic() and
-                            m.identifier.name == unforgeableAttr.identifier.name ]
+                            m.identifier.name == unforgeableMember.identifier.name ]
                 if len(shadows) != 0:
-                    locs = [unforgeableAttr.location] + [ s.location for s
-                                                          in shadows ]
+                    locs = [unforgeableMember.location] + [ s.location for s
+                                                            in shadows ]
                     raise WebIDLError("Interface %s shadows [Unforgeable] "
                                       "members of %s" %
                                       (self.identifier.name,
                                        ancestor.identifier.name),
                                       locs)
                 # And now just stick it in our members, since we won't be
                 # inheriting this down the proto chain.  If we really cared we
                 # could try to do something where we set up the unforgeable
-                # attributes of ancestor interfaces, with their corresponding
-                # getters, on our interface, but that gets pretty complicated
-                # and seems unnecessary.
-                self.members.append(unforgeableAttr)
+                # attributes/methods of ancestor interfaces, with their
+                # corresponding getters, on our interface, but that gets pretty
+                # complicated and seems unnecessary.
+                self.members.append(unforgeableMember)
 
         # Ensure that there's at most one of each {named,indexed}
         # {getter,setter,creator,deleter}, at most one stringifier,
         # and at most one legacycaller.  Note that this last is not
         # quite per spec, but in practice no one overloads
         # legacycallers.
         specialMembersSeen = {}
         for member in self.members:
@@ -780,16 +808,36 @@ class IDLInterface(IDLObjectWithScope):
                 if parent.getExtendedAttribute("OverrideBuiltins"):
                     raise WebIDLError("Interface with [Global] inherits from "
                                       "interface with [OverrideBuiltins]",
                                       [self.location, parent.location])
                 parent._isOnGlobalProtoChain = True
                 parent = parent.parent
 
     def validate(self):
+        # We don't support consequential unforgeable interfaces.  Need to check
+        # this here, becaue in finish() an interface might not know yet that
+        # it's consequential.
+        if self.getExtendedAttribute("Unforgeable") and self.isConsequential():
+            raise WebIDLError(
+                "%s is an unforgeable consequential interface" %
+                self.identifier.name,
+                [self.location] +
+                list(i.location for i in
+                     (self.interfacesBasedOnSelf - { self }) ))
+
+        # We also don't support inheriting from unforgeable interfaces.
+        if self.getExtendedAttribute("Unforgeable") and self.hasChildInterfaces():
+            raise WebIDLError("%s is an unforgeable ancestor interface" %
+                self.identifier.name,
+                [self.location] +
+                list(i.location for i in
+                     self.interfacesBasedOnSelf if i.parent == self))
+
+
         for member in self.members:
             member.validate()
 
             # Check that PutForwards refers to another attribute and that no
             # cycles exist in forwarded assignments.
             if member.isAttr():
                 iface = self
                 attr = member
@@ -987,16 +1035,17 @@ class IDLInterface(IDLObjectWithScope):
             elif identifier == "Global":
                 if not attr.noArguments():
                     raise WebIDLError("[Global] must take no arguments",
                                       [attr.location])
                 self._isOnGlobalProtoChain = True
             elif (identifier == "NeedNewResolve" or
                   identifier == "OverrideBuiltins" or
                   identifier == "ChromeOnly" or
+                  identifier == "Unforgeable" or
                   identifier == "LegacyEventInit"):
                 # Known extended attributes that do not take values
                 if not attr.noArguments():
                     raise WebIDLError("[%s] must take no arguments" % identifier,
                                       [attr.location])
             elif (identifier == "Pref" or
                   identifier == "JSImplementation" or
                   identifier == "HeaderFile" or
@@ -2884,19 +2933,16 @@ class IDLAttribute(IDLInterfaceMember):
                                   "with [CrossOriginReadable]",
                                   [attr.location, self.location])
             if self.getExtendedAttribute("CrossOriginWritable"):
                 raise WebIDLError("[LenientThis] is not allowed in combination "
                                   "with [CrossOriginWritable]",
                                   [attr.location, self.location])
             self.lenientThis = True
         elif identifier == "Unforgeable":
-            if not self.readonly:
-                raise WebIDLError("[Unforgeable] is only allowed on readonly "
-                                  "attributes", [attr.location, self.location])
             if self.isStatic():
                 raise WebIDLError("[Unforgeable] is only allowed on non-static "
                                   "attributes", [attr.location, self.location])
             self._unforgeable = True
         elif identifier == "SameObject" and not self.readonly:
             raise WebIDLError("[SameObject] only allowed on readonly attributes",
                               [attr.location, self.location])
         elif identifier == "Constant" and not self.readonly:
--- a/dom/bindings/parser/tests/test_unforgeable.py
+++ b/dom/bindings/parser/tests/test_unforgeable.py
@@ -79,31 +79,69 @@ def WebIDLTest(parser, harness):
                "Should have thrown when shadowing unforgeable attribute on "
                "parent with operation.")
 
     parser = parser.reset();
     threw = False
     try:
         parser.parse("""
             interface Child : Parent {
+              void foo();
+            };
+            interface Parent {
+              [Unforgeable] void foo();
+            };
+        """)
+
+        results = parser.finish()
+    except:
+        threw = True
+    harness.ok(threw,
+               "Should have thrown when shadowing unforgeable operation on "
+               "parent with operation.")
+
+    parser = parser.reset();
+    threw = False
+    try:
+        parser.parse("""
+            interface Child : Parent {
               attribute short foo;
             };
             interface Parent {
               [Unforgeable] readonly attribute long foo;
             };
         """)
 
         results = parser.finish()
     except Exception,x:
         threw = True
     harness.ok(threw,
                "Should have thrown when shadowing unforgeable attribute on "
                "parent with attribute.")
 
     parser = parser.reset();
+    threw = False
+    try:
+        parser.parse("""
+            interface Child : Parent {
+              attribute short foo;
+            };
+            interface Parent {
+              [Unforgeable] void foo();
+            };
+        """)
+
+        results = parser.finish()
+    except Exception,x:
+        threw = True
+    harness.ok(threw,
+               "Should have thrown when shadowing unforgeable operation on "
+               "parent with attribute.")
+
+    parser = parser.reset();
     parser.parse("""
             interface Child : Parent {
             };
             interface Parent {};
             interface Consequential {
               [Unforgeable] readonly attribute long foo;
             };
             Parent implements Consequential;
@@ -161,26 +199,48 @@ def WebIDLTest(parser, harness):
     harness.ok(threw,
                "Should have thrown when our consequential interface shadows unforgeable attribute "
                "of ancestor's consequential interface.")
 
     parser = parser.reset();
     threw = False
     try:
         parser.parse("""
-            interface iface {
-              [Unforgeable] attribute long foo;
+            interface Child : Parent {
             };
+            interface Parent : GrandParent {};
+            interface GrandParent {};
+            interface Consequential {
+              [Unforgeable] void foo();
+            };
+            GrandParent implements Consequential;
+            interface ChildConsequential {
+              void foo();
+            };
+            Child implements ChildConsequential;
         """)
 
         results = parser.finish()
     except:
         threw = True
 
-    harness.ok(threw, "Should have thrown for writable [Unforgeable] attribute.")
+    harness.ok(threw,
+               "Should have thrown when our consequential interface shadows unforgeable operation "
+               "of ancestor's consequential interface.")
+
+    parser = parser.reset();
+    parser.parse("""
+        interface iface {
+          [Unforgeable] attribute long foo;
+        };
+    """)
+
+    results = parser.finish()
+    harness.check(len(results), 1,
+                  "Should allow writable [Unforgeable] attribute.")
 
     parser = parser.reset();
     threw = False
     try:
         parser.parse("""
             interface iface {
               [Unforgeable] static readonly attribute long foo;
             };
--- a/dom/webidl/Location.webidl
+++ b/dom/webidl/Location.webidl
@@ -6,16 +6,15 @@
  * The origin of this IDL file is
  * http://www.whatwg.org/specs/web-apps/current-work/#the-location-interface
  *
  * © Copyright 2004-2011 Apple Computer, Inc., Mozilla Foundation, and
  * Opera Software ASA. You are granted a license to use, reproduce
  * and create derivative works of this document.
  */
 
-// No support for [Unforgeable] on interfaces yet
-//[Unforgeable]
+[Unforgeable]
 interface Location {
   void assign(DOMString url);
   void replace(DOMString url);
   void reload();
 };
 Location implements URLUtils;