Bug 855327 - Add capability to get currently active frame from marionette;r=jgriffin, a=test-only
authorWilliam Lachance <wlachance@mozilla.com>
Mon, 29 Jul 2013 13:46:01 -0400
changeset 119822 09cf0a87492951002e76e3bb63dd3403e5a5a85e
parent 119821 1fe3339e3d9638cb8b0a6d4bebed9df47b76e783
child 119823 ad9dcbd779cd92e853e811ccb969980229b2a529
push id1012
push userjgriffin@mozilla.com
push dateTue, 30 Jul 2013 19:49:32 +0000
reviewersjgriffin, test-only
bugs855327
milestone18.1
Bug 855327 - Add capability to get currently active frame from marionette;r=jgriffin, a=test-only
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
@@ -65,16 +65,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
@@ -1463,16 +1463,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;
   }
@@ -1525,19 +1528,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
@@ -141,16 +141,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 = {
@@ -256,16 +257,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.
@@ -274,16 +276,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
@@ -1188,16 +1191,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
@@ -2131,16 +2151,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 curWin = this.getCurrentWindow();
         let listenerWindow = curWin.QueryInterface(Ci.nsIInterfaceRequestor)
                                    .getInterface(Ci.nsIDOMWindowUtils)
                                    .getOuterWindowWithId(message.json.value);
@@ -2226,16 +2250,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,