microsoft / playwright

Playwright is a framework for Web Testing and Automation. It allows testing Chromium, Firefox and WebKit with a single API.
https://playwright.dev
Apache License 2.0
65.65k stars 3.57k forks source link

[BUG] Cannot detect iframe #26317

Open yanxiang-wang opened 1 year ago

yanxiang-wang commented 1 year ago

System info

Source code

Link to the GitHub repository with the repro

[https://github.com/your_profile/playwright_issue_title]

or

Test file (self-contained)

from playwright.sync_api import BrowserContext, sync_playwright
from loguru import logger

headless = False

def run(context: BrowserContext):
    pass

def bytedance():
    with sync_playwright() as p:
        browser = p.firefox.launch(headless=False,devtools=True)
        context = browser.new_context()
        page = context.new_page()
        page.goto("https://nowsecure.nl")
        page.wait_for_timeout(1000)
        try:
            logger.debug("Try to find the Cloudflare verify checkbox...")
            iframe = page.frame_locator("//iframe[@title='Widget containing a Cloudflare security challenge']")
            checkbox = iframe.locator(
                value='//*[@id="cf-stage"]//label[@class="ctp-checkbox-label"]/input',
            )
            if checkbox:
                logger.debug("Cloudflare verify checkbox found")
        except Exception:
            logger.debug("Cloudflare verify checkbox not found on the page.")

if __name__ == "__main__":
    bytedance()

Steps

Expected

I use the similar xpath selectors with Selenium, and it can detect the iframe.

import selenium.webdriver as webdriver
from loguru import logger
from selenium.webdriver.common.by import By
from selenium.webdriver.common.action_chains import ActionChains
import time
driver = webdriver.Chrome()

driver.get('https://nowsecure.nl')

time.sleep(2)

try:
    logger.debug("Try to find the Cloudflare verify checkbox...")
    iframe = driver.find_element(By.XPATH, "//iframe[@title='Widget containing a Cloudflare security challenge']")
    driver.switch_to.frame(iframe)
    checkbox = driver.find_element(
        by=By.XPATH,
        value='//*[@id="cf-stage"]//label[@class="ctp-checkbox-label"]/input',
    )
    if checkbox:
        logger.debug("Cloudflare verify checkbox found!")
except Exception:
    logger.debug("Cloudflare verify checkbox not found on the page.")
finally:
    driver.switch_to.default_content()

Actual

Selenium:

2023-08-04 07:50:40.793 | DEBUG    | __main__:<module>:24 - Try to find the Cloudflare verify checkbox...
2023-08-04 07:50:49.069 | DEBUG    | __main__:<module>:36 - Cloudflare verify checkbox found !

Playwright:

2023-08-04 07:50:16.744 | DEBUG    | __main__:<module>:24 - Try to find the Cloudflare verify checkbox...
2023-08-04 07:50:16.764 | DEBUG    | __main__:<module>:38 - Cloudflare verify checkbox not found on the page.
mxschmitt commented 1 year ago

Playwright and Selenium are working with different concepts. A Locator is always Truthy, only when you actually call locator.is_visible() it will check on the page if a certain element is visible.

So you should modify it to e.g.

            if checkbox.is_visible():
                logger.debug("Cloudflare verify checkbox found")
yanxiang-wang commented 1 year ago

Playwright and Selenium are working with different concepts. A Locator is always Truthy, only when you actually call locator.is_visible() it will check on the page if a certain element is visible.

So you should modify it to e.g.

            if checkbox.is_visible():
                logger.debug("Cloudflare verify checkbox found")

Thanks for the quick reply. Even the Locator checkbox is always True, the logger still show not detected. I tried with is_visible(). The output is the same. I guess the Playwright still didn't capture the iframe as selenium. What do you think?

Xewdy444 commented 1 year ago

This is related to https://github.com/microsoft/playwright/issues/21780.

dgozman commented 1 year ago

This is Firefox-specific, the same snippet works for me in Chromium. The issue is that cloudflare iframe is forced oopif or get some special security handling, has an empty url, and Playwright fails to evaluate inside.

Here is an example error when trying to access iframeElement.contentFrame():

elementHandle.contentFrame: Protocol error (Page.describeNode): error in channel "content::9/12/2": exception while running method "describeNode" in namespace "page": Permission denied to access property "docShell" on cross-origin object
_describeNode@chrome://juggler/content/content/PageAgent.js:417:47

Here is trying to evaluate inside page.frames()[1]:

frame.evaluate: Execution context was destroyed, most likely because of a navigation.