Bug 1313312 - Make Firefox Puppeteer a mixin class. draft
authorHenrik Skupin <mail@hskupin.info>
Tue, 01 Nov 2016 15:16:10 +0100
changeset 432238 ba1276ec3308a8f34e18db786ecfaaac2f9d22f6
parent 431996 2c773b97167252cedcba0be0c7af9d4cab192ef5
child 432239 3bd5300066d349b4309e3819825d0d2cb77f7494
push id34239
push userbmo:hskupin@gmail.com
push dateTue, 01 Nov 2016 14:19:19 +0000
bugs1313312
milestone52.0a1
Bug 1313312 - Make Firefox Puppeteer a mixin class. MozReview-Commit-ID: I99Nul0Jzna
dom/media/test/external/external_media_harness/testcase.py
testing/firefox-ui/harness/firefox_ui_harness/testcases.py
testing/firefox-ui/tests/puppeteer/test_l10n.py
testing/firefox-ui/tests/puppeteer/test_software_update.py
testing/firefox-ui/tests/puppeteer/test_windows.py
testing/puppeteer/firefox/firefox_puppeteer/__init__.py
testing/puppeteer/firefox/firefox_puppeteer/api/keys.py
testing/puppeteer/firefox/firefox_puppeteer/api/utils.py
testing/puppeteer/firefox/firefox_puppeteer/base.py
testing/puppeteer/firefox/firefox_puppeteer/decorators.py
testing/puppeteer/firefox/firefox_puppeteer/mixins.py
testing/puppeteer/firefox/firefox_puppeteer/testcases/__init__.py
testing/puppeteer/firefox/firefox_puppeteer/testcases/base.py
testing/puppeteer/firefox/firefox_puppeteer/ui/about_window/deck.py
testing/puppeteer/firefox/firefox_puppeteer/ui/about_window/window.py
testing/puppeteer/firefox/firefox_puppeteer/ui/browser/tabbar.py
testing/puppeteer/firefox/firefox_puppeteer/ui/browser/toolbars.py
testing/puppeteer/firefox/firefox_puppeteer/ui/browser/window.py
testing/puppeteer/firefox/firefox_puppeteer/ui/menu.py
testing/puppeteer/firefox/firefox_puppeteer/ui/pageinfo/deck.py
testing/puppeteer/firefox/firefox_puppeteer/ui/pageinfo/window.py
testing/puppeteer/firefox/firefox_puppeteer/ui/update_wizard/dialog.py
testing/puppeteer/firefox/firefox_puppeteer/ui/update_wizard/wizard.py
testing/puppeteer/firefox/firefox_puppeteer/ui/windows.py
testing/puppeteer/firefox/firefox_puppeteer/ui_base_lib.py
--- a/dom/media/test/external/external_media_harness/testcase.py
+++ b/dom/media/test/external/external_media_harness/testcase.py
@@ -1,30 +1,29 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 import re
 import os
-import time
 
 from marionette import BrowserMobProxyTestCaseMixin, MarionetteTestCase, Marionette
 from marionette_driver import Wait
 from marionette_driver.errors import TimeoutException
 from marionette.marionette_test import SkipTest
 
-from firefox_puppeteer.testcases import BaseFirefoxTestCase
+from firefox_puppeteer import FirefoxPuppeteerMixin
 from external_media_tests.utils import (timestamp_now, verbose_until)
 from external_media_tests.media_utils.video_puppeteer import (
     VideoException,
     VideoPuppeteer
 )
 
 
-class MediaTestCase(BaseFirefoxTestCase, MarionetteTestCase):
+class MediaTestCase(MarionetteTestCase, FirefoxPuppeteerMixin):
 
     """
     Necessary methods for MSE playback
 
     :param video_urls: Urls you are going to play as part of the tests.
     """
 
     def __init__(self, *args, **kwargs):
--- a/testing/firefox-ui/harness/firefox_ui_harness/testcases.py
+++ b/testing/firefox-ui/harness/firefox_ui_harness/testcases.py
@@ -7,24 +7,34 @@ import pprint
 from datetime import datetime
 
 import mozfile
 
 from marionette import MarionetteTestCase
 from marionette_driver import Wait
 from marionette_driver.errors import NoSuchWindowException
 
+from firefox_puppeteer import FirefoxPuppeteerMixin
 from firefox_puppeteer.api.prefs import Preferences
 from firefox_puppeteer.api.software_update import SoftwareUpdate
-from firefox_puppeteer.testcases import BaseFirefoxTestCase
 from firefox_puppeteer.ui.update_wizard import UpdateWizardDialog
 
 
-class FirefoxTestCase(BaseFirefoxTestCase, MarionetteTestCase):
-    """ Integrate MarionetteTestCase with BaseFirefoxTestCase by reordering MRO """
+class FirefoxTestCase(MarionetteTestCase, FirefoxPuppeteerMixin):
+    """Base TestCase class for Firefox Desktop tests.
+
+    This is designed to enhance MarionetteTestCase by inserting the Puppeteer
+    mixin class (so Firefox specific API modules are exposed to test scope) and
+    providing common set-up and tear-down code for Firefox tests.
+
+    If you're extending the inheritance tree further to make specialized
+    TestCases, favour the use of super() as opposed to explicit calls to a
+    parent class.
+
+    """
     pass
 
 
 class UpdateTestCase(FirefoxTestCase):
 
     TIMEOUT_UPDATE_APPLY = 300
     TIMEOUT_UPDATE_CHECK = 30
     TIMEOUT_UPDATE_DOWNLOAD = 360
@@ -50,17 +60,17 @@ class UpdateTestCase(FirefoxTestCase):
         self.update_mar_channels = set(kwargs.pop('update_mar_channels'))
         self.default_mar_channels = None
 
         self.updates = []
 
     def setUp(self, is_fallback=False):
         super(UpdateTestCase, self).setUp()
 
-        self.software_update = SoftwareUpdate(lambda: self.marionette)
+        self.software_update = SoftwareUpdate(self.marionette)
         self.download_duration = None
 
         # Bug 604364 - Preparation to test multiple update steps
         self.current_update_index = 0
 
         # Ensure that there exists no already partially downloaded update
         self.remove_downloaded_update()
 
