Merge find element logic for W3C spec.  (#159)

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

* Refactors out element finder logic for WebDriver and WebElement to common location for W3C spec.

*  Dartfmt.
diff --git a/lib/src/sync/w3c_spec/by.dart b/lib/src/sync/w3c_spec/by.dart
deleted file mode 100644
index 74bca6a..0000000
--- a/lib/src/sync/w3c_spec/by.dart
+++ /dev/null
@@ -1,41 +0,0 @@
-// 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/element_finder.dart b/lib/src/sync/w3c_spec/element_finder.dart
new file mode 100644
index 0000000..5d18062
--- /dev/null
+++ b/lib/src/sync/w3c_spec/element_finder.dart
@@ -0,0 +1,71 @@
+// 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 'web_element.dart';
+
+import '../common.dart';
+import '../web_driver.dart';
+import '../web_element.dart';
+
+/// Handles logic for finding elements in both WebDriver and element contexts.
+class ElementFinder {
+  final WebDriver _driver;
+  final SearchContext _context;
+  final Resolver _resolver;
+
+  ElementFinder(this._driver, this._resolver, this._context);
+
+  /// 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};
+  }
+
+  List<WebElement> findElements(By by) => _findElements(by, 'elements');
+
+  WebElement findElement(By by) => _findElements(by, 'element')[0];
+
+  List<String> _unwrapReponse(Map<String, String> response) => response.values;
+
+  List<WebElement> _findElements(By by, String command) {
+    final elements = _unwrapReponse(_resolver.post(command, _byToJson(by)));
+    int i = 0;
+
+    final webElements = new List<W3cWebElement>();
+    for (final element in elements) {
+      webElements.add(new W3cWebElement(_driver, element, _context, by, i++));
+    }
+    return webElements;
+  }
+}
diff --git a/lib/src/sync/w3c_spec/web_driver.dart b/lib/src/sync/w3c_spec/web_driver.dart
index 4efa539..62e6eb2 100644
--- a/lib/src/sync/w3c_spec/web_driver.dart
+++ b/lib/src/sync/w3c_spec/web_driver.dart
@@ -15,7 +15,7 @@
 import 'dart:convert' show BASE64;
 import 'package:stack_trace/stack_trace.dart' show Chain;
 
-import 'by.dart' show byToJson;
+import 'element_finder.dart';
 import 'target_locator.dart';
 import 'timeouts.dart';
 import 'web_element.dart';
@@ -45,6 +45,7 @@
   final String id;
   final Uri uri;
   final bool filterStackTraces;
+  ElementFinder _finder;
 
   @override
   bool notifyListeners = true;
@@ -55,7 +56,9 @@
       {this.filterStackTraces: true})
       : this.uri = uri,
         this.id = id,
-        this._prefix = uri.resolve('session/$id/');
+        this._prefix = uri.resolve('session/$id/') {
+    _finder = new ElementFinder(this, new Resolver(driver, ''), this);
+  }
 
   @override
   void addEventListener(WebDriverListener listener) =>
@@ -73,25 +76,11 @@
   @override
   String get title => getRequest('title') as String;
 
-  // TODO(staats): unify find logic with that in web_element.
   @override
-  List<WebElement> findElements(By by) {
-    final elements = postRequest('elements', byToJson(by));
-    int i = 0;
-
-    final webElements = new List<W3cWebElement>();
-    for (final element in elements) {
-      webElements
-          .add(new W3cWebElement(this, element[elementStr], this, by, i++));
-    }
-    return webElements;
-  }
+  List<WebElement> findElements(By by) => _finder.findElements(by);
 
   @override
-  WebElement findElement(By by) {
-    final element = postRequest('element', byToJson(by));
-    return new W3cWebElement(this, element[elementStr], this, by);
-  }
+  WebElement findElement(By by) => _finder.findElement(by);
 
   @override
   String get pageSource => getRequest('source') as String;
diff --git a/lib/src/sync/w3c_spec/web_element.dart b/lib/src/sync/w3c_spec/web_element.dart
index 764d415..ac90fcb 100644
--- a/lib/src/sync/w3c_spec/web_element.dart
+++ b/lib/src/sync/w3c_spec/web_element.dart
@@ -14,7 +14,7 @@
 
 import 'dart:math' show Point, Rectangle;
 
-import 'by.dart' show byToJson;
+import 'element_finder.dart';
 
 import '../common.dart';
 import '../web_driver.dart';
@@ -23,6 +23,7 @@
 class W3cWebElement implements WebElement, SearchContext {
   final String _elementPrefix;
   final Resolver _resolver;
+  ElementFinder _finder;
 
   @override
   final String id;
@@ -42,7 +43,9 @@
   W3cWebElement(this.driver, id, [this.context, this.locator, this.index])
       : this.id = id,
         _elementPrefix = 'element/$id',
-        _resolver = new Resolver(driver, 'element/$id');
+        _resolver = new Resolver(driver, 'element/$id') {
+    _finder = new ElementFinder(driver, _resolver, this);
+  }
 
   @override
   void click() => _resolver.post('click');
@@ -98,22 +101,10 @@
   String get text => _resolver.get('text') as String;
 
   @override
-  WebElement findElement(By by) {
-    final element = _resolver.post('element', byToJson(by));
-    return new W3cWebElement(driver, element[elementStr], this, by);
-  }
+  WebElement findElement(By by) => _finder.findElement(by);
 
   @override
-  List<WebElement> findElements(By by) {
-    final elements = _resolver.post('elements', byToJson(by)) as Iterable;
-    int i = 0;
-    final webElements = new List<WebElement>();
-    for (final element in elements) {
-      webElements
-          .add(new W3cWebElement(driver, element[elementStr], this, by, i++));
-    }
-    return webElements;
-  }
+  List<WebElement> findElements(By by) => _finder.findElements(by);
 
   @override
   Attributes get attributes =>