Bug 855327 - Add capability to get currently active frame from marionette;r=jgriffin
authorWilliam Lachance <wlachance@mozilla.com>
Mon, 29 Jul 2013 13:46:01 -0400
changeset 140380 650a9b022b24922365a84ca0423c3d19bbc8f1f5
parent 140379 1d7b0ea3baa08f607c4133ba5b39f2a8df92b54f
child 140381 0ccd3c78dd50d2cca91061b7373d03c322a97bb4
push id31710
push userwlachance@mozilla.com
push dateMon, 29 Jul 2013 17:46:12 +0000
treeherdermozilla-inbound@650a9b022b24 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjgriffin
bugs855327
milestone25.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 855327 - Add capability to get currently active frame from marionette;r=jgriffin
testing/marionette/client/marionette/marionette.py
testing/marionette/client/marionette/tests/unit/test_switch_frame.py
testing/marionette/client/marionette/tests/unit/test_switch_frame_chrome.py
testing/marionette/client/marionette/tests/unit/unit-tests.ini
testing/marionette/marionette-listener.js
testing/marionette/marionette-server.js
--- a/testing/marionette/client/marionette/marionette.py
+++ b/testing/marionette/client/marionette/marionette.py
@@ -511,16 +511,22 @@ class Marionette(object):
         assert(context == self.CONTEXT_CHROME or context == self.CONTEXT_CONTENT)
         return self._send_message('setContext', 'ok', value=context)
 
     def switch_to_window(self, window_id):
         response = self._send_message('switchToWindow', 'ok', value=window_id)
         self.window = window_id
         return response
 
+    def get_active_frame(self):
+        response = self._send_message('getActiveFrame', 'value')
+        if response:
+            return HTMLElement(self, response)
+        return None
+
     def switch_to_frame(self, frame=None, focus=True):
         if isinstance(frame, HTMLElement):
             response = self._send_message('switchToFrame', 'ok', element=frame.id, focus=focus)
         else:
             response = self._send_message('switchToFrame', 'ok', value=frame, focus=focus)
         return response
 
     def get_url(self):
--- a/testing/marionette/client/marionette/tests/unit/test_switch_frame.py
+++ b/testing/marionette/client/marionette/tests/unit/test_switch_frame.py
@@ -7,20 +7,28 @@ from marionette_test import MarionetteTe
 from errors import JavascriptException
 
 # boiler plate for the initial navigation and frame switch
 def switch_to_window_verify(test, start_url, frame, verify_title, verify_url):
     test.assertTrue(test.marionette.execute_script("window.location.href = 'about:blank'; return true;"))
     test.assertEqual("about:blank", test.marionette.execute_script("return window.location.href;"))
     test_html = test.marionette.absolute_url(start_url)
     test.marionette.navigate(test_html)
+    test.assertEqual(test.marionette.get_active_frame(), None)
     test.assertNotEqual("about:blank", test.marionette.execute_script("return window.location.href;"))
     test.assertEqual(verify_title, test.marionette.title)
     test.marionette.switch_to_frame(frame)
     test.assertTrue(verify_url in test.marionette.get_url())
+    inner_frame_element = test.marionette.get_active_frame()
+    # test that we can switch back to main frame, then switch back to the
+    # inner frame with the value we got from get_active_frame
+    test.marionette.switch_to_frame()
+    test.assertEqual(verify_title, test.marionette.title)
+    test.marionette.switch_to_frame(inner_frame_element)
+    test.assertTrue(verify_url in test.marionette.get_url())
 
 class TestSwitchFrame(MarionetteTestCase):
     def test_switch_simple(self):
         switch_to_window_verify(self, "test_iframe.html", "test_iframe", "Marionette IFrame Test", "test.html")
 
     def test_switch_nested(self):
         switch_to_window_verify(self, "test_nested_iframe.html", "test_iframe", "Marionette IFrame Test", "test_inner_iframe.html")
         self.marionette.switch_to_frame("inner_frame")