@@ -224,17 +234,17 @@ class UpdateTestCase(FirefoxTestCase):
         :param timeout: How long to wait for the download to finish. Optional, default to 360s.
         """
 
         def download_via_update_wizard(dialog):
             """ Download the update via the old update wizard dialog.
 
             :param dialog: Instance of :class:`UpdateWizardDialog`.
             """
-            prefs = Preferences(lambda: self.marionette)
+            prefs = Preferences(self.marionette)
             prefs.set_pref(self.PREF_APP_UPDATE_ALTWINDOWTYPE, dialog.window_type)
 
             try:
                 # If updates have already been found, proceed to download
                 if dialog.wizard.selected_panel in [dialog.wizard.updates_found_basic,
                                                     dialog.wizard.error_patching,
                                                     ]:
                     dialog.select_next_page()
--- a/testing/firefox-ui/tests/puppeteer/test_l10n.py
+++ b/testing/firefox-ui/tests/puppeteer/test_l10n.py
@@ -8,17 +8,17 @@ from marionette_driver.errors import Mar
 from firefox_puppeteer.api.l10n import L10n
 from firefox_ui_harness.testcases import FirefoxTestCase
 
 
 class TestL10n(FirefoxTestCase):
 
     def setUp(self):
         FirefoxTestCase.setUp(self)
-        self.l10n = L10n(lambda: self.marionette)
+        self.l10n = L10n(self.marionette)
 
     def tearDown(self):
         FirefoxTestCase.tearDown(self)
 
     def test_dtd_entity_chrome(self):
         dtds = ['chrome://global/locale/about.dtd',
                 'chrome://browser/locale/baseMenuOverlay.dtd']
 
--- a/testing/firefox-ui/tests/puppeteer/test_software_update.py
+++ b/testing/firefox-ui/tests/puppeteer/test_software_update.py
@@ -8,17 +8,17 @@ from firefox_ui_harness.testcases import
 
 from firefox_puppeteer.api.software_update import SoftwareUpdate
 
 
 class TestSoftwareUpdate(FirefoxTestCase):
 
     def setUp(self):
         FirefoxTestCase.setUp(self)
-        self.software_update = SoftwareUpdate(lambda: self.marionette)
+        self.software_update = SoftwareUpdate(self.marionette)
 
         self.saved_mar_channels = self.software_update.mar_channels.channels
         self.software_update.mar_channels.channels = set(['expected', 'channels'])
 
     def tearDown(self):
         try:
             self.software_update.mar_channels.channels = self.saved_mar_channels
         finally:
@@ -67,17 +67,17 @@ class TestSoftwareUpdate(FirefoxTestCase
     def test_staging_directory(self):
         self.assertTrue(self.software_update.staging_directory)
 
 
 class TestUpdateChannel(FirefoxTestCase):
 
     def setUp(self):
         FirefoxTestCase.setUp(self)
-        self.software_update = SoftwareUpdate(lambda: self.marionette)
+        self.software_update = SoftwareUpdate(self.marionette)
 
         self.saved_channel = self.software_update.update_channel.default_channel
         self.software_update.update_channel.default_channel = 'expected_channel'
 
     def tearDown(self):
         try:
             self.software_update.update_channel.default_channel = self.saved_channel
         finally:
@@ -93,17 +93,17 @@ class TestUpdateChannel(FirefoxTestCase)
         self.software_update.update_channel.default_channel = 'new_channel'
         self.assertEqual(self.software_update.update_channel.default_channel, 'new_channel')
 
 
 class TestMARChannels(FirefoxTestCase):
 
     def setUp(self):
         FirefoxTestCase.setUp(self)
-        self.software_update = SoftwareUpdate(lambda: self.marionette)
+        self.software_update = SoftwareUpdate(self.marionette)
 
         self.saved_mar_channels = self.software_update.mar_channels.channels
         self.software_update.mar_channels.channels = set(['expected', 'channels'])
 
     def tearDown(self):
         try:
             self.software_update.mar_channels.channels = self.saved_mar_channels
         finally:
--- a/testing/firefox-ui/tests/puppeteer/test_windows.py
+++ b/testing/firefox-ui/tests/puppeteer/test_windows.py
@@ -87,44 +87,42 @@ class TestBaseWindow(BaseWindowTestCase)
     def tearDown(self):
         try:
             self.windows.close_all([self.browser])
         finally:
             BaseWindowTestCase.tearDown(self)
 
     def test_basics(self):
         # force BaseWindow instance
-        win1 = BaseWindow(lambda: self.marionette, self.browser.handle)
+        win1 = BaseWindow(self.marionette, self.browser.handle)
 
         self.assertEquals(win1.handle, self.marionette.current_chrome_window_handle)
         self.assertEquals(win1.window_element,
                           self.marionette.find_element(By.CSS_SELECTOR, ':root'))
         self.assertEquals(win1.window_element.get_attribute('windowtype'),
                           self.marionette.get_window_type())
         self.assertFalse(win1.closed)
 
         # Test invalid parameters for BaseWindow constructor
-        self.assertRaises(TypeError,
-                          BaseWindow, self.marionette, self.browser.handle)
         self.assertRaises(errors.UnknownWindowError,
-                          BaseWindow, lambda: self.marionette, 10)
+                          BaseWindow, self.marionette, 10)
 
         # Test invalid shortcuts
         self.assertRaises(KeyError,
                           win1.send_shortcut, 'l', acel=True)
 
     def test_open_close(self):
         # force BaseWindow instance
-        win1 = BaseWindow(lambda: self.marionette, self.browser.handle)
+        win1 = BaseWindow(self.marionette, self.browser.handle)
 
         # Open a new window (will be focused), and check states
         win2 = win1.open_window()
 
         # force BaseWindow instance
-        win2 = BaseWindow(lambda: self.marionette, win2.handle)
+        win2 = BaseWindow(self.marionette, win2.handle)
 
         self.assertEquals(len(self.marionette.chrome_window_handles), 2)
         self.assertNotEquals(win1.handle, win2.handle)
         self.assertEquals(win2.handle, self.marionette.current_chrome_window_handle)
 
         win2.close()
 
         self.assertTrue(win2.closed)
@@ -139,37 +137,37 @@ class TestBaseWindow(BaseWindowTestCase)
             window.marionette.execute_script(""" window.open(); """)
 
         def closer(window):
             window.marionette.execute_script(""" window.close(); """)
 
         win2 = win1.open_window(callback=opener)
 
         # force BaseWindow instance
-        win2 = BaseWindow(lambda: self.marionette, win2.handle)
+        win2 = BaseWindow(self.marionette, win2.handle)
 
         self.assertEquals(len(self.marionette.chrome_window_handles), 2)
         win2.close(callback=closer)
 
         win1.focus()
 
         # Check for an unexpected window class
         self.assertRaises(errors.UnexpectedWindowTypeError,
                           win1.open_window, expected_window_class=BaseWindow)
         self.windows.close_all([win1])
 
     def test_switch_to_and_focus(self):
         # force BaseWindow instance
-        win1 = BaseWindow(lambda: self.marionette, self.browser.handle)
+        win1 = BaseWindow(self.marionette, self.browser.handle)
 
         # Open a new window (will be focused), and check states
         win2 = win1.open_window()
 
         # force BaseWindow instance
-        win2 = BaseWindow(lambda: self.marionette, win2.handle)
+        win2 = BaseWindow(self.marionette, win2.handle)
 
         self.assertEquals(win2.handle, self.marionette.current_chrome_window_handle)
         self.assertEquals(win2.handle, self.windows.focused_chrome_window_handle)
         self.assertFalse(win1.focused)
         self.assertTrue(win2.focused)
 
         # Switch back to win1 without moving the focus, but focus separately
         win1.switch_to()
--- a/testing/puppeteer/firefox/firefox_puppeteer/__init__.py
+++ b/testing/puppeteer/firefox/firefox_puppeteer/__init__.py
@@ -1,112 +1,8 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this file,
 # You can obtain one at http://mozilla.org/MPL/2.0/.
 
-from marionette_driver.marionette import HTMLElement
-
-from decorators import use_class_as_property
+from firefox_puppeteer.testcases.base import FirefoxPuppeteerMixin
 
 
 __version__ = '52.0.0'
-
-
-class Puppeteer(object):
-    """The puppeteer class is used to expose libraries to test cases.
-
-    example:
-    `class MyTestCase(MarionetteTestCase, Puppeteer)`
-
-    Each library can be referenced by its puppeteer name as a member of a
-    the TestCase instance. For example, from within a test method, the
-    "current_window" member of the "Browser" class can be accessed via
-    "self.browser.current_window".
-    """
-
-    def __init__(self):
-        self.marionette = None
-
-    def get_marionette(self):
-        return self.marionette
-
-    @use_class_as_property('api.appinfo.AppInfo')
-    def appinfo(self):
-        """
-        Provides access to members of the appinfo  api.
-
-        See the :class:`~api.appinfo.AppInfo` reference.
-        """
-
-    @use_class_as_property('api.keys.Keys')
-    def keys(self):
-        """
-        Provides a definition of control keys to use with keyboard shortcuts.
-        For example, keys.CONTROL or keys.ALT.
-        See the :class:`~api.keys.Keys` reference.
-        """
-
-    @use_class_as_property('api.places.Places')
-    def places(self):
-        """Provides low-level access to several bookmark and history related actions.
-
-        See the :class:`~api.places.Places` reference.
-        """
-
-    @use_class_as_property('api.utils.Utils')
-    def utils(self):
-        """Provides an api for interacting with utility actions.
-
-        See the :class:`~api.utils.Utils` reference.
-        """
-
-    @property
-    def platform(self):
-        """Returns the lowercased platform name.
-
-        :returns: Platform name
-        """
-        return self.marionette.session_capabilities['platformName']
-
-    @use_class_as_property('api.prefs.Preferences')
-    def prefs(self):
-        """
-        Provides an api for setting and inspecting preferences, as see in
-        about:config.
-
-        See the :class:`~api.prefs.Preferences` reference.
-        """
-
-    @use_class_as_property('api.security.Security')
-    def security(self):
-        """
-        Provides an api for accessing security related properties and helpers.
-
-        See the :class:`~api.security.Security` reference.
-        """
-
-    @use_class_as_property('ui.windows.Windows')
-    def windows(self):
-        """
-        Provides shortcuts to the top-level windows.
-
-        See the :class:`~ui.window.Windows` reference.
-        """
-
-
-class DOMElement(HTMLElement):
-    """
-    Class that inherits from HTMLElement and provides a way for subclasses to
-    expose new api's.
-    """
-
-    def __new__(cls, element):
-        instance = object.__new__(cls)
-        instance.__dict__ = element.__dict__.copy()
-        setattr(instance, 'inner', element)
-
-        return instance
-
-    def __init__(self, element):
-        pass
-
-    def get_marionette(self):
-        return self.marionette
--- a/testing/puppeteer/firefox/firefox_puppeteer/api/keys.py
+++ b/testing/puppeteer/firefox/firefox_puppeteer/api/keys.py
@@ -4,17 +4,15 @@
 
 import marionette_driver
 
 
 class Keys(marionette_driver.keys.Keys):
     """Proxy to marionette's keys with an "accel" provided for convenience
     testing across platforms."""
 
-    def __init__(self, marionette_getter):
-        self.marionette_getter = marionette_getter
-
-        caps = self.marionette_getter().session_capabilities
+    def __init__(self, marionette):
+        caps = marionette.session_capabilities
         self.isDarwin = caps['platformName'] == 'darwin'
 
     @property
     def ACCEL(self):
         return self.META if self.isDarwin else self.CONTROL
--- a/testing/puppeteer/firefox/firefox_puppeteer/api/utils.py
+++ b/testing/puppeteer/firefox/firefox_puppeteer/api/utils.py
@@ -8,17 +8,17 @@ from firefox_puppeteer.base import BaseL
 
 
 class Utils(BaseLib):
     """Low-level access to utility actions."""
 
     def __init__(self, *args, **kwargs):
         BaseLib.__init__(self, *args, **kwargs)
 
-        self._permissions = Permissions(lambda: self.marionette)
+        self._permissions = Permissions(self.marionette)
 
     @property
     def permissions(self):
         """Handling of various permissions for hosts.
 
         :returns: Instance of the Permissions class.
         """
         return self._permissions
--- a/testing/puppeteer/firefox/firefox_puppeteer/base.py
+++ b/testing/puppeteer/firefox/firefox_puppeteer/base.py
@@ -1,23 +1,10 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this file,
 # You can obtain one at http://mozilla.org/MPL/2.0/.
 
 
 class BaseLib(object):
-    """A base class that handles lazily setting the "client" class attribute."""
-
-    def __init__(self, marionette_getter):
-        if not callable(marionette_getter):
-            raise TypeError('Invalid callback for "marionette_getter": %s' % marionette_getter)
-
-        self._marionette = None
-        self._marionette_getter = marionette_getter
+    """The base class for various library classes."""
 
-    @property
-    def marionette(self):
-        if self._marionette is None:
-            self._marionette = self._marionette_getter()
-        return self._marionette
-
-    def get_marionette(self):
-        return self.marionette
+    def __init__(self, marionette):
+        self.marionette = marionette
--- a/testing/puppeteer/firefox/firefox_puppeteer/decorators.py
+++ b/testing/puppeteer/firefox/firefox_puppeteer/decorators.py
@@ -23,13 +23,13 @@ class use_class_as_property(object):
         @wraps(func)
         def _(cls, *args, **kwargs):
             tag = '_{}_{}'.format(self.mod_name, self.cls_name)
             prop = getattr(cls, tag, None)
 
             if not prop:
                 module = import_module('.{}'.format(self.mod_name),
                                        'firefox_puppeteer')
-                prop = getattr(module, self.cls_name)(cls.get_marionette)
+                prop = getattr(module, self.cls_name)(cls.marionette)
                 setattr(cls, tag, prop)
             func(cls, *args, **kwargs)
             return prop
         return _
new file mode 100644
--- /dev/null
+++ b/testing/puppeteer/firefox/firefox_puppeteer/mixins.py
@@ -0,0 +1,171 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+import unittest
+
+from firefox_puppeteer.decorators import use_class_as_property
+from firefox_puppeteer.ui.browser.window import BrowserWindow
+
+
+class FirefoxPuppeteerMixin(unittest.TestCase):
+    """The puppeteer mixin class is used to expose additional UI and API libraries
+    to test cases.
+
+    Each library can be referenced by its Puppeteer name as a member of a
+    TestCase instance. For example, from within a test method, the
+    `current_window` member of the `Browser` class can be accessed via
+    `self.browser.current_window`.
+
+    This is designed to enhance MarionetteTestCase by inserting the Puppeteer
+    mixin class (so Firefox specific API modules are exposed to test scope) and
+    providing common set-up and tear-down code for Firefox tests.
+
+    Child classes are expected to also subclass MarionetteTestCase such that
+    MarionetteTestCase is inserted into the MRO before FirefoxPuppeteerMixin.
+
+    example:
+    `class MyTestCase(BaseFirefoxTestCase, FirefoxPuppeteerMixin)`
+
+    The key role of MarionetteTestCase is to set self.marionette appropriately
+    in `setUp()`. Any TestCase class that satisfies this requirement is
+    compatible with this class.
+
+    If you're extending the inheritance tree further to make specialized
+    TestCases, favour the use of super() as opposed to explicit calls to a
+    parent class.
+    """
+
+    def _check_and_fix_leaked_handles(self):
+        handle_count = len(self.marionette.window_handles)
+        url = []
+
+        try:
+            # Verify the existence of leaked tabs and print their URLs.
+            if self._start_handle_count < handle_count:
+                message = ('A test must not leak window handles. This test started with '
+                           '%s open top level browsing contexts, but ended with %s.'
+                           ' Remaining Tabs URLs:') % (self._start_handle_count, handle_count)
+                with self.marionette.using_context('content'):
+                    for tab in self.marionette.window_handles:
+                        if tab not in self._init_tab_handles:
+                            url.append(' %s' % self.marionette.get_url())
+                self.assertListEqual(self._init_tab_handles, self.marionette.window_handles,
+                                     message + ','.join(url))
+        finally:
+            # For clean-up make sure we work on a proper browser window
+            if not self.browser or self.browser.closed:
+                # Find a proper replacement browser window
+                # TODO: We have to make this less error prone in case no browser is open.
+                self.browser = self.windows.switch_to(lambda win: type(win) is BrowserWindow)
+
+            # Ensure to close all the remaining chrome windows to give following
+            # tests a proper start condition and make them not fail.
+            self.windows.close_all([self.browser])
+            self.browser.focus()
+
+            # Also close all remaining tabs
+            self.browser.tabbar.close_all_tabs([self.browser.tabbar.tabs[0]])
+            self.browser.tabbar.tabs[0].switch_to()
+
+    def restart(self, **kwargs):
+        """Restart Firefox and re-initialize data.
+
+        :param flags: Specific restart flags for Firefox
+        """
+        if kwargs.get('clean'):
+            self.marionette.restart(clean=True)
+        else:
+            self.marionette.restart(in_app=True)
+
+        # Ensure that we always have a valid browser instance available
+        self.browser = self.windows.switch_to(lambda win: type(win) is BrowserWindow)
+
+    def setUp(self, *args, **kwargs):
+        super(FirefoxPuppeteerMixin, self).setUp(*args, **kwargs)
+
+        self._start_handle_count = len(self.marionette.window_handles)
+        self._init_tab_handles = self.marionette.window_handles
+        self.marionette.set_context('chrome')
+
+        self.browser = self.windows.current
+        self.browser.focus()
+        with self.marionette.using_context(self.marionette.CONTEXT_CONTENT):
+            # Ensure that we have a default page opened
+            self.marionette.navigate(self.prefs.get_pref('browser.newtab.url'))
+
+    def tearDown(self, *args, **kwargs):
+        self.marionette.set_context('chrome')
+
+        try:
+            self.prefs.restore_all_prefs()
+
+            # This code should be run after all other tearDown code
+            # so that in case of a failure, further tests will not run
+            # in a state that is more inconsistent than necessary.
+            self._check_and_fix_leaked_handles()
+        finally:
+            super(FirefoxPuppeteerMixin, self).tearDown(*args, **kwargs)
+
+    @use_class_as_property('api.appinfo.AppInfo')
+    def appinfo(self):
+        """
+        Provides access to members of the appinfo  api.
+
+        See the :class:`~api.appinfo.AppInfo` reference.
+        """
+
+    @use_class_as_property('api.keys.Keys')
+    def keys(self):
+        """
+        Provides a definition of control keys to use with keyboard shortcuts.
+        For example, keys.CONTROL or keys.ALT.
+        See the :class:`~api.keys.Keys` reference.
+        """
+
+    @use_class_as_property('api.places.Places')
+    def places(self):
+        """Provides low-level access to several bookmark and history related actions.
+
+        See the :class:`~api.places.Places` reference.
+        """
+
+    @use_class_as_property('api.utils.Utils')
+    def utils(self):
+        """Provides an api for interacting with utility actions.
+
+        See the :class:`~api.utils.Utils` reference.
+        """
+
+    @property
+    def platform(self):
+        """Returns the lowercased platform name.
+
+        :returns: Platform name
+        """
+        return self.marionette.session_capabilities['platformName']
+
+    @use_class_as_property('api.prefs.Preferences')
+    def prefs(self):
+        """
+        Provides an api for setting and inspecting preferences, as see in
+        about:config.
+
+        See the :class:`~api.prefs.Preferences` reference.
+        """
+
+    @use_class_as_property('api.security.Security')
+    def security(self):
+        """
+        Provides an api for accessing security related properties and helpers.
+
+        See the :class:`~api.security.Security` reference.
+        """
+
+    @use_class_as_property('ui.windows.Windows')
+    def windows(self):
+        """
+        Provides shortcuts to the top-level windows.
+
+        See the :class:`~ui.window.Windows` reference.
+        """
--- a/testing/puppeteer/firefox/firefox_puppeteer/testcases/__init__.py
+++ b/testing/puppeteer/firefox/firefox_puppeteer/testcases/__init__.py
@@ -1,5 +1,5 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
-from firefox_puppeteer.testcases.base import BaseFirefoxTestCase
+from firefox_puppeteer.testcases.base import FirefoxPuppeteerMixin
--- a/testing/puppeteer/firefox/firefox_puppeteer/testcases/base.py
+++ b/testing/puppeteer/firefox/firefox_puppeteer/testcases/base.py
@@ -1,43 +1,30 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 import unittest
 
-from firefox_puppeteer import Puppeteer
+from firefox_puppeteer.decorators import use_class_as_property
 from firefox_puppeteer.ui.browser.window import BrowserWindow
 
 
-class BaseFirefoxTestCase(unittest.TestCase, Puppeteer):
-    """Base TestCase class for Firefox Desktop tests.
-
-    This is designed to enhance MarionetteTestCase by inserting the Puppeteer
-    mixin class (so Firefox specific API modules are exposed to test scope) and
-    providing common set-up and tear-down code for Firefox tests.
-
-    Child classes are expected to also subclass MarionetteTestCase such that
-    MarionetteTestCase is inserted into the MRO after FirefoxTestCase but before
-    unittest.TestCase.
+class FirefoxPuppeteerMixin(unittest.TestCase):
+    """The puppeteer mixin class is used to expose additional ui and api libraries
+    to test cases.
 
     example:
-    `class AwesomeTestCase(FirefoxTestCase, MarionetteTestCase)`
-
-    The key role of MarionetteTestCase is to set self.marionette appropriately
-    in `__init__`. Any TestCase class that satisfies this requirement is
-    compatible with this class.
+    `class MyTestCase(BaseFirefoxTestCase, FirefoxPuppeteerMixin)`
 
-    If you're extending the inheritance tree further to make specialized
-    TestCases, favour the use of super() as opposed to explicit calls to a
-    parent class.
-
+    Each library can be referenced by its puppeteer name as a member of a
+    the TestCase instance. For example, from within a test method, the
+    "current_window" member of the "Browser" class can be accessed via
+    "self.browser.current_window".
     """
