0

The Firefox Web Console currently (version 80.0.1 as I type this) supports Javascript-context-switching to an iframe through a cd function (albeit set to be removed), as in

var ifr = document.getElementById('frame_id');
cd(ifr);

The same goal can be accomplished

  • by selecting a different Javascript context from a drop-down menu in the upper-right corner of the Web Console, as mentioned in that bug;
  • similarly in Chrome DevTools (see also an older SO exchange on this).

Question:

Is it possible to achieve the same effect while driving Firefox headless with selenium-webdriver?

Background:

I have a page loading a cross-origin iframe I have no control over, and would like to access DOM elements under that iframe. I can do this in the Web Console, but I have been unsuccessful in doing so via Selenium (specifically, node + the selenium-webdriver package):

Selecting the relevant iframe and then switching to it with

ifr = driver.findElement(By.id('frame_id'));
driver.switchTo().frame(ifr);

gives me access to a different DOM than what I see in the Web Console: in Selenium I do not have access to the buttons, forms, etc. available in the browser when I interact with the latter directly.

grobber
  • 1,083
  • 1
  • 9
  • 20

2 Answers2

0

I believe I'm getting the hang of this now. The answer is affirmative:

One can access all of the console functionality (including the cd command, buttons / menus, etc.) through Selenium.

What ultimately got me unstuck was the first comment on this other related question I posted. I will describe two possible ways to go about this in Firefox, matching the two ways one can access a (possibly cross-origin) iframe when working directly with the browser:


cd command in Selenium with Python

A script:


from selenium.webdriver import Firefox, DesiredCapabilities, FirefoxProfile
from selenium.webdriver.common.by import By
from selenium.webdriver.firefox.options import Options
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.keys import Keys

import time
import traceback

options = Options()

webdriver = Firefox(options=options)
webdriver.get(<url-that-embeds-frame>)

try:
    time.sleep(3)
    with webdriver.context(webdriver.CONTEXT_CHROME):

        console = webdriver.find_element(By.ID, "tabbrowser-tabs")
        console.send_keys(Keys.LEFT_CONTROL + Keys.LEFT_SHIFT + 'k')
        time.sleep(3)        

        scrpt = 'ifr = document.getElementById("duo_iframe"); cd(ifr); '
        console.send_keys(scrpt + Keys.ENTER)     
        
except Exception as error:
    traceback.print_exc()

This will

  • navigate to the url that embeds the frame
  • open the console
  • in that console, use Javascript to locate the frame and cd to it, switching to its DOM.

Firefox console frame-selection menu in Selenium with Python

Similar to the above:

from selenium.webdriver import Firefox, DesiredCapabilities, FirefoxProfile
from selenium.webdriver.common.by import By
from selenium.webdriver.firefox.options import Options
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.keys import Keys

import time
import traceback

options = Options()

webdriver = Firefox(options=options)
webdriver.get(<url-that-embeds-frame>)

try:
    time.sleep(3)
    with webdriver.context(webdriver.CONTEXT_CHROME):

        console = webdriver.find_element(By.ID, "tabbrowser-tabs")
        console.send_keys(Keys.LEFT_CONTROL + Keys.LEFT_SHIFT + 'k')
        time.sleep(3)        
        
        ifr = webdriver.find_element_by_class_name("devtools-toolbox-bottom-iframe")
        webdriver.switch_to.frame(ifr)
        
        btn = webdriver.find_element_by_id("command-button-frames")         
        btn.click()               
        
except Exception as error:
    traceback.print_exc()

What this will do is

  • navigate to the url that embeds the frame
  • open the Web Console
  • click the frame-selection menu in the top right corner of that console

One can presumably also cycle through the options, etc., but I have not done that.

grobber
  • 1,083
  • 1
  • 9
  • 20
0

I don't see why it should not work to access elements in a cross-origin frame directly with Selenium / geckodriver.

The following code example that loads a page from Domain A, switches to the iframe that holds a page from Domain B, let me directly access the h1 element and read out its text:

driver.get("https://output.jsbin.com/gayeyanosi/2")

iframe = driver.find_element_by_css_selector("iframe") driver.switch_to.frame(iframe)

heading = driver.find_element_by_css_selector("h1")
assert heading.text == "Example Domain"

Support for that is already around for a couple of Firefox and geckodriver releases.

If it still doesn't work for you I would suggest that you file an issue for geckodriver, and attach a trace level log to it. We can then investigate the problem in detail.

Henrik
  • 249
  • 1
  • 6
  • This does seem to work now (I could swear it didn't at the time). But there are other reasons I would want more control: how, for instance, do I get the cookies from the domain embedding the frame after having switched to that frame? I have tried retrieving cookies after the switch, only to get the ones for the *parent* domain.. – grobber May 08 '21 at 21:35
  • 1
    Ok, so getting cookies for the currently selected frame is actually a known problem. It's handled as https://bugzilla.mozilla.org/show_bug.cgi?id=1427807. Sadly I cannot promise when exactly we will be able to get it solved. – Henrik May 11 '21 at 08:09
  • OK, but then is it possible to somehow gain access to the `cookies` api the way extensions do? Using [browser.cookies.getAll()](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/cookies/getAll) for instance? – grobber May 11 '21 at 22:05