Version 1.10.0-dev.1.9

svn merge -c 45307 https://dart.googlecode.com/svn/branches/bleeding_edge trunk

git-svn-id: http://dart.googlecode.com/svn/trunk@45311 260f80e4-7a28-3924-810f-c04153c831b5
diff --git a/sdk/lib/html/dart2js/html_dart2js.dart b/sdk/lib/html/dart2js/html_dart2js.dart
index e780d2e..b767c0b 100644
--- a/sdk/lib/html/dart2js/html_dart2js.dart
+++ b/sdk/lib/html/dart2js/html_dart2js.dart
@@ -36383,6 +36383,10 @@
    * operation.
    *
    * If this corresponds to many elements, `null` is always returned.
+   *
+   * [value] must be a valid 'token' representing a single class, i.e. a
+   * non-empty string containing no whitespace.  To toggle multiple classes, use
+   * [toggleAll].
    */
   bool toggle(String value, [bool shouldAdd]);
 
@@ -36397,19 +36401,26 @@
    *
    * This is the Dart equivalent of jQuery's
    * [hasClass](http://api.jquery.com/hasClass/).
+   *
+   * [value] must be a valid 'token' representing a single class, i.e. a
+   * non-empty string containing no whitespace.
    */
   bool contains(String value);
 
   /**
    * Add the class [value] to element.
    *
-   * This is the Dart equivalent of jQuery's
+   * [add] and [addAll] are the Dart equivalent of jQuery's
    * [addClass](http://api.jquery.com/addClass/).
    *
-   * If this corresponds to one element. Returns true if [value] was added to
-   * the set, otherwise false.
+   * If this CssClassSet corresponds to one element. Returns true if [value] was
+   * added to the set, otherwise false.
    *
    * If this corresponds to many elements, `null` is always returned.
+   *
+   * [value] must be a valid 'token' representing a single class, i.e. a
+   * non-empty string containing no whitespace.  To add multiple classes use
+   * [addAll].
    */
   bool add(String value);
 
@@ -36417,24 +36428,34 @@
    * Remove the class [value] from element, and return true on successful
    * removal.
    *
-   * This is the Dart equivalent of jQuery's
+   * [remove] and [removeAll] are the Dart equivalent of jQuery's
    * [removeClass](http://api.jquery.com/removeClass/).
+   *
+   * [value] must be a valid 'token' representing a single class, i.e. a
+   * non-empty string containing no whitespace.  To remove multiple classes, use
+   * [removeAll].
    */
   bool remove(Object value);
 
   /**
    * Add all classes specified in [iterable] to element.
    *
-   * This is the Dart equivalent of jQuery's
+   * [add] and [addAll] are the Dart equivalent of jQuery's
    * [addClass](http://api.jquery.com/addClass/).
+   *
+   * Each element of [iterable] must be a valid 'token' representing a single
+   * class, i.e. a non-empty string containing no whitespace.
    */
   void addAll(Iterable<String> iterable);
 
   /**
    * Remove all classes specified in [iterable] from element.
    *
-   * This is the Dart equivalent of jQuery's
+   * [remove] and [removeAll] are the Dart equivalent of jQuery's
    * [removeClass](http://api.jquery.com/removeClass/).
+   *
+   * Each element of [iterable] must be a valid 'token' representing a single
+   * class, i.e. a non-empty string containing no whitespace.
    */
   void removeAll(Iterable<String> iterable);
 
@@ -36447,6 +36468,9 @@
    * If [shouldAdd] is true, then we always add all the classes in [iterable]
    * element. If [shouldAdd] is false then we always remove all the classes in
    * [iterable] from the element.
+   *
+   * Each element of [iterable] must be a valid 'token' representing a single
+   * class, i.e. a non-empty string containing no whitespace.
    */
   void toggleAll(Iterable<String> iterable, [bool shouldAdd]);
 }
@@ -36812,7 +36836,7 @@
     _element.className = '';
   }
 
-  bool contains(String value) {
+  bool contains(Object value) {
     return _contains(_element, value);
   }
 
@@ -36848,8 +36872,8 @@
     _removeWhere(_element, test, false);
   }
 