-    def __init__(self, *args, **kwargs):
-        super(BaseFirefoxTestCase, self).__init__(*args, **kwargs)
 
     def _check_and_fix_leaked_handles(self):
         handle_count = len(self.marionette.window_handles)
         url = []
 
         try:
             # Verify the existence of leaked tabs and print their URLs.
             if self._start_handle_count < handle_count:
@@ -75,17 +62,17 @@ class BaseFirefoxTestCase(unittest.TestC
             self.marionette.restart(clean=True)
         else:
             self.marionette.restart(in_app=True)
 
         # Ensure that we always have a valid browser instance available
         self.browser = self.windows.switch_to(lambda win: type(win) is BrowserWindow)
 
     def setUp(self, *args, **kwargs):
-        super(BaseFirefoxTestCase, self).setUp(*args, **kwargs)
+        super(FirefoxPuppeteerMixin, self).setUp(*args, **kwargs)
 
         self._start_handle_count = len(self.marionette.window_handles)
         self._init_tab_handles = self.marionette.window_handles
         self.marionette.set_context('chrome')
 
         self.browser = self.windows.current
         self.browser.focus()
         with self.marionette.using_context(self.marionette.CONTEXT_CONTENT):
@@ -98,9 +85,72 @@ class BaseFirefoxTestCase(unittest.TestC
         try:
             self.prefs.restore_all_prefs()
 
             # This code should be run after all other tearDown code
             # so that in case of a failure, further tests will not run
             # in a state that is more inconsistent than necessary.
             self._check_and_fix_leaked_handles()
         finally:
-            super(BaseFirefoxTestCase, self).tearDown(*args, **kwargs)
+            super(FirefoxPuppeteerMixin, self).tearDown(*args, **kwargs)
+
+    @use_class_as_property('api.appinfo.AppInfo')
+    def appinfo(self):
+        """
+        Provides access to members of the appinfo  api.
+
+        See the :class:`~api.appinfo.AppInfo` reference.
+        """
+
+    @use_class_as_property('api.keys.Keys')
+    def keys(self):
+        """
+        Provides a definition of control keys to use with keyboard shortcuts.
+        For example, keys.CONTROL or keys.ALT.
+        See the :class:`~api.keys.Keys` reference.
+        """
+
+    @use_class_as_property('api.places.Places')
+    def places(self):
+        """Provides low-level access to several bookmark and history related actions.
+
+        See the :class:`~api.places.Places` reference.
+        """
+
+    @use_class_as_property('api.utils.Utils')
+    def utils(self):
+        """Provides an api for interacting with utility actions.
+
+        See the :class:`~api.utils.Utils` reference.
+        """
+
+    @property
+    def platform(self):
+        """Returns the lowercased platform name.
+
+        :returns: Platform name
+        """
+        return self.marionette.session_capabilities['platformName']
+
+    @use_class_as_property('api.prefs.Preferences')
+    def prefs(self):
+        """
+        Provides an api for setting and inspecting preferences, as see in
+        about:config.
+
+        See the :class:`~api.prefs.Preferences` reference.
+        """
+
+    @use_class_as_property('api.security.Security')
+    def security(self):
+        """
+        Provides an api for accessing security related properties and helpers.
+
+        See the :class:`~api.security.Security` reference.
+        """
+
+    @use_class_as_property('ui.windows.Windows')
+    def windows(self):
+        """
+        Provides shortcuts to the top-level windows.
+
+        See the :class:`~ui.window.Windows` reference.
+        """
--- a/testing/puppeteer/firefox/firefox_puppeteer/ui/about_window/deck.py
+++ b/testing/puppeteer/firefox/firefox_puppeteer/ui/about_window/deck.py
@@ -22,17 +22,17 @@ class Deck(UIBaseLib):
                    'checkingForUpdates': CheckingForUpdatesPanel,
                    'downloadAndInstall': DownloadAndInstallPanel,
                    'downloadFailed': DownloadFailedPanel,
                    'downloading': DownloadingPanel,
                    'noUpdatesFound': NoUpdatesFoundPanel,
                    }
 
         panel = self.element.find_element(By.ID, panel_id)
-        return mapping.get(panel_id, Panel)(lambda: self.marionette, self.window, panel)
+        return mapping.get(panel_id, Panel)(self.marionette, self.window, panel)
 
     # Properties for visual elements of the deck #
 
     @property
     def apply(self):
         """The :class:`ApplyPanel` instance for the apply panel.
 
         :returns: :class:`ApplyPanel` instance.
--- a/testing/puppeteer/firefox/firefox_puppeteer/ui/about_window/window.py
+++ b/testing/puppeteer/firefox/firefox_puppeteer/ui/about_window/window.py
@@ -24,12 +24,12 @@ class AboutWindow(BaseWindow):
     def deck(self):
         """The :class:`Deck` instance which represents the deck.
 
         :returns: Reference to the deck.
         """
         self.switch_to()
 
         deck = self.window_element.find_element(By.ID, 'updateDeck')
-        return Deck(lambda: self.marionette, self, deck)
+        return Deck(self.marionette, self, deck)
 
 
 Windows.register_window(AboutWindow.window_type, AboutWindow)
--- a/testing/puppeteer/firefox/firefox_puppeteer/ui/browser/tabbar.py
+++ b/testing/puppeteer/firefox/firefox_puppeteer/ui/browser/tabbar.py
@@ -5,34 +5,33 @@
 from marionette_driver import (
     By, Wait
 )
 
 from marionette_driver.errors import NoSuchElementException
 
 import firefox_puppeteer.errors as errors
 
-from firefox_puppeteer import DOMElement
 from firefox_puppeteer.api.security import Security
-from firefox_puppeteer.ui_base_lib import UIBaseLib
+from firefox_puppeteer.ui_base_lib import DOMElement, UIBaseLib
 
 
 class TabBar(UIBaseLib):
     """Wraps the tabs toolbar DOM element inside a browser window."""
 
     # Properties for visual elements of the tabs toolbar #
 
     @property
     def menupanel(self):
         """A :class:`MenuPanel` instance which represents the menu panel
         at the far right side of the tabs toolbar.
 
         :returns: :class:`MenuPanel` instance.
         """
-        return MenuPanel(lambda: self.marionette, self.window)
+        return MenuPanel(self.marionette, self.window)
 
     @property
     def newtab_button(self):
         """The DOM element which represents the new tab button.
 
         :returns: Reference to the new tab button.
         """
         return self.toolbar.find_element(By.ANON_ATTRIBUTE, {'anonid': 'tabs-newtab-button'})
@@ -40,17 +39,17 @@ class TabBar(UIBaseLib):
     @property
     def tabs(self):
         """List of all the :class:`Tab` instances of the current browser window.
 
         :returns: List of :class:`Tab` instances.
         """
         tabs = self.toolbar.find_elements(By.TAG_NAME, 'tab')
 
-        return [Tab(lambda: self.marionette, self.window, tab) for tab in tabs]
+        return [Tab(self.marionette, self.window, tab) for tab in tabs]
 
     @property
     def toolbar(self):
         """The DOM element which represents the tab toolbar.
 
         :returns: Reference to the tabs toolbar.
         """
         return self.element
@@ -205,17 +204,17 @@ class TabBar(UIBaseLib):
 
 
 class Tab(UIBaseLib):
     """Wraps a tab DOM element."""
 
     def __init__(self, marionette_getter, window, element):
         UIBaseLib.__init__(self, marionette_getter, window, element)
 
-        self._security = Security(lambda: self.marionette)
+        self._security = Security(self.marionette)
         self._handle = None
 
     # Properties for visual elements of tabs #
 
     @property
     def close_button(self):
         """The DOM element which represents the tab close button.
 
--- a/testing/puppeteer/firefox/firefox_puppeteer/ui/browser/toolbars.py
+++ b/testing/puppeteer/firefox/firefox_puppeteer/ui/browser/toolbars.py
@@ -44,17 +44,17 @@ class NavBar(UIBaseLib):
     def locationbar(self):
         """Provides access to the DOM elements contained in the
         locationbar.
 
         See the :class:`LocationBar` reference.
         """
         if not self._locationbar:
             urlbar = self.marionette.find_element(By.ID, 'urlbar')
-            self._locationbar = LocationBar(lambda: self.marionette, self.window, urlbar)
+            self._locationbar = LocationBar(self.marionette, self.window, urlbar)
 
         return self._locationbar
 
     @property
     def menu_button(self):
         """Provides access to the DOM element menu button in the navbar.
 
         :returns: Reference to the menu button element.
@@ -83,17 +83,17 @@ class LocationBar(UIBaseLib):
     @property
     def autocomplete_results(self):
         """Provides access to and methods for the location bar
         autocomplete results.
 
         See the :class:`AutocompleteResults` reference."""
         if not self._autocomplete_results:
             popup = self.marionette.find_element(By.ID, 'PopupAutoCompleteRichResult')
-            self._autocomplete_results = AutocompleteResults(lambda: self.marionette,
+            self._autocomplete_results = AutocompleteResults(self.marionette,
                                                              self.window, popup)
 
         return self._autocomplete_results
 
     def clear(self):
         """Clears the contents of the url bar (via the DELETE shortcut)."""
         self.focus('shortcut')
         self.urlbar.send_keys(keys.Keys.DELETE)
@@ -207,17 +207,17 @@ class LocationBar(UIBaseLib):
     def identity_popup(self):
         """Provides utility members for accessing and manipulating the
         identity popup.
 
         See the :class:`IdentityPopup` reference.
         """
         if not self._identity_popup:
             popup = self.marionette.find_element(By.ID, 'identity-popup')
-            self._identity_popup = IdentityPopup(lambda: self.marionette,
+            self._identity_popup = IdentityPopup(self.marionette,
                                                  self.window, popup)
 
         return self._identity_popup
 
     def load_url(self, url):
         """Load the specified url in the location bar by synthesized
         keystrokes.
 
@@ -447,17 +447,17 @@ class IdentityPopup(UIBaseLib):
     def view(self):
         """Provides utility members for accessing and manipulating the
         identity popup's multi view.
 
         See the :class:`IdentityPopupMultiView` reference.
         """
         if not self._view:
             view = self.marionette.find_element(By.ID, 'identity-popup-multiView')
-            self._view = IdentityPopupMultiView(lambda: self.marionette, self.window, view)
+            self._view = IdentityPopupMultiView(self.marionette, self.window, view)
 
         return self._view
 
 
 class IdentityPopupMultiView(UIBaseLib):
 
     def _create_view_for_id(self, view_id):
         """Creates an instance of :class:`IdentityPopupView` for the specified view id.
@@ -466,17 +466,17 @@ class IdentityPopupMultiView(UIBaseLib):
 
         :returns: :class:`IdentityPopupView` instance
         """
         mapping = {'identity-popup-mainView': IdentityPopupMainView,
                    'identity-popup-securityView': IdentityPopupSecurityView,
                    }
 
         view = self.marionette.find_element(By.ID, view_id)
-        return mapping.get(view_id, IdentityPopupView)(lambda: self.marionette, self.window, view)
+        return mapping.get(view_id, IdentityPopupView)(self.marionette, self.window, view)
 
     @property
     def main(self):
         """The DOM element which represents the main view.
 
         :returns: Reference to the main view.
         """
         return self._create_view_for_id('identity-popup-mainView')
--- a/testing/puppeteer/firefox/firefox_puppeteer/ui/browser/window.py
+++ b/testing/puppeteer/firefox/firefox_puppeteer/ui/browser/window.py
@@ -74,17 +74,17 @@ class BrowserWindow(BaseWindow):
         the back, forward and home buttons. It also contains the location bar.
 
         See the :class:`~ui.browser.toolbars.NavBar` reference.
         """
         self.switch_to()
 
         if not self._navbar:
             navbar = self.window_element.find_element(By.ID, 'nav-bar')
-            self._navbar = NavBar(lambda: self.marionette, self, navbar)
+            self._navbar = NavBar(self.marionette, self, navbar)
 
         return self._navbar
 
     @property
     def notification(self):
         """Provides access to the currently displayed notification."""
 
         notifications_map = {
@@ -96,17 +96,17 @@ class BrowserWindow(BaseWindow):
         }
 
         try:
             notification = self.window_element.find_element(
                 By.CSS_SELECTOR, '#notification-popup popupnotification')
 
             notification_id = notification.get_attribute('id')
             return notifications_map.get(notification_id, BaseNotification)(
-                lambda: self.marionette, self, notification)
+                self.marionette, self, notification)
 
         except NoSuchElementException:
             return None  # no notification is displayed
 
     def wait_for_notification(self, notification_class=BaseNotification,
                               timeout=5):
         """Waits for the specified notification to be displayed.
 
@@ -137,17 +137,17 @@ class BrowserWindow(BaseWindow):
         """Provides access to the tab bar.
 
         See the :class:`~ui.browser.tabbar.TabBar` reference.
         """
         self.switch_to()
 
         if not self._tabbar:
             tabbrowser = self.window_element.find_element(By.ID, 'tabbrowser-tabs')
-            self._tabbar = TabBar(lambda: self.marionette, self, tabbrowser)
+            self._tabbar = TabBar(self.marionette, self, tabbrowser)
 
         return self._tabbar
 
     def close(self, trigger='menu', force=False):
         """Closes the current browser window by using the specified trigger.
 
         :param trigger: Optional, method to close the current browser window. This can
          be a string with one of `menu` or `shortcut`, or a callback which gets triggered
--- a/testing/puppeteer/firefox/firefox_puppeteer/ui/menu.py
+++ b/testing/puppeteer/firefox/firefox_puppeteer/ui/menu.py
@@ -1,17 +1,17 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this file,
 # You can obtain one at http://mozilla.org/MPL/2.0/.
 
 from marionette_driver import By
 from marionette_driver.errors import NoSuchElementException
 
-from firefox_puppeteer import DOMElement
 from firefox_puppeteer.base import BaseLib
+from firefox_puppeteer.ui_base_lib import DOMElement
 
 
 class MenuBar(BaseLib):
     """Wraps the menubar DOM element inside a browser window."""
 
     @property
     def menus(self):
         """A list of :class:`MenuElement` instances corresponding to
--- a/testing/puppeteer/firefox/firefox_puppeteer/ui/pageinfo/deck.py
+++ b/testing/puppeteer/firefox/firefox_puppeteer/ui/pageinfo/deck.py
@@ -20,17 +20,17 @@ class Deck(UIBaseLib):
         mapping = {'feedPanel': FeedPanel,
                    'generalPanel': GeneralPanel,
                    'mediaPanel': MediaPanel,
                    'permPanel': PermissionsPanel,
                    'securityPanel': SecurityPanel
                    }
 
         panel = self.element.find_element(By.ID, panel_id)
-        return mapping.get(panel_id, Panel)(lambda: self.marionette, self.window, panel)
+        return mapping.get(panel_id, Panel)(self.marionette, self.window, panel)
 
     # Properties for visual elements of the deck #
 
     @property
     def feed(self):
         """The :class:`FeedPanel` instance for the feed panel.
 
         :returns: :class:`FeedPanel` instance.
--- a/testing/puppeteer/firefox/firefox_puppeteer/ui/pageinfo/window.py
+++ b/testing/puppeteer/firefox/firefox_puppeteer/ui/pageinfo/window.py
@@ -28,17 +28,17 @@ class PageInfoWindow(BaseWindow):
 
     @property
     def deck(self):
         """The :class:`Deck` instance which represents the deck.
 
         :returns: Reference to the deck.
         """
         deck = self.window_element.find_element(By.ID, 'mainDeck')
-        return Deck(lambda: self.marionette, self, deck)
+        return Deck(self.marionette, self, deck)
 
     def close(self, trigger='shortcut', force=False):
         """Closes the current page info window by using the specified trigger.
 
         :param trigger: Optional, method to close the current window. This can
          be a string with one of `menu` (OS X only) or `shortcut`, or a callback
          which gets triggered with the current :class:`PageInfoWindow` as parameter.
          Defaults to `shortcut`.
--- a/testing/puppeteer/firefox/firefox_puppeteer/ui/update_wizard/dialog.py
+++ b/testing/puppeteer/firefox/firefox_puppeteer/ui/update_wizard/dialog.py
@@ -29,17 +29,17 @@ class UpdateWizardDialog(BaseWindow):
     @property
     def wizard(self):
         """The :class:`Wizard` instance which represents the wizard.
 
         :returns: Reference to the wizard.
         """
         # The deck is also the root element
         wizard = self.marionette.find_element(By.ID, 'updates')
-        return Wizard(lambda: self.marionette, self, wizard)
+        return Wizard(self.marionette, self, wizard)
 
     def select_next_page(self):
         """Clicks on "Next" button, and waits for the next page to show up."""
         current_panel = self.wizard.selected_panel
 
         self.wizard.next_button.click()
         Wait(self.marionette).until(
             lambda _: self.wizard.selected_panel != current_panel,
--- a/testing/puppeteer/firefox/firefox_puppeteer/ui/update_wizard/wizard.py
+++ b/testing/puppeteer/firefox/firefox_puppeteer/ui/update_wizard/wizard.py
@@ -37,17 +37,17 @@ class Wizard(UIBaseLib):
                    'updatesfoundbasic': UpdatesFoundBasicPanel,
 
                    # TODO: Remove once we no longer support version Firefox 45.0ESR
                    'incompatibleCheck': IncompatibleCheckPanel,
                    'incompatibleList': IncompatibleListPanel,
                    }
 
         panel = self.element.find_element(By.ID, panel_id)
-        return mapping.get(panel_id, Panel)(lambda: self.marionette, self.window, panel)
+        return mapping.get(panel_id, Panel)(self.marionette, self.window, panel)
 
     # Properties for visual buttons of the wizard #
 
     @property
     def _buttons(self):
         return self.element.find_element(By.ANON_ATTRIBUTE, {'anonid': 'Buttons'})
 
     @property
--- a/testing/puppeteer/firefox/firefox_puppeteer/ui/windows.py
+++ b/testing/puppeteer/firefox/firefox_puppeteer/ui/windows.py
@@ -113,17 +113,17 @@ class Windows(BaseLib):
                 )
 
             finally:
                 # Ensure to switch back to the original window
                 if handle != current_handle:
                     self.switch_to(current_handle)
 
             if window_type in self.windows_map:
