Refactor By toJson() to be spec-dependent.  (#158)

* Refactor command_processor to be defined at driver creation time. Allows separate command processors for different specs.

* Update By to not override toJson directly. Updated json_wire_spec to do this.

* Adds W3C complaint massage to JSON request params

* Dartfmt.

* Add by JSON converstion to web_element.
diff --git a/lib/src/sync/common.dart b/lib/src/sync/common.dart
index b0daef9..c1f1c69 100644
--- a/lib/src/sync/common.dart
+++ b/lib/src/sync/common.dart
@@ -64,10 +64,10 @@
 }
 
 class By {
-  final String _using;
-  final String _value;
+  final String using;
+  final String value;
 
-  const By(this._using, this._value);
+  const By(this.using, this.value);
 
   /// Returns an element whose ID attribute matches the search value.
   const By.id(String id) : this('id', id);
@@ -98,12 +98,10 @@
   /// Returns an element matching a CSS selector.
   const By.cssSelector(String cssSelector) : this('css selector', cssSelector);
 
-  Map<String, String> toJson() => {'using': _using, 'value': _value};
-
   @override
   String toString() {
     var constructor;
-    switch (_using) {
+    switch (using) {
       case 'link text':
         constructor = 'linkText';
         break;
@@ -120,15 +118,15 @@
         constructor = 'cssSelector';
         break;
       default:
-        constructor = _using;
+        constructor = using;
     }
-    return 'By.$constructor($_value)';
+    return 'By.$constructor($value)';
   }
 
   @override
-  int get hashCode => _using.hashCode * 3 + _value.hashCode;
+  int get hashCode => using.hashCode * 3 + value.hashCode;
 
   @override
   bool operator ==(other) =>
-      other is By && other._using == this._using && other._value == this._value;
+      other is By && other.using == this.using && other.value == this.value;
 }
diff --git a/lib/src/sync/json_wire_spec/by.dart b/lib/src/sync/json_wire_spec/by.dart
new file mode 100644
index 0000000..0dd04c5
--- /dev/null
+++ b/lib/src/sync/json_wire_spec/by.dart
@@ -0,0 +1,18 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import '../common.dart' show By;
+
+/// Conver [By] instances into JSON params.
+Map<String, String> byToJson(By by) => {'using': by.using, 'value': by.value};
diff --git a/lib/src/sync/json_wire_spec/web_driver.dart b/lib/src/sync/json_wire_spec/web_driver.dart
index 4cf31fb..b1cd59d 100644
--- a/lib/src/sync/json_wire_spec/web_driver.dart
+++ b/lib/src/sync/json_wire_spec/web_driver.dart
@@ -15,6 +15,7 @@
 import 'dart:convert' show BASE64;
 import 'package:stack_trace/stack_trace.dart' show Chain;
 
+import 'by.dart' show byToJson;
 import 'keyboard.dart';
 import 'logs.dart';
 import 'mouse.dart';
@@ -73,7 +74,7 @@
 
   @override
   List<WebElement> findElements(By by) {
-    final elements = postRequest('elements', by);
+    final elements = postRequest('elements', byToJson(by));
     int i = 0;
 
     final webElements = new List<JsonWireWebElement>();
@@ -86,7 +87,7 @@
 
   @override
   WebElement findElement(By by) {
-    final element = postRequest('element', by);
+    final element = postRequest('element', byToJson(by));
     return new JsonWireWebElement(this, element[elementStr], this, by);
   }
 
diff --git a/lib/src/sync/json_wire_spec/web_element.dart b/lib/src/sync/json_wire_spec/web_element.dart
index 29e7b87..c36ebfc 100644
--- a/lib/src/sync/json_wire_spec/web_element.dart
+++ b/lib/src/sync/json_wire_spec/web_element.dart
@@ -14,6 +14,7 @@
 
 import 'dart:math' show Point, Rectangle;
 
+import 'by.dart' show byToJson;
 import '../common.dart';
 import '../web_driver.dart';
 import '../web_element.dart';
@@ -101,13 +102,13 @@
 
   @override
   WebElement findElement(By by) {
-    final element = _resolver.post('element', by);
+    final element = _resolver.post('element', byToJson(by));
     return new JsonWireWebElement(driver, element[elementStr], this, by);
   }
 
   @override
   List<WebElement> findElements(By by) {
-    final elements = _resolver.post('elements', by) as Iterable;
+    final elements = _resolver.post('elements', byToJson(by)) as Iterable;
     int i = 0;
     final webElements = new List<WebElement>();
     for (final element in elements) {
diff --git a/lib/src/sync/w3c_spec/by.dart b/lib/src/sync/w3c_spec/by.dart
new file mode 100644
index 0000000..74bca6a
--- /dev/null
+++ b/lib/src/sync/w3c_spec/by.dart
@@ -0,0 +1,41 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import '../common.dart' show By;
+
+/// Here we massage [By] instances into viable W3C /element requests.
+///
+/// In principle, W3C spec implementations should be nearly the same as
+/// the existing JSON wire spec. In practice compliance is uneven.
+Map<String, String> byToJson(By by) {
+  var using;
+  var value;
+
+  switch (by.using) {
+    case 'id': // This doesn't exist in the W3C spec.
+      using = 'css selector';
+      value = '#${by.value}';
+      break;
+    case 'tag name': // This is in the W3C spec, but not in geckodriver.
+      using = 'css selector';
+      value = by.value;
+      break;
+    // xpath, css selector, link text, partial link text, seem fine.
+    default:
+      using = by.using;
+      value = by.value;
+  }
+
+  return {'using': using, 'value': value};
+}
diff --git a/lib/src/sync/w3c_spec/web_driver.dart b/lib/src/sync/w3c_spec/web_driver.dart
index a8bb391..4efa539 100644
--- a/lib/src/sync/w3c_spec/web_driver.dart
+++ b/lib/src/sync/w3c_spec/web_driver.dart
@@ -15,6 +15,7 @@
 import 'dart:convert' show BASE64;
 import 'package:stack_trace/stack_trace.dart' show Chain;
 
+import 'by.dart' show byToJson;
 import 'target_locator.dart';
 import 'timeouts.dart';
 import 'web_element.dart';
@@ -75,7 +76,7 @@
   // TODO(staats): unify find logic with that in web_element.
   @override
   List<WebElement> findElements(By by) {
-    final elements = postRequest('elements', by);
+    final elements = postRequest('elements', byToJson(by));
     int i = 0;
 
     final webElements = new List<W3cWebElement>();
@@ -88,7 +89,7 @@
 
   @override
   WebElement findElement(By by) {
-    final element = postRequest('element', by);
+    final element = postRequest('element', byToJson(by));
     return new W3cWebElement(this, element[elementStr], this, by);
   }
 
diff --git a/lib/src/sync/w3c_spec/web_element.dart b/lib/src/sync/w3c_spec/web_element.dart
index 0999881..764d415 100644
--- a/lib/src/sync/w3c_spec/web_element.dart
+++ b/lib/src/sync/w3c_spec/web_element.dart
@@ -14,6 +14,8 @@
 
 import 'dart:math' show Point, Rectangle;
 
+import 'by.dart' show byToJson;
+
 import '../common.dart';
 import '../web_driver.dart';
 import '../web_element.dart';
@@ -97,13 +99,13 @@
 
   @override
   WebElement findElement(By by) {
-    final element = _resolver.post('element', by);
+    final element = _resolver.post('element', byToJson(by));
     return new W3cWebElement(driver, element[elementStr], this, by);
   }
 
   @override
   List<WebElement> findElements(By by) {
-    final elements = _resolver.post('elements', by) as Iterable;
+    final elements = _resolver.post('elements', byToJson(by)) as Iterable;
     int i = 0;
     final webElements = new List<WebElement>();
     for (final element in elements) {