Refactor command_processor to be defined at driver creation time.  (#154)

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

* Updated with fixes for pull request comments.
diff --git a/lib/src/sync/json_wire_spec/command_processor.dart b/lib/src/sync/json_wire_spec/command_processor.dart
new file mode 100644
index 0000000..cc528ad
--- /dev/null
+++ b/lib/src/sync/json_wire_spec/command_processor.dart
@@ -0,0 +1,85 @@
+// 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 'dart:convert' show JSON;
+import 'dart:io' show ContentType, HttpHeaders;
+
+import 'package:sync_http/sync_http.dart';
+
+import '../command_processor.dart';
+import '../exception.dart' show WebDriverException;
+
+final ContentType _contentTypeJson =
+  new ContentType("application", "json", charset: "utf-8");
+
+class JsonWireCommandProcessor implements CommandProcessor {
+
+  @override
+  dynamic post(Uri uri, dynamic params, {bool value: true}) {
+    final request = SyncHttpClient.postUrl(uri);
+    _setUpRequest(request);
+    request.headers.contentType = _contentTypeJson;
+    if (params != null) {
+      var body = JSON.encode(params); // Cannot be changed from UTF8.
+      request.write(body);
+    }
+    return _processResponse(request.close(), value);
+  }
+
+  @override
+  dynamic get(Uri uri, {bool value: true}) {
+    final request = SyncHttpClient.getUrl(uri);
+    _setUpRequest(request);
+    return _processResponse(request.close(), value);
+  }
+
+  @override
+  dynamic delete(Uri uri, {bool value: true}) {
+    final request = SyncHttpClient.deleteUrl(uri);
+    _setUpRequest(request);
+    return _processResponse(request.close(), value);
+  }
+
+  @override
+  void close() {}
+
+  _processResponse(SyncHttpClientResponse response, bool value) {
+    Map responseBody;
+    try {
+      responseBody = JSON.decode(response.body);
+    } catch (e) {}
+
+    if (response.statusCode < 200 ||
+        response.statusCode > 299 ||
+        (responseBody is Map &&
+            responseBody['status'] != null &&
+            responseBody['status'] != 0)) {
+      throw new WebDriverException(
+          httpStatusCode: response.statusCode,
+          httpReasonPhrase: response.reasonPhrase,
+          jsonResp: responseBody);
+    }
+    if (value && responseBody is Map) {
+      return responseBody['value'];
+    }
+    return responseBody;
+  }
+
+  void _setUpRequest(SyncHttpClientRequest request) {
+    // TODO(staats): Follow redirects.
+    request.headers.add(HttpHeaders.ACCEPT, "application/json");
+    request.headers.add(HttpHeaders.CACHE_CONTROL, "no-cache");
+  }
+}
diff --git a/lib/sync_core.dart b/lib/sync_core.dart
index 7f4ec3f..b380997 100644
--- a/lib/sync_core.dart
+++ b/lib/sync_core.dart
@@ -17,19 +17,20 @@
 import 'dart:collection' show UnmodifiableMapView;
 
 import 'package:webdriver/src/sync/capabilities.dart' show Capabilities;
-import 'package:webdriver/src/sync/command_processor.dart'
-    show CommandProcessor;
 import 'package:webdriver/src/sync/web_driver.dart' show WebDriver;
+
+import 'package:webdriver/src/sync/json_wire_spec/command_processor.dart'
+  as jwireCommand;
 import 'package:webdriver/src/sync/json_wire_spec/web_driver.dart' as jwire;
 
 export 'package:webdriver/src/sync/alert.dart';
 export 'package:webdriver/src/sync/capabilities.dart';
 export 'package:webdriver/src/sync/command_event.dart';
-export 'package:webdriver/src/sync/command_processor.dart';
 export 'package:webdriver/src/sync/common.dart';
 export 'package:webdriver/src/sync/common_spec/cookies.dart';
 export 'package:webdriver/src/sync/common_spec/navigation.dart';
 export 'package:webdriver/src/sync/exception.dart';
+export 'package:webdriver/src/sync/json_wire_spec/command_processor.dart';
 export 'package:webdriver/src/sync/json_wire_spec/keyboard.dart';
 export 'package:webdriver/src/sync/json_wire_spec/logs.dart';
 export 'package:webdriver/src/sync/json_wire_spec/mouse.dart';
@@ -43,8 +44,7 @@
 
 //TODO(staats): when W3C spec created, infer spec during WebDriver creation.
 
-WebDriver createDriver(CommandProcessor processor,
-    {Uri uri, Map<String, dynamic> desired}) {
+WebDriver createDriver({Uri uri, Map<String, dynamic> desired}) {
   if (uri == null) {
     uri = defaultUri;
   }
@@ -53,6 +53,7 @@
     desired = Capabilities.empty;
   }
 
+  final processor = new jwireCommand.JsonWireCommandProcessor();
   Map response = processor.post(
       uri.resolve('session'), {'desiredCapabilities': desired},
       value: false) as Map<String, dynamic>;
@@ -60,14 +61,15 @@
       new UnmodifiableMapView(response['value'] as Map<String, dynamic>));
 }
 