-                window = self.windows_map[window_type](lambda: self.marionette, handle)
+                window = self.windows_map[window_type](self.marionette, handle)
             else:
                 raise errors.UnknownWindowError('Unknown window type "%s" for handle: "%s"' %
                                                 (window_type, handle))
 
             if expected_class is not None and type(window) is not expected_class:
                 raise errors.UnexpectedWindowTypeError('Expected window "%s" but got "%s"' %
                                                        (expected_class, type(window)))
 
@@ -214,21 +214,21 @@ class Windows(BaseLib):
 
 class BaseWindow(BaseLib):
     """Base class for any kind of chrome window."""
 
     # l10n class attributes will be set by each window class individually
     dtds = []
     properties = []
 
-    def __init__(self, marionette_getter, window_handle):
-        BaseLib.__init__(self, marionette_getter)
-        self._l10n = L10n(self.get_marionette)
-        self._prefs = Preferences(self.get_marionette)
-        self._windows = Windows(self.get_marionette)
+    def __init__(self, marionette, window_handle):
+        BaseLib.__init__(self, marionette)
+        self._l10n = L10n(self.marionette)
+        self._prefs = Preferences(self.marionette)
+        self._windows = Windows(self.marionette)
 
         if window_handle not in self.marionette.chrome_window_handles:
             raise errors.UnknownWindowError('Window with handle "%s" does not exist' %
                                             window_handle)
         self._handle = window_handle
 
     def __eq__(self, other):
         return self.handle == other.handle