@@ -53,46 +61,8 @@ class TestSwitchFrame(MarionetteTestCase
         addIFrame = self.marionette.find_element("id", "addBackFrame")
         addIFrame.click()
         self.marionette.find_element("id", "iframe1")
 
         self.marionette.switch_to_frame("iframe1");
 
         self.marionette.find_element("id", "checkbox")
 
-class TestSwitchFrameChrome(MarionetteTestCase):
-    def setUp(self):
-        MarionetteTestCase.setUp(self)
-        self.marionette.set_context("chrome")
-        self.win = self.marionette.current_window_handle
-        self.marionette.execute_script("window.open('chrome://marionette/content/test.xul', 'foo', 'chrome,centerscreen');")
-        self.marionette.switch_to_window('foo')
-        self.assertNotEqual(self.win, self.marionette.current_window_handle)
-
-    def tearDown(self):
-        self.assertNotEqual(self.win, self.marionette.current_window_handle)
-        self.marionette.execute_script("window.close();")
-        self.marionette.switch_to_window(self.win)
-        MarionetteTestCase.tearDown(self)
-
-    def test_switch_simple(self):
-        self.assertTrue("test.xul" in self.marionette.get_url())
-        self.marionette.switch_to_frame(0)
-        self.assertTrue("test2.xul" in self.marionette.get_url())
-        self.marionette.switch_to_frame()
-        self.assertTrue("test.xul" in self.marionette.get_url())
-        self.marionette.switch_to_frame("iframe")
-        self.assertTrue("test2.xul" in self.marionette.get_url())
-        self.marionette.switch_to_frame()
-        self.assertTrue("test.xul" in self.marionette.get_url())
-        self.marionette.switch_to_frame("iframename")
-        self.assertTrue("test2.xul" in self.marionette.get_url())
-        self.marionette.switch_to_frame()
-        self.assertTrue("test.xul" in self.marionette.get_url())
-        
-    def test_stack_trace(self):
-        self.assertTrue("test.xul" in self.marionette.get_url())
-        self.marionette.switch_to_frame(0)
-        self.assertRaises(JavascriptException, self.marionette.execute_async_script, "foo();")
-        try:
-            self.marionette.execute_async_script("foo();")
-        except JavascriptException as e:
-            self.assertTrue("foo" in e.msg)
new file mode 100644
--- /dev/null
+++ b/testing/marionette/client/marionette/tests/unit/test_switch_frame_chrome.py
@@ -0,0 +1,49 @@
+# 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_test import MarionetteTestCase
+from errors import JavascriptException
+
+class TestSwitchFrameChrome(MarionetteTestCase):
+    def setUp(self):
+        MarionetteTestCase.setUp(self)
+        self.marionette.set_context("chrome")
+        self.win = self.marionette.current_window_handle
+        self.marionette.execute_script("window.open('chrome://marionette/content/test.xul', 'foo', 'chrome,centerscreen');")
+        self.marionette.switch_to_window('foo')
+        self.assertNotEqual(self.win, self.marionette.current_window_handle)
+
+    def tearDown(self):
+        self.assertNotEqual(self.win, self.marionette.current_window_handle)
+        self.marionette.execute_script("window.close();")
+        self.marionette.switch_to_window(self.win)
+        MarionetteTestCase.tearDown(self)
+
+    def test_switch_simple(self):
+        self.assertTrue("test.xul" in self.marionette.get_url())
+        self.marionette.switch_to_frame(0)
+        self.assertTrue("test2.xul" in self.marionette.get_url())
+        self.marionette.switch_to_frame()
+        self.assertEqual(None, self.marionette.get_active_frame())
+        self.assertTrue("test.xul" in self.marionette.get_url())
+        self.marionette.switch_to_frame("iframe")
+        self.assertTrue("test2.xul" in self.marionette.get_url())
+        self.marionette.switch_to_frame()
+        self.assertTrue("test.xul" in self.marionette.get_url())
+        self.marionette.switch_to_frame("iframename")
+        self.assertTrue("test2.xul" in self.marionette.get_url())
+        iframe_element = self.marionette.get_active_frame()
+        self.marionette.switch_to_frame()
+        self.assertTrue("test.xul" in self.marionette.get_url())
+        self.marionette.switch_to_frame(iframe_element)
+        self.assertTrue("test2.xul" in self.marionette.get_url())
+
+    def test_stack_trace(self):
+        self.assertTrue("test.xul" in self.marionette.get_url())
+        self.marionette.switch_to_frame(0)
+        self.assertRaises(JavascriptException, self.marionette.execute_async_script, "foo();")
+        try:
+            self.marionette.execute_async_script("foo();")
+        except JavascriptException as e:
+            self.assertTrue("foo" in e.msg)
--- a/testing/marionette/client/marionette/tests/unit/unit-tests.ini
+++ b/testing/marionette/client/marionette/tests/unit/unit-tests.ini
@@ -62,16 +62,17 @@ b2g = true
 browser = false
 
 [test_simpletest_pass.js]
 [test_simpletest_sanity.py]
 [test_simpletest_chrome.js]
 [test_simpletest_timeout.js]
 [test_specialpowers.py]
 [test_switch_frame.py]
+[test_switch_frame_chrome.py]
 b2g = false
 
 [test_window_management.py]
 b2g = false
 
 [test_appcache.py]
 [test_screenshot.py]
 [test_cookies.py]
--- a/testing/marionette/marionette-listener.js
+++ b/testing/marionette/marionette-listener.js
@@ -1464,16 +1464,19 @@ function switchToFrame(msg) {
   } catch (e) {
     // We probably have a dead compartment so accessing it is going to make Firefox
     // very upset. Let's now try redirect everything to the top frame even if the 
     // user has given us a frame since search doesnt look up.
     msg.json.value = null;
     msg.json.element = null;
   }
   if ((msg.json.value == null) && (msg.json.element == null)) {
+    // returning to root frame
+    sendSyncMessage("Marionette:switchedToFrame", { frameValue: null });
+
     curWindow = content;
     if(msg.json.focus == true) {
       curWindow.focus();
     }
     sandbox = null;
     checkTimer.initWithCallback(checkLoad, 100, Ci.nsITimer.TYPE_ONE_SHOT);
     return;
   }
@@ -1526,19 +1529,24 @@ function switchToFrame(msg) {
   }
   if (foundFrame == null) {
     sendError("Unable to locate frame: " + msg.json.value, 8, null, command_id);
     return;
   }
 
   sandbox = null;
 
+  // send a synchronous message to let the server update the currently active
+  // frame element (for getActiveFrame)
+  let frameValue = elementManager.wrapValue(curWindow.wrappedJSObject)['ELEMENT'];
+  sendSyncMessage("Marionette:switchedToFrame", { frameValue: frameValue });
+
   if (curWindow.contentWindow == null) {
-    // The frame we want to switch to is a remote frame; notify our parent to handle
-    // the switch.
+    // The frame we want to switch to is a remote (out-of-process) frame;
+    // notify our parent to handle the switch.
     curWindow = content;
     sendToServer('Marionette:switchToFrame', {frame: foundFrame,
                                               win: parWindow,
                                               command_id: command_id});
   }
   else {
     curWindow = curWindow.contentWindow;
     if(msg.json.focus == true) {
--- a/testing/marionette/marionette-server.js
+++ b/testing/marionette/marionette-server.js
@@ -142,16 +142,17 @@ function MarionetteServerConnection(aPre
   this.pageTimeout = null;
   this.timer = null;
   this.marionetteLog = new MarionetteLogObj();
   this.command_id = null;
   this.mainFrame = null; //topmost chrome frame
   this.curFrame = null; //subframe that currently has focus
   this.importedScripts = FileUtils.getFile('TmpD', ['marionettescriptchrome']);
   this.currentRemoteFrame = null; // a member of remoteFrames
+  this.currentFrameElement = null;
   this.testName = null;
   this.mozBrowserClose = null;
 
   //register all message listeners
   this.addMessageManagerListeners(this.messageManager);
 }
 
 MarionetteServerConnection.prototype = {
@@ -257,16 +258,17 @@ MarionetteServerConnection.prototype = {
     messageManager.addMessageListener("Marionette:ok", this);
     messageManager.addMessageListener("Marionette:done", this);
     messageManager.addMessageListener("Marionette:error", this);
     messageManager.addMessageListener("Marionette:log", this);
     messageManager.addMessageListener("Marionette:shareData", this);
     messageManager.addMessageListener("Marionette:register", this);
     messageManager.addMessageListener("Marionette:runEmulatorCmd", this);
     messageManager.addMessageListener("Marionette:switchToFrame", this);
+    messageManager.addMessageListener("Marionette:switchedToFrame", this);
   },
 
   /**
    * Removes listeners for messages from content frame scripts.
    *
    * @param object messageManager
    *        The messageManager object (ChromeMessageBroadcaster or ChromeMessageSender)
    *        from which the listeners should be removed.
@@ -275,16 +277,17 @@ MarionetteServerConnection.prototype = {
     messageManager.removeMessageListener("Marionette:ok", this);
     messageManager.removeMessageListener("Marionette:done", this);
     messageManager.removeMessageListener("Marionette:error", this);
     messageManager.removeMessageListener("Marionette:log", this);
     messageManager.removeMessageListener("Marionette:shareData", this);
     messageManager.removeMessageListener("Marionette:register", this);
     messageManager.removeMessageListener("Marionette:runEmulatorCmd", this);
     messageManager.removeMessageListener("Marionette:switchToFrame", this);
+    messageManager.removeMessageListener("Marionette:switchedToFrame", this);
   },
 
   logRequest: function MDA_logRequest(type, data) {
     logger.debug("Got request: " + type + ", data: " + JSON.stringify(data) + ", id: " + this.command_id);
   },
 
   /**
    * Generic method to pass a response to the client
@@ -1189,16 +1192,33 @@ MarionetteServerConnection.prototype = {
         this.sendOk(command_id);
         return;
       }
     }
     this.sendError("Unable to locate window " + aRequest.value, 23, null,
                    command_id);
   },
  
+  getActiveFrame: function MDA_getActiveFrame() {
+    this.command_id = this.getCommandId();
+
+    if (this.context == "chrome") {
+      if (this.curFrame) {
+        let wrappedValue = this.curBrowser.elementManager.addToKnownElements(this.curFrame.frameElement);
+        this.sendResponse(wrappedValue, this.command_id);
+      } else {
+        // no current frame, we're at toplevel
+        this.sendResponse(null, this.command_id);
+      }
+    } else {
+      // not chrome
+      this.sendResponse(this.currentFrameElement, this.command_id);
+    }
+  },
+
   /**
    * Switch to a given frame within the current window
    *
    * @param object aRequest
    *        'element' is the element to switch to
    *        'value' if element is not set, then this
    *                holds either the id, name or index 
    *                of the frame to switch to
@@ -2129,16 +2149,20 @@ MarionetteServerConnection.prototype = {
         mm.loadFrameScript(FRAME_SCRIPT, true);
         this.messageManager = mm;
         let aFrame = new MarionetteRemoteFrame(message.json.win, message.json.frame);
         aFrame.messageManager = this.messageManager;
         aFrame.command_id = message.json.command_id;
         remoteFrames.push(aFrame);
         this.currentRemoteFrame = aFrame;
         break;
+      case "Marionette:switchedToFrame":
+        logger.info("Switched to frame: " + JSON.stringify(message.json));
+        this.currentFrameElement = message.json.frameValue;
+        break;
       case "Marionette:register":
         // This code processes the content listener's registration information
         // and either accepts the listener, or ignores it
         let nullPrevious = (this.curBrowser.curFrameId == null);
         let listenerWindow =
           Services.wm.getOuterWindowWithId(message.json.value);
 
         if (!listenerWindow || (listenerWindow.location.href != message.json.href) &&
@@ -2222,16 +2246,17 @@ MarionetteServerConnection.prototype.req
   "getPageSource": MarionetteServerConnection.prototype.getPageSource,
   "goUrl": MarionetteServerConnection.prototype.goUrl,
   "getUrl": MarionetteServerConnection.prototype.getUrl,
   "goBack": MarionetteServerConnection.prototype.goBack,
   "goForward": MarionetteServerConnection.prototype.goForward,
   "refresh":  MarionetteServerConnection.prototype.refresh,
   "getWindow":  MarionetteServerConnection.prototype.getWindow,
   "getWindows":  MarionetteServerConnection.prototype.getWindows,
+  "getActiveFrame": MarionetteServerConnection.prototype.getActiveFrame,
   "switchToFrame": MarionetteServerConnection.prototype.switchToFrame,
   "switchToWindow": MarionetteServerConnection.prototype.switchToWindow,
   "deleteSession": MarionetteServerConnection.prototype.deleteSession,
   "emulatorCmdResult": MarionetteServerConnection.prototype.emulatorCmdResult,
   "importScript": MarionetteServerConnection.prototype.importScript,
   "getAppCacheStatus": MarionetteServerConnection.prototype.getAppCacheStatus,
   "closeWindow": MarionetteServerConnection.prototype.closeWindow,
   "setTestName": MarionetteServerConnection.prototype.setTestName,