├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug_report.yml │ ├── config.yml │ ├── documentation.yml │ ├── feature_request.yml │ └── refactoring.yml ├── PULL_REQUEST_TEMPLATE │ ├── bug_fix.md │ ├── refactoring.md │ └── release.md ├── pull_request_template.md └── workflows │ ├── deploy-docs.yml │ ├── mypy.yml │ ├── publish.yml │ ├── release.yml │ ├── ruff-ci.yml │ └── tests.yml ├── .gitignore ├── .python-version ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── codecov.yml ├── cz.yaml ├── docs ├── api │ ├── browser │ │ ├── chrome.md │ │ ├── edge.md │ │ ├── managers.md │ │ ├── options.md │ │ └── tab.md │ ├── commands │ │ ├── browser.md │ │ ├── dom.md │ │ ├── fetch.md │ │ ├── input.md │ │ ├── network.md │ │ ├── page.md │ │ ├── runtime.md │ │ ├── storage.md │ │ └── target.md │ ├── connection │ │ ├── connection.md │ │ └── managers.md │ ├── core │ │ ├── constants.md │ │ ├── exceptions.md │ │ └── utils.md │ ├── elements │ │ ├── mixins.md │ │ └── web_element.md │ ├── index.md │ └── protocol │ │ ├── commands.md │ │ └── events.md ├── deep-dive │ ├── browser-domain.md │ ├── cdp.md │ ├── connection-layer.md │ ├── event-system.md │ ├── find-elements-mixin.md │ ├── index.md │ ├── network-capabilities.md │ ├── tab-domain.md │ └── webelement-domain.md ├── features.md ├── images │ ├── 0D141C-preto-azulado.png │ ├── 8400FF-roxo-escuro.png │ ├── E2ECED-cinza-azulado.png │ ├── ECE95B-amarelo-lima.png │ └── favicon.png ├── index.md ├── scripts │ ├── extra.js │ └── termynal.js └── stylesheets │ ├── extra.css │ └── termynal.css ├── examples └── cloudflare_bypass.py ├── mkdocs.yml ├── poetry.lock ├── pydoll ├── __init__.py ├── browser │ ├── __init__.py │ ├── chromium │ │ ├── __init__.py │ │ ├── base.py │ │ ├── chrome.py │ │ └── edge.py │ ├── interfaces.py │ ├── managers │ │ ├── __init__.py │ │ ├── browser_options_manager.py │ │ ├── browser_process_manager.py │ │ ├── proxy_manager.py │ │ └── temp_dir_manager.py │ ├── options.py │ └── tab.py ├── commands │ ├── __init__.py │ ├── browser_commands.py │ ├── dom_commands.py │ ├── fetch_commands.py │ ├── input_commands.py │ ├── network_commands.py │ ├── page_commands.py │ ├── runtime_commands.py │ ├── storage_commands.py │ └── target_commands.py ├── connection │ ├── __init__.py │ ├── connection_handler.py │ └── managers │ │ ├── __init__.py │ │ ├── commands_manager.py │ │ └── events_manager.py ├── constants.py ├── elements │ ├── __init__.py │ ├── mixins │ │ ├── __init__.py │ │ └── find_elements_mixin.py │ └── web_element.py ├── exceptions.py ├── protocol │ ├── __init__.py │ ├── base.py │ ├── browser │ │ ├── __init__.py │ │ ├── events.py │ │ ├── methods.py │ │ ├── params.py │ │ ├── responses.py │ │ └── types.py │ ├── dom │ │ ├── __init__.py │ │ ├── events.py │ │ ├── methods.py │ │ ├── params.py │ │ ├── responses.py │ │ └── types.py │ ├── fetch │ │ ├── __init__.py │ │ ├── events.py │ │ ├── methods.py │ │ ├── params.py │ │ ├── responses.py │ │ └── types.py │ ├── input │ │ ├── __init__.py │ │ ├── events.py │ │ ├── methods.py │ │ ├── params.py │ │ └── types.py │ ├── network │ │ ├── __init__.py │ │ ├── events.py │ │ ├── methods.py │ │ ├── params.py │ │ ├── responses.py │ │ └── types.py │ ├── page │ │ ├── __init__.py │ │ ├── events.py │ │ ├── methods.py │ │ ├── params.py │ │ ├── responses.py │ │ └── types.py │ ├── runtime │ │ ├── __init__.py │ │ ├── events.py │ │ ├── methods.py │ │ ├── params.py │ │ ├── responses.py │ │ └── types.py │ ├── storage │ │ ├── __init__.py │ │ ├── events.py │ │ ├── methods.py │ │ ├── params.py │ │ ├── responses.py │ │ └── types.py │ └── target │ │ ├── __init__.py │ │ ├── events.py │ │ ├── methods.py │ │ ├── params.py │ │ ├── responses.py │ │ └── types.py └── utils.py ├── pyproject.toml └── tests ├── test_browser ├── test_browser_base.py ├── test_browser_chrome.py ├── test_browser_edge.py ├── test_browser_options.py └── test_browser_tab.py ├── test_commands ├── test_browser_commands.py ├── test_dom_commands.py ├── test_fetch_commands.py ├── test_input_commands.py ├── test_network_commands.py ├── test_page_commands.py ├── test_runtime_commands.py ├── test_storage_commands.py └── test_target_commands.py ├── test_connection_handler.py ├── test_events.py ├── test_exceptions.py ├── test_find_elements_mixin.py ├── test_managers ├── test_browser_managers.py └── test_connection_managers.py ├── test_utils.py └── test_web_element.py /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | github: [thalissonvs] 3 | 4 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.yml: -------------------------------------------------------------------------------- 1 | name: Bug Report 2 | description: Report a bug in pydoll 3 | title: "[Bug]: " 4 | labels: ["bug", "needs-triage"] 5 | body: 6 | - type: markdown 7 | attributes: 8 | value: | 9 | # pydoll Bug Report 10 | 11 | Thank you for taking the time to report a bug. This form will guide you through providing the information needed to address the issue effectively. 12 | 13 | - type: checkboxes 14 | id: checklist 15 | attributes: 16 | label: Checklist before reporting 17 | description: Please make sure you've completed the following steps before submitting a bug report. 18 | options: 19 | - label: I have searched for [similar issues](https://github.com/thalissonvs/pydoll/issues) and didn't find a duplicate. 20 | required: true 21 | - label: I have updated to the latest version of pydoll to verify the issue still exists. 22 | required: true 23 | 24 | - type: input 25 | id: version 26 | attributes: 27 | label: pydoll Version 28 | description: What version of pydoll are you using when encountering this bug? 29 | placeholder: e.g., 1.3.2 30 | validations: 31 | required: true 32 | 33 | - type: input 34 | id: python_version 35 | attributes: 36 | label: Python Version 37 | description: What version of Python are you using? 38 | placeholder: e.g., 3.10.4 39 | validations: 40 | required: true 41 | 42 | - type: dropdown 43 | id: os 44 | attributes: 45 | label: Operating System 46 | description: What operating system are you using? 47 | options: 48 | - Windows 49 | - macOS 50 | - Linux 51 | - Other (specify in environment details) 52 | validations: 53 | required: true 54 | 55 | - type: textarea 56 | id: description 57 | attributes: 58 | label: Bug Description 59 | description: A clear and concise description of what the bug is. 60 | placeholder: When I try to use X feature, the library fails with error message Y... 61 | validations: 62 | required: true 63 | 64 | - type: textarea 65 | id: reproduction_steps 66 | attributes: 67 | label: Steps to Reproduce 68 | description: Step by step instructions to reproduce the bug. 69 | placeholder: | 70 | 1. Import the library using `import pydoll` 71 | 2. Set up the client with `...` 72 | 3. Call method X with parameters Y 73 | 4. See error 74 | validations: 75 | required: true 76 | 77 | - type: textarea 78 | id: code_example 79 | attributes: 80 | label: Code Example 81 | description: | 82 | A minimal, self-contained code example that demonstrates the issue. 83 | This will be automatically formatted into code, so no need for backticks. 84 | render: python 85 | placeholder: | 86 | from pydoll import Client 87 | 88 | client = Client(...) 89 | 90 | # Code that triggers the bug 91 | result = client.some_method(...) 92 | print(result) 93 | validations: 94 | required: true 95 | 96 | - type: textarea 97 | id: expected_behavior 98 | attributes: 99 | label: Expected Behavior 100 | description: A clear and concise description of what you expected to happen. 101 | placeholder: The method should return X or perform Y... 102 | validations: 103 | required: false 104 | 105 | - type: textarea 106 | id: actual_behavior 107 | attributes: 108 | label: Actual Behavior 109 | description: What actually happened instead? Include full error messages and stack traces if applicable. 110 | placeholder: The method raised an exception... 111 | validations: 112 | required: false 113 | 114 | - type: textarea 115 | id: logs 116 | attributes: 117 | label: Relevant Log Output 118 | description: | 119 | If applicable, include any logs or error messages. 120 | This will be automatically formatted, so no need for backticks. 121 | render: shell 122 | placeholder: | 123 | Traceback (most recent call last): 124 | File "example.py", line 10, in 125 | ... 126 | File ".../pydoll/...", line N, in some_method 127 | ... 128 | SomeError: Error message 129 | 130 | - type: textarea 131 | id: additional_context 132 | attributes: 133 | label: Additional Context 134 | description: Add any other context about the problem here (environment details, potential causes, solutions you've tried, etc.) 135 | placeholder: I've tried reinstalling the package and using a different Python version, but the issue persists... 136 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: Questions & Discussions 4 | url: https://github.com/thalissonvs/pydoll/discussions 5 | about: Please ask and answer questions here. 6 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/documentation.yml: -------------------------------------------------------------------------------- 1 | name: Documentation Issue 2 | description: Report missing, incorrect, or unclear documentation 3 | title: "[Docs]: " 4 | labels: ["documentation", "needs-triage"] 5 | body: 6 | - type: markdown 7 | attributes: 8 | value: | 9 | # pydoll Documentation Issue 10 | 11 | Thank you for helping us improve the documentation. This form will guide you through providing the information needed to address documentation issues effectively. 12 | 13 | - type: checkboxes 14 | id: checklist 15 | attributes: 16 | label: Checklist before reporting 17 | description: Please make sure you've completed the following steps before submitting a documentation issue. 18 | options: 19 | - label: I have searched for [similar documentation issues](https://github.com/thalissonvs/pydoll/issues) and didn't find a duplicate. 20 | required: true 21 | - label: I have checked the latest documentation to verify this issue still exists. 22 | required: true 23 | 24 | - type: dropdown 25 | id: type 26 | attributes: 27 | label: Type of Documentation Issue 28 | description: What type of documentation issue are you reporting? 29 | options: 30 | - Missing documentation (information does not exist) 31 | - Incorrect documentation (information is wrong) 32 | - Unclear documentation (information is confusing or ambiguous) 33 | - Outdated documentation (information is no longer valid) 34 | - Other (please specify in description) 35 | validations: 36 | required: true 37 | 38 | - type: input 39 | id: location 40 | attributes: 41 | label: Documentation Location 42 | description: Where is the documentation with issues located? Provide URLs, file paths, or section names. 43 | placeholder: e.g., https://docs.example.com/pydoll/api.html#section, README.md, API Reference for Client class 44 | validations: 45 | required: true 46 | 47 | - type: textarea 48 | id: description 49 | attributes: 50 | label: Issue Description 51 | description: Describe the issue with the documentation in detail. 52 | placeholder: | 53 | The documentation for the `Client.connect()` method doesn't mention the timeout parameter, 54 | which I discovered by looking at the source code. 55 | validations: 56 | required: true 57 | 58 | - type: textarea 59 | id: suggested_fix 60 | attributes: 61 | label: Suggested Fix 62 | description: If you have a suggestion for how to fix the documentation, please provide it here. 63 | placeholder: | 64 | Add the following to the `Client.connect()` documentation: 65 | 66 | ``` 67 | Parameters: 68 | timeout (float, optional): Connection timeout in seconds. Defaults to 30. 69 | ``` 70 | 71 | - type: textarea 72 | id: additional_info 73 | attributes: 74 | label: Additional Information 75 | description: Any additional context or information that might help address this documentation issue. 76 | placeholder: | 77 | I found this issue when trying to implement a connection with a shorter timeout for my specific use case. 78 | 79 | - type: dropdown 80 | id: contribution 81 | attributes: 82 | label: Contribution 83 | description: Would you be willing to contribute a fix for this documentation? 84 | options: 85 | - Yes, I'd be willing to submit a PR with the fix 86 | - No, I don't have the capacity to fix this 87 | validations: 88 | required: true -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.yml: -------------------------------------------------------------------------------- 1 | name: Feature Request 2 | description: Suggest a new feature or enhancement for pydoll 3 | title: "[Feature Request]: " 4 | labels: ["enhancement", "needs-triage"] 5 | body: 6 | - type: markdown 7 | attributes: 8 | value: | 9 | # pydoll Feature Request 10 | 11 | Thank you for taking the time to suggest a new feature. This form will guide you through providing the information needed to consider your suggestion effectively. 12 | 13 | - type: checkboxes 14 | id: checklist 15 | attributes: 16 | label: Checklist before requesting 17 | description: Please make sure you've completed the following steps before submitting a feature request. 18 | options: 19 | - label: I have searched for [similar feature requests](https://github.com/thalissonvs/pydoll/issues) and didn't find a duplicate. 20 | required: true 21 | - label: I have checked the documentation to confirm this feature doesn't already exist. 22 | required: true 23 | 24 | - type: textarea 25 | id: problem 26 | attributes: 27 | label: Problem Statement 28 | description: Is your feature request related to a problem? Please describe what you're trying to accomplish. 29 | placeholder: I'm trying to accomplish X, but I'm unable to because Y... 30 | validations: 31 | required: true 32 | 33 | - type: textarea 34 | id: solution 35 | attributes: 36 | label: Proposed Solution 37 | description: Describe the solution you'd like to see implemented. Be as specific as possible. 38 | placeholder: | 39 | I would like to see a new method/class that can... 40 | 41 | Example usage might look like: 42 | ```python 43 | client.new_feature(param1, param2) 44 | ``` 45 | validations: 46 | required: true 47 | 48 | - type: textarea 49 | id: alternatives 50 | attributes: 51 | label: Alternatives Considered 52 | description: Describe any alternative solutions or features you've considered. 53 | placeholder: I've tried accomplishing this using X and Y approaches, but they don't work well because... 54 | 55 | - type: textarea 56 | id: context 57 | attributes: 58 | label: Additional Context 59 | description: Add any other context, code examples, or references that might help explain your feature request. 60 | placeholder: | 61 | Other libraries like X and Y have similar features that work like... 62 | 63 | This would help users who need to... 64 | 65 | - type: dropdown 66 | id: importance 67 | attributes: 68 | label: Importance 69 | description: How important is this feature to your use case? 70 | options: 71 | - Nice to have 72 | - Important 73 | - Critical (blocking my usage) 74 | validations: 75 | required: true 76 | 77 | - type: dropdown 78 | id: contribution 79 | attributes: 80 | label: Contribution 81 | description: Would you be willing to contribute this feature yourself? 82 | options: 83 | - Yes, I'd be willing to implement this feature 84 | - I could help with parts of the implementation 85 | - No, I don't have the capacity to implement this -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/refactoring.yml: -------------------------------------------------------------------------------- 1 | name: Refactoring Request 2 | description: Suggest code refactoring to improve pydoll's quality, performance, or maintainability 3 | title: "[Refactor]: " 4 | labels: ["refactor", "needs-triage"] 5 | body: 6 | - type: markdown 7 | attributes: 8 | value: | 9 | # pydoll Refactoring Request 10 | 11 | Thank you for suggesting improvements to our codebase. This form will guide you through providing the information needed to consider your refactoring suggestion effectively. 12 | 13 | - type: checkboxes 14 | id: checklist 15 | attributes: 16 | label: Checklist before suggesting refactoring 17 | description: Please make sure you've completed the following steps before submitting a refactoring request. 18 | options: 19 | - label: I have searched for [similar refactoring requests](https://github.com/thalissonvs/pydoll/issues) and didn't find a duplicate. 20 | required: true 21 | - label: I have reviewed the current implementation to ensure my understanding is accurate. 22 | required: true 23 | 24 | - type: textarea 25 | id: current_implementation 26 | attributes: 27 | label: Current Implementation 28 | description: Describe the current implementation and its limitations. Include file paths if known. 29 | placeholder: | 30 | The current implementation in `pydoll/module/file.py` has the following issues: 31 | 1. It uses an inefficient algorithm for... 32 | 2. The code structure makes it difficult to maintain because... 33 | validations: 34 | required: true 35 | 36 | - type: textarea 37 | id: proposed_changes 38 | attributes: 39 | label: Proposed Changes 40 | description: Describe the changes you're suggesting. Be as specific as possible. 41 | placeholder: | 42 | I suggest refactoring this code to: 43 | 1. Replace the current algorithm with X, which would improve performance by... 44 | 2. Restructure the class hierarchy to better separate concerns by... 45 | 46 | Example code sketch (if applicable): 47 | ```python 48 | def improved_method(): 49 | # better implementation 50 | ``` 51 | validations: 52 | required: true 53 | 54 | - type: textarea 55 | id: benefits 56 | attributes: 57 | label: Benefits 58 | description: Explain the benefits of this refactoring. 59 | placeholder: | 60 | This refactoring would: 61 | - Improve performance by X% 62 | - Make the code more maintainable by... 63 | - Reduce code complexity by... 64 | - Fix potential bugs such as... 65 | validations: 66 | required: true 67 | 68 | - type: dropdown 69 | id: impact 70 | attributes: 71 | label: API Impact 72 | description: Would this refactoring change the public API? 73 | options: 74 | - No API changes (internal refactoring only) 75 | - Minor API changes (backward compatible) 76 | - Breaking API changes 77 | validations: 78 | required: true 79 | 80 | - type: textarea 81 | id: testing_approach 82 | attributes: 83 | label: Testing Approach 84 | description: How can we verify that the refactoring doesn't break existing functionality? 85 | placeholder: | 86 | The refactoring can be tested by: 87 | - Running the existing test suite 88 | - Adding new tests for edge cases such as... 89 | - Benchmarking performance before and after 90 | 91 | - type: dropdown 92 | id: contribution 93 | attributes: 94 | label: Contribution 95 | description: Would you be willing to contribute this refactoring yourself? 96 | options: 97 | - Yes, I'd be willing to implement this refactoring 98 | - I could help with parts of the implementation 99 | - No, I don't have the capacity to implement this -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE/bug_fix.md: -------------------------------------------------------------------------------- 1 | # Bug Fix Pull Request 2 | 3 | ## Related Issue(s) 4 | 5 | 6 | ## Bug Description 7 | 8 | 9 | ## Root Cause 10 | 11 | 12 | ## Solution 13 | 14 | 15 | ## Verification Steps 16 | 17 | 1. 18 | 2. 19 | 3. 20 | 21 | ## Code Example 22 | 23 | ```python 24 | # Example code showing the fix 25 | ``` 26 | 27 | ## Before / After 28 | 29 | 30 | ## Testing 31 | 32 | 33 | ## Testing Checklist 34 | - [ ] Added regression test that would have caught this bug 35 | - [ ] Modified existing tests to account for this fix 36 | - [ ] All tests pass 37 | - [ ] Edge cases have been tested 38 | 39 | ## Impact 40 | 41 | - [ ] Low (isolated fix with no side effects) 42 | - [ ] Medium (might affect closely related functionality) 43 | - [ ] High (affects multiple areas or changes core behavior) 44 | 45 | ## Backwards Compatibility 46 | - [ ] This change is fully backward compatible 47 | - [ ] This change introduces backward incompatibilities (explain below) 48 | 49 | ## Checklist before requesting a review 50 | - [ ] My code follows the style guidelines of this project 51 | - [ ] I have performed a self-review of my code 52 | - [ ] I have added test cases that prove my fix is effective 53 | - [ ] I have run `poetry run task lint` and fixed any issues 54 | - [ ] I have run `poetry run task test` and all tests pass 55 | - [ ] My commits follow the [conventional commits](https://www.conventionalcommits.org/) style with message explaining the fix -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE/refactoring.md: -------------------------------------------------------------------------------- 1 | # Refactoring Pull Request 2 | 3 | ## Refactoring Scope 4 | 5 | 6 | ## Related Issue(s) 7 | 8 | 9 | ## Description 10 | 11 | 12 | ## Motivation 13 | 14 | 15 | ## Before / After 16 | 17 | 18 | ### Before 19 | ```python 20 | # Original code 21 | ``` 22 | 23 | ### After 24 | ```python 25 | # Refactored code 26 | ``` 27 | 28 | ## Performance Impact 29 | 30 | - [ ] Performance improved 31 | - [ ] Performance potentially decreased 32 | - [ ] No significant performance change 33 | - [ ] Performance impact unknown 34 | 35 | ## Technical Debt 36 | 37 | 38 | ## API Changes 39 | - [ ] No changes to public API 40 | - [ ] Public API changed, but backward compatible 41 | - [ ] Breaking changes to public API 42 | 43 | ## Testing Strategy 44 | 45 | 46 | ## Testing Checklist 47 | - [ ] Existing tests updated 48 | - [ ] New tests added for previously uncovered cases 49 | - [ ] All tests pass 50 | - [ ] Code coverage maintained or improved 51 | 52 | ## Risks and Mitigations 53 | 54 | 55 | ## Checklist before requesting a review 56 | - [ ] My code follows the style guidelines of this project 57 | - [ ] I have performed a thorough self-review of the refactored code 58 | - [ ] I have commented my code, particularly in complex areas 59 | - [ ] I have updated documentation if needed 60 | - [ ] I have run `poetry run task lint` and fixed any issues 61 | - [ ] I have run `poetry run task test` and all tests pass 62 | - [ ] My commits follow the [conventional commits](https://www.conventionalcommits.org/) style -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE/release.md: -------------------------------------------------------------------------------- 1 | # Release Pull Request 2 | 3 | ## Version 4 | 5 | 6 | ## Release Date 7 | 8 | 9 | ## Release Type 10 | - [ ] Major (breaking changes) 11 | - [ ] Minor (new features, non-breaking) 12 | - [ ] Patch (bug fixes, non-breaking) 13 | 14 | ## Change Summary 15 | 16 | 17 | ## Key Changes 18 | 19 | 20 | ## Breaking Changes 21 | 22 | 23 | ## Dependencies 24 | 25 | 26 | ## Deprecations 27 | - While `get_element_text()` is still supported, it is **recommended** to use the new async property `element.text`. 28 | 29 | 30 | ## Documentation 31 | 32 | 33 | ## Release Checklist 34 | - [ ] Version number updated in pyproject.toml 35 | - [ ] Version number updated in cz.yaml 36 | - [ ] CHANGELOG.md updated with all changes 37 | - [ ] All tests passing 38 | - [ ] Documentation updated 39 | - [ ] API reference updated 40 | - [ ] Breaking changes documented 41 | - [ ] Migration guides prepared (if applicable) 42 | 43 | ## Additional Release Notes 44 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | 10 | 11 | # Pull Request 12 | 13 | ## Description 14 | 15 | 16 | ## Related Issue(s) 17 | 18 | 19 | ## Type of Change 20 | 21 | - [ ] Bug fix (non-breaking change which fixes an issue) 22 | - [ ] New feature (non-breaking change which adds functionality) 23 | - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) 24 | - [ ] Documentation update 25 | - [ ] Refactoring (no functional changes, no API changes) 26 | - [ ] Performance improvement 27 | - [ ] Tests (adding missing tests or correcting existing tests) 28 | - [ ] Build or CI/CD related changes 29 | 30 | ## How Has This Been Tested? 31 | 32 | 33 | ```python 34 | # Include code examples if relevant 35 | ``` 36 | 37 | ## Testing Checklist 38 | 39 | - [ ] Unit tests added/updated 40 | - [ ] Integration tests added/updated 41 | - [ ] All existing tests pass 42 | 43 | ## Screenshots 44 | 45 | 46 | ## Implementation Details 47 | 48 | 49 | ## API Changes 50 | 51 | 52 | ## Additional Info 53 | 54 | 55 | ## Checklist before requesting a review 56 | - [ ] My code follows the style guidelines of this project 57 | - [ ] I have performed a self-review of my code 58 | - [ ] I have commented my code, particularly in hard-to-understand areas 59 | - [ ] I have made corresponding changes to the documentation 60 | - [ ] My changes generate no new warnings 61 | - [ ] I have added tests that prove my fix is effective or that my feature works 62 | - [ ] New and existing unit tests pass locally with my changes 63 | - [ ] I have run `poetry run task lint` and fixed any issues 64 | - [ ] I have run `poetry run task test` and all tests pass 65 | - [ ] My commits follow the [conventional commits](https://www.conventionalcommits.org/) style -------------------------------------------------------------------------------- /.github/workflows/deploy-docs.yml: -------------------------------------------------------------------------------- 1 | name: Deploy MkDocs to GitHub Pages 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | deploy: 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - name: Code Checkout 14 | uses: actions/checkout@v3 15 | 16 | - name: Setup Python 17 | uses: actions/setup-python@v4 18 | with: 19 | python-version: '3.x' 20 | 21 | - name: Install Dependencies 22 | run: | 23 | python -m pip install --upgrade pip 24 | pip install mkdocs 25 | pip install mkdocs-material 26 | pip install pymdown-extensions 27 | pip install mkdocstrings[python] 28 | 29 | - name: Build the documentation 30 | run: mkdocs build 31 | 32 | - name: Deploy to GitHub Pages 33 | uses: peaceiris/actions-gh-pages@v3 34 | with: 35 | github_token: ${{ secrets.GITHUB_TOKEN }} 36 | publish_dir: ./site 37 | -------------------------------------------------------------------------------- /.github/workflows/mypy.yml: -------------------------------------------------------------------------------- 1 | name: MyPy CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - '*' # matches every branch that doesn't contain a '/' 7 | - '*/*' # matches every branch containing a single '/' 8 | - '**' # matches every branch 9 | pull_request: 10 | 11 | jobs: 12 | build: 13 | 14 | runs-on: ubuntu-latest 15 | 16 | strategy: 17 | max-parallel: 4 18 | matrix: 19 | python-version: ["3.11"] 20 | 21 | steps: 22 | - uses: actions/checkout@v2 23 | 24 | - name: Set up Python ${{ matrix.python-version }} 25 | uses: actions/setup-python@v2 26 | with: 27 | python-version: ${{ matrix.python-version }} 28 | 29 | - name: Install Dependencies 30 | run: | 31 | python -m pip install --upgrade pip 32 | python -m pip install mypy 33 | python -m pip install -e . 34 | python -m mypy --install-types --non-interactive pydoll 35 | 36 | - name: mypy 37 | run: python -m mypy . 38 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish to PyPI (Poetry) 2 | 3 | on: workflow_dispatch 4 | 5 | jobs: 6 | deploy: 7 | runs-on: ubuntu-latest 8 | 9 | steps: 10 | - name: Checkout code 11 | uses: actions/checkout@v3 12 | 13 | - name: Set up Python 14 | uses: actions/setup-python@v4 15 | with: 16 | python-version: "3.10" 17 | 18 | - name: Install Poetry 19 | run: | 20 | python -m pip install --upgrade pip 21 | pip install poetry 22 | 23 | - name: Configure Poetry 24 | run: poetry config pypi-token.pypi ${{ secrets.PYPI_API_TOKEN }} 25 | 26 | - name: Install dependencies 27 | run: poetry install 28 | 29 | - name: Build package 30 | run: poetry build 31 | 32 | - name: Publish to PyPI 33 | run: poetry publish 34 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | on: workflow_dispatch 3 | 4 | jobs: 5 | version-cz: 6 | runs-on: ubuntu-latest 7 | name: "Version CZ" 8 | outputs: 9 | version: ${{ steps.cz.outputs.version }} 10 | 11 | steps: 12 | - name: Checkout 13 | uses: actions/checkout@v4 14 | with: 15 | fetch-depth: 0 16 | token: ${{ secrets.GITHUB_TOKEN }} 17 | 18 | - id: cz 19 | name: Create bump and changelog 20 | uses: commitizen-tools/commitizen-action@master 21 | with: 22 | github_token: ${{ secrets.GITHUB_TOKEN }} 23 | 24 | - name: Print Version 25 | run: echo "Bumped to version ${{ steps.cz.outputs.version }}" 26 | 27 | version-pyproject: 28 | runs-on: ubuntu-latest 29 | name: "Version Pyproject" 30 | needs: version-cz 31 | outputs: 32 | version: ${{ needs.version-cz.outputs.version }} 33 | steps: 34 | - name: Checkout 35 | uses: actions/checkout@v4 36 | with: 37 | fetch-depth: 0 38 | token: ${{ secrets.GITHUB_TOKEN }} 39 | 40 | - name: Install Poetry 41 | run: | 42 | curl -sSL https://install.python-poetry.org | python3 - 43 | export PATH="$HOME/.local/bin:$PATH" 44 | 45 | - name: Update Poetry version in pyproject.toml 46 | run: | 47 | git config --global user.name "github-actions[bot]" 48 | git config --global user.email "github-actions[bot]@users.noreply.github.com" 49 | poetry version "${{ needs.version-cz.outputs.version }}" 50 | git add pyproject.toml 51 | git commit -m "Update pyproject.toml to version ${{ needs.version-cz.outputs.version }}" 52 | git pull --rebase 53 | git push 54 | 55 | - name: Update poetry.lock 56 | continue-on-error: true 57 | run: | 58 | git config --global user.name "github-actions[bot]" 59 | git config --global user.email "github-actions[bot]@users.noreply.github.com" 60 | poetry lock 61 | git add poetry.lock 62 | git commit -m "Update poetry.lock" 63 | git pull --rebase 64 | git push 65 | 66 | 67 | release: 68 | name: Release 69 | needs: version-pyproject 70 | runs-on: ubuntu-latest 71 | steps: 72 | - name: Create Release 73 | uses: softprops/action-gh-release@v1 74 | with: 75 | draft: false 76 | prerelease: false 77 | generate_release_notes: true 78 | tag_name: ${{ needs.version-pyproject.outputs.version }} 79 | env: 80 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 81 | -------------------------------------------------------------------------------- /.github/workflows/ruff-ci.yml: -------------------------------------------------------------------------------- 1 | name: Ruff CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - '*' # matches every branch that doesn't contain a '/' 7 | - '*/*' # matches every branch containing a single '/' 8 | - '**' # matches every branch 9 | pull_request: 10 | 11 | jobs: 12 | build: 13 | 14 | runs-on: ubuntu-latest 15 | 16 | strategy: 17 | max-parallel: 4 18 | matrix: 19 | python-version: ["3.11"] 20 | 21 | steps: 22 | - uses: actions/checkout@v2 23 | 24 | - name: Set up Python ${{ matrix.python-version }} 25 | uses: actions/setup-python@v2 26 | with: 27 | python-version: ${{ matrix.python-version }} 28 | 29 | - name: Install Dependencies 30 | run: | 31 | python -m pip install --upgrade pip 32 | python -m pip install ruff==0.7.1 33 | 34 | - name: ruff 35 | run: python -m ruff check . 36 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: PyDoll Tests Suite 2 | 3 | on: 4 | push: 5 | pull_request: 6 | 7 | jobs: 8 | tests: 9 | runs-on: ubuntu-latest 10 | strategy: 11 | fail-fast: false 12 | matrix: 13 | python-version: ["3.11", "3.12", "3.13"] 14 | steps: 15 | - uses: actions/checkout@v3 16 | - name: Set up Python ${{ matrix.python-version }} 17 | uses: actions/setup-python@v4 18 | with: 19 | python-version: ${{ matrix.python-version }} 20 | - name: Install dependencies 21 | run: | 22 | python -m pip install poetry 23 | poetry install 24 | - name: Run tests with coverage 25 | run: | 26 | poetry run pytest -s -x --cov=pydoll -vv --cov-report=xml 27 | 28 | - name: Upload coverage to Codecov 29 | uses: codecov/codecov-action@v5 30 | with: 31 | file: ./coverage.xml 32 | flags: tests 33 | name: PyDoll Tests 34 | fail_ci_if_error: true 35 | token: ${{ secrets.CODECOV_TOKEN }} 36 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__/ 2 | *.py[cod] 3 | *$py.class 4 | 5 | # C extensions 6 | *.so 7 | 8 | # Distribution / packaging 9 | .Python 10 | build/ 11 | develop-eggs/ 12 | dist/ 13 | downloads/ 14 | eggs/ 15 | .eggs/ 16 | lib/ 17 | lib64/ 18 | parts/ 19 | sdist/ 20 | var/ 21 | wheels/ 22 | share/python-wheels/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | MANIFEST 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .nox/ 42 | .coverage 43 | .coverage.* 44 | .cache 45 | nosetests.xml 46 | coverage.xml 47 | *.cover 48 | *.py,cover 49 | .hypothesis/ 50 | .pytest_cache/ 51 | cover/ 52 | 53 | # Translations 54 | *.mo 55 | *.pot 56 | 57 | # Django stuff: 58 | *.log 59 | local_settings.py 60 | db.sqlite3 61 | db.sqlite3-journal 62 | 63 | # Flask stuff: 64 | instance/ 65 | .webassets-cache 66 | 67 | # Scrapy stuff: 68 | .scrapy 69 | 70 | # Sphinx documentation 71 | docs/_build/ 72 | 73 | # PyBuilder 74 | .pybuilder/ 75 | 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | # For a library or package, you might want to ignore these files since the code is 86 | # intended to run in multiple environments; otherwise, check them in: 87 | # .python-version 88 | 89 | # pipenv 90 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 91 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 92 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 93 | # install all needed dependencies. 94 | #Pipfile.lock 95 | 96 | # poetry 97 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 98 | # This is especially recommended for binary packages to ensure reproducibility, and is more 99 | # commonly ignored for libraries. 100 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 101 | #poetry.lock 102 | 103 | # pdm 104 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 105 | #pdm.lock 106 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 107 | # in version control. 108 | # https://pdm.fming.dev/latest/usage/project/#working-with-version-control 109 | .pdm.toml 110 | .pdm-python 111 | .pdm-build/ 112 | 113 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 114 | __pypackages__/ 115 | 116 | # Celery stuff 117 | celerybeat-schedule 118 | celerybeat.pid 119 | 120 | # SageMath parsed files 121 | *.sage.py 122 | 123 | # Environments 124 | .env 125 | .venv 126 | env/ 127 | venv/ 128 | ENV/ 129 | env.bak/ 130 | venv.bak/ 131 | 132 | # Spyder project settings 133 | .spyderproject 134 | .spyproject 135 | 136 | # Rope project settings 137 | .ropeproject 138 | 139 | # mkdocs documentation 140 | /site 141 | 142 | # mypy 143 | .mypy_cache/ 144 | .dmypy.json 145 | dmypy.json 146 | 147 | # Pyre type checker 148 | .pyre/ 149 | 150 | # pytype static type analyzer 151 | .pytype/ 152 | 153 | # Cython debug symbols 154 | cython_debug/ 155 | 156 | # PyCharm 157 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 158 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 159 | # and can be added to the global gitignore or merged into this file. For a more nuclear 160 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 161 | #.idea/ 162 | 163 | .czrc 164 | .ruff_cache/ -------------------------------------------------------------------------------- /.python-version: -------------------------------------------------------------------------------- 1 | 3.12.5 2 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guide 2 | 3 | Thank you for your interest in contributing to the project! This document provides guidelines and instructions to help you contribute effectively. 4 | 5 | ## Table of Contents 6 | 7 | - [Environment Setup](#environment-setup) 8 | - [Development Workflow](#development-workflow) 9 | - [Code Standards](#code-standards) 10 | - [Testing](#testing) 11 | - [Commit Messages](#commit-messages) 12 | - [Pull Request Process](#pull-request-process) 13 | 14 | ## Environment Setup 15 | 16 | ### Prerequisites 17 | 18 | - Python 3.10 or higher 19 | - [Poetry](https://python-poetry.org/docs/#installation) for dependency management 20 | 21 | ### Installation 22 | 23 | 1. Clone the repository: 24 | ```bash 25 | git clone [REPOSITORY_URL] 26 | cd pydoll 27 | ``` 28 | 29 | 2. Install dependencies using Poetry: 30 | ```bash 31 | poetry install 32 | ``` 33 | 34 | 3. Activate the virtual environment: 35 | ```bash 36 | poetry shell 37 | ``` 38 | 39 | ## Development Workflow 40 | 41 | 1. Create a new branch for your contribution: 42 | ```bash 43 | git checkout -b feature/your-feature-name 44 | ``` 45 | or 46 | ```bash 47 | git checkout -b fix/your-fix-name 48 | ``` 49 | 50 | 2. Make your changes following the code and testing guidelines. 51 | 52 | 3. Check your code using the linter: 53 | ```bash 54 | poetry run task lint 55 | ``` 56 | 57 | 4. Format your code: 58 | ```bash 59 | poetry run task format 60 | ``` 61 | 62 | 5. Run the tests to ensure everything is working: 63 | ```bash 64 | poetry run task test 65 | ``` 66 | 67 | 6. Commit your changes following the commit conventions (see below). 68 | 69 | 7. Push your changes and open a Pull Request. 70 | 71 | ## Code Standards 72 | 73 | This project uses [Ruff](https://github.com/charliermarsh/ruff) for linting and code formatting. The code standards are defined in the `pyproject.toml` file. 74 | 75 | ### Linting and Formatting 76 | 77 | To check if your code follows the standards: 78 | 79 | ```bash 80 | poetry run task lint 81 | ``` 82 | 83 | To automatically fix some issues and format your code: 84 | 85 | ```bash 86 | poetry run task format 87 | ``` 88 | 89 | **Important:** Make sure to resolve all linting issues before submitting your changes. Code that doesn't pass the linting checks will not be accepted. 90 | 91 | ## Testing 92 | 93 | ### Writing Tests 94 | 95 | For each new feature or modification, it is **mandatory** to write corresponding tests. We use `pytest` for testing. 96 | 97 | - Tests should be placed in the `tests/` directory 98 | - Test file names should start with `test_` 99 | - Test function names should start with `test_` 100 | 101 | ### Running Tests 102 | 103 | To run all tests: 104 | 105 | ```bash 106 | poetry run task test 107 | ``` 108 | 109 | This will also generate a code coverage report (HTML) that can be viewed in the `htmlcov/` folder. 110 | 111 | ## Commit Messages 112 | 113 | This project follows the [Conventional Commits](https://www.conventionalcommits.org/) standard for commit messages. We use the `commitizen` tool to facilitate the creation of standardized commits. 114 | 115 | ### Commit Message Structure 116 | 117 | ``` 118 | [optional scope]: 119 | 120 | [optional body] 121 | 122 | [optional footer(s)] 123 | ``` 124 | 125 | ### Commit Types 126 | 127 | - **feat**: A new feature 128 | - **fix**: A bug fix 129 | - **docs**: Documentation-only changes 130 | - **style**: Changes that do not affect the meaning of the code (whitespace, formatting, etc.) 131 | - **refactor**: A code change that neither fixes a bug nor adds a feature 132 | - **perf**: A code change that improves performance 133 | - **test**: Adding or correcting tests 134 | - **build**: Changes that affect the build system or external dependencies 135 | - **ci**: Changes to CI configuration files 136 | - **chore**: Other changes that don't modify src or test files 137 | 138 | ### Examples of Good Commit Messages 139 | 140 | ``` 141 | feat(parser): add ability to parse arrays 142 | ``` 143 | 144 | ``` 145 | fix(networking): resolve connection timeout issue 146 | 147 | A problem was identified in the networking library that 148 | caused unexpected timeouts. This change increases the 149 | default timeout from 10s to 30s. 150 | ``` 151 | 152 | ## Pull Request Process 153 | 154 | 1. Verify that your code passes all tests and linting checks. 155 | 2. Push your branch to the repository. 156 | 3. Open a Pull Request to the main branch. 157 | 4. In the PR description, clearly explain what was changed and why. 158 | 5. Link any related issues to your PR. 159 | 6. Wait for the code review. Read the comments and make necessary changes. 160 | 161 | ## Questions? 162 | 163 | If you have questions or need help, open an issue in the repository or contact the project maintainers. 164 | 165 | --- 166 | 167 | We appreciate your contributions to make this project better! -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright © 2025 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | coverage: 2 | status: 3 | project: 4 | default: 5 | target: 90% 6 | threshold: 0% 7 | base: auto -------------------------------------------------------------------------------- /cz.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | commitizen: 3 | name: cz_conventional_commits 4 | tag_format: $version 5 | version: 2.0.1 6 | -------------------------------------------------------------------------------- /docs/api/browser/chrome.md: -------------------------------------------------------------------------------- 1 | # Chrome Browser 2 | 3 | ::: pydoll.browser.chromium.Chrome 4 | options: 5 | show_root_heading: true 6 | show_source: false 7 | heading_level: 2 -------------------------------------------------------------------------------- /docs/api/browser/edge.md: -------------------------------------------------------------------------------- 1 | # Edge Browser 2 | 3 | ::: pydoll.browser.chromium.Edge 4 | options: 5 | show_root_heading: true 6 | show_source: false 7 | heading_level: 2 -------------------------------------------------------------------------------- /docs/api/browser/managers.md: -------------------------------------------------------------------------------- 1 | # Browser Managers 2 | 3 | The managers module provides specialized classes for managing different aspects of browser lifecycle and configuration. 4 | 5 | ## Overview 6 | 7 | Browser managers handle specific responsibilities in browser automation: 8 | 9 | ::: pydoll.browser.managers 10 | options: 11 | show_root_heading: true 12 | show_source: false 13 | heading_level: 2 14 | filters: 15 | - "!^_" 16 | - "!^__" 17 | 18 | ## Manager Classes 19 | 20 | ### Browser Process Manager 21 | Manages the browser process lifecycle, including starting, stopping, and monitoring browser processes. 22 | 23 | ::: pydoll.browser.managers.browser_process_manager 24 | options: 25 | show_root_heading: true 26 | show_source: false 27 | heading_level: 3 28 | 29 | ### Browser Options Manager 30 | Handles browser configuration options and command-line arguments. 31 | 32 | ::: pydoll.browser.managers.browser_options_manager 33 | options: 34 | show_root_heading: true 35 | show_source: false 36 | heading_level: 3 37 | 38 | ### Proxy Manager 39 | Manages proxy configuration and authentication for browser instances. 40 | 41 | ::: pydoll.browser.managers.proxy_manager 42 | options: 43 | show_root_heading: true 44 | show_source: false 45 | heading_level: 3 46 | 47 | ### Temporary Directory Manager 48 | Handles creation and cleanup of temporary directories used by browser instances. 49 | 50 | ::: pydoll.browser.managers.temp_dir_manager 51 | options: 52 | show_root_heading: true 53 | show_source: false 54 | heading_level: 3 55 | 56 | ## Usage 57 | 58 | Managers are typically used internally by browser classes like `Chrome` and `Edge`. They provide modular functionality that can be composed together: 59 | 60 | ```python 61 | from pydoll.browser.managers.proxy_manager import ProxyManager 62 | from pydoll.browser.managers.temp_dir_manager import TempDirManager 63 | 64 | # Managers are used internally by browser classes 65 | # Direct usage is for advanced scenarios only 66 | proxy_manager = ProxyManager() 67 | temp_manager = TempDirManager() 68 | ``` 69 | 70 | !!! note "Internal Usage" 71 | These managers are primarily used internally by the browser classes. Direct usage is recommended only for advanced scenarios or when extending the library. -------------------------------------------------------------------------------- /docs/api/browser/options.md: -------------------------------------------------------------------------------- 1 | # Browser Options 2 | 3 | ## ChromiumOptions 4 | 5 | ::: pydoll.browser.options.ChromiumOptions 6 | options: 7 | show_root_heading: true 8 | show_source: false 9 | heading_level: 3 10 | 11 | ## Options Interface 12 | 13 | ::: pydoll.browser.interfaces.Options 14 | options: 15 | show_root_heading: true 16 | show_source: false 17 | heading_level: 3 18 | 19 | ## BrowserOptionsManager Interface 20 | 21 | ::: pydoll.browser.interfaces.BrowserOptionsManager 22 | options: 23 | show_root_heading: true 24 | show_source: false 25 | heading_level: 3 -------------------------------------------------------------------------------- /docs/api/browser/tab.md: -------------------------------------------------------------------------------- 1 | # Tab 2 | 3 | ::: pydoll.browser.tab.Tab 4 | options: 5 | show_root_heading: true 6 | show_source: false 7 | heading_level: 2 -------------------------------------------------------------------------------- /docs/api/commands/browser.md: -------------------------------------------------------------------------------- 1 | # Browser Commands 2 | 3 | Browser commands provide low-level control over browser instances and their configuration. 4 | 5 | ## Overview 6 | 7 | The browser commands module handles browser-level operations such as version information, target management, and browser-wide settings. 8 | 9 | ::: pydoll.commands.browser_commands 10 | options: 11 | show_root_heading: true 12 | show_source: false 13 | heading_level: 2 14 | filters: 15 | - "!^_" 16 | - "!^__" 17 | 18 | ## Usage 19 | 20 | Browser commands are typically used internally by browser classes to manage browser instances: 21 | 22 | ```python 23 | from pydoll.commands.browser_commands import get_version 24 | from pydoll.connection.connection_handler import ConnectionHandler 25 | 26 | # Get browser version information 27 | connection = ConnectionHandler() 28 | version_info = await get_version(connection) 29 | ``` 30 | 31 | ## Available Commands 32 | 33 | The browser commands module provides functions for: 34 | 35 | - Getting browser version and user agent information 36 | - Managing browser targets (tabs, windows) 37 | - Controlling browser-wide settings and permissions 38 | - Handling browser lifecycle events 39 | 40 | !!! note "Internal Usage" 41 | These commands are primarily used internally by the `Chrome` and `Edge` browser classes. Direct usage is recommended only for advanced scenarios. -------------------------------------------------------------------------------- /docs/api/commands/dom.md: -------------------------------------------------------------------------------- 1 | # DOM Commands 2 | 3 | DOM commands provide comprehensive functionality for interacting with the Document Object Model of web pages. 4 | 5 | ## Overview 6 | 7 | The DOM commands module is one of the most important modules in Pydoll, providing all the functionality needed to find, interact with, and manipulate HTML elements on web pages. 8 | 9 | ::: pydoll.commands.dom_commands 10 | options: 11 | show_root_heading: true 12 | show_source: false 13 | heading_level: 2 14 | filters: 15 | - "!^_" 16 | - "!^__" 17 | 18 | ## Usage 19 | 20 | DOM commands are used extensively by the `WebElement` class and element finding methods: 21 | 22 | ```python 23 | from pydoll.commands.dom_commands import query_selector, get_attributes 24 | from pydoll.connection.connection_handler import ConnectionHandler 25 | 26 | # Find element and get its attributes 27 | connection = ConnectionHandler() 28 | node_id = await query_selector(connection, selector="#username") 29 | attributes = await get_attributes(connection, node_id=node_id) 30 | ``` 31 | 32 | ## Key Functionality 33 | 34 | The DOM commands module provides functions for: 35 | 36 | ### Element Finding 37 | - `query_selector()` - Find single element by CSS selector 38 | - `query_selector_all()` - Find multiple elements by CSS selector 39 | - `get_document()` - Get the document root node 40 | 41 | ### Element Interaction 42 | - `click_element()` - Click on elements 43 | - `focus_element()` - Focus elements 44 | - `set_attribute_value()` - Set element attributes 45 | - `get_attributes()` - Get element attributes 46 | 47 | ### Element Information 48 | - `get_box_model()` - Get element positioning and dimensions 49 | - `describe_node()` - Get detailed element information 50 | - `get_outer_html()` - Get element HTML content 51 | 52 | ### DOM Manipulation 53 | - `remove_node()` - Remove elements from DOM 54 | - `set_node_value()` - Set element values 55 | - `request_child_nodes()` - Get child elements 56 | 57 | !!! tip "High-Level APIs" 58 | While these commands provide powerful low-level access, most users should use the higher-level `WebElement` class methods like `click()`, `type()`, and `get_attribute()` which use these commands internally. -------------------------------------------------------------------------------- /docs/api/commands/fetch.md: -------------------------------------------------------------------------------- 1 | # Fetch Commands 2 | 3 | Fetch commands provide advanced network request handling and interception capabilities using the Fetch API domain. 4 | 5 | ## Overview 6 | 7 | The fetch commands module enables sophisticated network request management, including request modification, response interception, and authentication handling. 8 | 9 | ::: pydoll.commands.fetch_commands 10 | options: 11 | show_root_heading: true 12 | show_source: false 13 | heading_level: 2 14 | filters: 15 | - "!^_" 16 | - "!^__" 17 | 18 | ## Usage 19 | 20 | Fetch commands are used for advanced network interception and request handling: 21 | 22 | ```python 23 | from pydoll.commands.fetch_commands import enable, request_paused, continue_request 24 | from pydoll.connection.connection_handler import ConnectionHandler 25 | 26 | # Enable fetch domain 27 | connection = ConnectionHandler() 28 | await enable(connection, patterns=[{ 29 | "urlPattern": "*", 30 | "requestStage": "Request" 31 | }]) 32 | 33 | # Handle paused requests 34 | async def handle_paused_request(request_id, request): 35 | # Modify request or continue as-is 36 | await continue_request(connection, request_id=request_id) 37 | ``` 38 | 39 | ## Key Functionality 40 | 41 | The fetch commands module provides functions for: 42 | 43 | ### Request Interception 44 | - `enable()` - Enable fetch domain with patterns 45 | - `disable()` - Disable fetch domain 46 | - `continue_request()` - Continue intercepted requests 47 | - `fail_request()` - Fail requests with specific errors 48 | 49 | ### Request Modification 50 | - Modify request headers 51 | - Change request URLs 52 | - Alter request methods (GET, POST, etc.) 53 | - Modify request bodies 54 | 55 | ### Response Handling 56 | - `fulfill_request()` - Provide custom responses 57 | - `get_response_body()` - Get response content 58 | - Response header modification 59 | - Response status code control 60 | 61 | ### Authentication 62 | - `continue_with_auth()` - Handle authentication challenges 63 | - Basic authentication support 64 | - Custom authentication flows 65 | 66 | ## Advanced Features 67 | 68 | ### Pattern-Based Interception 69 | ```python 70 | # Intercept specific URL patterns 71 | patterns = [ 72 | {"urlPattern": "*/api/*", "requestStage": "Request"}, 73 | {"urlPattern": "*.js", "requestStage": "Response"}, 74 | {"urlPattern": "https://example.com/*", "requestStage": "Request"} 75 | ] 76 | 77 | await enable(connection, patterns=patterns) 78 | ``` 79 | 80 | ### Request Modification 81 | ```python 82 | # Modify intercepted requests 83 | async def modify_request(request_id, request): 84 | # Add authentication header 85 | headers = request.headers.copy() 86 | headers["Authorization"] = "Bearer token123" 87 | 88 | # Continue with modified headers 89 | await continue_request( 90 | connection, 91 | request_id=request_id, 92 | headers=headers 93 | ) 94 | ``` 95 | 96 | ### Response Mocking 97 | ```python 98 | # Mock API responses 99 | await fulfill_request( 100 | connection, 101 | request_id=request_id, 102 | response_code=200, 103 | response_headers=[ 104 | {"name": "Content-Type", "value": "application/json"}, 105 | {"name": "Access-Control-Allow-Origin", "value": "*"} 106 | ], 107 | body='{"status": "success", "data": {"mocked": true}}' 108 | ) 109 | ``` 110 | 111 | ### Authentication Handling 112 | ```python 113 | # Handle authentication challenges 114 | await continue_with_auth( 115 | connection, 116 | request_id=request_id, 117 | auth_challenge_response={ 118 | "response": "ProvideCredentials", 119 | "username": "user", 120 | "password": "pass" 121 | } 122 | ) 123 | ``` 124 | 125 | ## Request Stages 126 | 127 | Fetch commands can intercept requests at different stages: 128 | 129 | | Stage | Description | Use Cases | 130 | |-------|-------------|-----------| 131 | | Request | Before request is sent | Modify headers, URL, method | 132 | | Response | After response received | Mock responses, modify content | 133 | 134 | ## Error Handling 135 | 136 | ```python 137 | # Fail requests with specific errors 138 | await fail_request( 139 | connection, 140 | request_id=request_id, 141 | error_reason="ConnectionRefused" # or "AccessDenied", "TimedOut", etc. 142 | ) 143 | ``` 144 | 145 | ## Integration with Network Commands 146 | 147 | Fetch commands work alongside network commands but provide more granular control: 148 | 149 | - **Network Commands**: Broader network monitoring and control 150 | - **Fetch Commands**: Specific request/response interception and modification 151 | 152 | !!! tip "Performance Considerations" 153 | Fetch interception can impact page loading performance. Use specific URL patterns and disable when not needed to minimize overhead. -------------------------------------------------------------------------------- /docs/api/commands/input.md: -------------------------------------------------------------------------------- 1 | # Input Commands 2 | 3 | Input commands handle mouse and keyboard interactions, providing human-like input simulation. 4 | 5 | ## Overview 6 | 7 | The input commands module provides functionality for simulating user input including mouse movements, clicks, keyboard typing, and key presses. 8 | 9 | ::: pydoll.commands.input_commands 10 | options: 11 | show_root_heading: true 12 | show_source: false 13 | heading_level: 2 14 | filters: 15 | - "!^_" 16 | - "!^__" 17 | 18 | ## Usage 19 | 20 | Input commands are used by element interaction methods and can be used directly for advanced input scenarios: 21 | 22 | ```python 23 | from pydoll.commands.input_commands import dispatch_mouse_event, dispatch_key_event 24 | from pydoll.connection.connection_handler import ConnectionHandler 25 | 26 | # Simulate mouse click 27 | connection = ConnectionHandler() 28 | await dispatch_mouse_event( 29 | connection, 30 | type="mousePressed", 31 | x=100, 32 | y=200, 33 | button="left" 34 | ) 35 | 36 | # Simulate keyboard typing 37 | await dispatch_key_event( 38 | connection, 39 | type="keyDown", 40 | key="Enter" 41 | ) 42 | ``` 43 | 44 | ## Key Functionality 45 | 46 | The input commands module provides functions for: 47 | 48 | ### Mouse Events 49 | - `dispatch_mouse_event()` - Mouse clicks, movements, and wheel events 50 | - Mouse button states (left, right, middle) 51 | - Coordinate-based positioning 52 | - Drag and drop operations 53 | 54 | ### Keyboard Events 55 | - `dispatch_key_event()` - Key press and release events 56 | - `insert_text()` - Direct text insertion 57 | - Special key handling (Enter, Tab, Arrow keys, etc.) 58 | - Modifier keys (Ctrl, Alt, Shift) 59 | 60 | ### Touch Events 61 | - Touch screen simulation 62 | - Multi-touch gestures 63 | - Touch coordinates and pressure 64 | 65 | ## Human-like Behavior 66 | 67 | The input commands support human-like behavior patterns: 68 | 69 | - Natural mouse movement curves 70 | - Realistic typing speeds and patterns 71 | - Random micro-delays between actions 72 | - Pressure-sensitive touch events 73 | 74 | !!! tip "Element Methods" 75 | For most use cases, use the higher-level element methods like `element.click()` and `element.type()` which provide a more convenient API and handle common scenarios automatically. -------------------------------------------------------------------------------- /docs/api/commands/network.md: -------------------------------------------------------------------------------- 1 | # Network Commands 2 | 3 | Network commands provide comprehensive control over network requests, responses, and browser networking behavior. 4 | 5 | ## Overview 6 | 7 | The network commands module enables request interception, response modification, cookie management, and network monitoring capabilities. 8 | 9 | ::: pydoll.commands.network_commands 10 | options: 11 | show_root_heading: true 12 | show_source: false 13 | heading_level: 2 14 | filters: 15 | - "!^_" 16 | - "!^__" 17 | 18 | ## Usage 19 | 20 | Network commands are used for advanced scenarios like request interception and network monitoring: 21 | 22 | ```python 23 | from pydoll.commands.network_commands import enable, set_request_interception 24 | from pydoll.connection.connection_handler import ConnectionHandler 25 | 26 | # Enable network monitoring 27 | connection = ConnectionHandler() 28 | await enable(connection) 29 | 30 | # Enable request interception 31 | await set_request_interception(connection, patterns=[{"urlPattern": "*"}]) 32 | ``` 33 | 34 | ## Key Functionality 35 | 36 | The network commands module provides functions for: 37 | 38 | ### Request Management 39 | - `enable()` / `disable()` - Enable/disable network monitoring 40 | - `set_request_interception()` - Intercept and modify requests 41 | - `continue_intercepted_request()` - Continue or modify intercepted requests 42 | - `get_request_post_data()` - Get request body data 43 | 44 | ### Response Handling 45 | - `get_response_body()` - Get response content 46 | - `fulfill_request()` - Provide custom responses 47 | - `fail_request()` - Simulate network failures 48 | 49 | ### Cookie Management 50 | - `get_cookies()` - Get browser cookies 51 | - `set_cookies()` - Set browser cookies 52 | - `delete_cookies()` - Delete specific cookies 53 | - `clear_browser_cookies()` - Clear all cookies 54 | 55 | ### Cache Control 56 | - `clear_browser_cache()` - Clear browser cache 57 | - `set_cache_disabled()` - Disable browser cache 58 | - `get_response_body_for_interception()` - Get cached responses 59 | 60 | ### Security & Headers 61 | - `set_user_agent_override()` - Override user agent 62 | - `set_extra_http_headers()` - Add custom headers 63 | - `emulate_network_conditions()` - Simulate network conditions 64 | 65 | ## Advanced Use Cases 66 | 67 | ### Request Interception 68 | ```python 69 | # Intercept and modify requests 70 | await set_request_interception(connection, patterns=[ 71 | {"urlPattern": "*/api/*", "requestStage": "Request"} 72 | ]) 73 | 74 | # Handle intercepted request 75 | async def handle_request(request): 76 | if "api/login" in request.url: 77 | # Modify request headers 78 | headers = request.headers.copy() 79 | headers["Authorization"] = "Bearer token" 80 | await continue_intercepted_request( 81 | connection, 82 | request_id=request.request_id, 83 | headers=headers 84 | ) 85 | ``` 86 | 87 | ### Response Mocking 88 | ```python 89 | # Mock API responses 90 | await fulfill_request( 91 | connection, 92 | request_id=request_id, 93 | response_code=200, 94 | response_headers={"Content-Type": "application/json"}, 95 | body='{"status": "success"}' 96 | ) 97 | ``` 98 | 99 | !!! warning "Performance Impact" 100 | Network interception can impact page loading performance. Use selectively and disable when not needed. -------------------------------------------------------------------------------- /docs/api/commands/page.md: -------------------------------------------------------------------------------- 1 | # Page Commands 2 | 3 | Page commands handle page navigation, lifecycle events, and page-level operations. 4 | 5 | ## Overview 6 | 7 | The page commands module provides functionality for navigating between pages, managing page lifecycle, handling JavaScript execution, and controlling page behavior. 8 | 9 | ::: pydoll.commands.page_commands 10 | options: 11 | show_root_heading: true 12 | show_source: false 13 | heading_level: 2 14 | filters: 15 | - "!^_" 16 | - "!^__" 17 | 18 | ## Usage 19 | 20 | Page commands are used extensively by the `Tab` class for navigation and page management: 21 | 22 | ```python 23 | from pydoll.commands.page_commands import navigate, reload, enable 24 | from pydoll.connection.connection_handler import ConnectionHandler 25 | 26 | # Navigate to a URL 27 | connection = ConnectionHandler() 28 | await enable(connection) # Enable page events 29 | await navigate(connection, url="https://example.com") 30 | 31 | # Reload the page 32 | await reload(connection) 33 | ``` 34 | 35 | ## Key Functionality 36 | 37 | The page commands module provides functions for: 38 | 39 | ### Navigation 40 | - `navigate()` - Navigate to URLs 41 | - `reload()` - Reload current page 42 | - `go_back()` - Navigate back in history 43 | - `go_forward()` - Navigate forward in history 44 | - `stop_loading()` - Stop page loading 45 | 46 | ### Page Lifecycle 47 | - `enable()` / `disable()` - Enable/disable page events 48 | - `get_frame_tree()` - Get page frame structure 49 | - `get_navigation_history()` - Get navigation history 50 | 51 | ### Content Management 52 | - `get_resource_content()` - Get page resource content 53 | - `search_in_resource()` - Search within page resources 54 | - `set_document_content()` - Set page HTML content 55 | 56 | ### Screenshots & PDF 57 | - `capture_screenshot()` - Take page screenshots 58 | - `print_to_pdf()` - Generate PDF from page 59 | - `capture_snapshot()` - Capture page snapshots 60 | 61 | ### JavaScript Execution 62 | - `add_script_to_evaluate_on_new_document()` - Add startup scripts 63 | - `remove_script_to_evaluate_on_new_document()` - Remove startup scripts 64 | 65 | ### Page Settings 66 | - `set_lifecycle_events_enabled()` - Control lifecycle events 67 | - `set_ad_blocking_enabled()` - Enable/disable ad blocking 68 | - `set_bypass_csp()` - Bypass Content Security Policy 69 | 70 | ## Advanced Features 71 | 72 | ### Frame Management 73 | ```python 74 | # Get all frames in the page 75 | frame_tree = await get_frame_tree(connection) 76 | for frame in frame_tree.child_frames: 77 | print(f"Frame: {frame.frame.url}") 78 | ``` 79 | 80 | ### Resource Interception 81 | ```python 82 | # Get resource content 83 | content = await get_resource_content( 84 | connection, 85 | frame_id=frame_id, 86 | url="https://example.com/script.js" 87 | ) 88 | ``` 89 | 90 | ### Page Events 91 | The page commands work with various page events: 92 | - `Page.loadEventFired` - Page load completed 93 | - `Page.domContentEventFired` - DOM content loaded 94 | - `Page.frameNavigated` - Frame navigation 95 | - `Page.frameStartedLoading` - Frame loading started 96 | 97 | !!! tip "Tab Class Integration" 98 | Most page operations are available through the `Tab` class methods like `tab.go_to()`, `tab.reload()`, and `tab.screenshot()` which provide a more convenient API. -------------------------------------------------------------------------------- /docs/api/commands/runtime.md: -------------------------------------------------------------------------------- 1 | # Runtime Commands 2 | 3 | Runtime commands provide JavaScript execution capabilities and runtime environment management. 4 | 5 | ## Overview 6 | 7 | The runtime commands module enables JavaScript code execution, object inspection, and runtime environment control within browser contexts. 8 | 9 | ::: pydoll.commands.runtime_commands 10 | options: 11 | show_root_heading: true 12 | show_source: false 13 | heading_level: 2 14 | filters: 15 | - "!^_" 16 | - "!^__" 17 | 18 | ## Usage 19 | 20 | Runtime commands are used for JavaScript execution and runtime management: 21 | 22 | ```python 23 | from pydoll.commands.runtime_commands import evaluate, enable 24 | from pydoll.connection.connection_handler import ConnectionHandler 25 | 26 | # Enable runtime events 27 | connection = ConnectionHandler() 28 | await enable(connection) 29 | 30 | # Execute JavaScript 31 | result = await evaluate( 32 | connection, 33 | expression="document.title", 34 | return_by_value=True 35 | ) 36 | print(result.value) # Page title 37 | ``` 38 | 39 | ## Key Functionality 40 | 41 | The runtime commands module provides functions for: 42 | 43 | ### JavaScript Execution 44 | - `evaluate()` - Execute JavaScript expressions 45 | - `call_function_on()` - Call functions on objects 46 | - `compile_script()` - Compile JavaScript for reuse 47 | - `run_script()` - Run compiled scripts 48 | 49 | ### Object Management 50 | - `get_properties()` - Get object properties 51 | - `release_object()` - Release object references 52 | - `release_object_group()` - Release object groups 53 | 54 | ### Runtime Control 55 | - `enable()` / `disable()` - Enable/disable runtime events 56 | - `discard_console_entries()` - Clear console entries 57 | - `set_custom_object_formatter_enabled()` - Enable custom formatters 58 | 59 | ### Exception Handling 60 | - `set_async_call_stack_depth()` - Set call stack depth 61 | - Exception capture and reporting 62 | - Error object inspection 63 | 64 | ## Advanced Usage 65 | 66 | ### Complex JavaScript Execution 67 | ```python 68 | # Execute complex JavaScript with error handling 69 | script = """ 70 | try { 71 | const elements = document.querySelectorAll('.item'); 72 | return Array.from(elements).map(el => ({ 73 | text: el.textContent, 74 | href: el.href 75 | })); 76 | } catch (error) { 77 | return { error: error.message }; 78 | } 79 | """ 80 | 81 | result = await evaluate( 82 | connection, 83 | expression=script, 84 | return_by_value=True, 85 | await_promise=True 86 | ) 87 | ``` 88 | 89 | ### Object Inspection 90 | ```python 91 | # Get detailed object properties 92 | properties = await get_properties( 93 | connection, 94 | object_id=object_id, 95 | own_properties=True, 96 | accessor_properties_only=False 97 | ) 98 | 99 | for prop in properties: 100 | print(f"{prop.name}: {prop.value}") 101 | ``` 102 | 103 | ### Console Integration 104 | Runtime commands integrate with browser console: 105 | - Console messages and errors 106 | - Console API method calls 107 | - Custom console formatters 108 | 109 | !!! note "Performance Considerations" 110 | JavaScript execution through runtime commands can be slower than native browser execution. Use judiciously for complex operations. -------------------------------------------------------------------------------- /docs/api/commands/storage.md: -------------------------------------------------------------------------------- 1 | # Storage Commands 2 | 3 | Storage commands provide comprehensive browser storage management including cookies, localStorage, sessionStorage, and IndexedDB. 4 | 5 | ## Overview 6 | 7 | The storage commands module enables management of all browser storage mechanisms, providing functionality for data persistence and retrieval. 8 | 9 | ::: pydoll.commands.storage_commands 10 | options: 11 | show_root_heading: true 12 | show_source: false 13 | heading_level: 2 14 | filters: 15 | - "!^_" 16 | - "!^__" 17 | 18 | ## Usage 19 | 20 | Storage commands are used for managing browser storage across different mechanisms: 21 | 22 | ```python 23 | from pydoll.commands.storage_commands import get_cookies, set_cookies, clear_data_for_origin 24 | from pydoll.connection.connection_handler import ConnectionHandler 25 | 26 | # Get cookies for a domain 27 | connection = ConnectionHandler() 28 | cookies = await get_cookies(connection, urls=["https://example.com"]) 29 | 30 | # Set a new cookie 31 | await set_cookies(connection, cookies=[{ 32 | "name": "session_id", 33 | "value": "abc123", 34 | "domain": "example.com", 35 | "path": "/", 36 | "httpOnly": True, 37 | "secure": True 38 | }]) 39 | 40 | # Clear all storage for an origin 41 | await clear_data_for_origin( 42 | connection, 43 | origin="https://example.com", 44 | storage_types="all" 45 | ) 46 | ``` 47 | 48 | ## Key Functionality 49 | 50 | The storage commands module provides functions for: 51 | 52 | ### Cookie Management 53 | - `get_cookies()` - Get cookies by URL or domain 54 | - `set_cookies()` - Set new cookies 55 | - `delete_cookies()` - Delete specific cookies 56 | - `clear_cookies()` - Clear all cookies 57 | 58 | ### Local Storage 59 | - `get_dom_storage_items()` - Get localStorage items 60 | - `set_dom_storage_item()` - Set localStorage item 61 | - `remove_dom_storage_item()` - Remove localStorage item 62 | - `clear_dom_storage()` - Clear localStorage 63 | 64 | ### Session Storage 65 | - Session storage operations (similar to localStorage) 66 | - Session-specific data management 67 | - Tab-isolated storage 68 | 69 | ### IndexedDB 70 | - `get_database_names()` - Get IndexedDB databases 71 | - `request_database()` - Access database structure 72 | - `request_data()` - Query database data 73 | - `clear_object_store()` - Clear object stores 74 | 75 | ### Cache Storage 76 | - `request_cache_names()` - Get cache names 77 | - `request_cached_response()` - Get cached responses 78 | - `delete_cache()` - Delete cache entries 79 | 80 | ### Application Cache (Deprecated) 81 | - Legacy application cache support 82 | - Manifest-based caching 83 | 84 | ## Advanced Features 85 | 86 | ### Bulk Operations 87 | ```python 88 | # Clear all storage types for multiple origins 89 | origins = ["https://example.com", "https://api.example.com"] 90 | for origin in origins: 91 | await clear_data_for_origin( 92 | connection, 93 | origin=origin, 94 | storage_types="cookies,local_storage,session_storage,indexeddb" 95 | ) 96 | ``` 97 | 98 | ### Storage Quotas 99 | ```python 100 | # Get storage quota information 101 | quota_info = await get_usage_and_quota(connection, origin="https://example.com") 102 | print(f"Used: {quota_info.usage} bytes") 103 | print(f"Quota: {quota_info.quota} bytes") 104 | ``` 105 | 106 | ### Cross-Origin Storage 107 | ```python 108 | # Manage storage across different origins 109 | await set_cookies(connection, cookies=[{ 110 | "name": "cross_site_token", 111 | "value": "token123", 112 | "domain": ".example.com", # Applies to all subdomains 113 | "sameSite": "None", 114 | "secure": True 115 | }]) 116 | ``` 117 | 118 | ## Storage Types 119 | 120 | The module supports various storage mechanisms: 121 | 122 | | Storage Type | Persistence | Scope | Capacity | 123 | |--------------|-------------|-------|----------| 124 | | Cookies | Persistent | Domain/Path | ~4KB per cookie | 125 | | localStorage | Persistent | Origin | ~5-10MB | 126 | | sessionStorage | Session | Tab | ~5-10MB | 127 | | IndexedDB | Persistent | Origin | Large (GB+) | 128 | | Cache API | Persistent | Origin | Large | 129 | 130 | !!! warning "Privacy Considerations" 131 | Storage operations can affect user privacy. Always handle storage data responsibly and in compliance with privacy regulations. -------------------------------------------------------------------------------- /docs/api/commands/target.md: -------------------------------------------------------------------------------- 1 | # Target Commands 2 | 3 | Target commands manage browser targets including tabs, windows, and other browsing contexts. 4 | 5 | ## Overview 6 | 7 | The target commands module provides functionality for creating, managing, and controlling browser targets such as tabs, popup windows, and service workers. 8 | 9 | ::: pydoll.commands.target_commands 10 | options: 11 | show_root_heading: true 12 | show_source: false 13 | heading_level: 2 14 | filters: 15 | - "!^_" 16 | - "!^__" 17 | 18 | ## Usage 19 | 20 | Target commands are used internally by browser classes to manage tabs and windows: 21 | 22 | ```python 23 | from pydoll.commands.target_commands import get_targets, create_target, close_target 24 | from pydoll.connection.connection_handler import ConnectionHandler 25 | 26 | # Get all browser targets 27 | connection = ConnectionHandler() 28 | targets = await get_targets(connection) 29 | 30 | # Create a new tab 31 | new_target = await create_target(connection, url="https://example.com") 32 | 33 | # Close a target 34 | await close_target(connection, target_id=new_target.target_id) 35 | ``` 36 | 37 | ## Key Functionality 38 | 39 | The target commands module provides functions for: 40 | 41 | ### Target Management 42 | - `get_targets()` - List all browser targets 43 | - `create_target()` - Create new tabs or windows 44 | - `close_target()` - Close specific targets 45 | - `activate_target()` - Bring target to foreground 46 | 47 | ### Target Information 48 | - `get_target_info()` - Get detailed target information 49 | - Target types: page, background_page, service_worker, browser 50 | - Target states: attached, detached, crashed 51 | 52 | ### Session Management 53 | - `attach_to_target()` - Attach to target for control 54 | - `detach_from_target()` - Detach from target 55 | - `send_message_to_target()` - Send commands to targets 56 | 57 | ### Browser Context 58 | - `create_browser_context()` - Create isolated browser context 59 | - `dispose_browser_context()` - Remove browser context 60 | - `get_browser_contexts()` - List browser contexts 61 | 62 | ## Target Types 63 | 64 | Different types of targets can be managed: 65 | 66 | ### Page Targets 67 | ```python 68 | # Create a new tab 69 | page_target = await create_target( 70 | connection, 71 | url="https://example.com", 72 | width=1920, 73 | height=1080, 74 | browser_context_id=None # Default context 75 | ) 76 | ``` 77 | 78 | ### Popup Windows 79 | ```python 80 | # Create a popup window 81 | popup_target = await create_target( 82 | connection, 83 | url="https://popup.example.com", 84 | width=800, 85 | height=600, 86 | new_window=True 87 | ) 88 | ``` 89 | 90 | ### Incognito Contexts 91 | ```python 92 | # Create incognito browser context 93 | incognito_context = await create_browser_context(connection) 94 | 95 | # Create tab in incognito context 96 | incognito_tab = await create_target( 97 | connection, 98 | url="https://private.example.com", 99 | browser_context_id=incognito_context.browser_context_id 100 | ) 101 | ``` 102 | 103 | ## Advanced Features 104 | 105 | ### Target Events 106 | Target commands work with various target events: 107 | - `Target.targetCreated` - New target created 108 | - `Target.targetDestroyed` - Target closed 109 | - `Target.targetInfoChanged` - Target information updated 110 | - `Target.targetCrashed` - Target crashed 111 | 112 | ### Multi-Target Coordination 113 | ```python 114 | # Manage multiple tabs 115 | targets = await get_targets(connection) 116 | page_targets = [t for t in targets if t.type == "page"] 117 | 118 | for target in page_targets: 119 | # Perform operations on each tab 120 | await activate_target(connection, target_id=target.target_id) 121 | # ... do work in this tab 122 | ``` 123 | 124 | ### Target Isolation 125 | ```python 126 | # Create isolated browser context for testing 127 | test_context = await create_browser_context(connection) 128 | 129 | # All targets in this context are isolated 130 | test_tab1 = await create_target( 131 | connection, 132 | url="https://test1.com", 133 | browser_context_id=test_context.browser_context_id 134 | ) 135 | 136 | test_tab2 = await create_target( 137 | connection, 138 | url="https://test2.com", 139 | browser_context_id=test_context.browser_context_id 140 | ) 141 | ``` 142 | 143 | !!! note "Browser Integration" 144 | Target commands are primarily used internally by the `Chrome` and `Edge` browser classes. The high-level browser APIs provide more convenient methods for tab management. -------------------------------------------------------------------------------- /docs/api/connection/connection.md: -------------------------------------------------------------------------------- 1 | # Connection Handler 2 | 3 | ::: pydoll.connection.connection_handler.ConnectionHandler 4 | options: 5 | show_root_heading: true 6 | show_source: false 7 | heading_level: 2 -------------------------------------------------------------------------------- /docs/api/connection/managers.md: -------------------------------------------------------------------------------- 1 | # Connection Managers 2 | 3 | ## CommandsManager 4 | 5 | ::: pydoll.connection.managers.commands_manager.CommandsManager 6 | options: 7 | show_root_heading: true 8 | show_source: false 9 | heading_level: 3 10 | 11 | ## EventsManager 12 | 13 | ::: pydoll.connection.managers.events_manager.EventsManager 14 | options: 15 | show_root_heading: true 16 | show_source: false 17 | heading_level: 3 -------------------------------------------------------------------------------- /docs/api/core/constants.md: -------------------------------------------------------------------------------- 1 | # Constants 2 | 3 | This section documents all constants, enums, and configuration values used throughout Pydoll. 4 | 5 | ::: pydoll.constants 6 | options: 7 | show_root_heading: true 8 | show_source: false 9 | heading_level: 2 10 | group_by_category: true 11 | members_order: source -------------------------------------------------------------------------------- /docs/api/core/exceptions.md: -------------------------------------------------------------------------------- 1 | # Exceptions 2 | 3 | This section documents all custom exceptions that can be raised by Pydoll operations. 4 | 5 | ::: pydoll.exceptions 6 | options: 7 | show_root_heading: true 8 | show_source: false 9 | heading_level: 2 10 | group_by_category: true 11 | members_order: source -------------------------------------------------------------------------------- /docs/api/core/utils.md: -------------------------------------------------------------------------------- 1 | # Utilities 2 | 3 | This section documents utility functions and helper classes used throughout Pydoll. 4 | 5 | ::: pydoll.utils 6 | options: 7 | show_root_heading: true 8 | show_source: false 9 | heading_level: 2 10 | group_by_category: true 11 | members_order: source -------------------------------------------------------------------------------- /docs/api/elements/mixins.md: -------------------------------------------------------------------------------- 1 | # Element Mixins 2 | 3 | The mixins module provides reusable functionality that can be mixed into element classes to extend their capabilities. 4 | 5 | ## Find Elements Mixin 6 | 7 | The `FindElementsMixin` provides element finding capabilities to classes that include it. 8 | 9 | ::: pydoll.elements.mixins.find_elements_mixin 10 | options: 11 | show_root_heading: true 12 | show_source: false 13 | heading_level: 2 14 | filters: 15 | - "!^_" 16 | - "!^__" 17 | 18 | ## Usage 19 | 20 | Mixins are typically used internally by the library to compose functionality. The `FindElementsMixin` is used by classes like `Tab` and `WebElement` to provide element finding methods: 21 | 22 | ```python 23 | # These methods come from FindElementsMixin 24 | element = await tab.find(id="username") 25 | elements = await tab.find(class_name="item", find_all=True) 26 | element = await tab.query("#submit-button") 27 | ``` 28 | 29 | ## Available Methods 30 | 31 | The `FindElementsMixin` provides several methods for finding elements: 32 | 33 | - `find()` - Modern element finding with keyword arguments 34 | - `query()` - CSS selector and XPath queries 35 | - `find_element()` - Legacy element finding method 36 | - `find_elements()` - Legacy method for finding multiple elements 37 | 38 | !!! tip "Modern vs Legacy" 39 | The `find()` method is the modern, recommended approach for finding elements. The `find_element()` and `find_elements()` methods are maintained for backward compatibility. -------------------------------------------------------------------------------- /docs/api/elements/web_element.md: -------------------------------------------------------------------------------- 1 | # WebElement 2 | 3 | ::: pydoll.elements.web_element.WebElement 4 | options: 5 | show_root_heading: true 6 | show_source: false 7 | heading_level: 2 8 | members_order: source 9 | group_by_category: true -------------------------------------------------------------------------------- /docs/api/index.md: -------------------------------------------------------------------------------- 1 | # API Reference 2 | 3 | Welcome to the Pydoll API Reference! This section provides comprehensive documentation for all classes, methods, and functions available in the Pydoll library. 4 | 5 | ## Overview 6 | 7 | Pydoll is organized into several key modules, each serving a specific purpose in browser automation: 8 | 9 | ### Browser Module 10 | The browser module contains classes for managing browser instances and their lifecycle. 11 | 12 | - **[Chrome](browser/chrome.md)** - Chrome browser automation 13 | - **[Edge](browser/edge.md)** - Microsoft Edge browser automation 14 | - **[Options](browser/options.md)** - Browser configuration options 15 | - **[Tab](browser/tab.md)** - Tab management and interaction 16 | - **[Managers](browser/managers.md)** - Browser lifecycle managers 17 | 18 | ### Elements Module 19 | The elements module provides classes for interacting with web page elements. 20 | 21 | - **[WebElement](elements/web_element.md)** - Individual element interaction 22 | - **[Mixins](elements/mixins.md)** - Reusable element functionality 23 | 24 | ### Connection Module 25 | The connection module handles communication with the browser through the Chrome DevTools Protocol. 26 | 27 | - **[Connection Handler](connection/connection.md)** - WebSocket connection management 28 | - **[Managers](connection/managers.md)** - Connection lifecycle managers 29 | 30 | ### Commands Module 31 | The commands module provides low-level Chrome DevTools Protocol command implementations. 32 | 33 | - **[Commands Overview](commands/index.md)** - CDP command implementations by domain 34 | 35 | ### Protocol Module 36 | The protocol module implements the Chrome DevTools Protocol commands and events. 37 | 38 | - **[Commands](protocol/commands.md)** - CDP command implementations 39 | - **[Events](protocol/events.md)** - CDP event handling 40 | 41 | ### Core Module 42 | The core module contains fundamental utilities, constants, and exceptions. 43 | 44 | - **[Constants](core/constants.md)** - Library constants and enums 45 | - **[Exceptions](core/exceptions.md)** - Custom exception classes 46 | - **[Utils](core/utils.md)** - Utility functions 47 | 48 | ## Quick Navigation 49 | 50 | ### Most Common Classes 51 | 52 | | Class | Purpose | Module | 53 | |-------|---------|--------| 54 | | `Chrome` | Chrome browser automation | `pydoll.browser.chromium` | 55 | | `Edge` | Edge browser automation | `pydoll.browser.chromium` | 56 | | `Tab` | Tab interaction and control | `pydoll.browser.tab` | 57 | | `WebElement` | Element interaction | `pydoll.elements.web_element` | 58 | | `ChromiumOptions` | Browser configuration | `pydoll.browser.options` | 59 | 60 | ### Key Enums and Constants 61 | 62 | | Name | Purpose | Module | 63 | |------|---------|--------| 64 | | `By` | Element selector strategies | `pydoll.constants` | 65 | | `Key` | Keyboard key constants | `pydoll.constants` | 66 | | `PermissionType` | Browser permission types | `pydoll.constants` | 67 | 68 | ### Common Exceptions 69 | 70 | | Exception | When Raised | Module | 71 | |-----------|-------------|--------| 72 | | `ElementNotFound` | Element not found in DOM | `pydoll.exceptions` | 73 | | `WaitElementTimeout` | Element wait timeout | `pydoll.exceptions` | 74 | | `BrowserNotStarted` | Browser not started | `pydoll.exceptions` | 75 | 76 | ## Usage Patterns 77 | 78 | ### Basic Browser Automation 79 | 80 | ```python 81 | from pydoll.browser.chromium import Chrome 82 | 83 | async with Chrome() as browser: 84 | tab = await browser.start() 85 | await tab.go_to("https://example.com") 86 | element = await tab.find(id="my-element") 87 | await element.click() 88 | ``` 89 | 90 | ### Element Finding 91 | 92 | ```python 93 | # Using the modern find() method 94 | element = await tab.find(id="username") 95 | element = await tab.find(tag_name="button", class_name="submit") 96 | 97 | # Using CSS selectors or XPath 98 | element = await tab.query("#username") 99 | element = await tab.query("//button[@class='submit']") 100 | ``` 101 | 102 | ### Event Handling 103 | 104 | ```python 105 | await tab.enable_page_events() 106 | await tab.on('Page.loadEventFired', handle_page_load) 107 | ``` 108 | 109 | ## Type Hints 110 | 111 | Pydoll is fully typed and provides comprehensive type hints for better IDE support and code safety. All public APIs include proper type annotations. 112 | 113 | ```python 114 | from typing import Optional, List 115 | from pydoll.elements.web_element import WebElement 116 | 117 | # Methods return properly typed objects 118 | element: Optional[WebElement] = await tab.find(id="test", raise_exc=False) 119 | elements: List[WebElement] = await tab.find(class_name="item", find_all=True) 120 | ``` 121 | 122 | ## Async/Await Support 123 | 124 | All Pydoll operations are asynchronous and must be used with `async`/`await`: 125 | 126 | ```python 127 | import asyncio 128 | 129 | async def main(): 130 | # All Pydoll operations are async 131 | async with Chrome() as browser: 132 | tab = await browser.start() 133 | await tab.go_to("https://example.com") 134 | 135 | asyncio.run(main()) 136 | ``` 137 | 138 | Browse the sections below to explore the complete API documentation for each module. -------------------------------------------------------------------------------- /docs/api/protocol/commands.md: -------------------------------------------------------------------------------- 1 | # Protocol Commands 2 | 3 | This section documents the Chrome DevTools Protocol command implementations used by Pydoll. 4 | 5 | ## Page Commands 6 | 7 | ::: pydoll.commands.page_commands 8 | options: 9 | show_root_heading: true 10 | show_source: false 11 | heading_level: 3 12 | 13 | ## Runtime Commands 14 | 15 | ::: pydoll.commands.runtime_commands 16 | options: 17 | show_root_heading: true 18 | show_source: false 19 | heading_level: 3 20 | 21 | ## DOM Commands 22 | 23 | ::: pydoll.commands.dom_commands 24 | options: 25 | show_root_heading: true 26 | show_source: false 27 | heading_level: 3 28 | 29 | ## Network Commands 30 | 31 | ::: pydoll.commands.network_commands 32 | options: 33 | show_root_heading: true 34 | show_source: false 35 | heading_level: 3 36 | 37 | ## Input Commands 38 | 39 | ::: pydoll.commands.input_commands 40 | options: 41 | show_root_heading: true 42 | show_source: false 43 | heading_level: 3 44 | 45 | ## Fetch Commands 46 | 47 | ::: pydoll.commands.fetch_commands 48 | options: 49 | show_root_heading: true 50 | show_source: false 51 | heading_level: 3 52 | 53 | ## Browser Commands 54 | 55 | ::: pydoll.commands.browser_commands 56 | options: 57 | show_root_heading: true 58 | show_source: false 59 | heading_level: 3 60 | 61 | ## Target Commands 62 | 63 | ::: pydoll.commands.target_commands 64 | options: 65 | show_root_heading: true 66 | show_source: false 67 | heading_level: 3 68 | 69 | ## Storage Commands 70 | 71 | ::: pydoll.commands.storage_commands 72 | options: 73 | show_root_heading: true 74 | show_source: false 75 | heading_level: 3 -------------------------------------------------------------------------------- /docs/api/protocol/events.md: -------------------------------------------------------------------------------- 1 | # Protocol Events 2 | 3 | This section documents the Chrome DevTools Protocol event constants and handlers used by Pydoll. 4 | 5 | ## Page Events 6 | 7 | ::: pydoll.protocol.page.events 8 | options: 9 | show_root_heading: true 10 | show_source: false 11 | heading_level: 3 12 | 13 | ## Network Events 14 | 15 | ::: pydoll.protocol.network.events 16 | options: 17 | show_root_heading: true 18 | show_source: false 19 | heading_level: 3 20 | 21 | ## DOM Events 22 | 23 | ::: pydoll.protocol.dom.events 24 | options: 25 | show_root_heading: true 26 | show_source: false 27 | heading_level: 3 28 | 29 | ## Runtime Events 30 | 31 | ::: pydoll.protocol.runtime.events 32 | options: 33 | show_root_heading: true 34 | show_source: false 35 | heading_level: 3 36 | 37 | ## Fetch Events 38 | 39 | ::: pydoll.protocol.fetch.events 40 | options: 41 | show_root_heading: true 42 | show_source: false 43 | heading_level: 3 44 | 45 | ## Browser Events 46 | 47 | ::: pydoll.protocol.browser.events 48 | options: 49 | show_root_heading: true 50 | show_source: false 51 | heading_level: 3 52 | 53 | ## Target Events 54 | 55 | ::: pydoll.protocol.target.events 56 | options: 57 | show_root_heading: true 58 | show_source: false 59 | heading_level: 3 60 | 61 | ## Storage Events 62 | 63 | ::: pydoll.protocol.storage.events 64 | options: 65 | show_root_heading: true 66 | show_source: false 67 | heading_level: 3 68 | 69 | ## Input Events 70 | 71 | ::: pydoll.protocol.input.events 72 | options: 73 | show_root_heading: true 74 | show_source: false 75 | heading_level: 3 -------------------------------------------------------------------------------- /docs/deep-dive/index.md: -------------------------------------------------------------------------------- 1 | # Deep Dive 2 | 3 | Welcome to the in-depth technical documentation section of Pydoll. This area is dedicated to developers who want to understand the internal workings of the library, its architectural design, and the technical principles behind its operation. 4 | 5 | ## What You'll Find Here 6 | 7 | Unlike the introduction and features sections that focus on "how to use" Pydoll, the Deep Dive section explores "how it works" and the "why" behind the design and implementation decisions. 8 | 9 | In this section, you'll find detailed documentation about: 10 | 11 | - **Chrome DevTools Protocol (CDP)** - How Pydoll communicates with browsers without relying on webdrivers 12 | - **Internal Architecture** - The layered structure that makes Pydoll efficient and extensible 13 | - **Domain Implementations** - Technical details of each functional domain (Browser, Page, WebElement) 14 | - **Event System** - How the reactive event system works internally 15 | - **Performance Optimizations** - Details about how we achieve high asynchronous performance 16 | 17 | ## Who This Section Is For 18 | 19 | This documentation is especially useful for: 20 | 21 | - Developers looking to contribute code to Pydoll 22 | - Engineers creating advanced integrations or extensions 23 | - Technical users who need to understand the execution model for debugging 24 | - Anyone interested in the technical aspects of browser automation 25 | 26 | Each topic in this section is self-contained, so you can navigate directly to the areas of greatest interest using the navigation menu. 27 | 28 | Explore the different domains and technical features using the sidebar links to dive deep into Pydoll's implementation details. -------------------------------------------------------------------------------- /docs/images/0D141C-preto-azulado.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/autoscrape-labs/pydoll/c518a7e81bf05f7d33fa2d1aa26d5db7c53a1557/docs/images/0D141C-preto-azulado.png -------------------------------------------------------------------------------- /docs/images/8400FF-roxo-escuro.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/autoscrape-labs/pydoll/c518a7e81bf05f7d33fa2d1aa26d5db7c53a1557/docs/images/8400FF-roxo-escuro.png -------------------------------------------------------------------------------- /docs/images/E2ECED-cinza-azulado.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/autoscrape-labs/pydoll/c518a7e81bf05f7d33fa2d1aa26d5db7c53a1557/docs/images/E2ECED-cinza-azulado.png -------------------------------------------------------------------------------- /docs/images/ECE95B-amarelo-lima.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/autoscrape-labs/pydoll/c518a7e81bf05f7d33fa2d1aa26d5db7c53a1557/docs/images/ECE95B-amarelo-lima.png -------------------------------------------------------------------------------- /docs/images/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/autoscrape-labs/pydoll/c518a7e81bf05f7d33fa2d1aa26d5db7c53a1557/docs/images/favicon.png -------------------------------------------------------------------------------- /docs/stylesheets/termynal.css: -------------------------------------------------------------------------------- 1 | /** 2 | * termynal.js 3 | * 4 | * @author Ines Montani 5 | * @version 0.0.1 6 | * @license MIT 7 | */ 8 | 9 | :root { 10 | --color-bg: #252a33; 11 | --color-text: #eee; 12 | --color-text-subtle: #a2a2a2; 13 | } 14 | 15 | [data-termynal] { 16 | width: 750px; 17 | max-width: 100%; 18 | background: var(--color-bg); 19 | color: var(--color-text); 20 | /* font-size: 18px; */ 21 | font-size: 15px; 22 | /* font-family: 'Fira Mono', Consolas, Menlo, Monaco, 'Courier New', Courier, monospace; */ 23 | font-family: 'Roboto Mono', 'Fira Mono', Consolas, Menlo, Monaco, 'Courier New', Courier, monospace; 24 | border-radius: 4px; 25 | padding: 75px 45px 35px; 26 | position: relative; 27 | -webkit-box-sizing: border-box; 28 | box-sizing: border-box; 29 | /* Custom line-height */ 30 | line-height: 1.2; 31 | } 32 | 33 | [data-termynal]:before { 34 | content: ''; 35 | position: absolute; 36 | top: 15px; 37 | left: 15px; 38 | display: inline-block; 39 | width: 15px; 40 | height: 15px; 41 | border-radius: 50%; 42 | /* A little hack to display the window buttons in one pseudo element. */ 43 | background: #d9515d; 44 | -webkit-box-shadow: 25px 0 0 #f4c025, 50px 0 0 #3ec930; 45 | box-shadow: 25px 0 0 #f4c025, 50px 0 0 #3ec930; 46 | } 47 | 48 | [data-termynal]:after { 49 | content: 'bash'; 50 | position: absolute; 51 | color: var(--color-text-subtle); 52 | top: 5px; 53 | left: 0; 54 | width: 100%; 55 | text-align: center; 56 | } 57 | 58 | a[data-terminal-control] { 59 | text-align: right; 60 | display: block; 61 | color: #aebbff; 62 | } 63 | 64 | [data-ty] { 65 | display: block; 66 | line-height: 2; 67 | } 68 | 69 | [data-ty]:before { 70 | /* Set up defaults and ensure empty lines are displayed. */ 71 | content: ''; 72 | display: inline-block; 73 | vertical-align: middle; 74 | } 75 | 76 | [data-ty="input"]:before, 77 | [data-ty-prompt]:before { 78 | margin-right: 0.75em; 79 | color: var(--color-text-subtle); 80 | } 81 | 82 | [data-ty="input"]:before { 83 | content: '$'; 84 | } 85 | 86 | [data-ty][data-ty-prompt]:before { 87 | content: attr(data-ty-prompt); 88 | } 89 | 90 | [data-ty-cursor]:after { 91 | content: attr(data-ty-cursor); 92 | font-family: monospace; 93 | margin-left: 0.5em; 94 | -webkit-animation: blink 1s infinite; 95 | animation: blink 1s infinite; 96 | } 97 | 98 | 99 | /* Cursor animation */ 100 | 101 | @-webkit-keyframes blink { 102 | 50% { 103 | opacity: 0; 104 | } 105 | } 106 | 107 | @keyframes blink { 108 | 50% { 109 | opacity: 0; 110 | } 111 | } -------------------------------------------------------------------------------- /examples/cloudflare_bypass.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | 3 | from pydoll.browser import Chrome 4 | from pydoll.constants import By 5 | 6 | 7 | async def example_with_context_manager(): 8 | """ 9 | Example using the context manager approach to handle Cloudflare captcha. 10 | 11 | This waits for the captcha to be processed before continuing. 12 | """ 13 | browser = Chrome() 14 | await browser.start() 15 | page = await browser.get_page() 16 | 17 | print('Using context manager approach...') 18 | async with page.expect_and_bypass_cloudflare_captcha( 19 | custom_selector=(By.ID, 'TAYH8'), time_before_click=5 20 | ): 21 | await page.go_to('https://www.planetminecraft.com/account/sign_in/') 22 | print('Page loaded, waiting for captcha to be handled...') 23 | 24 | print('Captcha handling completed, now we can continue...') 25 | await asyncio.sleep(3) 26 | await browser.stop() 27 | 28 | 29 | async def example_with_enable_disable(): 30 | """ 31 | Example using the enable/disable approach to handle Cloudflare captcha. 32 | 33 | This enables the auto-solving and continues execution immediately. 34 | The captcha will be solved in the background when it appears. 35 | """ 36 | browser = Chrome() 37 | await browser.start() 38 | page = await browser.get_page() 39 | 40 | print('Using enable/disable approach...') 41 | 42 | # Enable automatic captcha solving before navigating 43 | await page.enable_auto_solve_cloudflare_captcha() 44 | 45 | # Navigate to the page - captcha will be handled automatically 46 | await page.go_to('https://www.planetminecraft.com/account/sign_in/') 47 | print('Page loaded, captcha will be handled in the background...') 48 | 49 | # Continue with other operations immediately 50 | # The captcha will be solved in the background when it appears 51 | await asyncio.sleep(5) 52 | 53 | # Disable auto-solving when no longer needed 54 | await page.disable_auto_solve_cloudflare_captcha() 55 | print('Auto-solving disabled') 56 | 57 | await browser.stop() 58 | 59 | 60 | async def main(): 61 | # Choose which example to run 62 | await example_with_context_manager() 63 | # Or uncomment the line below to run the enable/disable example 64 | # await example_with_enable_disable() 65 | 66 | 67 | if __name__ == '__main__': 68 | asyncio.run(main()) 69 | -------------------------------------------------------------------------------- /mkdocs.yml: -------------------------------------------------------------------------------- 1 | site_name: Pydoll - Async Web Automation Library 2 | repo_url: https://github.com/autoscrape-labs/pydoll 3 | repo_name: autoscrape-labs/pydoll 4 | 5 | # Configuração da navegação 6 | nav: 7 | - Pydoll: index.md 8 | - Features: features.md 9 | - Deep Dive: 10 | - Overview: deep-dive/index.md 11 | - Chrome DevTools Protocol: deep-dive/cdp.md 12 | - The Connection Layer: deep-dive/connection-layer.md 13 | - The Browser Domain: deep-dive/browser-domain.md 14 | - The Tab Domain: deep-dive/tab-domain.md 15 | - The Find Elements Mixin: deep-dive/find-elements-mixin.md 16 | - The WebElement Domain: deep-dive/webelement-domain.md 17 | - The Event System: deep-dive/event-system.md 18 | - Network Capabilities: deep-dive/network-capabilities.md 19 | - API Reference: 20 | - Overview: api/index.md 21 | - Browser: 22 | - Chrome: api/browser/chrome.md 23 | - Edge: api/browser/edge.md 24 | - Options: api/browser/options.md 25 | - Tab: api/browser/tab.md 26 | - Managers: api/browser/managers.md 27 | - Elements: 28 | - WebElement: api/elements/web_element.md 29 | - Mixins: api/elements/mixins.md 30 | - Connection: 31 | - Connection Handler: api/connection/connection.md 32 | - Managers: api/connection/managers.md 33 | - Commands: 34 | - Overview: api/commands/index.md 35 | - Browser: api/commands/browser.md 36 | - DOM: api/commands/dom.md 37 | - Input: api/commands/input.md 38 | - Network: api/commands/network.md 39 | - Page: api/commands/page.md 40 | - Runtime: api/commands/runtime.md 41 | - Storage: api/commands/storage.md 42 | - Target: api/commands/target.md 43 | - Fetch: api/commands/fetch.md 44 | - Protocol: 45 | - Commands: api/protocol/commands.md 46 | - Events: api/protocol/events.md 47 | - Core: 48 | - Constants: api/core/constants.md 49 | - Exceptions: api/core/exceptions.md 50 | - Utils: api/core/utils.md 51 | 52 | theme: 53 | name: material 54 | font: 55 | text: Roboto 56 | code: Roboto Mono 57 | palette: 58 | - media: "(prefers-color-scheme: dark)" 59 | scheme: slate 60 | primary: custom 61 | accent: indigo 62 | toggle: 63 | icon: material/toggle-switch-off-outline 64 | name: Dark Mode 65 | 66 | - media: "(prefers-color-scheme: light)" 67 | scheme: default 68 | primary: custom 69 | accent: indigo 70 | toggle: 71 | icon: material/toggle-switch 72 | name: Light Mode 73 | icon: 74 | repo: fontawesome/brands/github 75 | logo: material/lightning-bolt 76 | favicon: images/favicon.png 77 | features: 78 | - navigation.tabs 79 | - navigation.tabs.sticky 80 | - navigation.sections 81 | - navigation.indexes 82 | - navigation.expand 83 | - navigation.path 84 | - navigation.top 85 | - toc.follow 86 | 87 | plugins: 88 | - search 89 | - mkdocstrings: 90 | handlers: 91 | python: 92 | options: 93 | show_root_heading: true 94 | show_if_no_docstring: true 95 | inherited_members: true 96 | members_order: source 97 | separate_signature: true 98 | filters: 99 | - '!^_' 100 | - '!^__' 101 | merge_init_into_class: true 102 | docstring_section_style: spacy 103 | signature_crossrefs: true 104 | show_symbol_type_heading: true 105 | show_symbol_type_toc: true 106 | show_source: false 107 | show_bases: true 108 | heading_level: 1 109 | 110 | extra_css: 111 | - stylesheets/termynal.css 112 | - stylesheets/extra.css 113 | 114 | extra_javascript: 115 | - scripts/termynal.js 116 | - scripts/extra.js 117 | - https://unpkg.com/mermaid@10.0.0/dist/mermaid.min.js 118 | 119 | markdown_extensions: 120 | - pymdownx.critic 121 | - pymdownx.highlight: 122 | anchor_linenums: true 123 | line_spans: __span 124 | pygments_lang_class: true 125 | - pymdownx.inlinehilite 126 | - pymdownx.snippets 127 | - pymdownx.superfences: 128 | custom_fences: 129 | - name: mermaid 130 | class: mermaid 131 | format: !!python/name:pymdownx.superfences.fence_code_format 132 | - pymdownx.details 133 | - pymdownx.keys 134 | - footnotes 135 | - admonition 136 | - markdown.extensions.attr_list 137 | - pymdownx.tabbed: 138 | alternate_style: true 139 | - attr_list 140 | - pymdownx.emoji: 141 | emoji_index: !!python/name:material.extensions.emoji.twemoji 142 | emoji_generator: !!python/name:material.extensions.emoji.to_svg 143 | - md_in_html -------------------------------------------------------------------------------- /pydoll/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/autoscrape-labs/pydoll/c518a7e81bf05f7d33fa2d1aa26d5db7c53a1557/pydoll/__init__.py -------------------------------------------------------------------------------- /pydoll/browser/__init__.py: -------------------------------------------------------------------------------- 1 | from pydoll.browser.chromium.chrome import Chrome 2 | from pydoll.browser.chromium.edge import Edge 3 | 4 | __all__ = ['Chrome', 'Edge'] 5 | -------------------------------------------------------------------------------- /pydoll/browser/chromium/__init__.py: -------------------------------------------------------------------------------- 1 | from pydoll.browser.chromium.chrome import Chrome 2 | from pydoll.browser.chromium.edge import Edge 3 | 4 | __all__ = [ 5 | 'Edge', 6 | 'Chrome', 7 | ] 8 | -------------------------------------------------------------------------------- /pydoll/browser/chromium/chrome.py: -------------------------------------------------------------------------------- 1 | import platform 2 | from typing import Optional 3 | 4 | from pydoll.browser.chromium.base import Browser 5 | from pydoll.browser.managers import ChromiumOptionsManager 6 | from pydoll.browser.options import ChromiumOptions 7 | from pydoll.exceptions import UnsupportedOS 8 | from pydoll.utils import validate_browser_paths 9 | 10 | 11 | class Chrome(Browser): 12 | """Chrome browser implementation for CDP automation.""" 13 | 14 | def __init__( 15 | self, 16 | options: Optional[ChromiumOptions] = None, 17 | connection_port: Optional[int] = None, 18 | ): 19 | """ 20 | Initialize Chrome browser instance. 21 | 22 | Args: 23 | options: Chrome configuration options (default if None). 24 | connection_port: CDP WebSocket port (random if None). 25 | """ 26 | options_manager = ChromiumOptionsManager(options) 27 | super().__init__(options_manager, connection_port) 28 | 29 | @staticmethod 30 | def _get_default_binary_location(): 31 | """ 32 | Get default Chrome executable path based on OS. 33 | 34 | Returns: 35 | Path to Chrome executable. 36 | 37 | Raises: 38 | UnsupportedOS: If OS is not supported. 39 | ValueError: If executable not found at default location. 40 | """ 41 | os_name = platform.system() 42 | 43 | browser_paths = { 44 | 'Windows': [ 45 | r'C:\Program Files\Google\Chrome\Application\chrome.exe', 46 | r'C:\Program Files (x86)\Google\Chrome\Application\chrome.exe', 47 | ], 48 | 'Linux': [ 49 | '/usr/bin/google-chrome', 50 | ], 51 | 'Darwin': [ 52 | '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome', 53 | ], 54 | } 55 | 56 | browser_path = browser_paths.get(os_name) 57 | 58 | if not browser_path: 59 | raise UnsupportedOS(f'Unsupported OS: {os_name}') 60 | 61 | return validate_browser_paths(browser_path) 62 | -------------------------------------------------------------------------------- /pydoll/browser/chromium/edge.py: -------------------------------------------------------------------------------- 1 | import platform 2 | from typing import Optional 3 | 4 | from pydoll.browser.chromium.base import Browser 5 | from pydoll.browser.managers import ChromiumOptionsManager 6 | from pydoll.browser.options import Options 7 | from pydoll.exceptions import UnsupportedOS 8 | from pydoll.utils import validate_browser_paths 9 | 10 | 11 | class Edge(Browser): 12 | """Edge browser implementation for CDP automation.""" 13 | 14 | def __init__( 15 | self, 16 | options: Optional[Options] = None, 17 | connection_port: Optional[int] = None, 18 | ): 19 | """ 20 | Initialize Edge browser instance. 21 | 22 | Args: 23 | options: Edge configuration options (default if None). 24 | connection_port: CDP WebSocket port (random if None). 25 | """ 26 | options_manager = ChromiumOptionsManager(options) 27 | super().__init__(options_manager, connection_port) 28 | 29 | @staticmethod 30 | def _get_default_binary_location(): 31 | """ 32 | Get default Edge executable path based on OS. 33 | 34 | Returns: 35 | Path to Edge executable. 36 | 37 | Raises: 38 | UnsupportedOS: If OS is not supported. 39 | ValueError: If executable not found at default location. 40 | """ 41 | os_name = platform.system() 42 | 43 | browser_paths = { 44 | 'Windows': [ 45 | ( 46 | r'C:\Program Files\Microsoft\Edge\Application' 47 | r'\msedge.exe' 48 | ), 49 | ( 50 | r'C:\Program Files (x86)\Microsoft\Edge' 51 | r'\Application\msedge.exe' 52 | ), 53 | ], 54 | 'Linux': [ 55 | '/usr/bin/microsoft-edge', 56 | ], 57 | 'Darwin': [ 58 | ('/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge'), 59 | ], 60 | } 61 | 62 | browser_path = browser_paths.get(os_name) 63 | 64 | if not browser_path: 65 | raise UnsupportedOS() 66 | 67 | return validate_browser_paths(browser_path) 68 | -------------------------------------------------------------------------------- /pydoll/browser/interfaces.py: -------------------------------------------------------------------------------- 1 | from abc import ABC, abstractmethod 2 | 3 | 4 | class Options(ABC): 5 | @property 6 | @abstractmethod 7 | def arguments(self) -> list[str]: 8 | pass 9 | 10 | @property 11 | @abstractmethod 12 | def binary_location(self) -> str: 13 | pass 14 | 15 | @abstractmethod 16 | def add_argument(self, argument: str): 17 | pass 18 | 19 | 20 | class BrowserOptionsManager(ABC): 21 | @abstractmethod 22 | def initialize_options(self) -> Options: 23 | pass 24 | 25 | @abstractmethod 26 | def add_default_arguments(self): 27 | pass 28 | -------------------------------------------------------------------------------- /pydoll/browser/managers/__init__.py: -------------------------------------------------------------------------------- 1 | from pydoll.browser.managers.browser_options_manager import ( 2 | ChromiumOptionsManager, 3 | ) 4 | from pydoll.browser.managers.browser_process_manager import ( 5 | BrowserProcessManager, 6 | ) 7 | from pydoll.browser.managers.proxy_manager import ProxyManager 8 | from pydoll.browser.managers.temp_dir_manager import TempDirectoryManager 9 | 10 | __all__ = [ 11 | 'ChromiumOptionsManager', 12 | 'BrowserProcessManager', 13 | 'ProxyManager', 14 | 'TempDirectoryManager', 15 | ] 16 | -------------------------------------------------------------------------------- /pydoll/browser/managers/browser_options_manager.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | from pydoll.browser.interfaces import BrowserOptionsManager, Options 4 | from pydoll.browser.options import ChromiumOptions 5 | from pydoll.exceptions import InvalidOptionsObject 6 | 7 | 8 | class ChromiumOptionsManager(BrowserOptionsManager): 9 | """ 10 | Manages browser options configuration for Chromium-based browsers. 11 | 12 | Handles options creation, validation, and applies default CDP arguments 13 | for Chrome and Edge browsers. 14 | """ 15 | 16 | def __init__(self, options: Optional[Options] = None): 17 | self.options = options 18 | 19 | def initialize_options( 20 | self, 21 | ) -> ChromiumOptions: 22 | """ 23 | Initialize and validate browser options. 24 | 25 | Creates ChromiumOptions if none provided, validates existing options, 26 | and applies default CDP arguments. 27 | 28 | Returns: 29 | Properly configured ChromiumOptions instance. 30 | 31 | Raises: 32 | InvalidOptionsObject: If provided options is not ChromiumOptions. 33 | """ 34 | if self.options is None: 35 | self.options = ChromiumOptions() 36 | 37 | if not isinstance(self.options, ChromiumOptions): 38 | raise InvalidOptionsObject(f'Expected ChromiumOptions, got {type(self.options)}') 39 | 40 | self.add_default_arguments() 41 | return self.options 42 | 43 | def add_default_arguments(self): 44 | """Add default arguments required for CDP integration.""" 45 | self.options.add_argument('--no-first-run') 46 | self.options.add_argument('--no-default-browser-check') 47 | -------------------------------------------------------------------------------- /pydoll/browser/managers/browser_process_manager.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | from typing import Callable, Optional 3 | 4 | 5 | class BrowserProcessManager: 6 | """ 7 | Manages browser process lifecycle for CDP automation. 8 | 9 | Handles process creation, monitoring, and termination with proper 10 | resource cleanup and graceful shutdown. 11 | """ 12 | 13 | def __init__( 14 | self, 15 | process_creator: Optional[Callable[[list[str]], subprocess.Popen]] = None, 16 | ): 17 | """ 18 | Initialize browser process manager. 19 | 20 | Args: 21 | process_creator: Custom function to create browser processes. 22 | Must accept command list and return subprocess.Popen object. 23 | Uses default subprocess implementation if None. 24 | """ 25 | self._process_creator = process_creator or self._default_process_creator 26 | self._process: Optional[subprocess.Popen] = None 27 | 28 | def start_browser_process( 29 | self, 30 | binary_location: str, 31 | port: int, 32 | arguments: list[str], 33 | ) -> subprocess.Popen: 34 | """ 35 | Launch browser process with CDP debugging enabled. 36 | 37 | Args: 38 | binary_location: Path to browser executable. 39 | port: TCP port for CDP WebSocket connections. 40 | arguments: Additional command-line arguments. 41 | 42 | Returns: 43 | Started browser process instance. 44 | 45 | Note: 46 | Automatically adds --remote-debugging-port argument. 47 | """ 48 | self._process = self._process_creator([ 49 | binary_location, 50 | f'--remote-debugging-port={port}', 51 | *arguments, 52 | ]) 53 | return self._process 54 | 55 | @staticmethod 56 | def _default_process_creator(command: list[str]) -> subprocess.Popen: 57 | """Create browser process with output capture to prevent console clutter.""" 58 | return subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 59 | 60 | def stop_process(self): 61 | """ 62 | Terminate browser process with graceful shutdown. 63 | 64 | Attempts SIGTERM first, then SIGKILL after 15-second timeout. 65 | Safe to call even if no process is running. 66 | """ 67 | if self._process: 68 | self._process.terminate() 69 | try: 70 | self._process.wait(timeout=15) 71 | except subprocess.TimeoutExpired: 72 | self._process.kill() 73 | -------------------------------------------------------------------------------- /pydoll/browser/managers/proxy_manager.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | from pydoll.browser.options import Options 4 | 5 | 6 | class ProxyManager: 7 | """ 8 | Manages proxy configuration and credentials for CDP automation. 9 | 10 | Extracts embedded credentials from proxy URLs, secures authentication 11 | information, and sanitizes command-line arguments. 12 | """ 13 | 14 | def __init__(self, options: Options): 15 | """ 16 | Initialize proxy manager with browser options. 17 | 18 | Args: 19 | options: Browser options potentially containing proxy configuration. 20 | Will be modified if credentials are found. 21 | """ 22 | self.options = options 23 | 24 | def get_proxy_credentials(self) -> tuple[bool, tuple[Optional[str], Optional[str]]]: 25 | """ 26 | Extract and secure proxy authentication credentials. 27 | 28 | Searches for proxy settings, extracts embedded credentials, 29 | and sanitizes options to remove credential exposure. 30 | 31 | Returns: 32 | Tuple of (has_private_proxy, (username, password)). 33 | """ 34 | private_proxy = False 35 | credentials: tuple[Optional[str], Optional[str]] = (None, None) 36 | 37 | proxy_arg = self._find_proxy_argument() 38 | 39 | if proxy_arg is not None: 40 | index, proxy_value = proxy_arg 41 | has_credentials, username, password, clean_proxy = self._parse_proxy(proxy_value) 42 | 43 | if has_credentials: 44 | self._update_proxy_argument(index, clean_proxy) 45 | private_proxy = True 46 | credentials = (username, password) 47 | 48 | return private_proxy, credentials 49 | 50 | def _find_proxy_argument(self) -> Optional[tuple[int, str]]: 51 | """ 52 | Find proxy server configuration in browser options. 53 | 54 | Returns: 55 | Tuple of (index, proxy_url) if found, None otherwise. 56 | """ 57 | for index, arg in enumerate(self.options.arguments): 58 | if arg.startswith('--proxy-server='): 59 | return index, arg.split('=', 1)[1] 60 | return None 61 | 62 | @staticmethod 63 | def _parse_proxy(proxy_value: str) -> tuple[bool, Optional[str], Optional[str], str]: 64 | """ 65 | Parse proxy URL to extract authentication credentials. 66 | 67 | Args: 68 | proxy_value: Proxy URL potentially containing username:password@server:port. 69 | 70 | Returns: 71 | Tuple of (has_credentials, username, password, clean_proxy_url). 72 | """ 73 | if '@' not in proxy_value: 74 | return False, None, None, proxy_value 75 | 76 | try: 77 | creds_part, server_part = proxy_value.split('@', 1) 78 | username, password = creds_part.split(':', 1) 79 | return True, username, password, server_part 80 | except ValueError: 81 | return False, None, None, proxy_value 82 | 83 | def _update_proxy_argument(self, index: int, clean_proxy: str) -> None: 84 | """Replace proxy argument with credential-free version.""" 85 | self.options.arguments[index] = f'--proxy-server={clean_proxy}' 86 | -------------------------------------------------------------------------------- /pydoll/browser/managers/temp_dir_manager.py: -------------------------------------------------------------------------------- 1 | import shutil 2 | import time 3 | from pathlib import Path 4 | from tempfile import TemporaryDirectory 5 | from typing import Callable 6 | 7 | 8 | class TempDirectoryManager: 9 | """ 10 | Manages temporary directory lifecycle for CDP browser automation. 11 | 12 | Creates isolated temporary directories for browser profiles and handles 13 | secure cleanup with retry mechanisms for locked files. 14 | """ 15 | 16 | def __init__(self, temp_dir_factory: Callable[[], TemporaryDirectory] = TemporaryDirectory): 17 | """ 18 | Initialize temporary directory manager. 19 | 20 | Args: 21 | temp_dir_factory: Function to create temporary directories. 22 | Must return TemporaryDirectory-compatible object. 23 | """ 24 | self._temp_dir_factory = temp_dir_factory 25 | self._temp_dirs: list[TemporaryDirectory] = [] 26 | 27 | def create_temp_dir(self) -> TemporaryDirectory: 28 | """ 29 | Create and track new temporary directory for browser use. 30 | 31 | Returns: 32 | TemporaryDirectory object for browser --user-data-dir argument. 33 | """ 34 | temp_dir = self._temp_dir_factory() 35 | self._temp_dirs.append(temp_dir) 36 | return temp_dir 37 | 38 | @staticmethod 39 | def retry_process_file(func: Callable[[str], None], path: str, retry_times: int = 10): 40 | """ 41 | Execute file operation with retry logic for locked files. 42 | 43 | Args: 44 | func: Function to execute on path. 45 | path: File or directory path to operate on. 46 | retry_times: Maximum retry attempts (negative = unlimited). 47 | 48 | Raises: 49 | PermissionError: If operation fails after all retries. 50 | """ 51 | retry_time = 0 52 | while retry_times < 0 or retry_time < retry_times: 53 | retry_time += 1 54 | try: 55 | func(path) 56 | break 57 | except PermissionError: 58 | time.sleep(0.1) 59 | else: 60 | raise PermissionError() 61 | 62 | def handle_cleanup_error(self, func: Callable[[str], None], path: str, exc_info: tuple): 63 | """ 64 | Handle errors during directory cleanup with browser-specific workarounds. 65 | 66 | Args: 67 | func: Original function that failed. 68 | path: Path that could not be processed. 69 | exc_info: Exception information tuple. 70 | 71 | Note: 72 | Handles Chromium-specific locked files like CrashpadMetrics. 73 | """ 74 | matches = ['CrashpadMetrics-active.pma'] 75 | exc_type, exc_value, _ = exc_info 76 | 77 | if exc_type is PermissionError: 78 | if Path(path).name in matches: 79 | try: 80 | self.retry_process_file(func, path) 81 | return 82 | except PermissionError: 83 | raise exc_value 84 | elif exc_type is OSError: 85 | return 86 | raise exc_value 87 | 88 | def cleanup(self): 89 | """ 90 | Remove all tracked temporary directories with error handling. 91 | 92 | Uses custom error handler for browser-specific file lock issues. 93 | Continues cleanup even if some files resist deletion. 94 | """ 95 | for temp_dir in self._temp_dirs: 96 | shutil.rmtree(temp_dir.name, onerror=self.handle_cleanup_error) 97 | -------------------------------------------------------------------------------- /pydoll/browser/options.py: -------------------------------------------------------------------------------- 1 | from pydoll.browser.interfaces import Options 2 | from pydoll.exceptions import ArgumentAlreadyExistsInOptions 3 | 4 | 5 | class ChromiumOptions(Options): 6 | """ 7 | A class to manage command-line options for a browser instance. 8 | 9 | This class allows the user to specify command-line arguments and 10 | the binary location of the browser executable. 11 | """ 12 | 13 | def __init__(self): 14 | """ 15 | Initializes the Options instance. 16 | 17 | Sets up an empty list for command-line arguments and a string 18 | for the binary location of the browser. 19 | """ 20 | self._arguments = [] 21 | self._binary_location = '' 22 | 23 | @property 24 | def arguments(self) -> list[str]: 25 | """ 26 | Gets the list of command-line arguments. 27 | 28 | Returns: 29 | list: A list of command-line arguments added to the options. 30 | """ 31 | return self._arguments 32 | 33 | @arguments.setter 34 | def arguments(self, args_list: list[str]): 35 | """ 36 | Sets the list of command-line arguments. 37 | 38 | Args: 39 | args_list (list): A list of command-line arguments. 40 | """ 41 | self._arguments = args_list 42 | 43 | @property 44 | def binary_location(self) -> str: 45 | """ 46 | Gets the location of the browser binary. 47 | 48 | Returns: 49 | str: The file path to the browser executable. 50 | """ 51 | return self._binary_location 52 | 53 | @binary_location.setter 54 | def binary_location(self, location: str): 55 | """ 56 | Sets the location of the browser binary. 57 | 58 | Args: 59 | location (str): The file path to the browser executable. 60 | """ 61 | self._binary_location = location 62 | 63 | def add_argument(self, argument: str): 64 | """ 65 | Adds a command-line argument to the options. 66 | 67 | Args: 68 | argument (str): The command-line argument to be added. 69 | 70 | Raises: 71 | ArgumentAlreadyExistsInOptions: If the argument is already in the list of arguments. 72 | """ 73 | if argument not in self._arguments: 74 | self._arguments.append(argument) 75 | else: 76 | raise ArgumentAlreadyExistsInOptions(f'Argument already exists: {argument}') 77 | -------------------------------------------------------------------------------- /pydoll/commands/__init__.py: -------------------------------------------------------------------------------- 1 | # global imports 2 | from pydoll.commands.browser_commands import BrowserCommands 3 | from pydoll.commands.dom_commands import DomCommands 4 | from pydoll.commands.fetch_commands import FetchCommands 5 | from pydoll.commands.input_commands import InputCommands 6 | from pydoll.commands.network_commands import NetworkCommands 7 | from pydoll.commands.page_commands import PageCommands 8 | from pydoll.commands.runtime_commands import RuntimeCommands 9 | from pydoll.commands.storage_commands import StorageCommands 10 | from pydoll.commands.target_commands import TargetCommands 11 | 12 | __all__ = [ 13 | 'DomCommands', 14 | 'FetchCommands', 15 | 'InputCommands', 16 | 'NetworkCommands', 17 | 'PageCommands', 18 | 'RuntimeCommands', 19 | 'StorageCommands', 20 | 'BrowserCommands', 21 | 'TargetCommands', 22 | ] 23 | -------------------------------------------------------------------------------- /pydoll/connection/__init__.py: -------------------------------------------------------------------------------- 1 | from pydoll.connection.connection_handler import ConnectionHandler 2 | 3 | __all__ = [ 4 | 'ConnectionHandler', 5 | ] 6 | -------------------------------------------------------------------------------- /pydoll/connection/managers/__init__.py: -------------------------------------------------------------------------------- 1 | from pydoll.connection.managers.commands_manager import CommandsManager 2 | from pydoll.connection.managers.events_manager import EventsManager 3 | 4 | __all__ = [ 5 | 'CommandsManager', 6 | 'EventsManager', 7 | ] 8 | -------------------------------------------------------------------------------- /pydoll/connection/managers/commands_manager.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import logging 3 | 4 | from pydoll.protocol.base import Command 5 | 6 | logger = logging.getLogger(__name__) 7 | 8 | 9 | class CommandsManager: 10 | """ 11 | Manages command lifecycle and ID assignment for CDP commands. 12 | 13 | Handles command future creation, ID generation, and response resolution 14 | for asynchronous command execution. 15 | """ 16 | 17 | def __init__(self) -> None: 18 | """Initialize command manager with empty state.""" 19 | self._pending_commands: dict[int, asyncio.Future] = {} 20 | self._id = 1 21 | 22 | def create_command_future(self, command: Command) -> asyncio.Future: 23 | """ 24 | Create future for command and assign unique ID. 25 | 26 | Args: 27 | command: Command to prepare for execution. 28 | 29 | Returns: 30 | Future that resolves when command completes. 31 | """ 32 | command['id'] = self._id 33 | future = asyncio.Future() # type: ignore 34 | self._pending_commands[self._id] = future 35 | self._id += 1 36 | return future 37 | 38 | def resolve_command(self, response_id: int, result: str): 39 | """Resolve pending command with its result.""" 40 | if response_id in self._pending_commands: 41 | self._pending_commands[response_id].set_result(result) 42 | del self._pending_commands[response_id] 43 | 44 | def remove_pending_command(self, command_id: int): 45 | """Remove pending command without resolving (for timeouts/cancellations).""" 46 | if command_id in self._pending_commands: 47 | del self._pending_commands[command_id] 48 | -------------------------------------------------------------------------------- /pydoll/connection/managers/events_manager.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import logging 3 | from typing import Any, Callable, cast 4 | 5 | from pydoll.protocol.base import Event 6 | from pydoll.protocol.page.types import ( 7 | JavascriptDialogOpeningEvent, 8 | JavascriptDialogOpeningEventParams, 9 | ) 10 | 11 | logger = logging.getLogger(__name__) 12 | 13 | 14 | class EventsManager: 15 | """ 16 | Manages event callbacks, processing, and network logs. 17 | 18 | Handles event callback registration, triggering, and maintains state 19 | for network logs and dialog information. 20 | """ 21 | 22 | def __init__(self) -> None: 23 | """Initialize events manager with empty state.""" 24 | self._event_callbacks: dict[int, dict] = {} 25 | self._callback_id = 0 26 | self.network_logs: list[Event] = [] 27 | self.dialog = JavascriptDialogOpeningEvent(method='') 28 | logger.info('EventsManager initialized') 29 | 30 | def register_callback( 31 | self, event_name: str, callback: Callable[[dict], Any], temporary: bool = False 32 | ) -> int: 33 | """ 34 | Register callback for specific event type. 35 | 36 | Args: 37 | event_name: Event name to listen for. 38 | callback: Function called when event occurs. 39 | temporary: If True, callback removed after first trigger. 40 | 41 | Returns: 42 | Callback ID for later removal. 43 | """ 44 | self._callback_id += 1 45 | self._event_callbacks[self._callback_id] = { 46 | 'event': event_name, 47 | 'callback': callback, 48 | 'temporary': temporary, 49 | } 50 | logger.info(f"Registered callback '{event_name}' with ID {self._callback_id}") 51 | return self._callback_id 52 | 53 | def remove_callback(self, callback_id: int) -> bool: 54 | """Remove callback by ID.""" 55 | if callback_id not in self._event_callbacks: 56 | logger.warning(f'Callback ID {callback_id} not found') 57 | return False 58 | 59 | del self._event_callbacks[callback_id] 60 | logger.info(f'Removed callback ID {callback_id}') 61 | return True 62 | 63 | def clear_callbacks(self): 64 | """Remove all registered callbacks.""" 65 | self._event_callbacks.clear() 66 | logger.info('All callbacks cleared') 67 | 68 | async def process_event(self, event_data: Event): 69 | """ 70 | Process received event and trigger callbacks. 71 | 72 | Handles special events (network requests, dialogs) and updates 73 | internal state before triggering registered callbacks. 74 | """ 75 | event_name = event_data['method'] 76 | logger.debug(f'Processing event: {event_name}') 77 | 78 | if 'Network.requestWillBeSent' in event_name: 79 | self._update_network_logs(event_data) 80 | 81 | if 'Page.javascriptDialogOpening' in event_name: 82 | self.dialog = JavascriptDialogOpeningEvent( 83 | method=event_data['method'], 84 | params=cast(JavascriptDialogOpeningEventParams, event_data['params']), 85 | ) 86 | 87 | if 'Page.javascriptDialogClosed' in event_name: 88 | self.dialog = JavascriptDialogOpeningEvent(method='') 89 | 90 | await self._trigger_callbacks(event_name, event_data) 91 | 92 | def _update_network_logs(self, event_data: Event): 93 | """Add network event to logs (keeps last 10000 entries).""" 94 | self.network_logs.append(event_data) 95 | self.network_logs = self.network_logs[-10000:] # keep only last 10000 logs 96 | 97 | async def _trigger_callbacks(self, event_name: str, event_data: Event): 98 | """Trigger all registered callbacks for event, removing temporary ones.""" 99 | callbacks_to_remove = [] 100 | 101 | for cb_id, cb_data in list(self._event_callbacks.items()): 102 | if cb_data['event'] == event_name: 103 | try: 104 | if asyncio.iscoroutinefunction(cb_data['callback']): 105 | await cb_data['callback'](event_data) 106 | else: 107 | cb_data['callback'](event_data) 108 | except Exception as e: 109 | logger.error(f'Error in callback {cb_id}: {str(e)}') 110 | 111 | if cb_data['temporary']: 112 | callbacks_to_remove.append(cb_id) 113 | 114 | for cb_id in callbacks_to_remove: 115 | self.remove_callback(cb_id) 116 | -------------------------------------------------------------------------------- /pydoll/elements/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/autoscrape-labs/pydoll/c518a7e81bf05f7d33fa2d1aa26d5db7c53a1557/pydoll/elements/__init__.py -------------------------------------------------------------------------------- /pydoll/elements/mixins/__init__.py: -------------------------------------------------------------------------------- 1 | from pydoll.elements.mixins.find_elements_mixin import FindElementsMixin 2 | 3 | __all__ = [ 4 | 'FindElementsMixin', 5 | ] 6 | -------------------------------------------------------------------------------- /pydoll/protocol/__init__.py: -------------------------------------------------------------------------------- 1 | """Chrome DevTools Protocol implementation.""" 2 | -------------------------------------------------------------------------------- /pydoll/protocol/base.py: -------------------------------------------------------------------------------- 1 | from typing import Generic, NotRequired, TypedDict, TypeVar 2 | 3 | T_CommandResponse = TypeVar('T_CommandResponse') 4 | 5 | 6 | class CommandParams(TypedDict, total=False): 7 | """Base structure for all command parameters.""" 8 | 9 | pass 10 | 11 | 12 | class Command(TypedDict, Generic[T_CommandResponse]): 13 | """Base structure for all commands. 14 | 15 | Attributes: 16 | method: The command method name 17 | params: Optional dictionary of parameters for the command 18 | """ 19 | 20 | id: NotRequired[int] 21 | method: str 22 | params: NotRequired[CommandParams] 23 | 24 | 25 | class ResponseResult(TypedDict, total=False): 26 | """Base structure for all response results.""" 27 | 28 | pass 29 | 30 | 31 | class Response(TypedDict): 32 | """Base structure for all responses. 33 | 34 | Attributes: 35 | id: The ID that matches the command ID 36 | result: The result data for the command 37 | """ 38 | 39 | id: int 40 | result: ResponseResult 41 | 42 | 43 | class Event(TypedDict): 44 | """Base structure for all events.""" 45 | 46 | method: str 47 | params: NotRequired[dict[str, str]] 48 | -------------------------------------------------------------------------------- /pydoll/protocol/browser/__init__.py: -------------------------------------------------------------------------------- 1 | """Browser domain implementation.""" 2 | -------------------------------------------------------------------------------- /pydoll/protocol/browser/events.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | 3 | 4 | class BrowserEvent(str, Enum): 5 | """ 6 | Events from the Browser domain of the Chrome DevTools Protocol. 7 | 8 | This enumeration contains the names of browser-related events that can be 9 | received from the Chrome DevTools Protocol. These events provide information 10 | about browser activities and state changes. 11 | """ 12 | 13 | DOWNLOAD_PROGRESS = 'Browser.downloadProgress' 14 | """ 15 | Fired when download makes progress. The last call has |done| == true. 16 | 17 | Args: 18 | guid (str): Global unique identifier of the download. 19 | totalBytes (int): Total expected bytes to download. 20 | receivedBytes (int): Total bytes received. 21 | state (str): Download status. 22 | Allowed values: 'inProgress', 'completed', 'canceled' 23 | """ 24 | 25 | DOWNLOAD_WILL_BEGIN = 'Browser.downloadWillBegin' 26 | """ 27 | Fired when page is about to start a download. 28 | 29 | Args: 30 | frameId (str): Id of the frame that caused the download to begin. 31 | guid (str): Global unique identifier of the download. 32 | url (str): URL of the resource being downloaded. 33 | suggestedFilename (str): Suggested file name of the resource 34 | (the actual name of the file saved on disk may differ). 35 | """ 36 | -------------------------------------------------------------------------------- /pydoll/protocol/browser/methods.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | 3 | 4 | class BrowserMethod(str, Enum): 5 | ADD_PRIVACY_SANDBOX_COORDINATOR_KEY_CONFIG = 'Browser.addPrivacySandboxCoordinatorKeyConfig' 6 | ADD_PRIVACY_SANDBOX_ENROLLMENT_OVERRIDE = 'Browser.addPrivacySandboxEnrollmentOverride' 7 | CLOSE = 'Browser.close' 8 | GET_VERSION = 'Browser.getVersion' 9 | RESET_PERMISSIONS = 'Browser.resetPermissions' 10 | CANCEL_DOWNLOAD = 'Browser.cancelDownload' 11 | CRASH = 'Browser.crash' 12 | CRASH_GPU_PROCESS = 'Browser.crashGpuProcess' 13 | EXECUTE_BROWSER_COMMAND = 'Browser.executeBrowserCommand' 14 | GET_BROWSER_COMMAND_LINE = 'Browser.getBrowserCommandLine' 15 | GET_HISTOGRAM = 'Browser.getHistogram' 16 | GET_HISTOGRAMS = 'Browser.getHistograms' 17 | GET_WINDOW_BOUNDS = 'Browser.getWindowBounds' 18 | GET_WINDOW_FOR_TARGET = 'Browser.getWindowForTarget' 19 | GRANT_PERMISSIONS = 'Browser.grantPermissions' 20 | SET_DOCK_TILE = 'Browser.setDockTile' 21 | SET_DOWNLOAD_BEHAVIOR = 'Browser.setDownloadBehavior' 22 | SET_PERMISSION = 'Browser.setPermission' 23 | SET_WINDOW_BOUNDS = 'Browser.setWindowBounds' 24 | -------------------------------------------------------------------------------- /pydoll/protocol/browser/params.py: -------------------------------------------------------------------------------- 1 | from typing import NotRequired 2 | 3 | from pydoll.constants import DownloadBehavior, PermissionType 4 | from pydoll.protocol.base import CommandParams 5 | from pydoll.protocol.browser.types import WindowBoundsDict 6 | 7 | 8 | class GetWindowForTargetParams(CommandParams): 9 | """Parameters for getting window by target ID.""" 10 | 11 | targetId: str 12 | 13 | 14 | class SetDownloadBehaviorParams(CommandParams): 15 | """Parameters for setting download behavior.""" 16 | 17 | behavior: DownloadBehavior 18 | downloadPath: NotRequired[str] 19 | browserContextId: NotRequired[str] 20 | eventsEnabled: NotRequired[bool] 21 | 22 | 23 | class SetWindowBoundsParams(CommandParams): 24 | """Parameters for setting window bounds.""" 25 | 26 | windowId: int 27 | bounds: WindowBoundsDict 28 | 29 | 30 | class ResetPermissionsParams(CommandParams): 31 | """Parameters for resetting permissions.""" 32 | 33 | browserContextId: NotRequired[str] 34 | 35 | 36 | class CancelDownloadParams(CommandParams): 37 | """Parameters for cancelling downloads.""" 38 | 39 | guid: str 40 | browserContextId: NotRequired[str] 41 | 42 | 43 | class GrantPermissionsParams(CommandParams): 44 | """Parameters for granting permissions.""" 45 | 46 | permissions: list[PermissionType] 47 | origin: NotRequired[str] 48 | browserContextId: NotRequired[str] 49 | -------------------------------------------------------------------------------- /pydoll/protocol/browser/responses.py: -------------------------------------------------------------------------------- 1 | from typing import TypedDict 2 | 3 | from pydoll.protocol.browser.types import WindowBoundsDict 4 | 5 | 6 | class GetWindowForTargetResultDict(TypedDict): 7 | """Result structure for GetWindowForTarget command.""" 8 | 9 | windowId: int 10 | bounds: WindowBoundsDict 11 | 12 | 13 | class GetVersionResultDict(TypedDict): 14 | """Result structure for GetVersion command.""" 15 | 16 | protocolVersion: str 17 | product: str 18 | revision: str 19 | userAgent: str 20 | jsVersion: str 21 | 22 | 23 | class GetWindowForTargetResponse(TypedDict): 24 | """Response structure for GetWindowForTarget command.""" 25 | 26 | result: GetWindowForTargetResultDict 27 | 28 | 29 | class GetVersionResponse(TypedDict): 30 | """Response structure for GetVersion command.""" 31 | 32 | result: GetVersionResultDict 33 | -------------------------------------------------------------------------------- /pydoll/protocol/browser/types.py: -------------------------------------------------------------------------------- 1 | from typing import NotRequired, TypedDict 2 | 3 | from pydoll.constants import WindowState 4 | 5 | 6 | class WindowBoundsDict(TypedDict): 7 | """Structure for window bounds parameters.""" 8 | 9 | windowState: WindowState 10 | width: NotRequired[int] 11 | height: NotRequired[int] 12 | x: NotRequired[int] 13 | y: NotRequired[int] 14 | -------------------------------------------------------------------------------- /pydoll/protocol/dom/__init__.py: -------------------------------------------------------------------------------- 1 | """DOM domain implementation.""" 2 | -------------------------------------------------------------------------------- /pydoll/protocol/dom/events.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | 3 | 4 | class DomEvent(str, Enum): 5 | """ 6 | Events from the DOM domain of the Chrome DevTools Protocol. 7 | 8 | This enumeration contains the names of DOM-related events that can be 9 | received from the Chrome DevTools Protocol. These events provide information 10 | about changes to the DOM structure, attributes, and other DOM-related activities. 11 | """ 12 | 13 | ATTRIBUTE_MODIFIED = 'DOM.attributeModified' 14 | """ 15 | Fired when Element's attribute is modified. 16 | 17 | Args: 18 | nodeId (NodeId): Id of the node that has changed. 19 | name (str): Attribute name. 20 | value (str): Attribute value. 21 | """ 22 | 23 | ATTRIBUTE_REMOVED = 'DOM.attributeRemoved' 24 | """ 25 | Fired when Element's attribute is removed. 26 | 27 | Args: 28 | nodeId (NodeId): Id of the node that has changed. 29 | name (str): Attribute name. 30 | """ 31 | 32 | CHARACTER_DATA_MODIFIED = 'DOM.characterDataModified' 33 | """ 34 | Mirrors DOMCharacterDataModified event. 35 | 36 | Args: 37 | nodeId (NodeId): Id of the node that has changed. 38 | characterData (str): New text value. 39 | """ 40 | 41 | CHILD_NODE_COUNT_UPDATED = 'DOM.childNodeCountUpdated' 42 | """ 43 | Fired when Container's child node count has changed. 44 | 45 | Args: 46 | nodeId (NodeId): Id of the node that has changed. 47 | childNodeCount (int): New node count. 48 | """ 49 | 50 | CHILD_NODE_INSERTED = 'DOM.childNodeInserted' 51 | """ 52 | Mirrors DOMNodeInserted event. 53 | 54 | Args: 55 | parentNodeId (NodeId): Id of the node that has changed. 56 | previousNodeId (NodeId): Id of the previous sibling. 57 | node (Node): Inserted node data. 58 | """ 59 | 60 | CHILD_NODE_REMOVED = 'DOM.childNodeRemoved' 61 | """ 62 | Mirrors DOMNodeRemoved event. 63 | 64 | Args: 65 | parentNodeId (NodeId): Parent id. 66 | nodeId (NodeId): Id of the node that has been removed. 67 | """ 68 | 69 | DOCUMENT_UPDATED = 'DOM.documentUpdated' 70 | """ 71 | Fired when Document has been totally updated. Node ids are no longer valid. 72 | """ 73 | 74 | SET_CHILD_NODES = 'DOM.setChildNodes' 75 | """ 76 | Fired when backend wants to provide client with the missing DOM structure. 77 | This happens upon most of the calls requesting node ids. 78 | 79 | Args: 80 | parentId (NodeId): Parent node id to populate with children. 81 | nodes (array[Node]): Child nodes array. 82 | """ 83 | 84 | DISTRIBUTED_NODES_UPDATED = 'DOM.distributedNodesUpdated' 85 | """ 86 | Called when distribution is changed. 87 | 88 | Args: 89 | insertionPointId (NodeId): Insertion point where distributed nodes were updated. 90 | distributedNodes (array[BackendNode]): Distributed nodes for given insertion point. 91 | """ 92 | 93 | INLINE_STYLE_INVALIDATED = 'DOM.inlineStyleInvalidated' 94 | """ 95 | Fired when Element's inline style is modified via a CSS property modification. 96 | 97 | Args: 98 | nodeIds (array[NodeId]): Ids of the nodes for which the inline styles have been invalidated. 99 | """ 100 | 101 | PSEUDO_ELEMENT_ADDED = 'DOM.pseudoElementAdded' 102 | """ 103 | Called when a pseudo element is added to an element. 104 | 105 | Args: 106 | parentId (NodeId): Pseudo element's parent element id. 107 | pseudoElement (Node): The added pseudo element. 108 | """ 109 | 110 | PSEUDO_ELEMENT_REMOVED = 'DOM.pseudoElementRemoved' 111 | """ 112 | Called when a pseudo element is removed from an element. 113 | 114 | Args: 115 | parentId (NodeId): Pseudo element's parent element id. 116 | pseudoElementId (NodeId): The removed pseudo element id. 117 | """ 118 | 119 | SCROLLABLE_FLAG_UPDATED = 'DOM.scrollableFlagUpdated' 120 | """ 121 | Fired when a node's scrollability state changes. 122 | 123 | Args: 124 | nodeId (DOM.NodeId): The id of the node. 125 | isScrollable (bool): If the node is scrollable. 126 | """ 127 | 128 | SHADOW_ROOT_POPPED = 'DOM.shadowRootPopped' 129 | """ 130 | Called when shadow root is popped from the element. 131 | 132 | Args: 133 | hostId (NodeId): Host element id. 134 | rootId (NodeId): Shadow root id. 135 | """ 136 | 137 | SHADOW_ROOT_PUSHED = 'DOM.shadowRootPushed' 138 | """ 139 | Called when shadow root is pushed into the element. 140 | 141 | Args: 142 | hostId (NodeId): Host element id. 143 | root (Node): Shadow root. 144 | """ 145 | 146 | TOP_LAYER_ELEMENTS_UPDATED = 'DOM.topLayerElementsUpdated' 147 | """ 148 | Called when top layer elements are changed. 149 | """ 150 | -------------------------------------------------------------------------------- /pydoll/protocol/dom/methods.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | 3 | 4 | class DomMethod(str, Enum): 5 | DESCRIBE_NODE = 'DOM.describeNode' 6 | DISABLE = 'DOM.disable' 7 | ENABLE = 'DOM.enable' 8 | FOCUS = 'DOM.focus' 9 | GET_ATTRIBUTES = 'DOM.getAttributes' 10 | GET_BOX_MODEL = 'DOM.getBoxModel' 11 | GET_DOCUMENT = 'DOM.getDocument' 12 | GET_NODE_FOR_LOCATION = 'DOM.getNodeForLocation' 13 | GET_OUTER_HTML = 'DOM.getOuterHTML' 14 | HIDE_HIGHLIGHT = 'DOM.hideHighlight' 15 | HIGHLIGHT_NODE = 'DOM.highlightNode' 16 | HIGHLIGHT_RECT = 'DOM.highlightRect' 17 | MOVE_TO = 'DOM.moveTo' 18 | QUERY_SELECTOR = 'DOM.querySelector' 19 | QUERY_SELECTOR_ALL = 'DOM.querySelectorAll' 20 | REMOVE_ATTRIBUTE = 'DOM.removeAttribute' 21 | REMOVE_NODE = 'DOM.removeNode' 22 | REQUEST_CHILD_NODES = 'DOM.requestChildNodes' 23 | REQUEST_NODE = 'DOM.requestNode' 24 | RESOLVE_NODE = 'DOM.resolveNode' 25 | SCROLL_INTO_VIEW_IF_NEEDED = 'DOM.scrollIntoViewIfNeeded' 26 | SET_ATTRIBUTES_AS_TEXT = 'DOM.setAttributesAsText' 27 | SET_ATTRIBUTE_VALUE = 'DOM.setAttributeValue' 28 | SET_FILE_INPUT_FILES = 'DOM.setFileInputFiles' 29 | SET_NODE_NAME = 'DOM.setNodeName' 30 | SET_NODE_VALUE = 'DOM.setNodeValue' 31 | SET_OUTER_HTML = 'DOM.setOuterHTML' 32 | # Métodos experimentais 33 | COLLECT_CLASS_NAMES_FROM_SUBTREE = 'DOM.collectClassNamesFromSubtree' 34 | COPY_TO = 'DOM.copyTo' 35 | DISCARD_SEARCH_RESULTS = 'DOM.discardSearchResults' 36 | GET_ANCHOR_ELEMENT = 'DOM.getAnchorElement' 37 | GET_CONTAINER_FOR_NODE = 'DOM.getContainerForNode' 38 | GET_CONTENT_QUADS = 'DOM.getContentQuads' 39 | GET_DETACHED_DOM_NODES = 'DOM.getDetachedDomNodes' 40 | GET_ELEMENT_BY_RELATION = 'DOM.getElementByRelation' 41 | GET_FILE_INFO = 'DOM.getFileInfo' 42 | GET_FRAME_OWNER = 'DOM.getFrameOwner' 43 | GET_NODES_FOR_SUBTREE_BY_STYLE = 'DOM.getNodesForSubtreeByStyle' 44 | GET_NODE_STACK_TRACES = 'DOM.getNodeStackTraces' 45 | GET_QUERYING_DESCENDANTS_FOR_CONTAINER = 'DOM.getQueryingDescendantsForContainer' 46 | GET_RELAYOUT_BOUNDARY = 'DOM.getRelayoutBoundary' 47 | GET_SEARCH_RESULTS = 'DOM.getSearchResults' 48 | GET_TOP_LAYER_ELEMENTS = 'DOM.getTopLayerElements' 49 | MARK_UNDOABLE_STATE = 'DOM.markUndoableState' 50 | PERFORM_SEARCH = 'DOM.performSearch' 51 | PUSH_NODE_BY_PATH_TO_FRONTEND = 'DOM.pushNodeByPathToFrontend' 52 | PUSH_NODES_BY_BACKEND_IDS_TO_FRONTEND = 'DOM.pushNodesByBackendIdsToFrontend' 53 | REDO = 'DOM.redo' 54 | SET_INSPECTED_NODE = 'DOM.setInspectedNode' 55 | SET_NODE_STACK_TRACES_ENABLED = 'DOM.setNodeStackTracesEnabled' 56 | UNDO = 'DOM.undo' 57 | -------------------------------------------------------------------------------- /pydoll/protocol/dom/params.py: -------------------------------------------------------------------------------- 1 | from typing import NotRequired 2 | 3 | from pydoll.constants import ElementRelation, IncludeWhitespace, LogicalAxes, PhysicalAxes 4 | from pydoll.protocol.base import CommandParams 5 | from pydoll.protocol.dom.types import ( 6 | CSSComputedStyleProperty, 7 | Rect, 8 | ) 9 | 10 | 11 | class DescribeNodeParams(CommandParams): 12 | nodeId: NotRequired[int] 13 | backendNodeId: NotRequired[int] 14 | objectId: NotRequired[str] 15 | depth: NotRequired[int] 16 | pierce: NotRequired[bool] 17 | 18 | 19 | class DomEnableParams(CommandParams): 20 | includeWhitespace: NotRequired[IncludeWhitespace] 21 | 22 | 23 | class DomFocusParams(CommandParams): 24 | nodeId: NotRequired[int] 25 | backendNodeId: NotRequired[int] 26 | objectId: NotRequired[str] 27 | 28 | 29 | class GetAttributesParams(CommandParams): 30 | nodeId: int 31 | 32 | 33 | class GetBoxModelParams(CommandParams): 34 | nodeId: NotRequired[int] 35 | backendNodeId: NotRequired[int] 36 | objectId: NotRequired[str] 37 | 38 | 39 | class GetDocumentParams(CommandParams): 40 | depth: NotRequired[int] 41 | pierce: NotRequired[bool] 42 | 43 | 44 | class GetNodeForLocationParams(CommandParams): 45 | x: int 46 | y: int 47 | includeUserAgentShadowDOM: NotRequired[bool] 48 | ignorePointerEventsNone: NotRequired[bool] 49 | 50 | 51 | class GetOuterHTMLParams(CommandParams): 52 | nodeId: NotRequired[int] 53 | backendNodeId: NotRequired[int] 54 | objectId: NotRequired[str] 55 | 56 | 57 | class MoveToParams(CommandParams): 58 | nodeId: int 59 | targetNodeId: int 60 | insertBeforeNodeId: NotRequired[int] 61 | 62 | 63 | class QuerySelectorParams(CommandParams): 64 | nodeId: int 65 | selector: str 66 | 67 | 68 | class QuerySelectorAllParams(CommandParams): 69 | nodeId: int 70 | selector: str 71 | 72 | 73 | class RemoveAttributeParams(CommandParams): 74 | nodeId: int 75 | name: str 76 | 77 | 78 | class RemoveNodeParams(CommandParams): 79 | nodeId: int 80 | 81 | 82 | class RequestChildNodesParams(CommandParams): 83 | nodeId: int 84 | depth: NotRequired[int] 85 | pierce: NotRequired[bool] 86 | 87 | 88 | class RequestNodeParams(CommandParams): 89 | objectId: str 90 | 91 | 92 | class ResolveNodeParams(CommandParams): 93 | nodeId: NotRequired[int] 94 | backendNodeId: NotRequired[int] 95 | objectGroup: NotRequired[str] 96 | executionContextId: NotRequired[int] 97 | 98 | 99 | class ScrollIntoViewIfNeededParams(CommandParams): 100 | nodeId: NotRequired[int] 101 | backendNodeId: NotRequired[int] 102 | objectId: NotRequired[str] 103 | rect: NotRequired[Rect] 104 | 105 | 106 | class SetAttributeAsTextParams(CommandParams): 107 | nodeId: int 108 | text: str 109 | name: NotRequired[str] 110 | 111 | 112 | class SetAttributeValueParams(CommandParams): 113 | nodeId: int 114 | name: str 115 | value: str 116 | 117 | 118 | class SetFileInputFilesParams(CommandParams): 119 | files: list[str] 120 | nodeId: NotRequired[int] 121 | backendNodeId: NotRequired[int] 122 | objectId: NotRequired[str] 123 | 124 | 125 | class SetNodeNameParams(CommandParams): 126 | nodeId: int 127 | name: str 128 | 129 | 130 | class SetNodeValueParams(CommandParams): 131 | nodeId: int 132 | value: str 133 | 134 | 135 | class SetOuterHTMLParams(CommandParams): 136 | nodeId: int 137 | outerHTML: str 138 | 139 | 140 | class CollectClassNamesFromSubtreeParams(CommandParams): 141 | nodeId: int 142 | 143 | 144 | class CopyToParams(CommandParams): 145 | nodeId: int 146 | targetNodeId: int 147 | insertBeforeNodeId: NotRequired[int] 148 | 149 | 150 | class DiscardSearchResultsParams(CommandParams): 151 | searchId: str 152 | 153 | 154 | class GetAnchorElementParams(CommandParams): 155 | nodeId: int 156 | anchorSpecifier: NotRequired[str] 157 | 158 | 159 | class GetContainerForNodeParams(CommandParams): 160 | nodeId: int 161 | containerName: NotRequired[str] 162 | physicalAxes: NotRequired[PhysicalAxes] 163 | logicalAxes: NotRequired[LogicalAxes] 164 | queriesScrollState: NotRequired[bool] 165 | 166 | 167 | class GetContentQuadsParams(CommandParams): 168 | nodeId: NotRequired[int] 169 | backendNodeId: NotRequired[int] 170 | objectId: NotRequired[str] 171 | 172 | 173 | class GetElementByRelationParams(CommandParams): 174 | nodeId: int 175 | relation: ElementRelation 176 | 177 | 178 | class GetFileInfoParams(CommandParams): 179 | objectId: str 180 | 181 | 182 | class GetFrameOwnerParams(CommandParams): 183 | frameId: str 184 | 185 | 186 | class GetNodesForSubtreeByStyleParams(CommandParams): 187 | nodeId: int 188 | computedStyles: list[CSSComputedStyleProperty] 189 | pierce: NotRequired[bool] 190 | 191 | 192 | class GetNodeStackTracesParams(CommandParams): 193 | nodeId: int 194 | 195 | 196 | class GetQueryingDescendantForContainerParams(CommandParams): 197 | nodeId: int 198 | 199 | 200 | class GetRelayoutBoundaryParams(CommandParams): 201 | nodeId: int 202 | 203 | 204 | class GetSearchResultsParams(CommandParams): 205 | searchId: str 206 | fromIndex: int 207 | toIndex: int 208 | 209 | 210 | class PerformSearchParams(CommandParams): 211 | query: str 212 | includeUserAgentShadowDOM: NotRequired[bool] 213 | 214 | 215 | class PushNodeByPathToFrontendParams(CommandParams): 216 | path: str 217 | 218 | 219 | class PushNodesByBackendIdsToFrontendParams(CommandParams): 220 | backendNodeIds: list[int] 221 | 222 | 223 | class SetNodeStackTracesEnabledParams(CommandParams): 224 | enable: bool 225 | 226 | 227 | class SetInspectedNodeParams(CommandParams): 228 | nodeId: int 229 | -------------------------------------------------------------------------------- /pydoll/protocol/dom/types.py: -------------------------------------------------------------------------------- 1 | from typing import Annotated, Any, NotRequired, TypedDict 2 | 3 | from pydoll.constants import CompatibilityMode, PseudoType, ShadowRootType 4 | 5 | Quad = Annotated[list[float], 'Format: [x1, y1, x2, y2, x3, y3, x4, y4]'] 6 | 7 | 8 | class Rect(TypedDict): 9 | """Rectangle for capturing screenshot or clip rectangle.""" 10 | 11 | x: float 12 | y: float 13 | width: float 14 | height: float 15 | 16 | 17 | class CSSComputedStyleProperty(TypedDict): 18 | name: str 19 | value: str 20 | 21 | 22 | class BackendNode(TypedDict): 23 | nodeType: int 24 | nodeName: str 25 | backendNodeId: int 26 | 27 | 28 | class Node(TypedDict): 29 | nodeId: int 30 | parentId: NotRequired[int] 31 | backendNodeId: int 32 | nodeType: int 33 | nodeName: str 34 | localName: str 35 | nodeValue: str 36 | childNodeCount: NotRequired[int] 37 | children: NotRequired[list['Node']] 38 | attributes: NotRequired[list[str]] 39 | documentURL: NotRequired[str] 40 | baseURL: NotRequired[str] 41 | publicId: NotRequired[str] 42 | systemId: NotRequired[str] 43 | internalSubset: NotRequired[str] 44 | xmlVersion: NotRequired[str] 45 | name: NotRequired[str] 46 | value: NotRequired[str] 47 | pseudoType: NotRequired[PseudoType] 48 | pseudoIdentifier: NotRequired[str] 49 | shadowRootType: NotRequired[ShadowRootType] 50 | frameId: NotRequired[str] 51 | contentDocument: NotRequired['Node'] 52 | shadowRoots: NotRequired[list['Node']] 53 | templateContent: NotRequired['Node'] 54 | pseudoElements: NotRequired[list['Node']] 55 | importedDocument: NotRequired['Node'] 56 | distributedNodes: NotRequired[list[BackendNode]] 57 | isSVG: NotRequired[bool] 58 | compatibilityMode: NotRequired[CompatibilityMode] 59 | assignedSlot: NotRequired[BackendNode] 60 | isScrollable: NotRequired[bool] 61 | 62 | 63 | class DetachedElementInfo(TypedDict): 64 | treeNode: Node 65 | retainedNodeIds: list[int] 66 | 67 | 68 | class ShapeOutsideInfo(TypedDict): 69 | bounds: Quad 70 | shape: list[Any] 71 | marginShape: list[Any] 72 | 73 | 74 | class BoxModel(TypedDict): 75 | content: Quad 76 | padding: Quad 77 | border: Quad 78 | margin: Quad 79 | width: int 80 | height: int 81 | shapeOutside: NotRequired[ShapeOutsideInfo] 82 | -------------------------------------------------------------------------------- /pydoll/protocol/fetch/__init__.py: -------------------------------------------------------------------------------- 1 | """Fetch domain implementation.""" 2 | -------------------------------------------------------------------------------- /pydoll/protocol/fetch/events.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | 3 | 4 | class FetchEvent(str, Enum): 5 | """ 6 | Events from the Fetch domain of the Chrome DevTools Protocol. 7 | 8 | This enumeration contains the names of Fetch-related events that can be 9 | received from the Chrome DevTools Protocol. These events provide information 10 | about network requests that can be intercepted, modified, or responded to 11 | by the client. 12 | """ 13 | 14 | AUTH_REQUIRED = 'Fetch.authRequired' 15 | """ 16 | Issued when the domain is enabled with handleAuthRequests set to true. 17 | The request is paused until client responds with continueWithAuth. 18 | 19 | Args: 20 | requestId (RequestId): Each request the page makes will have a unique id. 21 | request (Network.Request): The details of the request. 22 | frameId (Page.FrameId): The id of the frame that initiated the request. 23 | resourceType (Network.ResourceType): How the requested resource will be used. 24 | authChallenge (AuthChallenge): Details of the Authorization Challenge encountered. 25 | If this is set, client should respond with continueRequest that contains 26 | AuthChallengeResponse. 27 | """ 28 | 29 | REQUEST_PAUSED = 'Fetch.requestPaused' 30 | """ 31 | Issued when the domain is enabled and the request URL matches the specified filter. 32 | 33 | The request is paused until the client responds with one of continueRequest, 34 | failRequest or fulfillRequest. The stage of the request can be determined by 35 | presence of responseErrorReason and responseStatusCode -- the request is at the 36 | response stage if either of these fields is present and in the request stage otherwise. 37 | 38 | Redirect responses and subsequent requests are reported similarly to regular responses 39 | and requests. Redirect responses may be distinguished by the value of responseStatusCode 40 | (which is one of 301, 302, 303, 307, 308) along with presence of the location header. 41 | Requests resulting from a redirect will have redirectedRequestId field set. 42 | 43 | Args: 44 | requestId (RequestId): Each request the page makes will have a unique id. 45 | request (Network.Request): The details of the request. 46 | frameId (Page.FrameId): The id of the frame that initiated the request. 47 | resourceType (Network.ResourceType): How the requested resource will be used. 48 | responseErrorReason (Network.ErrorReason): Response error if intercepted at response stage. 49 | responseStatusCode (int): Response code if intercepted at response stage. 50 | responseStatusText (str): Response status text if intercepted at response stage. 51 | responseHeaders (array[HeaderEntry]): Response headers if intercepted at the response stage. 52 | networkId (Network.RequestId): If the intercepted request had a corresponding 53 | Network.requestWillBeSent event fired for it, then this networkId will be 54 | the same as the requestId present in the requestWillBeSent event. 55 | redirectedRequestId (RequestId): If the request is due to a redirect response 56 | from the server, the id of the request that has caused the redirect. 57 | """ 58 | -------------------------------------------------------------------------------- /pydoll/protocol/fetch/methods.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | 3 | 4 | class FetchMethod(str, Enum): 5 | CONTINUE_REQUEST = 'Fetch.continueRequest' 6 | CONTINUE_WITH_AUTH = 'Fetch.continueWithAuth' 7 | DISABLE = 'Fetch.disable' 8 | ENABLE = 'Fetch.enable' 9 | FAIL_REQUEST = 'Fetch.failRequest' 10 | FULFILL_REQUEST = 'Fetch.fulfillRequest' 11 | GET_RESPONSE_BODY = 'Fetch.getResponseBody' 12 | TAKE_RESPONSE_BODY_AS_STREAM = 'Fetch.takeResponseBodyAsStream' 13 | CONTINUE_RESPONSE = 'Fetch.continueResponse' 14 | -------------------------------------------------------------------------------- /pydoll/protocol/fetch/params.py: -------------------------------------------------------------------------------- 1 | from typing import NotRequired 2 | 3 | from pydoll.constants import NetworkErrorReason, RequestMethod 4 | from pydoll.protocol.base import CommandParams 5 | from pydoll.protocol.fetch.types import ( 6 | AuthChallengeResponseDict, 7 | HeaderEntry, 8 | RequestPattern, 9 | ) 10 | 11 | 12 | class ContinueRequestParams(CommandParams): 13 | """Parameters for continuing a request.""" 14 | 15 | requestId: str 16 | url: NotRequired[str] 17 | method: NotRequired[RequestMethod] 18 | postData: NotRequired[str] 19 | headers: NotRequired[list[HeaderEntry]] 20 | interceptResponse: NotRequired[bool] 21 | 22 | 23 | class ContinueWithAuthParams(CommandParams): 24 | requestId: str 25 | authChallengeResponse: AuthChallengeResponseDict 26 | 27 | 28 | class FetchEnableParams(CommandParams): 29 | patterns: NotRequired[list[RequestPattern]] 30 | handleAuthRequests: NotRequired[bool] 31 | 32 | 33 | class FailRequestParams(CommandParams): 34 | requestId: str 35 | errorReason: NetworkErrorReason 36 | 37 | 38 | class FulfillRequestParams(CommandParams): 39 | requestId: str 40 | responseCode: int 41 | responseHeaders: NotRequired[list[HeaderEntry]] 42 | body: NotRequired[dict] 43 | responsePhrase: NotRequired[str] 44 | 45 | 46 | class GetResponseBodyParams(CommandParams): 47 | requestId: str 48 | 49 | 50 | class TakeResponseBodyAsStreamParams(CommandParams): 51 | requestId: str 52 | 53 | 54 | class ContinueResponseParams(CommandParams): 55 | requestId: str 56 | responseCode: NotRequired[int] 57 | responsePhrase: NotRequired[str] 58 | responseHeaders: NotRequired[list[HeaderEntry]] 59 | -------------------------------------------------------------------------------- /pydoll/protocol/fetch/responses.py: -------------------------------------------------------------------------------- 1 | from typing import TypedDict 2 | 3 | 4 | class GetResponseBodyResultDict(TypedDict): 5 | body: str 6 | base64encoded: bool 7 | 8 | 9 | class TakeResponseBodyAsStreamResultDict(TypedDict): 10 | stream: str 11 | 12 | 13 | class GetResponseBodyResponse(TypedDict): 14 | result: GetResponseBodyResultDict 15 | 16 | 17 | class TakeResponseBodyAsStreamResponse(TypedDict): 18 | result: TakeResponseBodyAsStreamResultDict 19 | -------------------------------------------------------------------------------- /pydoll/protocol/fetch/types.py: -------------------------------------------------------------------------------- 1 | from typing import NotRequired, TypedDict 2 | 3 | from pydoll.constants import AuthChallengeResponseValues, RequestStage, ResourceType 4 | 5 | 6 | class HeaderEntry(TypedDict): 7 | """HTTP header entry structure.""" 8 | 9 | name: str 10 | value: str 11 | 12 | 13 | class AuthChallengeResponseDict(TypedDict): 14 | response: AuthChallengeResponseValues 15 | username: NotRequired[str] 16 | password: NotRequired[str] 17 | 18 | 19 | class RequestPattern(TypedDict): 20 | urlPattern: str 21 | resourceType: NotRequired[ResourceType] 22 | requestStage: NotRequired[RequestStage] 23 | -------------------------------------------------------------------------------- /pydoll/protocol/input/__init__.py: -------------------------------------------------------------------------------- 1 | """Input domain implementation.""" 2 | -------------------------------------------------------------------------------- /pydoll/protocol/input/events.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | 3 | 4 | class InputEvent(str, Enum): 5 | """ 6 | Events from the Input domain of the Chrome DevTools Protocol. 7 | 8 | This enumeration contains the names of Input-related events that can be 9 | received from the Chrome DevTools Protocol. These events provide information 10 | about user input interactions that can be intercepted or simulated. 11 | """ 12 | 13 | DRAG_INTERCEPTED = 'Input.dragIntercepted' 14 | """ 15 | Emitted only when Input.setInterceptDrags is enabled. Use this data with 16 | Input.dispatchDragEvent to restore normal drag and drop behavior. 17 | 18 | Args: 19 | data (DragData): Contains information about the dragged data. 20 | """ 21 | -------------------------------------------------------------------------------- /pydoll/protocol/input/methods.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | 3 | 4 | class InputMethod(str, Enum): 5 | CANCEL_DRAGGING = 'Input.cancelDragging' 6 | DISPATCH_KEY_EVENT = 'Input.dispatchKeyEvent' 7 | DISPATCH_MOUSE_EVENT = 'Input.dispatchMouseEvent' 8 | DISPATCH_TOUCH_EVENT = 'Input.dispatchTouchEvent' 9 | SET_IGNORE_INPUT_EVENTS = 'Input.setIgnoreInputEvents' 10 | DISPATCH_DRAG_EVENT = 'Input.dispatchDragEvent' 11 | EMULATE_TOUCH_FROM_MOUSE_EVENT = 'Input.emulateTouchFromMouseEvent' 12 | IME_SET_COMPOSITION = 'Input.imeSetComposition' 13 | INSERT_TEXT = 'Input.insertText' 14 | SET_INTERCEPT_DRAGS = 'Input.setInterceptDrags' 15 | SYNTHESIZE_PINCH_GESTURE = 'Input.synthesizePinchGesture' 16 | SYNTHESIZE_SCROLL_GESTURE = 'Input.synthesizeScrollGesture' 17 | SYNTHESIZE_TAP_GESTURE = 'Input.synthesizeTapGesture' 18 | -------------------------------------------------------------------------------- /pydoll/protocol/input/params.py: -------------------------------------------------------------------------------- 1 | from typing import NotRequired 2 | 3 | from pydoll.constants import ( 4 | DragEventType, 5 | GestureSourceType, 6 | KeyEventType, 7 | KeyLocation, 8 | KeyModifier, 9 | MouseButton, 10 | MouseEventType, 11 | PointerType, 12 | TouchEventType, 13 | ) 14 | from pydoll.protocol.base import CommandParams 15 | from pydoll.protocol.input.types import ( 16 | DragData, 17 | TouchPoint, 18 | ) 19 | 20 | 21 | class DispatchKeyEventParams(CommandParams): 22 | type: KeyEventType 23 | modifiers: NotRequired[KeyModifier] 24 | timestamp: NotRequired[float] 25 | text: NotRequired[str] 26 | unmodifiedText: NotRequired[str] 27 | keyIdentifier: NotRequired[str] 28 | code: NotRequired[str] 29 | key: NotRequired[str] 30 | windowsVirtualKeyCode: NotRequired[int] 31 | nativeVirtualKeyCode: NotRequired[int] 32 | autoRepeat: NotRequired[bool] 33 | isKeypad: NotRequired[bool] 34 | isSystemKey: NotRequired[bool] 35 | location: NotRequired[KeyLocation] 36 | commands: NotRequired[list[str]] 37 | 38 | 39 | class DispatchMouseEventParams(CommandParams): 40 | type: MouseEventType 41 | x: int 42 | y: int 43 | modifiers: NotRequired[KeyModifier] 44 | timestamp: NotRequired[float] 45 | button: NotRequired[MouseButton] 46 | clickCount: NotRequired[int] 47 | force: NotRequired[float] 48 | tangentialPressure: NotRequired[float] 49 | tiltX: NotRequired[float] 50 | tiltY: NotRequired[float] 51 | twist: NotRequired[int] 52 | deltaX: NotRequired[float] 53 | deltaY: NotRequired[float] 54 | pointerType: NotRequired[PointerType] 55 | 56 | 57 | class DispatchTouchEventParams(CommandParams): 58 | type: TouchEventType 59 | touchPoints: NotRequired[list[TouchPoint]] 60 | modifiers: NotRequired[KeyModifier] 61 | timestamp: NotRequired[float] 62 | 63 | 64 | class SetIgnoreInputEventsParams(CommandParams): 65 | enabled: bool 66 | 67 | 68 | class DispatchDragEventParams(CommandParams): 69 | type: DragEventType 70 | x: int 71 | y: int 72 | data: NotRequired[DragData] 73 | modifiers: NotRequired[KeyModifier] 74 | 75 | 76 | class EmulateTouchFromMouseEventParams(CommandParams): 77 | type: MouseEventType 78 | x: int 79 | y: int 80 | button: MouseButton 81 | timestamp: NotRequired[float] 82 | deltaX: NotRequired[float] 83 | deltaY: NotRequired[float] 84 | modifiers: NotRequired[KeyModifier] 85 | clickCount: NotRequired[int] 86 | 87 | 88 | class ImeSetCompositionParams(CommandParams): 89 | text: str 90 | selectionStart: int 91 | selectionEnd: int 92 | replacementStart: NotRequired[int] 93 | replacementEnd: NotRequired[int] 94 | 95 | 96 | class InsertTextParams(CommandParams): 97 | text: str 98 | 99 | 100 | class SetInterceptDragsParams(CommandParams): 101 | enabled: bool 102 | 103 | 104 | class SynthesizePinchGestureParams(CommandParams): 105 | x: int 106 | y: int 107 | scaleFactor: float 108 | relativeSpeed: NotRequired[float] 109 | gestureSourceType: NotRequired[GestureSourceType] 110 | 111 | 112 | class SynthesizeScrollGestureParams(CommandParams): 113 | x: int 114 | y: int 115 | xDistance: NotRequired[float] 116 | yDistance: NotRequired[float] 117 | xOverscroll: NotRequired[float] 118 | yOverscroll: NotRequired[float] 119 | preventFling: NotRequired[bool] 120 | speed: NotRequired[int] 121 | gestureSourceType: NotRequired[GestureSourceType] 122 | repeatCount: NotRequired[int] 123 | repeatDelayMs: NotRequired[int] 124 | interactionMarkerName: NotRequired[str] 125 | 126 | 127 | class SynthesizeTapGestureParams(CommandParams): 128 | x: int 129 | y: int 130 | duration: NotRequired[int] 131 | tapCount: NotRequired[int] 132 | gestureSourceType: NotRequired[GestureSourceType] 133 | -------------------------------------------------------------------------------- /pydoll/protocol/input/types.py: -------------------------------------------------------------------------------- 1 | from typing import NotRequired, TypedDict 2 | 3 | 4 | class TouchPoint(TypedDict): 5 | x: int 6 | y: int 7 | radiusX: NotRequired[float] 8 | radiusY: NotRequired[float] 9 | rotationAngle: NotRequired[float] 10 | force: NotRequired[float] 11 | tangentialPressure: NotRequired[float] 12 | tiltX: NotRequired[float] 13 | tiltY: NotRequired[float] 14 | twist: NotRequired[int] 15 | id: NotRequired[int] 16 | 17 | 18 | class DragDataItem(TypedDict): 19 | mimeType: str 20 | data: str 21 | title: NotRequired[str] 22 | baseURL: NotRequired[str] 23 | 24 | 25 | class DragData(TypedDict): 26 | items: list[DragDataItem] 27 | files: NotRequired[list[str]] 28 | dragOperationMask: int 29 | -------------------------------------------------------------------------------- /pydoll/protocol/network/__init__.py: -------------------------------------------------------------------------------- 1 | """Network domain implementation.""" 2 | -------------------------------------------------------------------------------- /pydoll/protocol/network/methods.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | 3 | 4 | class NetworkMethod(str, Enum): 5 | CLEAR_BROWSER_CACHE = 'Network.clearBrowserCache' 6 | CLEAR_BROWSER_COOKIES = 'Network.clearBrowserCookies' 7 | DELETE_COOKIES = 'Network.deleteCookies' 8 | DISABLE = 'Network.disable' 9 | EMULATE_NETWORK_CONDITIONS = 'Network.emulateNetworkConditions' 10 | ENABLE = 'Network.enable' 11 | GET_COOKIES = 'Network.getCookies' 12 | GET_REQUEST_POST_DATA = 'Network.getRequestPostData' 13 | GET_RESPONSE_BODY = 'Network.getResponseBody' 14 | SET_BYPASS_SERVICE_WORKER = 'Network.setBypassServiceWorker' 15 | SET_CACHE_DISABLED = 'Network.setCacheDisabled' 16 | SET_COOKIE = 'Network.setCookie' 17 | SET_COOKIES = 'Network.setCookies' 18 | SET_EXTRA_HTTP_HEADERS = 'Network.setExtraHTTPHeaders' 19 | SET_USER_AGENT_OVERRIDE = 'Network.setUserAgentOverride' 20 | CLEAR_ACCEPTED_ENCODINGS_OVERRIDE = 'Network.clearAcceptedEncodingsOverride' 21 | ENABLE_REPORTING_API = 'Network.enableReportingApi' 22 | GET_CERTIFICATE = 'Network.getCertificate' 23 | GET_RESPONSE_BODY_FOR_INTERCEPTION = 'Network.getResponseBodyForInterception' 24 | GET_SECURITY_ISOLATION_STATUS = 'Network.getSecurityIsolationStatus' 25 | LOAD_NETWORK_RESOURCE = 'Network.loadNetworkResource' 26 | REPLAY_XHR = 'Network.replayXHR' 27 | SEARCH_IN_RESPONSE_BODY = 'Network.searchInResponseBody' 28 | SET_ACCEPTED_ENCODINGS = 'Network.setAcceptedEncodings' 29 | SET_ATTACH_DEBUG_STACK = 'Network.setAttachDebugStack' 30 | SET_BLOCKED_URLS = 'Network.setBlockedURLs' 31 | SET_COOKIE_CONTROLS = 'Network.setCookieControls' 32 | STREAM_RESOURCE_CONTENT = 'Network.streamResourceContent' 33 | TAKE_RESPONSE_BODY_FOR_INTERCEPTION_AS_STREAM = ( 34 | 'Network.takeResponseBodyForInterceptionAsStream' 35 | ) 36 | -------------------------------------------------------------------------------- /pydoll/protocol/network/responses.py: -------------------------------------------------------------------------------- 1 | from typing import NotRequired, TypedDict 2 | 3 | from pydoll.protocol.fetch.types import HeaderEntry 4 | from pydoll.protocol.network.types import Cookie, SearchMatch, SecurityIsolationStatus 5 | 6 | 7 | class GetCookiesResultDict(TypedDict): 8 | """Response result for getCookies command.""" 9 | 10 | cookies: list[Cookie] 11 | 12 | 13 | class GetRequestPostDataResultDict(TypedDict): 14 | """Response result for getRequestPostData command.""" 15 | 16 | postData: str 17 | 18 | 19 | class GetResponseBodyResultDict(TypedDict): 20 | """Response result for getResponseBody command.""" 21 | 22 | body: str 23 | base64Encoded: bool 24 | 25 | 26 | class GetResponseBodyForInterceptionResultDict(TypedDict): 27 | """Response result for getResponseBodyForInterception command.""" 28 | 29 | body: str 30 | base64Encoded: bool 31 | 32 | 33 | class GetCertificateResultDict(TypedDict): 34 | """Response result for getCertificate command.""" 35 | 36 | tableNames: list[str] 37 | 38 | 39 | class SearchInResponseBodyResultDict(TypedDict): 40 | """Response result for searchInResponseBody command.""" 41 | 42 | result: list[SearchMatch] 43 | 44 | 45 | class SetCookieResultDict(TypedDict): 46 | """Response result for setCookie command.""" 47 | 48 | success: bool 49 | 50 | 51 | class StreamResourceContentResultDict(TypedDict): 52 | """Response result for streamResourceContent command.""" 53 | 54 | bufferedData: str 55 | 56 | 57 | class TakeResponseBodyForInterceptionAsStreamResultDict(TypedDict): 58 | """Response result for takeResponseBodyForInterceptionAsStream command.""" 59 | 60 | stream: str 61 | 62 | 63 | class CanClearBrowserCacheResultDict(TypedDict): 64 | """Response result for canClearBrowserCache command.""" 65 | 66 | result: bool 67 | 68 | 69 | class CanClearBrowserCookiesResultDict(TypedDict): 70 | """Response result for canClearBrowserCookies command.""" 71 | 72 | result: bool 73 | 74 | 75 | class CanEmulateNetworkConditionsResultDict(TypedDict): 76 | """Response result for canEmulateNetworkConditions command.""" 77 | 78 | result: bool 79 | 80 | 81 | class GetSecurityIsolationStatusResultDict(TypedDict): 82 | """Response result for getSecurityIsolationStatus command.""" 83 | 84 | status: SecurityIsolationStatus 85 | 86 | 87 | class LoadNetworkResourceResultDict(TypedDict): 88 | """Response result for loadNetworkResource command.""" 89 | 90 | success: bool 91 | netError: NotRequired[float] 92 | netErrorName: NotRequired[str] 93 | httpStatusCode: NotRequired[float] 94 | stream: NotRequired[str] 95 | headers: NotRequired[list[HeaderEntry]] 96 | 97 | 98 | # Response classes that inherit from Response 99 | class GetCookiesResponse(TypedDict): 100 | """Response for getCookies command.""" 101 | 102 | result: GetCookiesResultDict 103 | 104 | 105 | class GetRequestPostDataResponse(TypedDict): 106 | """Response for getRequestPostData command.""" 107 | 108 | result: GetRequestPostDataResultDict 109 | 110 | 111 | class GetResponseBodyResponse(TypedDict): 112 | """Response for getResponseBody command.""" 113 | 114 | result: GetResponseBodyResultDict 115 | 116 | 117 | class GetResponseBodyForInterceptionResponse(TypedDict): 118 | """Response for getResponseBodyForInterception command.""" 119 | 120 | result: GetResponseBodyForInterceptionResultDict 121 | 122 | 123 | class GetCertificateResponse(TypedDict): 124 | """Response for getCertificate command.""" 125 | 126 | result: GetCertificateResultDict 127 | 128 | 129 | class SearchInResponseBodyResponse(TypedDict): 130 | """Response for searchInResponseBody command.""" 131 | 132 | result: SearchInResponseBodyResultDict 133 | 134 | 135 | class SetCookieResponse(TypedDict): 136 | """Response for setCookie command.""" 137 | 138 | result: SetCookieResultDict 139 | 140 | 141 | class StreamResourceContentResponse(TypedDict): 142 | """Response for streamResourceContent command.""" 143 | 144 | result: StreamResourceContentResultDict 145 | 146 | 147 | class TakeResponseBodyForInterceptionAsStreamResponse(TypedDict): 148 | """Response for takeResponseBodyForInterceptionAsStream command.""" 149 | 150 | result: TakeResponseBodyForInterceptionAsStreamResultDict 151 | 152 | 153 | class CanClearBrowserCacheResponse(TypedDict): 154 | """Response for canClearBrowserCache command.""" 155 | 156 | result: CanClearBrowserCacheResultDict 157 | 158 | 159 | class CanClearBrowserCookiesResponse(TypedDict): 160 | """Response for canClearBrowserCookies command.""" 161 | 162 | result: CanClearBrowserCookiesResultDict 163 | 164 | 165 | class CanEmulateNetworkConditionsResponse(TypedDict): 166 | """Response for canEmulateNetworkConditions command.""" 167 | 168 | result: CanEmulateNetworkConditionsResultDict 169 | 170 | 171 | class GetSecurityIsolationStatusResponse(TypedDict): 172 | """Response for getSecurityIsolationStatus command.""" 173 | 174 | result: GetSecurityIsolationStatusResultDict 175 | 176 | 177 | class LoadNetworkResourceResponse(TypedDict): 178 | """Response for loadNetworkResource command.""" 179 | 180 | result: LoadNetworkResourceResultDict 181 | -------------------------------------------------------------------------------- /pydoll/protocol/network/types.py: -------------------------------------------------------------------------------- 1 | from typing import NotRequired, TypedDict 2 | 3 | from pydoll.constants import ( 4 | ContentSecurityPolicySource, 5 | CookiePriority, 6 | CookieSameSite, 7 | CookieSourceScheme, 8 | MixedContentType, 9 | ReferrerPolicy, 10 | RefreshPolicy, 11 | ResourcePriority, 12 | TrustTokenOperationType, 13 | ) 14 | 15 | 16 | class SearchMatch(TypedDict): 17 | """Search match object.""" 18 | 19 | lineNumber: int 20 | lineContent: str 21 | 22 | 23 | class CookiePartitionKey(TypedDict): 24 | topLevelSite: str 25 | hasCrossSiteAncestor: bool 26 | 27 | 28 | class Cookie(TypedDict): 29 | """Cookie object.""" 30 | 31 | name: str 32 | value: str 33 | domain: str 34 | path: str 35 | expires: float 36 | size: int 37 | httpOnly: bool 38 | secure: bool 39 | session: bool 40 | sameSite: NotRequired[CookieSameSite] 41 | priority: NotRequired[CookiePriority] 42 | sameParty: NotRequired[bool] 43 | sourceScheme: NotRequired[CookieSourceScheme] 44 | sourcePort: NotRequired[int] 45 | partitionKey: NotRequired[CookiePartitionKey] 46 | partitionKeyOpaque: NotRequired[bool] 47 | 48 | 49 | class CrossOriginOpenerPolicyStatus(TypedDict): 50 | value: 'CrossOriginOpenerPolicyStatus' 51 | reportOnlyValue: 'CrossOriginOpenerPolicyStatus' 52 | reportingEndpoint: NotRequired[str] 53 | reportOnlyReportingEndpoint: NotRequired[str] 54 | 55 | 56 | class CrossOriginEmbedderPolicyStatus(TypedDict): 57 | value: 'CrossOriginEmbedderPolicyStatus' 58 | reportOnlyValue: 'CrossOriginEmbedderPolicyStatus' 59 | reportingEndpoint: NotRequired[str] 60 | reportOnlyReportingEndpoint: NotRequired[str] 61 | 62 | 63 | class ContentSecurityPolicyStatus(TypedDict): 64 | """Content security policy status object.""" 65 | 66 | effectiveDirective: str 67 | isEnforced: bool 68 | source: ContentSecurityPolicySource 69 | 70 | 71 | class SecurityIsolationStatus(TypedDict): 72 | """Security isolation status object.""" 73 | 74 | coop: NotRequired[CrossOriginOpenerPolicyStatus] 75 | coep: NotRequired[CrossOriginEmbedderPolicyStatus] 76 | csp: NotRequired[list[ContentSecurityPolicyStatus]] 77 | 78 | 79 | class LoadNetworkResourceOptions(TypedDict): 80 | """Load network resource options object.""" 81 | 82 | disableCache: NotRequired[bool] 83 | includeCredentials: NotRequired[bool] 84 | 85 | 86 | class CookieParam(TypedDict): 87 | name: str 88 | value: str 89 | url: NotRequired[str] 90 | domain: NotRequired[str] 91 | path: NotRequired[str] 92 | secure: NotRequired[bool] 93 | httpOnly: NotRequired[bool] 94 | sameSite: NotRequired[CookieSameSite] 95 | expires: NotRequired[float] 96 | priority: NotRequired[CookiePriority] 97 | sameParty: NotRequired[bool] 98 | sourceScheme: NotRequired[CookieSourceScheme] 99 | sourcePort: NotRequired[int] 100 | partitionKey: NotRequired[CookiePartitionKey] 101 | 102 | 103 | class UserAgentBrand(TypedDict): 104 | brand: str 105 | version: str 106 | 107 | 108 | class UserAgentMetadata(TypedDict): 109 | brands: NotRequired[list[UserAgentBrand]] 110 | fullVersionList: NotRequired[list[UserAgentBrand]] 111 | platform: str 112 | platformVersion: str 113 | architecture: str 114 | model: str 115 | mobile: bool 116 | bitness: NotRequired[str] 117 | wow64: NotRequired[bool] 118 | 119 | 120 | class PostDataEntry(TypedDict): 121 | bytes: NotRequired[str] 122 | 123 | 124 | class TrustTokenParams(TypedDict): 125 | operation: TrustTokenOperationType 126 | refreshPolicy: RefreshPolicy 127 | issuers: NotRequired[list[str]] 128 | 129 | 130 | class Request(TypedDict): 131 | url: str 132 | urlFragment: NotRequired[str] 133 | method: str 134 | headers: NotRequired[dict] 135 | hasPostData: NotRequired[bool] 136 | postDataEntries: NotRequired[list[PostDataEntry]] 137 | mixedContentType: NotRequired[MixedContentType] 138 | initialPriority: NotRequired[ResourcePriority] 139 | referrerPolicy: NotRequired[ReferrerPolicy] 140 | isLinkPreload: NotRequired[bool] 141 | trustTokenParams: NotRequired[TrustTokenParams] 142 | isSameSite: NotRequired[bool] 143 | 144 | 145 | class RequestPausedEventParams(TypedDict): 146 | requestId: str 147 | request: Request 148 | 149 | 150 | class RequestPausedEvent(TypedDict): 151 | method: str 152 | params: RequestPausedEventParams 153 | -------------------------------------------------------------------------------- /pydoll/protocol/page/__init__.py: -------------------------------------------------------------------------------- 1 | """Page domain implementation.""" 2 | -------------------------------------------------------------------------------- /pydoll/protocol/page/methods.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | 3 | 4 | class PageMethod(str, Enum): 5 | ADD_SCRIPT_TO_EVALUATE_ON_NEW_DOCUMENT = 'Page.addScriptToEvaluateOnNewDocument' 6 | BRING_TO_FRONT = 'Page.bringToFront' 7 | CAPTURE_SCREENSHOT = 'Page.captureScreenshot' 8 | CLOSE = 'Page.close' 9 | CREATE_ISOLATED_WORLD = 'Page.createIsolatedWorld' 10 | DISABLE = 'Page.disable' 11 | ENABLE = 'Page.enable' 12 | GET_APP_MANIFEST = 'Page.getAppManifest' 13 | GET_FRAME_TREE = 'Page.getFrameTree' 14 | GET_LAYOUT_METRICS = 'Page.getLayoutMetrics' 15 | GET_NAVIGATION_HISTORY = 'Page.getNavigationHistory' 16 | HANDLE_JAVASCRIPT_DIALOG = 'Page.handleJavaScriptDialog' 17 | NAVIGATE = 'Page.navigate' 18 | NAVIGATE_TO_HISTORY_ENTRY = 'Page.navigateToHistoryEntry' 19 | PRINT_TO_PDF = 'Page.printToPDF' 20 | RELOAD = 'Page.reload' 21 | REMOVE_SCRIPT_TO_EVALUATE_ON_NEW_DOCUMENT = 'Page.removeScriptToEvaluateOnNewDocument' 22 | RESET_NAVIGATION_HISTORY = 'Page.resetNavigationHistory' 23 | SET_BYPASS_CSP = 'Page.setBypassCSP' 24 | SET_DOCUMENT_CONTENT = 'Page.setDocumentContent' 25 | SET_INTERCEPT_FILE_CHOOSER_DIALOG = 'Page.setInterceptFileChooserDialog' 26 | SET_LIFECYCLE_EVENTS_ENABLED = 'Page.setLifecycleEventsEnabled' 27 | STOP_LOADING = 'Page.stopLoading' 28 | ADD_COMPILATION_CACHE = 'Page.addCompilationCache' 29 | CAPTURE_SNAPSHOT = 'Page.captureSnapshot' 30 | CLEAR_COMPILATION_CACHE = 'Page.clearCompilationCache' 31 | CRASH = 'Page.crash' 32 | GENERATE_TEST_REPORT = 'Page.generateTestReport' 33 | GET_AD_SCRIPT_ANCESTRY_IDS = 'Page.getAdScriptAncestryIds' 34 | GET_APP_ID = 'Page.getAppId' 35 | GET_INSTALLABILITY_ERRORS = 'Page.getInstallabilityErrors' 36 | GET_ORIGIN_TRIALS = 'Page.getOriginTrials' 37 | GET_PERMISSIONS_POLICY_STATE = 'Page.getPermissionsPolicyState' 38 | GET_RESOURCE_CONTENT = 'Page.getResourceContent' 39 | GET_RESOURCE_TREE = 'Page.getResourceTree' 40 | PRODUCE_COMPILATION_CACHE = 'Page.produceCompilationCache' 41 | SCREENCAST_FRAME_ACK = 'Page.screencastFrameAck' 42 | SEARCH_IN_RESOURCE = 'Page.searchInResource' 43 | SET_AD_BLOCKING_ENABLED = 'Page.setAdBlockingEnabled' 44 | SET_FONT_FAMILIES = 'Page.setFontFamilies' 45 | SET_FONT_SIZES = 'Page.setFontSizes' 46 | SET_PRERENDERING_ALLOWED = 'Page.setPrerenderingAllowed' 47 | SET_RPH_REGISTRATION_MODE = 'Page.setRPHRegistrationMode' 48 | SET_SPC_TRANSACTION_MODE = 'Page.setSPCTransactionMode' 49 | SET_WEB_LIFECYCLE_STATE = 'Page.setWebLifecycleState' 50 | START_SCREENCAST = 'Page.startScreencast' 51 | STOP_SCREENCAST = 'Page.stopScreencast' 52 | WAIT_FOR_DEBUGGER = 'Page.waitForDebugger' 53 | -------------------------------------------------------------------------------- /pydoll/protocol/runtime/__init__.py: -------------------------------------------------------------------------------- 1 | """Runtime domain implementation.""" 2 | -------------------------------------------------------------------------------- /pydoll/protocol/runtime/events.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | 3 | 4 | class RuntimeEvent(str, Enum): 5 | """ 6 | Events from the Runtime domain of the Chrome DevTools Protocol. 7 | 8 | This enumeration contains the names of Runtime-related events that can be 9 | received from the Chrome DevTools Protocol. These events provide information 10 | about JavaScript execution, console API calls, exceptions, and execution contexts. 11 | """ 12 | 13 | CONSOLE_API_CALLED = 'Runtime.consoleAPICalled' 14 | """ 15 | Issued when console API was called. 16 | 17 | Args: 18 | type (str): Type of the call. 19 | Allowed Values: log, debug, info, error, warning, dir, dirxml, table, trace, 20 | clear, startGroup, startGroupCollapsed, endGroup, assert, profile, profileEnd, 21 | count, timeEnd 22 | args (array[RemoteObject]): Call arguments. 23 | executionContextId (ExecutionContextId): Identifier of the context where the call was made. 24 | timestamp (Timestamp): Call timestamp. 25 | stackTrace (StackTrace): Stack trace captured when the call was made. The async stack 26 | chain is automatically reported for the following call types: assert, error, 27 | trace, warning. For other types the async call chain can be retrieved using 28 | Debugger.getStackTrace and stackTrace.parentId field. 29 | context (str): Console context descriptor for calls on non-default console context 30 | (not console.*): 'anonymous#unique-logger-id' for call on unnamed context, 31 | 'name#unique-logger-id' for call on named context. 32 | """ 33 | 34 | EXCEPTION_REVOKED = 'Runtime.exceptionRevoked' 35 | """ 36 | Issued when unhandled exception was revoked. 37 | 38 | Args: 39 | reason (str): Reason describing why exception was revoked. 40 | exceptionId (int): The id of revoked exception, as reported in exceptionThrown. 41 | """ 42 | 43 | EXCEPTION_THROWN = 'Runtime.exceptionThrown' 44 | """ 45 | Issued when exception was thrown and unhandled. 46 | 47 | Args: 48 | timestamp (Timestamp): Timestamp of the exception. 49 | exceptionDetails (ExceptionDetails): Details about the exception. 50 | """ 51 | 52 | EXECUTION_CONTEXT_CREATED = 'Runtime.executionContextCreated' 53 | """ 54 | Issued when new execution context is created. 55 | 56 | Args: 57 | context (ExecutionContextDescription): A newly created execution context. 58 | """ 59 | 60 | EXECUTION_CONTEXT_DESTROYED = 'Runtime.executionContextDestroyed' 61 | """ 62 | Issued when execution context is destroyed. 63 | 64 | Args: 65 | executionContextId (ExecutionContextId): Id of the destroyed context. 66 | executionContextUniqueId (str): Unique Id of the destroyed context. 67 | """ 68 | 69 | EXECUTION_CONTEXTS_CLEARED = 'Runtime.executionContextsCleared' 70 | """ 71 | Issued when all executionContexts were cleared in browser. 72 | """ 73 | 74 | INSPECT_REQUESTED = 'Runtime.inspectRequested' 75 | """ 76 | Issued when object should be inspected 77 | (for example, as a result of inspect() command line API call). 78 | 79 | Args: 80 | object (RemoteObject): Object to inspect. 81 | hints (object): Hints. 82 | executionContextId (ExecutionContextId): Identifier of the context where the call was made. 83 | """ 84 | 85 | BINDING_CALLED = 'Runtime.bindingCalled' 86 | """ 87 | Notification is issued every time when binding is called. 88 | 89 | Args: 90 | name (str): Name of the binding. 91 | payload (str): Payload of the binding. 92 | executionContextId (ExecutionContextId): Identifier of the context where the call was made. 93 | """ 94 | -------------------------------------------------------------------------------- /pydoll/protocol/runtime/methods.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | 3 | 4 | class RuntimeMethod(str, Enum): 5 | ADD_BINDING = 'Runtime.addBinding' 6 | AWAIT_PROMISE = 'Runtime.awaitPromise' 7 | CALL_FUNCTION_ON = 'Runtime.callFunctionOn' 8 | COMPILE_SCRIPT = 'Runtime.compileScript' 9 | DISABLE = 'Runtime.disable' 10 | DISCARD_CONSOLE_ENTRIES = 'Runtime.discardConsoleEntries' 11 | ENABLE = 'Runtime.enable' 12 | EVALUATE = 'Runtime.evaluate' 13 | GET_PROPERTIES = 'Runtime.getProperties' 14 | GLOBAL_LEXICAL_SCOPE_NAMES = 'Runtime.globalLexicalScopeNames' 15 | QUERY_OBJECTS = 'Runtime.queryObjects' 16 | RELEASE_OBJECT = 'Runtime.releaseObject' 17 | RELEASE_OBJECT_GROUP = 'Runtime.releaseObjectGroup' 18 | REMOVE_BINDING = 'Runtime.removeBinding' 19 | RUN_IF_WAITING_FOR_DEBUGGER = 'Runtime.runIfWaitingForDebugger' 20 | RUN_SCRIPT = 'Runtime.runScript' 21 | SET_ASYNC_CALL_STACK_DEPTH = 'Runtime.setAsyncCallStackDepth' 22 | GET_EXCEPTION_DETAILS = 'Runtime.getExceptionDetails' 23 | GET_HEAP_USAGE = 'Runtime.getHeapUsage' 24 | GET_ISOLATE_ID = 'Runtime.getIsolateId' 25 | SET_CUSTOM_OBJECT_FORMATTER_ENABLED = 'Runtime.setCustomObjectFormatterEnabled' 26 | SET_MAX_CALL_STACK_SIZE_TO_CAPTURE = 'Runtime.setMaxCallStackSizeToCapture' 27 | TERMINATE_EXECUTION = 'Runtime.terminateExecution' 28 | -------------------------------------------------------------------------------- /pydoll/protocol/runtime/params.py: -------------------------------------------------------------------------------- 1 | from typing import NotRequired 2 | 3 | from pydoll.protocol.base import CommandParams 4 | from pydoll.protocol.runtime.types import CallArgument, SerializationOptions 5 | 6 | 7 | class AddBindingParams(CommandParams): 8 | name: str 9 | executionContextName: NotRequired[str] 10 | 11 | 12 | class AwaitPromiseParams(CommandParams): 13 | promiseObjectId: str 14 | returnByValue: NotRequired[bool] 15 | generatePreview: NotRequired[bool] 16 | 17 | 18 | class CallFunctionOnParams(CommandParams): 19 | functionDeclaration: str 20 | objectId: NotRequired[str] 21 | arguments: NotRequired[list[CallArgument]] 22 | silent: NotRequired[bool] 23 | returnByValue: NotRequired[bool] 24 | generatePreview: NotRequired[bool] 25 | userGesture: NotRequired[bool] 26 | awaitPromise: NotRequired[bool] 27 | executionContextId: NotRequired[str] 28 | objectGroup: NotRequired[str] 29 | throwOnSideEffect: NotRequired[bool] 30 | uniqueContextId: NotRequired[str] 31 | serializationOptions: NotRequired[SerializationOptions] 32 | 33 | 34 | class CompileScriptParams(CommandParams): 35 | expression: str 36 | sourceURL: NotRequired[str] 37 | persistScript: NotRequired[bool] 38 | executionContextId: NotRequired[str] 39 | 40 | 41 | class EvaluateParams(CommandParams): 42 | expression: str 43 | objectGroup: NotRequired[str] 44 | includeCommandLineAPI: NotRequired[bool] 45 | silent: NotRequired[bool] 46 | contextId: NotRequired[str] 47 | returnByValue: NotRequired[bool] 48 | generatePreview: NotRequired[bool] 49 | userGesture: NotRequired[bool] 50 | awaitPromise: NotRequired[bool] 51 | throwOnSideEffect: NotRequired[bool] 52 | timeout: NotRequired[float] 53 | disableBreaks: NotRequired[bool] 54 | replMode: NotRequired[bool] 55 | allowUnsafeEvalBlockedByCSP: NotRequired[bool] 56 | uniqueContextId: NotRequired[str] 57 | serializationOptions: NotRequired[SerializationOptions] 58 | 59 | 60 | class GetPropertiesParams(CommandParams): 61 | objectId: str 62 | ownProperties: NotRequired[bool] 63 | accessorPropertiesOnly: NotRequired[bool] 64 | generatePreview: NotRequired[bool] 65 | nonIndexedPropertiesOnly: NotRequired[bool] 66 | 67 | 68 | class GlobalLexicalScopeNamesParams(CommandParams): 69 | executionContextId: NotRequired[str] 70 | 71 | 72 | class QueryObjectsParams(CommandParams): 73 | prototypeObjectId: str 74 | objectGroup: NotRequired[str] 75 | 76 | 77 | class ReleaseObjectParams(CommandParams): 78 | objectId: str 79 | 80 | 81 | class ReleaseObjectGroupParams(CommandParams): 82 | objectGroup: str 83 | 84 | 85 | class RemoveBindingParams(CommandParams): 86 | name: str 87 | 88 | 89 | class RunScriptParams(CommandParams): 90 | scriptId: str 91 | executionContextId: NotRequired[str] 92 | objectGroup: NotRequired[str] 93 | silent: NotRequired[bool] 94 | includeCommandLineAPI: NotRequired[bool] 95 | returnByValue: NotRequired[bool] 96 | generatePreview: NotRequired[bool] 97 | awaitPromise: NotRequired[bool] 98 | 99 | 100 | class SetAsyncCallStackDepthParams(CommandParams): 101 | maxDepth: int 102 | 103 | 104 | class GetExceptionDetailsParams(CommandParams): 105 | errorObjectId: str 106 | 107 | 108 | class SetCustomObjectFormatterEnabledParams(CommandParams): 109 | enabled: bool 110 | 111 | 112 | class SetMaxCallStackSizeToCaptureParams(CommandParams): 113 | size: int 114 | -------------------------------------------------------------------------------- /pydoll/protocol/runtime/responses.py: -------------------------------------------------------------------------------- 1 | from typing import NotRequired, TypedDict 2 | 3 | from pydoll.protocol.runtime.types import ( 4 | ExceptionDetails, 5 | InternalPropertyDescriptor, 6 | PrivatePropertyDescriptor, 7 | PropertyDescriptor, 8 | RemoteObject, 9 | ) 10 | 11 | 12 | class AwaitPromiseResultDict(TypedDict): 13 | result: RemoteObject 14 | exceptionDetails: NotRequired[ExceptionDetails] 15 | 16 | 17 | class CallFunctionOnResultDict(TypedDict): 18 | result: RemoteObject 19 | exceptionDetails: NotRequired[ExceptionDetails] 20 | 21 | 22 | class CompileScriptResultDict(TypedDict): 23 | scriptId: NotRequired[str] 24 | exceptionDetails: NotRequired[ExceptionDetails] 25 | 26 | 27 | class EvaluateResultDict(TypedDict): 28 | result: RemoteObject 29 | exceptionDetails: NotRequired[ExceptionDetails] 30 | 31 | 32 | class GetPropertiesResultDict(TypedDict): 33 | result: list[PropertyDescriptor] 34 | internalProperties: NotRequired[list[InternalPropertyDescriptor]] 35 | privateProperties: NotRequired[list[PrivatePropertyDescriptor]] 36 | exceptionDetails: NotRequired[ExceptionDetails] 37 | 38 | 39 | class GlobalLexicalScopeNamesResultDict(TypedDict): 40 | names: list[str] 41 | 42 | 43 | class QueryObjectsResultDict(TypedDict): 44 | objects: list[RemoteObject] 45 | 46 | 47 | class RunScriptResultDict(TypedDict): 48 | result: RemoteObject 49 | exceptionDetails: NotRequired[ExceptionDetails] 50 | 51 | 52 | class GetExceptionDetailsResultDict(TypedDict): 53 | exceptionDetails: ExceptionDetails 54 | 55 | 56 | class GetHeapUsageResultDict(TypedDict): 57 | usedSize: float 58 | totalSize: float 59 | embedderHeapUsedSize: float 60 | backingStorageSize: float 61 | 62 | 63 | class GetIsolateIdResultDict(TypedDict): 64 | id: str 65 | 66 | 67 | class AwaitPromiseResponse(TypedDict): 68 | result: AwaitPromiseResultDict 69 | 70 | 71 | class CallFunctionOnResponse(TypedDict): 72 | result: CallFunctionOnResultDict 73 | 74 | 75 | class CompileScriptResponse(TypedDict): 76 | result: CompileScriptResultDict 77 | 78 | 79 | class EvaluateResponse(TypedDict): 80 | result: EvaluateResultDict 81 | 82 | 83 | class GetPropertiesResponse(TypedDict): 84 | result: GetPropertiesResultDict 85 | 86 | 87 | class GlobalLexicalScopeNamesResponse(TypedDict): 88 | result: GlobalLexicalScopeNamesResultDict 89 | 90 | 91 | class QueryObjectsResponse(TypedDict): 92 | result: QueryObjectsResultDict 93 | 94 | 95 | class RunScriptResponse(TypedDict): 96 | result: RunScriptResultDict 97 | 98 | 99 | class GetHeapUsageResponse(TypedDict): 100 | result: GetHeapUsageResultDict 101 | 102 | 103 | class GetIsolateIdResponse(TypedDict): 104 | result: GetIsolateIdResultDict 105 | -------------------------------------------------------------------------------- /pydoll/protocol/runtime/types.py: -------------------------------------------------------------------------------- 1 | from typing import Any, NotRequired, TypedDict, Union 2 | 3 | from pydoll.constants import ( 4 | DeepSerializedValueType, 5 | ObjectPreviewSubtype, 6 | ObjectPreviewType, 7 | PropertyPreviewSubtype, 8 | PropertyPreviewType, 9 | RemoteObjectSubtype, 10 | RemoteObjectType, 11 | SerializationValue, 12 | UnserializableEnum, 13 | ) 14 | 15 | 16 | class PropertyPreview(TypedDict): 17 | name: str 18 | type: PropertyPreviewType 19 | value: NotRequired[str] 20 | valuePreview: NotRequired['ObjectPreview'] 21 | subtype: NotRequired[PropertyPreviewSubtype] 22 | 23 | 24 | class EntryPreview(TypedDict): 25 | key: 'ObjectPreview' 26 | value: 'ObjectPreview' 27 | 28 | 29 | class DeepSerializedValue(TypedDict): 30 | type: DeepSerializedValueType 31 | value: NotRequired[Any] 32 | objectId: NotRequired[str] 33 | weakLocalObjectReference: NotRequired[int] 34 | 35 | 36 | class ObjectPreview(TypedDict): 37 | type: ObjectPreviewType 38 | subtype: NotRequired[ObjectPreviewSubtype] 39 | description: NotRequired[str] 40 | overflow: bool 41 | properties: list[PropertyPreview] 42 | entries: NotRequired[list[EntryPreview]] 43 | 44 | 45 | class CustomPreview(TypedDict): 46 | header: str 47 | bodyGetterId: NotRequired[str] 48 | 49 | 50 | class RemoteObject(TypedDict): 51 | type: RemoteObjectType 52 | subtype: NotRequired[RemoteObjectSubtype] 53 | className: NotRequired[str] 54 | value: NotRequired[Any] 55 | unserializableValue: NotRequired[Union[UnserializableEnum, str]] 56 | description: NotRequired[str] 57 | deepSerializedValue: NotRequired[DeepSerializedValue] 58 | objectId: NotRequired[str] 59 | preview: NotRequired[ObjectPreview] 60 | customPreview: NotRequired[CustomPreview] 61 | 62 | 63 | class CallFrame(TypedDict): 64 | functionName: str 65 | scriptId: str 66 | url: str 67 | lineNumber: int 68 | columnNumber: int 69 | 70 | 71 | class StackTraceId(TypedDict): 72 | id: str 73 | debuggerId: str 74 | 75 | 76 | class StackTrace(TypedDict): 77 | description: NotRequired[str] 78 | callFrames: list[CallFrame] 79 | parent: NotRequired['StackTrace'] 80 | parentId: NotRequired[StackTraceId] 81 | 82 | 83 | class ExceptionDetails(TypedDict): 84 | exceptionId: int 85 | text: str 86 | lineNumber: int 87 | columnNumber: int 88 | scriptId: NotRequired[str] 89 | url: NotRequired[str] 90 | stackTrace: NotRequired[StackTrace] 91 | exception: NotRequired[RemoteObject] 92 | executionContextId: NotRequired[int] 93 | exceptionMetaData: NotRequired[dict] 94 | 95 | 96 | class PropertyDescriptor(TypedDict): 97 | name: str 98 | value: NotRequired[RemoteObject] 99 | writable: bool 100 | get: NotRequired[RemoteObject] 101 | set: NotRequired[RemoteObject] 102 | configurable: bool 103 | enumerable: bool 104 | wasThrown: NotRequired[bool] 105 | isOwn: NotRequired[bool] 106 | symbol: NotRequired[RemoteObject] 107 | 108 | 109 | class InternalPropertyDescriptor(TypedDict): 110 | name: str 111 | value: NotRequired[RemoteObject] 112 | 113 | 114 | class PrivatePropertyDescriptor(TypedDict): 115 | name: str 116 | value: NotRequired[RemoteObject] 117 | get: NotRequired[RemoteObject] 118 | set: NotRequired[RemoteObject] 119 | 120 | 121 | class CallArgument(TypedDict): 122 | value: NotRequired[str] 123 | unserializableValue: NotRequired[Union[UnserializableEnum, str]] 124 | objectId: NotRequired[str] 125 | 126 | 127 | class SerializationOptions(TypedDict): 128 | serialization: SerializationValue 129 | maxDepth: NotRequired[int] 130 | additionalParameters: NotRequired[dict] 131 | -------------------------------------------------------------------------------- /pydoll/protocol/storage/__init__.py: -------------------------------------------------------------------------------- 1 | """Storage domain implementation.""" 2 | -------------------------------------------------------------------------------- /pydoll/protocol/storage/methods.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | 3 | 4 | class StorageMethod(str, Enum): 5 | CLEAR_COOKIES = 'Storage.clearCookies' 6 | CLEAR_DATA_FOR_ORIGIN = 'Storage.clearDataForOrigin' 7 | CLEAR_DATA_FOR_STORAGE_KEY = 'Storage.clearDataForStorageKey' 8 | GET_COOKIES = 'Storage.getCookies' 9 | GET_STORAGE_KEY_FOR_FRAME = 'Storage.getStorageKeyForFrame' 10 | GET_USAGE_AND_QUOTA = 'Storage.getUsageAndQuota' 11 | SET_COOKIES = 'Storage.setCookies' 12 | SET_PROTECTED_AUDIENCE_K_ANONYMITY = 'Storage.setProtectedAudienceKAnonymity' 13 | TRACK_CACHE_STORAGE_FOR_ORIGIN = 'Storage.trackCacheStorageForOrigin' 14 | TRACK_CACHE_STORAGE_FOR_STORAGE_KEY = 'Storage.trackCacheStorageForStorageKey' 15 | TRACK_INDEXED_DB_FOR_ORIGIN = 'Storage.trackIndexedDBForOrigin' 16 | TRACK_INDEXED_DB_FOR_STORAGE_KEY = 'Storage.trackIndexedDBForStorageKey' 17 | UNTRACK_CACHE_STORAGE_FOR_ORIGIN = 'Storage.untrackCacheStorageForOrigin' 18 | UNTRACK_CACHE_STORAGE_FOR_STORAGE_KEY = 'Storage.untrackCacheStorageForStorageKey' 19 | UNTRACK_INDEXED_DB_FOR_ORIGIN = 'Storage.untrackIndexedDBForOrigin' 20 | UNTRACK_INDEXED_DB_FOR_STORAGE_KEY = 'Storage.untrackIndexedDBForStorageKey' 21 | CLEAR_SHARED_STORAGE_ENTRIES = 'Storage.clearSharedStorageEntries' 22 | CLEAR_TRUST_TOKENS = 'Storage.clearTrustTokens' 23 | DELETE_SHARED_STORAGE_ENTRY = 'Storage.deleteSharedStorageEntry' 24 | DELETE_STORAGE_BUCKET = 'Storage.deleteStorageBucket' 25 | GET_AFFECTED_URLS_FOR_THIRD_PARTY_COOKIE_METADATA = ( 26 | 'Storage.getAffectedUrlsForThirdPartyCookieMetadata' 27 | ) 28 | GET_INTEREST_GROUP_DETAILS = 'Storage.getInterestGroupDetails' 29 | GET_RELATED_WEBSITE_SETS = 'Storage.getRelatedWebsiteSets' 30 | GET_SHARED_STORAGE_ENTRIES = 'Storage.getSharedStorageEntries' 31 | GET_SHARED_STORAGE_METADATA = 'Storage.getSharedStorageMetadata' 32 | GET_TRUST_TOKENS = 'Storage.getTrustTokens' 33 | OVERRIDE_QUOTA_FOR_ORIGIN = 'Storage.overrideQuotaForOrigin' 34 | RESET_SHARED_STORAGE_BUDGET = 'Storage.resetSharedStorageBudget' 35 | RUN_BOUNCE_TRACKING_MITIGATIONS = 'Storage.runBounceTrackingMitigations' 36 | SEND_PENDING_ATTRIBUTION_REPORTS = 'Storage.sendPendingAttributionReports' 37 | SET_ATTRIBUTION_REPORTING_LOCAL_TESTING_MODE = 'Storage.setAttributionReportingLocalTestingMode' 38 | SET_ATTRIBUTION_REPORTING_TRACKING = 'Storage.setAttributionReportingTracking' 39 | SET_INTEREST_GROUP_AUCTION_TRACKING = 'Storage.setInterestGroupAuctionTracking' 40 | SET_INTEREST_GROUP_TRACKING = 'Storage.setInterestGroupTracking' 41 | SET_SHARED_STORAGE_ENTRY = 'Storage.setSharedStorageEntry' 42 | SET_SHARED_STORAGE_TRACKING = 'Storage.setSharedStorageTracking' 43 | SET_STORAGE_BUCKET_TRACKING = 'Storage.setStorageBucketTracking' 44 | -------------------------------------------------------------------------------- /pydoll/protocol/storage/params.py: -------------------------------------------------------------------------------- 1 | from typing import NotRequired 2 | 3 | from pydoll.protocol.base import CommandParams 4 | from pydoll.protocol.network.types import CookieParam 5 | from pydoll.protocol.storage.types import RelatedWebsiteSet, StorageBucket 6 | 7 | 8 | class ClearCookiesParams(CommandParams): 9 | browserContextId: NotRequired[str] 10 | 11 | 12 | class ClearDataForOriginParams(CommandParams): 13 | origin: str 14 | storageTypes: str 15 | 16 | 17 | class ClearDataForStorageKeyParams(CommandParams): 18 | storageKey: str 19 | storageTypes: str 20 | 21 | 22 | class GetCookiesParams(CommandParams): 23 | browserContextId: NotRequired[str] 24 | 25 | 26 | class GetStorageKeyForFrameParams(CommandParams): 27 | frameId: str 28 | 29 | 30 | class GetUsageAndQuotaParams(CommandParams): 31 | origin: str 32 | 33 | 34 | class SetCookiesParams(CommandParams): 35 | cookies: list[CookieParam] 36 | browserContextId: NotRequired[str] 37 | 38 | 39 | class SetProtectedAudienceKAnonymityParams(CommandParams): 40 | owner: str 41 | name: str 42 | hashes: list[str] 43 | 44 | 45 | class TrackCacheStorageForOriginParams(CommandParams): 46 | origin: str 47 | 48 | 49 | class TrackCacheStorageForStorageKeyParams(CommandParams): 50 | storageKey: str 51 | 52 | 53 | class TrackIndexedDBForOriginParams(CommandParams): 54 | origin: str 55 | 56 | 57 | class TrackIndexedDBForStorageKeyParams(CommandParams): 58 | storageKey: str 59 | 60 | 61 | class UntrackCacheStorageForOriginParams(CommandParams): 62 | origin: str 63 | 64 | 65 | class UntrackCacheStorageForStorageKeyParams(CommandParams): 66 | storageKey: str 67 | 68 | 69 | class UntrackIndexedDBForOriginParams(CommandParams): 70 | origin: str 71 | 72 | 73 | class UntrackIndexedDBForStorageKeyParams(CommandParams): 74 | storageKey: str 75 | 76 | 77 | class ClearSharedStorageEntriesParams(CommandParams): 78 | ownerOrigin: str 79 | 80 | 81 | class ClearTrustTokensParams(CommandParams): 82 | issuerOrigin: str 83 | 84 | 85 | class DeleteSharedStorageEntryParams(CommandParams): 86 | ownerOrigin: str 87 | key: str 88 | 89 | 90 | class DeleteStorageBucketParams(CommandParams): 91 | bucket: StorageBucket 92 | 93 | 94 | class GetAffectedUrlsForThirdPartyCookieMetadataParams(CommandParams): 95 | firstPartyUrl: str 96 | thirdPartyUrls: list[str] 97 | 98 | 99 | class GetInterestGroupDetailsParams(CommandParams): 100 | ownerOrigin: str 101 | name: str 102 | 103 | 104 | class GetRelatedWebsiteSetsParams(CommandParams): 105 | sets: list[RelatedWebsiteSet] 106 | 107 | 108 | class GetSharedStorageEntriesParams(CommandParams): 109 | ownerOrigin: str 110 | 111 | 112 | class GetSharedStorageMetadataParams(CommandParams): 113 | ownerOrigin: str 114 | 115 | 116 | class OverrideQuotaForOriginParams(CommandParams): 117 | origin: str 118 | quotaSize: NotRequired[float] 119 | 120 | 121 | class ResetSharedStorageBudgetParams(CommandParams): 122 | ownerOrigin: str 123 | 124 | 125 | class SetAttributionReportingLocalTestingModeParams(CommandParams): 126 | enable: bool 127 | 128 | 129 | class SetAttributionReportingTrackingParams(CommandParams): 130 | enable: bool 131 | 132 | 133 | class SetInterestGroupAuctionTrackingParams(CommandParams): 134 | enable: bool 135 | 136 | 137 | class SetInterestGroupTrackingParams(CommandParams): 138 | enable: bool 139 | 140 | 141 | class SetSharedStorageEntryParams(CommandParams): 142 | ownerOrigin: str 143 | key: str 144 | value: str 145 | ignoreIfPresent: NotRequired[bool] 146 | 147 | 148 | class SetSharedStorageTrackingParams(CommandParams): 149 | enable: bool 150 | 151 | 152 | class SetStorageBucketTrackingParams(CommandParams): 153 | storageKey: str 154 | enable: bool 155 | -------------------------------------------------------------------------------- /pydoll/protocol/storage/responses.py: -------------------------------------------------------------------------------- 1 | from typing import TypedDict 2 | 3 | from pydoll.protocol.network.types import Cookie 4 | from pydoll.protocol.storage.types import ( 5 | RelatedWebsiteSet, 6 | SharedStorageEntry, 7 | SharedStorageMetadata, 8 | TrustToken, 9 | UsageForType, 10 | ) 11 | 12 | 13 | class GetCookiesResultDict(TypedDict): 14 | cookies: list[Cookie] 15 | 16 | 17 | class GetStorageKeyForFrameResultDict(TypedDict): 18 | storageKey: str 19 | 20 | 21 | class GetUsageAndQuotaResultDict(TypedDict): 22 | usage: float 23 | quota: float 24 | overrideActive: bool 25 | usageBreakdown: list[UsageForType] 26 | 27 | 28 | class ClearTrustTokensResultDict(TypedDict): 29 | didDeleteTokens: bool 30 | 31 | 32 | class GetAffectedUrlsForThirdPartyCookieMetadataResultDict(TypedDict): 33 | matchedUrls: list[str] 34 | 35 | 36 | class GetInterestGroupDetailsResultDict(TypedDict): 37 | details: dict 38 | 39 | 40 | class GetRelatedWebsiteSetsResultDict(TypedDict): 41 | sets: list[RelatedWebsiteSet] 42 | 43 | 44 | class GetSharedStorageEntriesResultDict(TypedDict): 45 | entries: list[SharedStorageEntry] 46 | 47 | 48 | class GetSharedStorageMetadataResultDict(TypedDict): 49 | metadata: SharedStorageMetadata 50 | 51 | 52 | class GetTrustTokensResultDict(TypedDict): 53 | tokens: list[TrustToken] 54 | 55 | 56 | class RunBounceTrackingMitigationsResultDict(TypedDict): 57 | deletedSites: list[str] 58 | 59 | 60 | class SendPendingAttributionReportsResultDict(TypedDict): 61 | numSent: int 62 | 63 | 64 | class GetCookiesResponse(TypedDict): 65 | result: GetCookiesResultDict 66 | 67 | 68 | class GetStorageKeyForFrameResponse(TypedDict): 69 | result: GetStorageKeyForFrameResultDict 70 | 71 | 72 | class GetUsageAndQuotaResponse(TypedDict): 73 | result: GetUsageAndQuotaResultDict 74 | 75 | 76 | class ClearTrustTokensResponse(TypedDict): 77 | result: ClearTrustTokensResultDict 78 | 79 | 80 | class GetAffectedUrlsForThirdPartyCookieMetadataResponse(TypedDict): 81 | result: GetAffectedUrlsForThirdPartyCookieMetadataResultDict 82 | 83 | 84 | class GetInterestGroupDetailsResponse(TypedDict): 85 | result: GetInterestGroupDetailsResultDict 86 | 87 | 88 | class GetRelatedWebsiteSetsResponse(TypedDict): 89 | result: GetRelatedWebsiteSetsResultDict 90 | 91 | 92 | class GetSharedStorageEntriesResponse(TypedDict): 93 | result: GetSharedStorageEntriesResultDict 94 | 95 | 96 | class GetSharedStorageMetadataResponse(TypedDict): 97 | result: GetSharedStorageMetadataResultDict 98 | 99 | 100 | class GetTrustTokensResponse(TypedDict): 101 | result: GetTrustTokensResultDict 102 | 103 | 104 | class RunBounceTrackingMitigationsResponse(TypedDict): 105 | result: RunBounceTrackingMitigationsResultDict 106 | 107 | 108 | class SendPendingAttributionReportsResponse(TypedDict): 109 | result: SendPendingAttributionReportsResultDict 110 | -------------------------------------------------------------------------------- /pydoll/protocol/storage/types.py: -------------------------------------------------------------------------------- 1 | from typing import NotRequired, TypedDict 2 | 3 | from pydoll.constants import StorageType 4 | 5 | 6 | class StorageBucket(TypedDict): 7 | storageKey: str 8 | name: NotRequired[str] 9 | 10 | 11 | class RelatedWebsiteSet(TypedDict): 12 | primarySites: list[str] 13 | associatedSites: list[str] 14 | serviceSites: list[str] 15 | 16 | 17 | class UsageForType(TypedDict): 18 | storageType: StorageType 19 | usage: float 20 | 21 | 22 | class SharedStorageEntry(TypedDict): 23 | key: str 24 | value: str 25 | 26 | 27 | class SharedStorageMetadata(TypedDict): 28 | creationTime: float 29 | length: int 30 | remainingBudget: float 31 | bytesUsed: int 32 | 33 | 34 | class TrustToken(TypedDict): 35 | issuerOrigin: str 36 | count: float 37 | -------------------------------------------------------------------------------- /pydoll/protocol/target/__init__.py: -------------------------------------------------------------------------------- 1 | """Target domain implementation.""" 2 | -------------------------------------------------------------------------------- /pydoll/protocol/target/events.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | 3 | 4 | class TargetEvent(str, Enum): 5 | """ 6 | Events from the Target domain of the Chrome DevTools Protocol. 7 | 8 | This enumeration contains the names of Target-related events that can be 9 | received from the Chrome DevTools Protocol. These events provide information 10 | about target creation, destruction, and communication between targets. 11 | """ 12 | 13 | RECEIVED_MESSAGE_FROM_TARGET = 'Target.receivedMessageFromTarget' 14 | """ 15 | Notifies about a new protocol message received from the session 16 | (as reported in attachedToTarget event). 17 | 18 | Args: 19 | sessionId (SessionID): Identifier of a session which sends a message. 20 | message (str): The message content. 21 | targetId (TargetID): Deprecated. 22 | """ 23 | 24 | TARGET_CRASHED = 'Target.targetCrashed' 25 | """ 26 | Issued when a target has crashed. 27 | 28 | Args: 29 | targetId (TargetID): Identifier of the crashed target. 30 | status (str): Termination status type. 31 | errorCode (int): Termination error code. 32 | """ 33 | 34 | TARGET_CREATED = 'Target.targetCreated' 35 | """ 36 | Issued when a possible inspection target is created. 37 | 38 | Args: 39 | targetInfo (TargetInfo): Information about the created target. 40 | """ 41 | 42 | TARGET_DESTROYED = 'Target.targetDestroyed' 43 | """ 44 | Issued when a target is destroyed. 45 | 46 | Args: 47 | targetId (TargetID): Identifier of the destroyed target. 48 | """ 49 | 50 | TARGET_INFO_CHANGED = 'Target.targetInfoChanged' 51 | """ 52 | Issued when some information about a target has changed. 53 | This only happens between targetCreated and targetDestroyed. 54 | 55 | Args: 56 | targetInfo (TargetInfo): Updated information about the target. 57 | """ 58 | 59 | ATTACHED_TO_TARGET = 'Target.attachedToTarget' 60 | """ 61 | Issued when attached to target because of auto-attach or attachToTarget command. 62 | 63 | Args: 64 | sessionId (SessionID): Identifier assigned to the session used to send/receive messages. 65 | targetInfo (TargetInfo): Information about the target. 66 | waitingForDebugger (bool): Whether the target is waiting for debugger to attach. 67 | """ 68 | 69 | DETACHED_FROM_TARGET = 'Target.detachedFromTarget' 70 | """ 71 | Issued when detached from target for any reason (including detachFromTarget command). 72 | Can be issued multiple times per target if multiple sessions have been attached to it. 73 | 74 | Args: 75 | sessionId (SessionID): Detached session identifier. 76 | targetId (TargetID): Deprecated. 77 | """ 78 | -------------------------------------------------------------------------------- /pydoll/protocol/target/methods.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | 3 | 4 | class TargetMethod(str, Enum): 5 | ACTIVATE_TARGET = 'Target.activateTarget' 6 | ATTACH_TO_TARGET = 'Target.attachToTarget' 7 | CLOSE_TARGET = 'Target.closeTarget' 8 | CREATE_BROWSER_CONTEXT = 'Target.createBrowserContext' 9 | CREATE_TARGET = 'Target.createTarget' 10 | DETACH_FROM_TARGET = 'Target.detachFromTarget' 11 | DISPOSE_BROWSER_CONTEXT = 'Target.disposeBrowserContext' 12 | GET_BROWSER_CONTEXTS = 'Target.getBrowserContexts' 13 | GET_TARGETS = 'Target.getTargets' 14 | SET_AUTO_ATTACH = 'Target.setAutoAttach' 15 | SET_DISCOVER_TARGETS = 'Target.setDiscoverTargets' 16 | # Métodos experimentais 17 | ATTACH_TO_BROWSER_TARGET = 'Target.attachToBrowserTarget' 18 | AUTO_ATTACH_RELATED = 'Target.autoAttachRelated' 19 | EXPOSE_DEV_TOOLS_PROTOCOL = 'Target.exposeDevToolsProtocol' 20 | GET_TARGET_INFO = 'Target.getTargetInfo' 21 | SET_REMOTE_LOCATIONS = 'Target.setRemoteLocations' 22 | -------------------------------------------------------------------------------- /pydoll/protocol/target/params.py: -------------------------------------------------------------------------------- 1 | from typing import NotRequired 2 | 3 | from pydoll.constants import WindowState 4 | from pydoll.protocol.base import CommandParams 5 | from pydoll.protocol.target.types import RemoteLocation 6 | 7 | 8 | class ActivateTargetParams(CommandParams): 9 | targetId: str 10 | 11 | 12 | class AttachToTargetParams(CommandParams): 13 | targetId: str 14 | flatten: NotRequired[bool] 15 | 16 | 17 | class CloseTargetParams(CommandParams): 18 | targetId: str 19 | 20 | 21 | class CreateBrowserContextParams(CommandParams): 22 | disposeOnDetach: NotRequired[bool] 23 | proxyServer: NotRequired[str] 24 | proxyBypassList: NotRequired[str] 25 | originsWithUniversalNetworkAccess: NotRequired[list[str]] 26 | 27 | 28 | class CreateTargetParams(CommandParams): 29 | url: str 30 | left: NotRequired[int] 31 | top: NotRequired[int] 32 | width: NotRequired[int] 33 | height: NotRequired[int] 34 | windowState: NotRequired[WindowState] 35 | browserContextId: NotRequired[str] 36 | enableBeginFrameControl: NotRequired[bool] 37 | newWindow: NotRequired[bool] 38 | background: NotRequired[bool] 39 | forTab: NotRequired[bool] 40 | hidden: NotRequired[bool] 41 | 42 | 43 | class DetachFromTargetParams(CommandParams): 44 | sessionId: NotRequired[str] 45 | 46 | 47 | class DisposeBrowserContextParams(CommandParams): 48 | browserContextId: str 49 | 50 | 51 | class GetTargetsParams(CommandParams): 52 | filter: NotRequired[list] 53 | 54 | 55 | class SetAutoAttachParams(CommandParams): 56 | autoAttach: bool 57 | waitForDebuggerOnStart: NotRequired[bool] 58 | flatten: NotRequired[bool] 59 | filter: NotRequired[list] 60 | 61 | 62 | class SetDiscoverTargetsParams(CommandParams): 63 | discover: bool 64 | filter: NotRequired[list] 65 | 66 | 67 | class AttachToBrowserTargetParams(CommandParams): 68 | sessionId: str 69 | 70 | 71 | class AutoAttachRelatedParams(CommandParams): 72 | targetId: str 73 | waitForDebuggerOnStart: NotRequired[bool] 74 | filter: NotRequired[list] 75 | 76 | 77 | class ExposeDevToolsProtocolParams(CommandParams): 78 | targetId: str 79 | bindingName: NotRequired[str] 80 | inherintPermissions: NotRequired[bool] 81 | 82 | 83 | class GetTargetInfoParams(CommandParams): 84 | targetId: str 85 | 86 | 87 | class SetRemoteLocationsParams(CommandParams): 88 | locations: list[RemoteLocation] 89 | -------------------------------------------------------------------------------- /pydoll/protocol/target/responses.py: -------------------------------------------------------------------------------- 1 | from typing import TypedDict 2 | 3 | from pydoll.protocol.target.types import TargetInfo 4 | 5 | 6 | class AttachToTargetResultDict(TypedDict): 7 | sessionId: str 8 | 9 | 10 | class CreateBrowserContextResultDict(TypedDict): 11 | browserContextId: str 12 | 13 | 14 | class CreateTargetResultDict(TypedDict): 15 | targetId: str 16 | 17 | 18 | class GetBrowserContextsResultDict(TypedDict): 19 | browserContextIds: list[str] 20 | 21 | 22 | class GetTargetsResultDict(TypedDict): 23 | targetInfos: list[TargetInfo] 24 | 25 | 26 | class AttachToBrowserTargetResultDict(TypedDict): 27 | sessionId: str 28 | 29 | 30 | class GetTargetInfoResultDict(TypedDict): 31 | targetInfo: TargetInfo 32 | 33 | 34 | class AttachToTargetResponse(TypedDict): 35 | result: AttachToTargetResultDict 36 | 37 | 38 | class CreateBrowserContextResponse(TypedDict): 39 | result: CreateBrowserContextResultDict 40 | 41 | 42 | class CreateTargetResponse(TypedDict): 43 | result: CreateTargetResultDict 44 | 45 | 46 | class GetBrowserContextsResponse(TypedDict): 47 | result: GetBrowserContextsResultDict 48 | 49 | 50 | class GetTargetsResponse(TypedDict): 51 | result: GetTargetsResultDict 52 | 53 | 54 | class AttachToBrowserTargetResponse(TypedDict): 55 | result: AttachToBrowserTargetResultDict 56 | 57 | 58 | class GetTargetInfoResponse(TypedDict): 59 | result: GetTargetInfoResultDict 60 | -------------------------------------------------------------------------------- /pydoll/protocol/target/types.py: -------------------------------------------------------------------------------- 1 | from typing import NotRequired, TypedDict 2 | 3 | 4 | class RemoteLocation(TypedDict): 5 | host: str 6 | port: int 7 | 8 | 9 | class TargetInfo(TypedDict): 10 | targetId: str 11 | type: str 12 | title: str 13 | url: str 14 | attached: bool 15 | openerId: NotRequired[str] 16 | canAccessOpener: bool 17 | openerFrameId: NotRequired[str] 18 | browserContextId: NotRequired[str] 19 | subtype: NotRequired[str] 20 | -------------------------------------------------------------------------------- /pydoll/utils.py: -------------------------------------------------------------------------------- 1 | import base64 2 | import logging 3 | import os 4 | 5 | import aiohttp 6 | 7 | from pydoll.exceptions import InvalidBrowserPath, InvalidResponse, NetworkError 8 | 9 | logger = logging.getLogger(__name__) 10 | 11 | 12 | def decode_base64_to_bytes(image: str) -> bytes: 13 | """ 14 | Decodes a base64 image string to bytes. 15 | 16 | Args: 17 | image (str): The base64 image string to decode. 18 | 19 | Returns: 20 | bytes: The decoded image as bytes. 21 | """ 22 | return base64.b64decode(image.encode('utf-8')) 23 | 24 | 25 | async def get_browser_ws_address(port: int) -> str: 26 | """ 27 | Fetches the WebSocket address for the browser instance. 28 | 29 | Returns: 30 | str: The WebSocket address for the browser. 31 | 32 | Raises: 33 | NetworkError: If the address cannot be fetched due to network errors 34 | or missing data. 35 | InvalidResponse: If the response is not valid JSON. 36 | """ 37 | try: 38 | async with aiohttp.ClientSession() as session: 39 | async with session.get(f'http://localhost:{port}/json/version') as response: 40 | response.raise_for_status() 41 | data = await response.json() 42 | return data['webSocketDebuggerUrl'] 43 | 44 | except aiohttp.ClientError as e: 45 | raise NetworkError(f'Failed to get browser ws address: {e}') 46 | 47 | except KeyError as e: 48 | raise InvalidResponse(f'Failed to get browser ws address: {e}') 49 | 50 | 51 | def validate_browser_paths(paths: list[str]) -> str: 52 | """ 53 | Validates potential browser executable paths and returns the first valid one. 54 | 55 | Checks a list of possible browser binary locations to find an existing, 56 | executable browser. This is used by browser-specific subclasses to locate 57 | the browser executable when no explicit binary path is provided. 58 | 59 | Args: 60 | paths: List of potential file paths to check for the browser executable. 61 | These should be absolute paths appropriate for the current OS. 62 | 63 | Returns: 64 | str: The first valid browser executable path found. 65 | 66 | Raises: 67 | InvalidBrowserPath: If the browser executable is not found at the path. 68 | """ 69 | for path in paths: 70 | if os.path.exists(path) and os.access(path, os.X_OK): 71 | return path 72 | raise InvalidBrowserPath(f'No valid browser path found in: {paths}') 73 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "pydoll-python" 3 | version = "2.0.1" 4 | description = "" 5 | authors = ["Thalison Fernandes "] 6 | readme = "README.md" 7 | packages = [ 8 | {include = "pydoll"} 9 | ] 10 | 11 | [tool.poetry.dependencies] 12 | python = "^3.10" 13 | websockets = "^13.1" 14 | aiohttp = "^3.9.5" 15 | aiofiles = "^23.2.1" 16 | bs4 = "^0.0.2" 17 | mkdocstrings = "^0.29.1" 18 | 19 | 20 | [tool.poetry.group.dev.dependencies] 21 | ruff = "^0.7.1" 22 | pytest = "^8.3.3" 23 | taskipy = "^1.14.0" 24 | pytest-asyncio = "^0.24.0" 25 | pytest-cov = "^6.0.0" 26 | aioresponses = "^0.7.7" 27 | mkdocs = "^1.6.1" 28 | mkdocs-material = "^9.6.11" 29 | pymdown-extensions = "^10.14.3" 30 | mkdocstrings = {extras = ["python"], version = "^0.29.1"} 31 | griffe-typingdoc = "^0.2.8" 32 | 33 | [build-system] 34 | requires = ["poetry-core"] 35 | build-backend = "poetry.core.masonry.api" 36 | 37 | [tool.ruff] 38 | line-length = 100 39 | target-version = "py310" 40 | 41 | 42 | [tool.ruff.lint] 43 | preview = true 44 | select = ['I', 'F', 'E', 'W', 'PL', 'PT'] 45 | exclude = ['tests', 'tests/*'] 46 | 47 | [tool.ruff.format] 48 | preview = true 49 | quote-style = 'single' 50 | docstring-code-format = true 51 | docstring-code-line-length = 79 52 | exclude = ['tests', 'tests/*'] 53 | 54 | [tool.pytest.ini_options] 55 | pythonpath = "." 56 | addopts = '-p no:warnings' 57 | 58 | [tool.taskipy.tasks] 59 | lint = 'ruff check .; ruff check . --diff' 60 | format = 'ruff check . --fix; ruff format .' 61 | test = 'pytest -s -x --cov=pydoll -vv' 62 | post_test = 'coverage html' 63 | 64 | [tool.mypy] 65 | exclude = [ 66 | "tests/", 67 | ] -------------------------------------------------------------------------------- /tests/test_browser/test_browser_options.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from pydoll.browser.options import ChromiumOptions as Options 4 | 5 | from pydoll.exceptions import ArgumentAlreadyExistsInOptions 6 | 7 | def test_initial_arguments(): 8 | options = Options() 9 | assert options.arguments == [] 10 | 11 | 12 | def test_initial_binary_location(): 13 | options = Options() 14 | assert not options.binary_location 15 | 16 | 17 | def test_set_binary_location(): 18 | options = Options() 19 | options.binary_location = '/path/to/browser' 20 | assert options.binary_location == '/path/to/browser' 21 | 22 | 23 | def test_add_argument(): 24 | options = Options() 25 | options.add_argument('--headless') 26 | assert options.arguments == ['--headless'] 27 | 28 | 29 | def test_add_duplicate_argument(): 30 | options = Options() 31 | options.add_argument('--headless') 32 | with pytest.raises( 33 | ArgumentAlreadyExistsInOptions, match='Argument already exists: --headless' 34 | ): 35 | options.add_argument('--headless') 36 | 37 | 38 | def test_add_multiple_arguments(): 39 | options = Options() 40 | options.add_argument('--headless') 41 | options.add_argument('--no-sandbox') 42 | assert options.arguments == ['--headless', '--no-sandbox'] 43 | --------------------------------------------------------------------------------