--- a/testing/puppeteer/firefox/firefox_puppeteer/ui_base_lib.py
+++ b/testing/puppeteer/firefox/firefox_puppeteer/ui_base_lib.py
@@ -3,25 +3,42 @@
 # You can obtain one at http://mozilla.org/MPL/2.0/.
 
 from marionette_driver.marionette import HTMLElement
 
 from firefox_puppeteer.base import BaseLib
 from firefox_puppeteer.ui.windows import BaseWindow
 
 
+class DOMElement(HTMLElement):
+    """
+    Class that inherits from HTMLElement and provides a way for subclasses to
+    expose new api's.
+    """
+
+    def __new__(cls, element):
+        instance = object.__new__(cls)
+        instance.__dict__ = element.__dict__.copy()
+        setattr(instance, 'inner', element)
+
+        return instance
+
+    def __init__(self, element):
+        pass
+
+
 class UIBaseLib(BaseLib):
     """A base class for all UI element wrapper classes inside a chrome window."""
 
-    def __init__(self, marionette_getter, window, element):
+    def __init__(self, marionette, window, element):
 
         assert isinstance(window, BaseWindow)
         assert isinstance(element, HTMLElement)
 
-        BaseLib.__init__(self, marionette_getter)
+        BaseLib.__init__(self, marionette)
         self._window = window
         self._element = element
 
     @property
     def element(self):
         """Returns the reference to the underlying DOM element.
 
         :returns: Reference to the DOM element