diff --git a/README.md b/README.md
index 742f30f..52a0e5f 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
 php-webdriver -- A very thin wrapper of WebDriver
 =================================================
 
-## DESCRIPTION
+##  DESCRIPTION
 
 This client aims to be as thin as possible, abusing the dynamic nature of PHP to allow almost all API calls to be a direct transformation of what is defined in the WebDriver protocol itself.
 
@@ -9,49 +9,159 @@ Most clients require you to first read the protocol to see what's possible, then
 
 Each command is just the name of a function call, and each additional path is just another chained function call.  The function parameter is then either an array() if the command takes JSON parameters, or an individual primitive if it takes a URL parameter.
 
-## SIMPLE EXAMPLES
+The function's return value is exactly what is returned from the server as part of the protocol definition.  If an error is returned, the function will throw the appropriate WebDriverException instance.
+
+##  GETTING STARTED
+
+*   All you need as the server for this client is the selenium-server-standalone-#.jar file provided here:  http://code.google.com/p/selenium/downloads/list
+
+*   Download and run that file, replacing # with the current server version.
+
+        java -jar selenium-server-standalone-#.jar
+
+*   Then when you create a session, be sure to pass the url to where your server is running.
+
+        // This would be the url of the host running the server-standalone.jar
+        $wd_host = 'http://localhost:4444/wd/hub'; // this is the default
+        $web_driver = new WebDriver($wd_host);
+
+        // First param to session() is the 'browserName' (default = 'firefox')
+        // Second param is a JSON object of additional 'desiredCapabilities'
+
+        // POST /session
+        $session = $web_driver->session('firefox');
+
+* See also [wiki page for launching different browsers](https://github.com/facebook/php-webdriver/wiki/Launching-Browsers).
+
+##  SIMPLE EXAMPLES
+
 ### Note that all of these match the Protocol exactly
+*   Move to a specific spot on the screen
+
+        // POST /session/:sessionId/moveto
+        $session->moveto(array('xoffset' => 3, 'yoffset' => 300));
+
+*   Get the current url
+
+        // GET /session/:sessionId/url
+        $session->url();
+
+*   Change focus to another frame
 
-* Get a session (opens a new browser window)
+        // POST /session/:sessionId/frame
+        $session->frame(array('id' => 'some_frame_id'));
 
-  $web_driver = new WebDriver(); // could pass a host besides localhost
+*   Get a list of window handles for all open windows
 
-  $session = $web_driver->session(); // could pass a browser name
+        // GET /session/:sessionId/window_handles
+        $session->window_handles();
 
-* Move to a specific spot on the screen
+*   Accept the currently displayed alert dialog
 
-  $session->moveto(array('xoffset' => 3, 'yoffset' => 300));
+        // POST /session/:sessionId/accept_alert
+        $session->accept_alert();
 
-* Change asynchronous script timeout
+*   Change asynchronous script timeout
 
-  $session->timeouts()->async_script(array('ms' => 2000));
+        // POST /session/:sessionId/timeouts/async_script
+        $session->timeouts()->async_script(array('ms' => 2000));
 
-* Touch screen
+*   Doubleclick an element on a touch screen
 
-  $session->touch()->scroll($element->getID())
+        // POST session/:sessionId/touch/doubleclick
+        $session->touch()->doubleclick(array('element' => $element->getID())
 
-* Check if two elements are equal
+*   Check if two elements are equal
 
-  $element->equals($other_element->getID()))
+        // GET /session/:sessionId/element/:id/equals/:other
+        $element->equals($other_element->getID()))
 
-* Get value of css property on element
+*   Get value of a css property on element
 
-  $element->css($property_name)
+        // GET /session/:sessionId/element/:id/css/:propertyName
+        $element->css($property_name)
 
 ## 'GET', 'POST', or 'DELETE' to the same command examples
 
