Bug 1149618 - Implement push_permission in Marionette, r=dburns
authorJonathan Griffin <jgriffin@mozilla.com>
Mon, 04 May 2015 09:49:08 -0700
changeset 272152 73ec15af7075580103103974c8c4f495e83ff4d5
parent 272151 42484455a38391f548969276ce2b1032745c6d85
child 272153 82a9bf38481bcf9492e14abc7c1dec2c614ec776
push id4830
push userjlund@mozilla.com
push dateMon, 29 Jun 2015 20:18:48 +0000
treeherdermozilla-beta@4c2175bb0420 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdburns
bugs1149618
milestone40.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1149618 - Implement push_permission in Marionette, r=dburns
testing/marionette/client/marionette/marionette_test.py
testing/marionette/client/marionette/runner/base.py
testing/marionette/client/marionette/runner/mixins/reporting.py
testing/marionette/client/marionette/tests/unit/test_getactiveframe_oop.py
testing/marionette/client/marionette/tests/unit/test_switch_remote_frame.py
testing/marionette/driver/marionette_driver/marionette.py
--- a/testing/marionette/client/marionette/marionette_test.py
+++ b/testing/marionette/client/marionette/marionette_test.py
@@ -641,16 +641,17 @@ class MarionetteTestCase(CommonTestCase)
         CommonTestCase.setUp(self)
         self.marionette.test_name = self.test_name
         self.marionette.execute_script("log('TEST-START: %s:%s')" %
                                        (self.filepath.replace('\\', '\\\\'), self.methodName))
 
     def tearDown(self):
         if not self.marionette.check_for_crash():
            self.marionette.set_context("content")
+           self.marionette.clear_imported_scripts()
            self.marionette.execute_script("log('TEST-END: %s:%s')" %
                                           (self.filepath.replace('\\', '\\\\'), self.methodName))
         self.marionette.test_name = None
         CommonTestCase.tearDown(self)
 
     def get_new_emulator(self):
         self.extra_emulator_index += 1
         if len(self.marionette.extra_emulators) == self.extra_emulator_index:
--- a/testing/marionette/client/marionette/runner/base.py
+++ b/testing/marionette/client/marionette/runner/base.py
@@ -488,16 +488,17 @@ class BaseMarionetteOptions(OptionParser
             handler(options, tests)
 
         return (options, tests)
 
 
 class BaseMarionetteTestRunner(object):
 
     textrunnerclass = MarionetteTextTestRunner
+    driverclass = Marionette
 
     def __init__(self, address=None, emulator=None, emulator_binary=None,
                  emulator_img=None, emulator_res='480x800', homedir=None,
                  app=None, app_args=None, binary=None, profile=None,
                  logger=None, no_window=False, logdir=None, logcat_stdout=False,
                  xml_output=None, repeat=0, testvars=None, tree=None, type=None,
                  device_serial=None, symbols_path=None, timeout=None,
                  shuffle=False, shuffle_seed=random.randint(0, sys.maxint),
@@ -691,17 +692,17 @@ class BaseMarionetteTestRunner(object):
                 'emulator_img': self.emulator_img,
                 'emulator_res': self.emulator_res,
                 'no_window': self.no_window,
                 'sdcard': self.sdcard,
             })
         return kwargs
 
     def start_marionette(self):