-  static bool _contains(Element _element, String value) {
-    return _classListContains(_classListOf(_element), value);
+  static bool _contains(Element _element, Object value) {
+    return value is String && _classListContains(_classListOf(_element), value);
   }
 
   static bool _add(Element _element, String value) {
diff --git a/sdk/lib/html/dartium/html_dartium.dart b/sdk/lib/html/dartium/html_dartium.dart
index 63e26ae..50ce4bb 100644
--- a/sdk/lib/html/dartium/html_dartium.dart
+++ b/sdk/lib/html/dartium/html_dartium.dart
@@ -37305,6 +37305,10 @@
    * operation.
    *
    * If this corresponds to many elements, `null` is always returned.
+   *
+   * [value] must be a valid 'token' representing a single class, i.e. a
+   * non-empty string containing no whitespace.  To toggle multiple classes, use
+   * [toggleAll].
    */
   bool toggle(String value, [bool shouldAdd]);
 
@@ -37319,19 +37323,26 @@
    *
    * This is the Dart equivalent of jQuery's
    * [hasClass](http://api.jquery.com/hasClass/).
+   *
+   * [value] must be a valid 'token' representing a single class, i.e. a
+   * non-empty string containing no whitespace.
    */
   bool contains(String value);
 
   /**
    * Add the class [value] to element.
    *
-   * This is the Dart equivalent of jQuery's
+   * [add] and [addAll] are the Dart equivalent of jQuery's
    * [addClass](http://api.jquery.com/addClass/).
    *
-   * If this corresponds to one element. Returns true if [value] was added to
-   * the set, otherwise false.
+   * If this CssClassSet corresponds to one element. Returns true if [value] was
+   * added to the set, otherwise false.
    *
    * If this corresponds to many elements, `null` is always returned.
+   *
+   * [value] must be a valid 'token' representing a single class, i.e. a
+   * non-empty string containing no whitespace.  To add multiple classes use
+   * [addAll].
    */
   bool add(String value);
 
@@ -37339,24 +37350,34 @@
    * Remove the class [value] from element, and return true on successful
    * removal.
    *
-   * This is the Dart equivalent of jQuery's
+   * [remove] and [removeAll] are the Dart equivalent of jQuery's
    * [removeClass](http://api.jquery.com/removeClass/).
+   *
+   * [value] must be a valid 'token' representing a single class, i.e. a
+   * non-empty string containing no whitespace.  To remove multiple classes, use
+   * [removeAll].
    */
   bool remove(Object value);
 
   /**
    * Add all classes specified in [iterable] to element.
    *
-   * This is the Dart equivalent of jQuery's
+   * [add] and [addAll] are the Dart equivalent of jQuery's
    * [addClass](http://api.jquery.com/addClass/).
+   *
+   * Each element of [iterable] must be a valid 'token' representing a single
+   * class, i.e. a non-empty string containing no whitespace.
    */
   void addAll(Iterable<String> iterable);
 
   /**
    * Remove all classes specified in [iterable] from element.
    *
-   * This is the Dart equivalent of jQuery's
+   * [remove] and [removeAll] are the Dart equivalent of jQuery's
    * [removeClass](http://api.jquery.com/removeClass/).
+   *
+   * Each element of [iterable] must be a valid 'token' representing a single
+   * class, i.e. a non-empty string containing no whitespace.
    */
   void removeAll(Iterable<String> iterable);
 
@@ -37369,6 +37390,9 @@
    * If [shouldAdd] is true, then we always add all the classes in [iterable]
    * element. If [shouldAdd] is false then we always remove all the classes in
    * [iterable] from the element.
+   *
+   * Each element of [iterable] must be a valid 'token' representing a single
+   * class, i.e. a non-empty string containing no whitespace.
    */
   void toggleAll(Iterable<String> iterable, [bool shouldAdd]);
 }
diff --git a/sdk/lib/html/html_common/css_class_set.dart b/sdk/lib/html/html_common/css_class_set.dart
index 0380d2d..9c4b443 100644
--- a/sdk/lib/html/html_common/css_class_set.dart
+++ b/sdk/lib/html/html_common/css_class_set.dart
@@ -6,6 +6,13 @@
 
 abstract class CssClassSetImpl implements CssClassSet {
 
+  static final RegExp _validTokenRE = new RegExp(r'^\S+$');
+
+  String _validateToken(String value) {
+    if (_validTokenRE.hasMatch(value)) return value;
+    throw new ArgumentError.value(value, 'value', 'Not a valid class token');
+  }
+
   String toString() {
     return readClasses().join(' ');
   }
@@ -18,6 +25,7 @@
    * [shouldAdd] is false then we always remove [value] from the element.
    */
   bool toggle(String value, [bool shouldAdd]) {
+    _validateToken(value);
     Set<String> s = readClasses();
     bool result = false;
     if (shouldAdd == null) shouldAdd = !s.contains(value);
@@ -81,7 +89,11 @@
    * This is the Dart equivalent of jQuery's
    * [hasClass](http://api.jquery.com/hasClass/).
    */
-  bool contains(Object value) => readClasses().contains(value);
+  bool contains(Object value) {
+    if (value is! String) return false;
+    _validateToken(value);
+    return readClasses().contains(value);
+  }
 
   /** Lookup from the Set interface. Not interesting for a String set. */
   String lookup(Object value) => contains(value) ? value : null;
@@ -93,6 +105,7 @@
    * [addClass](http://api.jquery.com/addClass/).
    */
   bool add(String value) {
+    _validateToken(value);
     // TODO - figure out if we need to do any validation here
     // or if the browser natively does enough.
     return modify((s) => s.add(value));
@@ -106,6 +119,7 @@
    * [removeClass](http://api.jquery.com/removeClass/).
    */
   bool remove(Object value) {
+    _validateToken(value);
     if (value is! String) return false;
     Set<String> s = readClasses();
     bool result = s.remove(value);
@@ -121,7 +135,7 @@
    */
   void addAll(Iterable<String> iterable) {
     // TODO - see comment above about validation.
-    modify((s) => s.addAll(iterable));
+    modify((s) => s.addAll(iterable.map(_validateToken)));
   }
 
   /**
@@ -131,7 +145,7 @@
    * [removeClass](http://api.jquery.com/removeClass/).
    */
   void removeAll(Iterable<Object> iterable) {
-    modify((s) => s.removeAll(iterable));
+    modify((s) => s.removeAll(iterable.map(_validateToken)));
   }
 
   /**
diff --git a/tests/html/element_classes_test.dart b/tests/html/element_classes_test.dart
index 14af4bd..2846d0d 100644
--- a/tests/html/element_classes_test.dart
+++ b/tests/html/element_classes_test.dart
@@ -129,6 +129,15 @@
     expect(makeClassSet().contains('qux'), isFalse);
   });
 
+  test('contains-bad', () {
+    // Non-strings return `false`.
+    // Strings need to be valid tokens.
+    final classes = makeClassSet();
+    expect(classes.contains(1), isFalse);
+    expect(() => classes.contains(''), throws);
+    expect(() => classes.contains('foo bar'), throws);
+  });
+
   test('add', () {
     final classes = makeClassSet();
     var added = classes.add('qux');
@@ -143,6 +152,12 @@
         reason: "The class set shouldn't have duplicate elements.");
   });
 
+  test('add-bad', () {
+    final classes = makeClassSet();
+    expect(() => classes.add(''), throws);
+    expect(() => classes.add('foo bar'), throws);
+  });
+
   test('remove', () {
     final classes = makeClassSet();
     classes.remove('bar');
@@ -151,6 +166,12 @@
     expect(classes, orderedEquals(['foo', 'baz']));
   });
 
+  test('remove-bad', () {
+    final classes = makeClassSet();
+    expect(() => classes.remove(''), throws);
+    expect(() => classes.remove('foo bar'), throws);
+  });
+
   test('toggle', () {
     final classes = makeClassSet();
     classes.toggle('bar');
@@ -168,6 +189,16 @@
     expect(classes, orderedEquals(['foo', 'baz', 'qux']));
   });
 
+  test('toggle-bad', () {
+    final classes = makeClassSet();
+    expect(() => classes.toggle(''), throws);
+    expect(() => classes.toggle('', true), throws);
+    expect(() => classes.toggle('', false), throws);
+    expect(() => classes.toggle('foo bar'), throws);
+    expect(() => classes.toggle('foo bar', true), throws);
+    expect(() => classes.toggle('foo bar', false), throws);
+  });
+
   test('addAll', () {
     final classes = makeClassSet();
     classes.addAll(['bar', 'qux', 'bip']);
diff --git a/tests/html/svgelement_test.dart b/tests/html/svgelement_test.dart
index 7ab3d5138..dcfeb4c 100644
--- a/tests/html/svgelement_test.dart
+++ b/tests/html/svgelement_test.dart
@@ -423,6 +423,33 @@
       classes.toggle('foo');
       expect(el.classes.length, 0);
     });
+
+    test('classes-add-bad', () {
+      var el = new svg.CircleElement();
+      expect(() => el.classes.add(''), throws);
+      expect(() => el.classes.add('foo bar'), throws);
+    });
+    test('classes-remove-bad', () {
+      var el = new svg.CircleElement();
+      expect(() => el.classes.remove(''), throws);
+      expect(() => el.classes.remove('foo bar'), throws);
+    });
+    test('classes-toggle-token', () {
+      var el = new svg.CircleElement();
+      expect(() => el.classes.toggle(''), throws);
+      expect(() => el.classes.toggle('', true), throws);
+      expect(() => el.classes.toggle('', false), throws);
+      expect(() => el.classes.toggle('foo bar'), throws);
+      expect(() => el.classes.toggle('foo bar', true), throws);
+      expect(() => el.classes.toggle('foo bar', false), throws);
+    });
+    test('classes-contains-bad', () {
+      var el = new svg.CircleElement();
+      // Non-strings => false, strings must be valid tokens.
+      expect(el.classes.contains(1), isFalse);
+      expect(() => el.classes.contains(''), throws);
+      expect(() => el.classes.contains('foo bar'), throws);
+    });
   });
 
   group('getBoundingClientRect', () {
diff --git a/tools/VERSION b/tools/VERSION
index 031f2b2..6359d8e 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -28,4 +28,4 @@
 MINOR 10
 PATCH 0
 PRERELEASE 1
-PRERELEASE_PATCH 8
+PRERELEASE_PATCH 9
diff --git a/tools/dom/src/CssClassSet.dart b/tools/dom/src/CssClassSet.dart
index 0c405e4..8224b77 100644
--- a/tools/dom/src/CssClassSet.dart
+++ b/tools/dom/src/CssClassSet.dart
@@ -19,6 +19,10 @@
    * operation.
    *
    * If this corresponds to many elements, `null` is always returned.
+   *
+   * [value] must be a valid 'token' representing a single class, i.e. a
+   * non-empty string containing no whitespace.  To toggle multiple classes, use
+   * [toggleAll].
    */
   bool toggle(String value, [bool shouldAdd]);
 
@@ -33,19 +37,26 @@
    *
    * This is the Dart equivalent of jQuery's
    * [hasClass](http://api.jquery.com/hasClass/).
+   *
+   * [value] must be a valid 'token' representing a single class, i.e. a
+   * non-empty string containing no whitespace.
    */
   bool contains(String value);
 
   /**
    * Add the class [value] to element.
    *
-   * This is the Dart equivalent of jQuery's
+   * [add] and [addAll] are the Dart equivalent of jQuery's
    * [addClass](http://api.jquery.com/addClass/).
    *
-   * If this corresponds to one element. Returns true if [value] was added to
-   * the set, otherwise false.
+   * If this CssClassSet corresponds to one element. Returns true if [value] was
+   * added to the set, otherwise false.
    *
    * If this corresponds to many elements, `null` is always returned.
+   *
+   * [value] must be a valid 'token' representing a single class, i.e. a
+   * non-empty string containing no whitespace.  To add multiple classes use
+   * [addAll].
    */
   bool add(String value);
 
@@ -53,24 +64,34 @@
    * Remove the class [value] from element, and return true on successful
    * removal.
    *
-   * This is the Dart equivalent of jQuery's
+   * [remove] and [removeAll] are the Dart equivalent of jQuery's
    * [removeClass](http://api.jquery.com/removeClass/).
+   *
+   * [value] must be a valid 'token' representing a single class, i.e. a
+   * non-empty string containing no whitespace.  To remove multiple classes, use
+   * [removeAll].
    */
   bool remove(Object value);
 
   /**
    * Add all classes specified in [iterable] to element.
    *
-   * This is the Dart equivalent of jQuery's
+   * [add] and [addAll] are the Dart equivalent of jQuery's
    * [addClass](http://api.jquery.com/addClass/).
+   *
+   * Each element of [iterable] must be a valid 'token' representing a single
+   * class, i.e. a non-empty string containing no whitespace.
    */
   void addAll(Iterable<String> iterable);
 
   /**
    * Remove all classes specified in [iterable] from element.
    *
-   * This is the Dart equivalent of jQuery's
+   * [remove] and [removeAll] are the Dart equivalent of jQuery's
    * [removeClass](http://api.jquery.com/removeClass/).
+   *
+   * Each element of [iterable] must be a valid 'token' representing a single
+   * class, i.e. a non-empty string containing no whitespace.
    */
   void removeAll(Iterable<String> iterable);
 
@@ -83,6 +104,9 @@
    * If [shouldAdd] is true, then we always add all the classes in [iterable]
    * element. If [shouldAdd] is false then we always remove all the classes in
    * [iterable] from the element.
+   *
+   * Each element of [iterable] must be a valid 'token' representing a single
+   * class, i.e. a non-empty string containing no whitespace.
    */
   void toggleAll(Iterable<String> iterable, [bool shouldAdd]);
 }
diff --git a/tools/dom/src/dart2js_CssClassSet.dart b/tools/dom/src/dart2js_CssClassSet.dart
index 783828b..cf5fa33 100644
--- a/tools/dom/src/dart2js_CssClassSet.dart
+++ b/tools/dom/src/dart2js_CssClassSet.dart
@@ -100,7 +100,7 @@
     _element.className = '';
   }
 
-  bool contains(String value) {
+  bool contains(Object value) {
     return _contains(_element, value);
   }
 
@@ -136,8 +136,8 @@
     _removeWhere(_element, test, false);
   }
 
-  static bool _contains(Element _element, String value) {
-    return _classListContains(_classListOf(_element), value);
+  static bool _contains(Element _element, Object value) {
+    return value is String && _classListContains(_classListOf(_element), value);
   }
 
   static bool _add(Element _element, String value) {