diff --git a/testing/marionette/client/docs/advanced/actions.rst b/testing/marionette/client/docs/advanced/actions.rst
new file mode 100644
index 000000000000..294855a6f7ef
--- /dev/null
+++ b/testing/marionette/client/docs/advanced/actions.rst
@@ -0,0 +1,46 @@
+Actions
+=======
+
+.. py:currentmodule:: marionette
+
+Action Sequences
+----------------
+
+:class:`Actions` are designed as a way to simulate user input as closely as possible
+on a touch device like a smart phone. A common operation is to tap the screen
+and drag your finger to another part of the screen and lift it off.
+
+This can be simulated using an Action::
+
+ from marionette import Actions
+
+ start_element = marionette.find_element('id', 'start')
+ end_element = marionette.find_element('id', 'end')
+
+ action = Actions(marionette)
+ action.press(start_element).wait(1).move(end_element).release()
+ action.perform()
+
+This will simulate pressing an element, waiting for one second, moving the
+finger over to another element and then lifting the finger off the screen. The
+wait is optional in this case, but can be useful for simulating delays typical
+to a users behaviour.
+
+Multi-Action Sequences
+----------------------
+
+Sometimes it may be necessary to simulate multiple actions at the same time.
+For example a user may be dragging one finger while tapping another. This is
+where :class:`MultiActions` come in. MultiActions are simply a way of combining
+two or more actions together and performing them all at the same time::
+
+ action1 = Actions(marionette)
+ action1.press(start_element).move(end_element).release()
+
+ action2 = Actions(marionette)
+ action2.press(another_element).wait(1).release()
+
+ multi = MultiActions(marionette)
+ multi.add(action1)
+ multi.add(action2)
+ multi.perform()
diff --git a/testing/marionette/client/docs/advanced/debug.rst b/testing/marionette/client/docs/advanced/debug.rst
new file mode 100644
index 000000000000..e72d2549bd02
--- /dev/null
+++ b/testing/marionette/client/docs/advanced/debug.rst
@@ -0,0 +1,54 @@
+Debugging
+=========
+
+.. py:currentmodule:: marionette
+
+Sometimes when working with Marionette you'll run into unexpected behaviour and
+need to do some debugging. This page outlines some of the Marionette methods
+that can be useful to you.
+
+Please note that the best tools for debugging are the `ones that ship with
+Gecko`_. This page doesn't describe how to use those with Marionette. Also see
+a related topic about `using the debugger with Marionette`_ on MDN.
+
+.. _ones that ship with Gecko: https://developer.mozilla.org/en-US/docs/Tools
+.. _using the debugger with Marionette: https://developer.mozilla.org/en-US/docs/Marionette/Debugging
+
+
+Storing Logs on the Server
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+By calling `~Marionette.log` it is possible to store a message on the server.
+Logs can later be retrieved using `~Marionette.get_logs`. For example::
+
+ try:
+ marionette.log("Sending a click event") # logged at INFO level
+ elem.click()
+ except:
+ marionette.log("Something went wrong!", "ERROR")
+
+ print(marionette.get_logs())
+
+Disclaimer: Example for illustrative purposes only, don't actually hide
+tracebacks like that!
+
+
+Seeing What's on the Page
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Sometimes it's difficult to tell what is actually on the page that is being
+manipulated. Either because it happens too fast, the window isn't big enough or
+you are manipulating a remote server! There are two methods that can help you
+out. The first is `~Marionette.screenshot`::
+
+ marionette.screenshot() # takes screenshot of entire frame
+ elem = marionette.find_element(By.ID, 'some-div')
+ marionette.screenshot(elem) # takes a screenshot of only the given element
+
+Sometimes you just want to see the DOM layout. You can do this with the
+`~Marionette.page_source` property. Note that the page source depends on the
+context you are in::
+
+ print(marionette.page_source)
+ marionette.set_context('chrome')
+ print(marionette.page_source)
diff --git a/testing/marionette/client/docs/advanced/findelement.rst b/testing/marionette/client/docs/advanced/findelement.rst
new file mode 100644
index 000000000000..abcbc8e89a81
--- /dev/null
+++ b/testing/marionette/client/docs/advanced/findelement.rst
@@ -0,0 +1,126 @@
+Finding Elements
+================
+.. py:currentmodule:: marionette
+
+One of the most common and yet often most difficult tasks in Marionette is
+finding a DOM element on a webpage or in the chrome UI. Marionette provides
+several different search strategies to use when finding elements. All search
+strategies work with both :func:`~Marionette.find_element` and
+:func:`~Marionette.find_elements`, though some strategies are not implemented
+in chrome scope.
+
+In the event that more than one element is matched by the query,
+:func:`~Marionette.find_element` will only return the first element found. In
+the event that no elements are matched by the query,
+:func:`~Marionette.find_element` will raise `NoSuchElementException` while
+:func:`~Marionette.find_elements` will return an empty list.
+
+Search Strategies
+-----------------
+
+Search strategies are defined in the :class:`By` class::
+
+ from marionette import By
+ print(By.ID)
+
+The strategies are:
+
+* `id` - The easiest way to find an element is to refer to its id directly::
+
+ container = client.find_element(By.ID, 'container')
+
+* `class name` - To find elements belonging to a certain class, use `class name`::
+
+ buttons = client.find_elements(By.CLASS_NAME, 'button')
+
+* `css selector` - It's also possible to find elements using a `css selector`_::
+
+ container_buttons = client.find_elements(By.CSS_SELECTOR, '#container .buttons')
+
+* `name` - Find elements by their name attribute (not implemented in chrome
+ scope)::
+
+ form = client.find_element(By.NAME, 'signup')
+
+* `tag name` - To find all the elements with a given tag, use `tag name`::
+
+ paragraphs = client.find_elements(By.TAG_NAME, 'p')
+
+* `link text` - A convenience strategy for finding link elements by their
+ innerHTML (not implemented in chrome scope)::
+
+ link = client.find_element(By.LINK_TEXT, 'Click me!')
+
+* `partial link text` - Same as `link text` except substrings of the innerHTML
+ are matched (not implemented in chrome scope)::
+
+ link = client.find_element(By.PARTIAL_LINK_TEXT, 'Clic')
+
+* `xpath` - Find elements using an xpath_ query::
+
+ elem = client.find_element(By.XPATH, './/*[@id="foobar"')
+
+.. _css selector: https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Getting_Started/Selectors
+.. _xpath: https://developer.mozilla.org/en-US/docs/Web/XPath
+
+
+
+Chaining Searches
+-----------------
+
+In addition to the methods on the Marionette object, HTMLElement objects also
+provide :func:`~HTMLElement.find_element` and :func:`~HTMLElement.find_elements`
+methods. The difference is that only child nodes of the element will be searched.
+Consider the following html snippet::
+
+
+
+
+
+
+Doing the following will work::
+
+ client.find_element(By.ID, 'container').find_element(By.ID, 'main')
+
+But this will raise a `NoSuchElementException`::
+
+ client.find_element(By.ID, 'container').find_element(By.ID, 'footer')
+
+
+Finding Anonymous Nodes
+-----------------------
+
+When working in chrome scope, for example manipulating the Firefox user
+interface, you may run into something called an anonymous node.
+
+Firefox uses a markup language called XUL_ for its interface. XUL is similar
+to HTML in that it has a DOM and tags that render controls on the display. One
+ability of XUL is to create re-useable widgets that are made up out of several
+smaller XUL elements. These widgets can be bound to the DOM using something
+called the `XML binding language (XBL)`_.
+
+The end result is that the DOM sees the widget as a single entity. It doesn't
+know anything about how that widget is made up. All of the smaller XUL elements
+that make up the widget are called `anonymous content`_. It is not possible to
+query such elements using traditional DOM methods like `getElementById`.
+
+Marionette provides two special strategies used for finding anonymous content.
+Unlike normal elements, anonymous nodes can only be seen by their parent. So
+it's necessary to first find the parent element and then search for the
+anonymous children from there.
+
+* `anon` - Finds all anonymous children of the element, there is no search term
+ so `None` must be passed in::
+
+ anon_children = client.find_element('id', 'parent').find_elements('anon', None)
+
+* `anon attribute` - Find an anonymous child based on an attribute. An
+ unofficial convention is for anonymous nodes to have an
+ `anonid` attribute::
+
+ anon_child = client.find_element('id', 'parent').find_element('anon attribute', {'anonid': 'container'})
+
+
+.. _XUL: https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XUL
+.. _XML binding language (XBL): https://developer.mozilla.org/en-US/docs/XBL
+.. _anonymous content: https://developer.mozilla.org/en-US/docs/XBL/XBL_1.0_Reference/Anonymous_Content
diff --git a/testing/marionette/client/docs/advanced/landing.rst b/testing/marionette/client/docs/advanced/landing.rst
new file mode 100644
index 000000000000..0a44de63d7f4
--- /dev/null
+++ b/testing/marionette/client/docs/advanced/landing.rst
@@ -0,0 +1,13 @@
+Advanced Topics
+===============
+
+Here are a collection of articles explaining some of the more complicated
+aspects of Marionette.
+
+.. toctree::
+ :maxdepth: 1
+
+ findelement
+ stale
+ actions
+ debug
diff --git a/testing/marionette/client/docs/advanced/stale.rst b/testing/marionette/client/docs/advanced/stale.rst
new file mode 100644
index 000000000000..0af5768653d6
--- /dev/null
+++ b/testing/marionette/client/docs/advanced/stale.rst
@@ -0,0 +1,71 @@
+Dealing with Stale Elements
+===========================
+.. py:currentmodule:: marionette
+
+Marionette does not keep a live representation of the DOM saved. All it can do
+is send commands to the Marionette server which queries the DOM on the client's
+behalf. References to elements are also not passed from server to client. A
+unique id is generated for each element that gets referenced and a mapping of
+id to element object is stored on the server. When commands such as
+:func:`~HTMLElement.click()` are run, the client sends the element's id along
+with the command. The server looks up the proper DOM element in its reference
+table and executes the command on it.
+
+In practice this means that the DOM can change state and Marionette will never
+know until it sends another query. For example, look at the following HTML::
+
+
+
+
+
+
+
+
+
+
+
+Care needs to be taken as the DOM is being modified after the page has loaded.
+The following code has a race condition::
+
+ button = client.find_element('id', 'button')
+ button.click()
+ assert len(client.find_elements('css selector', '#container div')) > 0
+
+
+Explicit Waiting and Expected Conditions
+----------------------------------------
+
+To avoid the above scenario, manual synchronisation is needed. Waits are used
+to pause program execution until a given condition is true. This is a useful
+technique to employ when documents load new content or change after
+``Document.readyState``'s value changes to "complete".
+
+The :class:`Wait` helper class provided by Marionette avoids some of the
+caveats of ``time.sleep(n)``. It will return immediately once the provided
+condition evaluates to true.
+
+To avoid the race condition in the above example, one could do::
+
+ button = client.find_element('id', 'button')
+ button.click()
+
+ def find_divs():
+ return client.find_elements('css selector', '#container div')
+
+ divs = Wait(client).until(find_divs)
+ assert len(divs) > 0
+
+This avoids the race condition. Because finding elements is a common condition
+to wait for, it is built in to Marionette. Instead of the above, you could
+write::
+
+ button = client.find_element('id', 'button')
+ button.click()
+ assert len(Wait(client).until(expected.elements_present('css selector', '#container div'))) > 0
+
+For a full list of built-in conditions, see :mod:`~marionette.expected`.
diff --git a/testing/marionette/client/docs/basics.rst b/testing/marionette/client/docs/basics.rst
new file mode 100644
index 000000000000..0f117b1f1e55
--- /dev/null
+++ b/testing/marionette/client/docs/basics.rst
@@ -0,0 +1,185 @@
+.. py:currentmodule:: marionette
+
+Marionette Python Client
+========================
+
+The Marionette python client library allows you to remotely control a
+Gecko-based browser or device which is running a Marionette_
+server. This includes desktop Firefox and FirefoxOS (support for
+Firefox for Android is planned, but not yet fully implemented).
+
+The Marionette server is built directly into Gecko and can be started by
+passing in a command line option to Gecko, or by using a Marionette-enabled
+build. The server listens for connections from various clients. Clients can
+then control Gecko by sending commands to the server.
+
+This is the official python client for Marionette. There also exists a
+`NodeJS client`_ maintained by the Firefox OS automation team.
+
+.. _Marionette: https://developer.mozilla.org/en-US/docs/Marionette
+.. _NodeJS client: https://github.com/mozilla-b2g/marionette-js-client
+
+Getting the Client
+------------------
+
+The python client is officially supported. To install it, first make sure you
+have `pip installed`_ then run:
+
+.. parsed-literal::
+ pip install marionette_client
+
+It's highly recommended to use virtualenv_ when installing Marionette to avoid
+package conflicts and other general nastiness.
+
+You should now be ready to start using Marionette. The best way to learn is to
+play around with it. Start a `Marionette-enabled instance of Firefox`_, fire up
+a python shell and follow along with the
+:doc:`interactive tutorial `!
+
+.. _pip installed: https://pip.pypa.io/en/latest/installing.html
+.. _virtualenv: http://virtualenv.readthedocs.org/en/latest/
+.. _Marionette-enabled instance of Firefox: https://developer.mozilla.org/en-US/docs/Mozilla/QA/Marionette/Builds
+
+Using the Client for Testing
+----------------------------
+
+Please visit the `Marionette Tests`_ section on MDN for information regarding
+testing with Marionette.
+
+.. _Marionette Tests: https://developer.mozilla.org/en/Marionette/Tests
+
+Session Management
+------------------
+A session is a single instance of a Marionette client connected to a Marionette
+server. Before you can start executing commands, you need to start a session
+with :func:`start_session() `:
+
+.. parsed-literal::
+ client = Marionette('localhost', port=2828)
+ client.start_session()
+
+This returns a session id and an object listing the capabilities of the
+Marionette server. For example, a server running on a Firefox OS device will
+have the ability to rotate the window, while a server running from Firefox
+won't. It's also possible to access the capabilities using the
+:attr:`~Marionette.session_capabilities` attribute. After finishing with a
+session, you can delete it with :func:`~Marionette.delete_session()`. Note that
+this will also happen automatically when the Marionette object is garbage
+collected.
+
+Context Management
+------------------
+Commands can only be executed in a single window, frame and scope at a time. In
+order to run commands elsewhere, it's necessary to explicitly switch to the
+appropriate context.
+
+Use :func:`~Marionette.switch_to_window` to execute commands in the context of a
+new window:
+
+.. parsed-literal::
+ original_window = client.current_window_handle
+ for handle in client.window_handles:
+ if handle != original_window:
+ client.switch_to_window(handle)
+ print("Switched to window with '{}' loaded.".format(client.get_url()))
+ client.switch_to_window(original_window)
+
+Similarly, use :func:`~Marionette.switch_to_frame` to execute commands in the
+context of a new frame (e.g an