-### If you can do multiple http methods for the same command, such as 'orientation', where 'POST' changes the orientation but 'GET' fetches it, call the command directly for the getter, and prepend the http method for the writers.
+### When you can do multiple http methods for the same command, call the command directly for the 'GET', and prepend the http method for the 'POST' or 'DELETE'.
+
+*   Set landscape orientation with 'POST'
+
+        // POST /session/:sessionId/orientation
+        $session->postOrientation(array('orientation' => 'LANDSCAPE'));
+
+*   Get landscape orientation with normal 'GET'
+
+        // GET /session/:sessionId/orientation
+        $session->orientation();
+
+*   Set size of window that has $window_handle with 'POST'
+
+        // If excluded, $window_handle defaults to 'current'
+        // POST /session/:sessionId/window/:windowHandle/size
+        $session
+          ->window($window_handle)
+          ->postSize(array('width' => 10, 'height' => 10));
+
+*   Get current window size with 'GET'
+
+        // GET /session/:sessionId/window/:windowHandle/size
+        $session->window()->size();
+
+## Some unavoidable exceptions to direct protocol translation.
+
+*   Opening pages
+
+        // POST /session/:sessionId/url
+        $session->open('http://www.facebook.com');
+
+*   Dealing with the session
+
+        // DELETE /session/:sessionId
+        $session->close();
+
+        // GET /session/:sessionId
+        $session->capabilities();
+        
+*   To find elements
+
+        // POST /session/:sessionId/element
+        $element = $session->element($using, $value);
+
+        // POST /session/:sessionId/elements
+        $session->elements($using, $value);
+
+        // POST /session/:sessionId/element/:id/element
+        $element->element($using, $value);
+
+        // POST /session/:sessionId/element/:id/elements
+        $element->elements($using, $value);
+
+*   To get the active element
+
+        // POST /session/:sessionId/element/active
+        $session->activeElement();
+
+*   To manipulate cookies
+
+        // GET /session/:sessionId/cookie
+        $session->getAllCookies();
+
+        // POST /session/:sessionId/cookie
+        $session->setCookie($cookie_json);
 
-* Set landscape orientation
+        // DELETE /session/:sessionId/cookie
+        $session->deleteAllCookies()
 
-  $session->postOrientation(array('orientation' => 'LANDSCAPE'));
+        // DELETE /session/:sessionId/cookie/:name
+        $session->deleteCookie($name)
 
-* Get landscape orientation
+*   To manipulate windows
 
-  $session->orientation();
+        // POST /session/:sessionId/window
+        $session->focusWindow($window_handle);
 
-## A few Element/Cookie/Session convenience exceptions.
+        // DELETE /session/:sessionId/window
+        $session->deleteWindow();
 
-* To find elements, use element($using, $value) and elements($using, $value), where $using is the locator strategy ('xpath', 'id', 'name', etc)
-* Use getAllCookies(), setCookie($cookie_json), deleteAllCookies(), and deleteCookie($name)
-* Visit pages with open(), close the browser window with close()
+### See also [wiki page of examples](https://github.com/facebook/php-webdriver/wiki/Example-command-reference).
\ No newline at end of file
diff --git a/WebDriver.php b/WebDriver.php
index 6de232c..a2dbbc2 100644
--- a/WebDriver.php
+++ b/WebDriver.php
@@ -20,8 +20,10 @@ protected function methods() {
     );
   }
 