-WebDriver fromExistingSession(CommandProcessor processor, String sessionId,
+WebDriver fromExistingSession(String sessionId,
     {Uri uri}) {
   if (uri == null) {
     uri = defaultUri;
   }
 
+  final processor = new jwireCommand.JsonWireCommandProcessor();
   var response =
       processor.get(uri.resolve('session/$sessionId')) as Map<String, dynamic>;
-  return new jwire.JsonWireWebDriver(
-      processor, uri, sessionId, new UnmodifiableMapView(response));
+  return new jwire.JsonWireWebDriver(processor,
+      uri, sessionId, new UnmodifiableMapView(response));
 }
diff --git a/lib/sync_io.dart b/lib/sync_io.dart
index 7aabdbc..52f3759 100644
--- a/lib/sync_io.dart
+++ b/lib/sync_io.dart
@@ -14,15 +14,10 @@
 
 library webdriver.sync_io;
 
-import 'dart:convert' show JSON;
 import 'dart:io' show ContentType, HttpHeaders;
 
-import 'package:sync_http/sync_http.dart';
 import 'package:webdriver/sync_core.dart' as core
     show createDriver, fromExistingSession, WebDriver;
-import 'package:webdriver/src/sync/command_processor.dart'
-    show CommandProcessor;
-import 'package:webdriver/src/sync/exception.dart' show WebDriverException;
 
 export 'package:webdriver/sync_core.dart'
     hide createDriver, fromExistingSession;
@@ -33,7 +28,7 @@
 /// [uri]. Therefore, if [uri] does not end with a trailing slash, the
 /// last path component will be dropped.
 core.WebDriver createDriver({Uri uri, Map<String, dynamic> desired}) =>
-    core.createDriver(new IOCommandProcessor(), uri: uri, desired: desired);
+    core.createDriver(uri: uri, desired: desired);
 
 /// Creates a WebDriver instance connected to an existing session.
 ///
@@ -41,68 +36,8 @@
 /// [uri]. Therefore, if [uri] does not end with a trailing slash, the
 /// last path component will be dropped.
 core.WebDriver fromExistingSession(String sessionId, {Uri uri}) =>
-    core.fromExistingSession(new IOCommandProcessor(), sessionId, uri: uri);
+    core.fromExistingSession(sessionId, uri: uri);
 
 final ContentType _contentTypeJson =
     new ContentType("application", "json", charset: "utf-8");
 
-class IOCommandProcessor implements CommandProcessor {
-  @override
-  dynamic post(Uri uri, dynamic params, {bool value: true}) {
-    final request = SyncHttpClient.postUrl(uri);
-    _setUpRequest(request);
-    request.headers.contentType = _contentTypeJson;
-    if (params != null) {
-      var body = JSON.encode(params); // Cannot be changed from UTF8.
-      request.write(body);
-    }
-    return _processResponse(request.close(), value);
-  }
-
-  @override
-  dynamic get(Uri uri, {bool value: true}) {
-    final request = SyncHttpClient.getUrl(uri);
-    _setUpRequest(request);
-    return _processResponse(request.close(), value);
-  }
-
-  @override
-  dynamic delete(Uri uri, {bool value: true}) {
-    final request = SyncHttpClient.deleteUrl(uri);
-    _setUpRequest(request);
-    return _processResponse(request.close(), value);
-  }
-
-  @override
-  void close() {}
-
-  _processResponse(SyncHttpClientResponse response, bool value) {
-    final respDecoded = response.body;
-    Map respBody;
-    try {
-      respBody = JSON.decode(respDecoded);
-    } catch (e) {}
-
-    // TODO(staats): Update to infer protocols.
-    if (response.statusCode < 200 ||
-        response.statusCode > 299 ||
-        (respBody is Map &&
-            respBody['status'] != null &&
-            respBody['status'] != 0)) {
-      throw new WebDriverException(
-          httpStatusCode: response.statusCode,
-          httpReasonPhrase: response.reasonPhrase,
-          jsonResp: respBody);
-    }
-    if (value && respBody is Map) {
-      return respBody['value'];
-    }
-    return respBody;
-  }
-
-  void _setUpRequest(SyncHttpClientRequest request) {
-    // TODO(staats): Follow redirects.
-    request.headers.add(HttpHeaders.ACCEPT, "application/json");
-    request.headers.add(HttpHeaders.CACHE_CONTROL, "no-cache");
-  }
-}