-        self.marionette = Marionette(**self._build_kwargs())
+        self.marionette = self.driverclass(**self._build_kwargs())
 
     def launch_test_container(self):
         if self.marionette.session is None:
             self.marionette.start_session()
         self.marionette.set_context(self.marionette.CONTEXT_CONTENT)
 
         result = self.marionette.execute_async_script("""
 if((navigator.mozSettings == undefined) || (navigator.mozSettings == null) || (navigator.mozApps == undefined) || (navigator.mozApps == null)) {
--- a/testing/marionette/client/marionette/runner/mixins/reporting.py
+++ b/testing/marionette/client/marionette/runner/mixins/reporting.py
@@ -250,20 +250,20 @@ class HTMLReportingTestResultMixin(objec
         # In the event we're gathering debug without starting a session, skip marionette commands
         if self.marionette.session is not None:
             try:
                 self.marionette.set_context(self.marionette.CONTEXT_CHROME)
                 debug['screenshot'] = self.marionette.screenshot()
                 self.marionette.set_context(self.marionette.CONTEXT_CONTENT)
                 debug['source'] = self.marionette.page_source
                 self.marionette.switch_to_frame()
+                self.marionette.push_permission('settings-read', True)
+                self.marionette.push_permission('settings-api-read', True)
                 debug['settings'] = json.dumps(self.marionette.execute_async_script("""
-SpecialPowers.addPermission('settings-read', true, document);
-SpecialPowers.addPermission('settings-api-read', true, document);
 var req = window.navigator.mozSettings.createLock().get('*');
 req.onsuccess = function() {
   marionetteScriptFinished(req.result);
-}""", special_powers=True), sort_keys=True, indent=4, separators=(',', ': '))
+}""", sandbox='system'), sort_keys=True, indent=4, separators=(',', ': '))
             except:
                 logger = get_default_logger()
                 logger.warning('Failed to gather test failure debug.', exc_info=True)
         return debug
 
