daijro / camoufox

🦊 Anti-detect browser
https://camoufox.com
Mozilla Public License 2.0
187 stars 18 forks source link

Scripts Not Being Added to Context via add_init_script #48

Open NCGSolutions opened 2 weeks ago

NCGSolutions commented 2 weeks ago

Describe the bug:

Scripts are not being properly injected/attached(?) to the browser context via add_init_script. I provide an example below that I use to open Closed Shadow DOMs but it can be reproduced with any script needed to be added via add_init_script.

To Reproduce:

Run code below, that shows that the add_init_script function works differently for playwright versus camoufox!

main.py

import asyncio
from pathlib import Path
from camoufox.async_api import AsyncCamoufox
from playwright.async_api import async_playwright

async def test_shadow_root(page, browser_type):
    current_dir = Path.cwd()
    html_example_file = current_dir / 'closed_shadow_root.html'

    file_url = f'file://{html_example_file.absolute()}'
    await page.goto(file_url)
    await page.wait_for_load_state('networkidle')

    result = await page.evaluate("""(browserType) => {
        const host = document.getElementById('host');
        console.log(`${browserType} shadowRoot access attempt:`, host.shadowRoot);
        return host.shadowRoot ? 'Shadow root is accessible' : 'Shadow root is not accessible';
    }""", browser_type)
    print(f"{browser_type} access attempt result: {result}")
    await asyncio.sleep(3)

async def main():
    # Load initialization script
    js_file_path = Path.cwd() / 'shadow_root.js'
    with open(js_file_path, 'r') as file:
        shadow_root_opener = file.read()

    # Test with CamouFox
    print("\n=== Testing with CamouFox ===")
    async with AsyncCamoufox(headless=False) as browser:
        context = await browser.new_context()
        await context.add_init_script(shadow_root_opener)
        page = await context.new_page()
        await test_shadow_root(page, "CamouFox")

    # Test with Playwright Firefox
    print("\n=== Testing with Playwright (Firefox) ===")
    async with async_playwright() as p:
        browser = await p.firefox.launch(headless=False)
        context = await browser.new_context()
        await context.add_init_script(shadow_root_opener)
        page = await context.new_page()
        await test_shadow_root(page, "Playwright Firefox")
        await asyncio.sleep(50)

if __name__ == "__main__":
    asyncio.run(main())

shadow_root.js

(function() { const originalAttachShadow = Element.prototype.attachShadow; Element.prototype.attachShadow = function() { return originalAttachShadow.call(this, { mode: 'open' }); }; })();

closed_shadow_root.html

<!DOCTYPE html>
<html>
<head>
    <title>Closed Shadow Root Example</title>
</head>
<body>
    <div id="host"></div>

    <script>
        // Get the host element
        const host = document.getElementById('host');

        // Create a shadow root with mode: 'closed'
        const shadowRoot = host.attachShadow({ mode: 'closed' });

        // Add content to the shadow root
        shadowRoot.innerHTML = `
            <style>
                .shadow-content {
                    padding: 20px;
                    background-color: #f0f0f0;
                    border: 2px solid #333;
                }
            </style>
            <div class="shadow-content">
                <h2>This content is in a closed shadow root</h2>
                <p>This content is encapsulated and cannot be accessed from outside the shadow root.</p>
            </div>
        `;
    </script>
</body>
</html>

Version:

Pip package: v0.3.0 Camoufox: v130.0.1-beta.13 (Up to date!)

Note:

This should also solve this issue as well! https://github.com/daijro/camoufox/issues/2

daijro commented 2 weeks ago

In Camoufox, all of Playwright's JavaScript runs in an isolated context. This prevents Playwright from running JavaScript that writes to the main world/context of the page.

While this is helpful with preventing detection of the Playwright page agent, it causes some issues with native Playwright functions like setting file inputs, executing JavaScript, adding page init scripts, etc. These features might need to be implemented separately.

A current workaround for this might be to create a small dummy addon to inject into the browser.

Thanks for reporting this!