-  public function session($browser = 'firefox',
-                          $additional_capabilities = array()) {
+  public function session(
+    $browser = 'firefox',
+    $additional_capabilities = array()) {
+
     $desired_capabilities = array_merge(
       $additional_capabilities,
       array('browserName' => $browser));
@@ -34,4 +36,14 @@ public function session($browser = 'firefox',
 
     return new WebDriverSession($results['info']['url']);
   }
+
+  public function sessions() {
+    $result = $this->curl('GET', '/sessions');
+    $sessions = array();
+    foreach ($result['value'] as $session) {
+      $sessions[] = new WebDriverSession(
+        $this->url . '/session/' . $session['id']);
+    }
+    return $sessions;
+  }
 }
diff --git a/WebDriverBase.php b/WebDriverBase.php
index f7555c7..6d17fc5 100644
--- a/WebDriverBase.php
+++ b/WebDriverBase.php
@@ -15,75 +15,75 @@
 
 abstract class WebDriverBase {
 
-  public static function throwException($status_code, $message) {
+  public static function throwException($status_code, $message, $results) {
     switch ($status_code) {
       case 0:
         // Success
         break;
       case 1:
-        throw new IndexOutOfBoundsWebDriverError($message);
+        throw new IndexOutOfBoundsWebDriverError($message, $results);
       case 2:
-        throw new NoCollectionWebDriverError($message);
+        throw new NoCollectionWebDriverError($message, $results);
       case 3:
-        throw new NoStringWebDriverError($message);
+        throw new NoStringWebDriverError($message, $results);
       case 4:
-        throw new NoStringLengthWebDriverError($message);
+        throw new NoStringLengthWebDriverError($message, $results);
       case 5:
-        throw new NoStringWrapperWebDriverError($message);
+        throw new NoStringWrapperWebDriverError($message, $results);
       case 6:
-        throw new NoSuchDriverWebDriverError($message);
+        throw new NoSuchDriverWebDriverError($message, $results);
       case 7:
-        throw new NoSuchElementWebDriverError($message);
+        throw new NoSuchElementWebDriverError($message, $results);
       case 8:
-        throw new NoSuchFrameWebDriverError($message);
+        throw new NoSuchFrameWebDriverError($message, $results);
       case 9:
-        throw new UnknownCommandWebDriverError($message);
+        throw new UnknownCommandWebDriverError($message, $results);
       case 10:
-        throw new ObsoleteElementWebDriverError($message);
+        throw new ObsoleteElementWebDriverError($message, $results);
       case 11:
-        throw new ElementNotDisplayedWebDriverError($message);
+        throw new ElementNotDisplayedWebDriverError($message, $results);
       case 12:
-        throw new InvalidElementStateWebDriverError($message);
+        throw new InvalidElementStateWebDriverError($message, $results);
       case 13:
-        throw new UnhandledWebDriverError($message);
+        throw new UnhandledWebDriverError($message, $results);
       case 14:
-        throw new ExpectedWebDriverError($message);
+        throw new ExpectedWebDriverError($message, $results);
       case 15:
-        throw new ElementNotSelectableWebDriverError($message);
+        throw new ElementNotSelectableWebDriverError($message, $results);
       case 16:
-        throw new NoSuchDocumentWebDriverError($message);
+        throw new NoSuchDocumentWebDriverError($message, $results);
       case 17:
-        throw new UnexpectedJavascriptWebDriverError($message);
+        throw new UnexpectedJavascriptWebDriverError($message, $results);
       case 18:
-        throw new NoScriptResultWebDriverError($message);
+        throw new NoScriptResultWebDriverError($message, $results);
       case 19:
-        throw new XPathLookupWebDriverError($message);
+        throw new XPathLookupWebDriverError($message, $results);
       case 20:
-        throw new NoSuchCollectionWebDriverError($message);
+        throw new NoSuchCollectionWebDriverError($message, $results);
       case 21:
-        throw new TimeOutWebDriverError($message);
+        throw new TimeOutWebDriverError($message, $results);
       case 22:
-        throw new NullPointerWebDriverError($message);
+        throw new NullPointerWebDriverError($message, $results);
       case 23:
-        throw new NoSuchWindowWebDriverError($message);
+        throw new NoSuchWindowWebDriverError($message, $results);
       case 24:
-        throw new InvalidCookieDomainWebDriverError($message);
+        throw new InvalidCookieDomainWebDriverError($message, $results);
       case 25:
-        throw new UnableToSetCookieWebDriverError($message);
+        throw new UnableToSetCookieWebDriverError($message, $results);
       case 26:
-        throw new UnexpectedAlertOpenWebDriverError($message);
+        throw new UnexpectedAlertOpenWebDriverError($message, $results);
       case 27:
-        throw new NoAlertOpenWebDriverError($message);
+        throw new NoAlertOpenWebDriverError($message, $results);
       case 28:
-        throw new ScriptTimeoutWebDriverError($message);
+        throw new ScriptTimeoutWebDriverError($message, $results);
       case 29:
-        throw new InvalidElementCoordinatesWebDriverError($message);
+        throw new InvalidElementCoordinatesWebDriverError($message, $results);
       case 30:
-        throw new IMENotAvailableWebDriverError($message);
+        throw new IMENotAvailableWebDriverError($message, $results);
       case 31:
-        throw new IMEEngineActivationFailedWebDriverError($message);
+        throw new IMEEngineActivationFailedWebDriverError($message, $results);
       case 32:
-        throw new InvalidSelectorWebDriverError($message);
+        throw new InvalidSelectorWebDriverError($message, $results);
     }
   }
 
@@ -110,10 +110,12 @@ public function getURL() {
    *               If a number or string, "/$params" is appended to url
    * $extra_opts   key=>value pairs of curl options to pass to curl_setopt()
    */
-  protected function curl($http_method,
-                          $command,
-                          $params = null,
-                          $extra_opts = array()) {
+  protected function curl(
+    $http_method,
+    $command,
+    $params = null,
+    $extra_opts = array()) {
+
     if ($params && is_array($params) && $http_method !== 'POST') {
       throw new Exception(sprintf(
         'The http method called for %s is %s but it has to be POST' .
@@ -129,10 +131,13 @@ protected function curl($http_method,
     }
 
     $curl = curl_init($url);
-    curl_setopt($curl, CURLOPT_TIMEOUT, 60);
     curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
-    curl_setopt($curl, CURLOPT_HTTPHEADER,
-                array('application/json;charset=UTF-8'));
+    curl_setopt(
+      $curl,
+      CURLOPT_HTTPHEADER,
+      array(
+        'Content-Type: application/json;charset=UTF-8',
+        'Accept: application/json'));
 
     if ($http_method === 'POST') {
       curl_setopt($curl, CURLOPT_POST, true);
@@ -174,7 +179,7 @@ protected function curl($http_method,
       $message = $value['message'];
     }
 
-    self::throwException($results['status'], $message);
+    self::throwException($results['status'], $message, $results);
 
     return array('value' => $value, 'info' => $info);
   }
diff --git a/WebDriverContainer.php b/WebDriverContainer.php
index d74f88c..9787ae7 100644
--- a/WebDriverContainer.php
+++ b/WebDriverContainer.php
@@ -27,7 +27,8 @@ public function element($using, $value) {
         sprintf(
           'Element not found with %s, %s',
           $using,
-          $value) . "\n\n" . $e->getMessage());
+          $value) . "\n\n" . $e->getMessage(),
+        $e->getResults());
     }
 
     return $this->webDriverElement($results['value']);
@@ -46,11 +47,11 @@ public function elements($using, $value) {
       array($this, 'webDriverElement'), $results['value']));
   }
 
-  private function webDriverElement($value) {
-    return (array_key_exists('ELEMENT', (array) $value)) ?
+  protected function webDriverElement($value) {
+    return array_key_exists('ELEMENT', (array) $value) ?
       new WebDriverElement(
-        $this->getElementPath($value['ELEMENT']),
-        $value['ELEMENT']) :
+        $this->getElementPath($value['ELEMENT']), // url
+        $value['ELEMENT']) : // id
       null;
   }
 
diff --git a/WebDriverElement.php b/WebDriverElement.php
index fb0b15d..2b806e4 100644
--- a/WebDriverElement.php
+++ b/WebDriverElement.php
@@ -16,7 +16,6 @@
 final class WebDriverElement extends WebDriverContainer {
   protected function methods() {
     return array(
-      'active' => 'POST',
       'click' => 'POST',
       'submit' => 'POST',
       'text' => 'GET',
diff --git a/WebDriverExceptions.php b/WebDriverExceptions.php
index ad952c4..71d6256 100644
--- a/WebDriverExceptions.php
+++ b/WebDriverExceptions.php
@@ -15,7 +15,19 @@
 
 final class WebDriverCurlException extends Exception {} // When curls fail
 
-abstract class WebDriverException extends Exception {}
+abstract class WebDriverException extends Exception {
+  private $results;
+
+  public function __construct($message, $results = null) {
+    parent::__construct($message);
+    $this->results = $results;
+  }
+
+  public function getResults() {
+    return $this->results;
+  }
+}
+
 class IndexOutOfBoundsWebDriverError extends WebDriverException {} // 1
 class NoCollectionWebDriverError extends WebDriverException {} // 2
 class NoStringWebDriverError extends WebDriverException {} // 3
diff --git a/WebDriverSession.php b/WebDriverSession.php
index 4f41e31..5679b78 100644
--- a/WebDriverSession.php
+++ b/WebDriverSession.php
@@ -26,10 +26,9 @@ protected function methods() {
       'window_handle' => 'GET',
       'window_handles' => 'GET',
       'frame' => 'POST',
-      'window' => array('POST', 'DELETE'),
       'source' => 'GET',
       'title' => 'GET',
-      'modifier' => 'POST',
+      'keys' => 'POST',
       'orientation' => array('GET', 'POST'),
       'alert_text' => array('GET', 'POST'),
       'accept_alert' => 'POST',
@@ -50,17 +49,20 @@ public function open($url) {
 
   // /session/:sessionId (GET)
   public function capabilities() {
-    return $this->curl('GET', '');
+    $result = $this->curl('GET', '');
+    return $result['value'];
   }
 
   // /session/:sessionId (DELETE)
   public function close() {
-    return $this->curl('DELETE', '');
+    $result = $this->curl('DELETE', '');
+    return $result['value'];
   }
 
   // /session/:sessionId/cookie (GET)
   public function getAllCookies() {
-    return $this->curl('GET', '/cookie');
+    $result = $this->curl('GET', '/cookie');
+    return $result['value'];
   }
 
   // /session/:sessionId/cookie (POST)
@@ -100,6 +102,32 @@ public function ime() {
     ));
   }
 
+  // /session/:sessionId/window (DELETE)
+  public function deleteWindow() {
+    $this->curl('DELETE', '/window');
+    return $this;
+  }
+
+  // /session/:sessionId/window (POST)
+  public function focusWindow($name) {
+    $this->curl('POST', '/window', array('name' => $name));
+    return $this;
+  }
+
+  public function window($window_handle = 'current') {
+    $item = new WebDriverSimpleItem($this->url . '/window/' . $window_handle);
+    return $item->setMethods(array(
+      'size' => array('GET', 'POST'),
+      'position' => array('GET', 'POST'),
+    ));
+  }
+
+  // /session/:sessionId/element/active (POST)
+  public function activeElement() {
+    $results = $this->curl('POST', '/element/active');
+    return $this->webDriverElement($results['value']);
+  }
+
   public function touch() {
     $item = new WebDriverSimpleItem($this->url . '/touch');
     return $item->setMethods(array(
diff --git a/__init__.php b/__init__.php
index 53b1553..d2d4aa9 100644
--- a/__init__.php
+++ b/__init__.php
@@ -1,5 +1,17 @@
 <?php
 // Copyright 2004-present Facebook. 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.
 
 require_once('WebDriverBase.php');
 require_once('WebDriver.php');