--- a/testing/marionette/client/marionette/tests/unit/test_getactiveframe_oop.py
+++ b/testing/marionette/client/marionette/tests/unit/test_getactiveframe_oop.py
@@ -24,17 +24,17 @@ class TestGetActiveFrameOOP(MarionetteTe
             SpecialPowers.setBoolPref('dom.ipc.browser_frames.oop_by_default', true);
             """)
         self.marionette.execute_script("""
             SpecialPowers.setBoolPref('dom.mozBrowserFramesEnabled', true);
             """)
 
     def test_active_frame_oop(self):
         self.marionette.navigate(self.marionette.absolute_url("test.html"))
-        self.marionette.execute_script("SpecialPowers.addPermission('browser', true, document)")
+        self.marionette.push_permission('browser', True)
 
         # Create first OOP frame
         self.marionette.execute_script("""
             let iframe1 = document.createElement("iframe");
             SpecialPowers.wrap(iframe1).mozbrowser = true;
             SpecialPowers.wrap(iframe1).remote = true;
             iframe1.id = "remote_iframe1";
             iframe1.style.height = "100px";
--- a/testing/marionette/client/marionette/tests/unit/test_switch_remote_frame.py
+++ b/testing/marionette/client/marionette/tests/unit/test_switch_remote_frame.py
@@ -28,18 +28,17 @@ class TestSwitchRemoteFrame(MarionetteTe
                 try {
                   return Services.appinfo.browserTabsRemoteAutostart;
                 } catch (e) {
                   return false;
                 }""")
 
     def test_remote_frame(self):
         self.marionette.navigate(self.marionette.absolute_url("test.html"))
-        self.marionette.execute_async_script(
-            "SpecialPowers.pushPermissions([{'type': 'browser', 'allow': true, 'context': document}], marionetteScriptFinished);")
+        self.marionette.push_permission('browser', True)
         self.marionette.execute_script("""
             let iframe = document.createElement("iframe");
             SpecialPowers.wrap(iframe).mozbrowser = true;
             SpecialPowers.wrap(iframe).remote = true;
             iframe.id = "remote_iframe";
             iframe.style.height = "100px";
             iframe.style.width = "100%%";
             iframe.src = "%s";
@@ -50,18 +49,17 @@ class TestSwitchRemoteFrame(MarionetteTe
         main_process = self.marionette.execute_script("""
             return SpecialPowers.isMainProcess();
             """)
         self.assertFalse(main_process)
 
     def test_remote_frame_revisit(self):
         # test if we can revisit a remote frame (this takes a different codepath)
         self.marionette.navigate(self.marionette.absolute_url("test.html"))
-        self.marionette.execute_async_script(
-            "SpecialPowers.pushPermissions([{'type': 'browser', 'allow': true, 'context': document}], marionetteScriptFinished);")
+        self.marionette.push_permission('browser', True)
         self.marionette.execute_script("""
             let iframe = document.createElement("iframe");
             SpecialPowers.wrap(iframe).mozbrowser = true;
             SpecialPowers.wrap(iframe).remote = true;
             iframe.id = "remote_iframe";
             iframe.style.height = "100px";
             iframe.style.width = "100%%";
             iframe.src = "%s";
@@ -84,18 +82,17 @@ class TestSwitchRemoteFrame(MarionetteTe
         main_process = self.marionette.execute_script("""
             return SpecialPowers.isMainProcess();
             """)
         self.assertFalse(main_process)
 
     def test_we_can_switch_to_a_remote_frame_by_index(self):
         # test if we can revisit a remote frame (this takes a different codepath)
         self.marionette.navigate(self.marionette.absolute_url("test.html"))
-        self.marionette.execute_async_script(
-            "SpecialPowers.pushPermissions([{'type': 'browser', 'allow': true, 'context': document}], marionetteScriptFinished);")
+        self.marionette.push_permission('browser', True)
         self.marionette.execute_script("""
             let iframe = document.createElement("iframe");
             SpecialPowers.wrap(iframe).mozbrowser = true;
             SpecialPowers.wrap(iframe).remote = true;
             iframe.id = "remote_iframe";
             iframe.style.height = "100px";
             iframe.style.width = "100%%";
             iframe.src = "%s";
--- a/testing/marionette/driver/marionette_driver/marionette.py
+++ b/testing/marionette/driver/marionette_driver/marionette.py
@@ -782,16 +782,78 @@ class Marionette(object):
                 val = str(val)
                 for i in range(len(val)):
                     typing.append(val[i])
             else:
                 for i in range(len(val)):
                     typing.append(val[i])
         return typing
 
+    def push_permission(self, perm_type, allow):
+        with self.using_context('content'):
+            perm = self.execute_script("""
+                let allow = arguments[0];
+                if (allow)
+                  allow = Components.interfaces.nsIPermissionManager.ALLOW_ACTION;
+                else
+                  allow = Components.interfaces.nsIPermissionManager.DENY_ACTION;
+                let perm_type = arguments[1];
+
+                Components.utils.import("resource://gre/modules/Services.jsm");
+                window.wrappedJSObject.permChanged = false;
+                window.wrappedJSObject.permObserver = function(subject, topic, data) {
+                  if (topic == "perm-changed") {
+                    let permission = subject.QueryInterface(Components.interfaces.nsIPermission);
+                    if (perm_type == permission.type) {
+                      Services.obs.removeObserver(window.wrappedJSObject.permObserver, "perm-changed");
+                      window.wrappedJSObject.permChanged = true;
+                    }
+                  }
+                };
+                Services.obs.addObserver(window.wrappedJSObject.permObserver,
+                                         "perm-changed", false);
+
+                let value = {
+                              'url': document.nodePrincipal.URI.spec,
+                              'appId': document.nodePrincipal.appId,
+                              'isInBrowserElement': document.nodePrincipal.isInBrowserElement,
+                              'type': perm_type,
+                              'action': allow
+                            };
+                return value;
+                """, script_args=[allow, perm_type], sandbox='system')
+
+        with self.using_context('chrome'):
+            waiting = self.execute_script("""
+                Components.utils.import("resource://gre/modules/Services.jsm");
+                let perm = arguments[0];
+                let secMan = Services.scriptSecurityManager;
+                let principal = secMan.getAppCodebasePrincipal(Services.io.newURI(perm.url, null, null), 
+                                perm.appId, perm.isInBrowserElement);
+                let testPerm = Services.perms.testPermissionFromPrincipal(principal, perm.type, perm.action);
+                if (testPerm == perm.action) {
+                  return false;
+                }
+                Services.perms.addFromPrincipal(principal, perm.type, perm.action);
+                return true;
+                """, script_args=[perm])
+
+        with self.using_context('content'):
+            if waiting:
+                self.execute_async_script("""
+                    waitFor(marionetteScriptFinished, function() {
+                      return window.wrappedJSObject.permChanged;
+                    });
+                    """, sandbox='system')
+            else:
+                self.execute_script("""
+                    Components.utils.import("resource://gre/modules/Services.jsm");
+                    Services.obs.removeObserver(window.wrappedJSObject.permObserver, "perm-changed");
+                    """, sandbox='system')
+
     def enforce_gecko_prefs(self, prefs):
         """
         Checks if the running instance has the given prefs. If not, it will kill the
         currently running instance, and spawn a new instance with the requested preferences.
 
         : param prefs: A dictionary whose keys are preference names.
         """
         if not self.instance: