tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

basics.rst (6927B)


      1 .. py:currentmodule:: marionette_driver.marionette
      2 
      3 Marionette Python Client
      4 ========================
      5 
      6 The Marionette Python client library allows you to remotely control a
      7 Gecko-based browser or device which is running a Marionette_
      8 server. This includes Firefox Desktop and Firefox for Android.
      9 
     10 The Marionette server is built directly into Gecko and can be started by
     11 passing in a command line option to Gecko, or by using a Marionette-enabled
     12 build. The server listens for connections from various clients. Clients can
     13 then control Gecko by sending commands to the server.
     14 
     15 This is the official Python client for Marionette. There also exists a
     16 `NodeJS client`_ maintained by the Firefox OS automation team.
     17 
     18 .. _Marionette: https://developer.mozilla.org/en-US/docs/Marionette
     19 .. _NodeJS client: https://github.com/mozilla-b2g/gaia/tree/master/tests/jsmarionette
     20 
     21 Getting the Client
     22 ------------------
     23 
     24 The Python client is officially supported. To install it, first make sure you
     25 have `pip installed`_ then run:
     26 
     27 .. code-block:: bash
     28 
     29   $ pip install marionette_driver
     30 
     31 It's highly recommended to use virtualenv_ when installing Marionette to avoid
     32 package conflicts and other general nastiness.
     33 
     34 You should now be ready to start using Marionette. The best way to learn is to
     35 play around with it. Start a `Marionette-enabled instance of Firefox`_, fire up
     36 a python shell and follow along with the
     37 :doc:`interactive tutorial <interactive>`!
     38 
     39 .. _pip installed: https://pip.pypa.io/en/latest/installing.html
     40 .. _virtualenv: http://virtualenv.readthedocs.org/en/latest/
     41 .. _Marionette-enabled instance of Firefox: https://developer.mozilla.org/en-US/docs/Mozilla/QA/Marionette/Builds
     42 
     43 Using the Client for Testing
     44 ----------------------------
     45 
     46 Please visit the `Marionette Tests`_ section on MDN for information regarding
     47 testing with Marionette.
     48 
     49 .. _Marionette Tests: https://developer.mozilla.org/en/Marionette/Tests
     50 
     51 Session Management
     52 ------------------
     53 A session is a single instance of a Marionette client connected to a Marionette
     54 server. Before you can start executing commands, you need to start a session
     55 with :func:`start_session() <Marionette.start_session>`:
     56 
     57 .. code-block:: python
     58 
     59   from marionette_driver.marionette import Marionette
     60 
     61   client = Marionette('127.0.0.1', port=2828)
     62   client.start_session()
     63 
     64 This returns a session id and an object listing the capabilities of the
     65 Marionette server. For example, a server running on Firefox Desktop will
     66 have some features which a server running from Firefox Android won't.
     67 It's also possible to access the capabilities using the
     68 :attr:`~Marionette.session_capabilities` attribute. After finishing with a
     69 session, you can delete it with :func:`~Marionette.delete_session()`. Note that
     70 this will also happen automatically when the Marionette object is garbage
     71 collected.
     72 
     73 Context Management
     74 ------------------
     75 Commands can only be executed in a single window, frame and scope at a time. In
     76 order to run commands elsewhere, it's necessary to explicitly switch to the
     77 appropriate context.
     78 
     79 Use :func:`~Marionette.switch_to_window` to execute commands in the context of a
     80 new window:
     81 
     82 .. code-block:: python
     83 
     84   original_window = client.current_window_handle
     85   for handle in client.window_handles:
     86       if handle != original_window:
     87           client.switch_to_window(handle)
     88           print("Switched to window with '{}' loaded.".format(client.get_url()))
     89   client.switch_to_window(original_window)
     90 
     91 Similarly, use :func:`~Marionette.switch_to_frame` to execute commands in the
     92 context of a new frame (e.g an <iframe> element):
     93 
     94 .. code-block:: python
     95 
     96   iframe = client.find_element(By.TAG_NAME, 'iframe')
     97   client.switch_to_frame(iframe)
     98 
     99 Finally Marionette can switch between `chrome` and `content` scope. Chrome is a
    100 privileged scope where you can access things like the Firefox UI itself.
    101 Content scope is where things like webpages live. You can switch between
    102 `chrome` and `content` using the :func:`~Marionette.set_context` and :func:`~Marionette.using_context` functions:
    103 
    104 .. code-block:: python
    105 
    106   client.set_context(client.CONTEXT_CONTENT)
    107   # content scope
    108   with client.using_context(client.CONTEXT_CHROME):
    109       #chrome scope
    110       pass  # ... do stuff ...
    111   # content scope restored
    112 
    113 
    114 Navigation
    115 ----------
    116 
    117 Use :func:`~Marionette.navigate` to open a new website. It's also possible to
    118 move through the back/forward cache using :func:`~Marionette.go_forward` and
    119 :func:`~Marionette.go_back` respectively. To retrieve the currently
    120 open website, use :func:`~Marionette.get_url`:
    121 
    122 .. code-block:: python
    123 
    124   url = 'http://mozilla.org'
    125   client.navigate(url)
    126   client.go_back()
    127   client.go_forward()
    128   assert client.get_url() == url
    129 
    130 
    131 DOM Elements
    132 ------------
    133 
    134 In order to inspect or manipulate actual DOM elements, they must first be found
    135 using the :func:`~Marionette.find_element` or :func:`~Marionette.find_elements`
    136 methods:
    137 
    138 .. code-block:: python
    139 
    140   from marionette_driver.marionette import WebElement
    141   element = client.find_element(By.ID, 'my-id')
    142   assert type(element) == WebElement
    143   elements = client.find_elements(By.TAG_NAME, 'a')
    144   assert type(elements) == list
    145 
    146 For a full list of valid search strategies, see :doc:`advanced/findelement`.
    147 
    148 Now that an element has been found, it's possible to manipulate it:
    149 
    150 .. code-block:: python
    151 
    152   element.click()
    153   element.send_keys('hello!')
    154   print(element.get_attribute('style'))
    155 
    156 For the full list of possible commands, see the :class:`WebElement`
    157 reference.
    158 
    159 Be warned that a reference to an element object can become stale if it was
    160 modified or removed from the document. See :doc:`advanced/stale` for tips
    161 on working around this limitation.
    162 
    163 Script Execution
    164 ----------------
    165 
    166 Sometimes Marionette's provided APIs just aren't enough and it is necessary to
    167 run arbitrary javascript. This is accomplished with the
    168 :func:`~Marionette.execute_script` and :func:`~Marionette.execute_async_script`
    169 functions. They accomplish what their names suggest, the former executes some
    170 synchronous JavaScript, while the latter provides a callback mechanism for
    171 running asynchronous JavaScript:
    172 
    173 .. code-block:: python
    174 
    175   result = client.execute_script("return arguments[0] + arguments[1];",
    176                                  script_args=[2, 3])
    177   assert result == 5
    178 
    179 The async method works the same way, except it won't return until the
    180 `resolve()` function is called:
    181 
    182 .. code-block:: python
    183 
    184   result = client.execute_async_script("""
    185       let [resolve] = arguments;
    186       setTimeout(function() {
    187         resolve("all done");
    188       }, arguments[0]);
    189   """, script_args=[1000])
    190   assert result == "all done"
    191 
    192 Beware that running asynchronous scripts can potentially hang the program
    193 indefinitely if they are not written properly. It is generally a good idea to
    194 set a script timeout using :func:`~Marionette.timeout.script` and handling
    195 `ScriptTimeoutException`.