1212 TypeAlias ,
1313 Union ,
1414 cast ,
15+ overload ,
1516)
1617
1718import aiofiles
3233 IFrameNotFound ,
3334 InvalidFileExtension ,
3435 InvalidIFrame ,
36+ InvalidScriptWithElement ,
3537 NetworkEventsNotEnabled ,
3638 NoDialogPresent ,
3739 NotAnIFrame ,
4345from pydoll .protocol .network .types import Cookie , CookieParam , NetworkLog
4446from pydoll .protocol .page .events import PageEvent
4547from pydoll .protocol .page .responses import CaptureScreenshotResponse , PrintToPDFResponse
46- from pydoll .protocol .runtime .responses import EvaluateResponse
48+ from pydoll .protocol .runtime .responses import CallFunctionOnResponse , EvaluateResponse
4749from pydoll .protocol .storage .responses import GetCookiesResponse
48- from pydoll .utils import decode_base64_to_bytes
50+ from pydoll .utils import (
51+ decode_base64_to_bytes ,
52+ has_return_outside_function ,
53+ is_script_already_function ,
54+ )
4955
5056if TYPE_CHECKING :
5157 from pydoll .browser .chromium .base import Browser
@@ -554,7 +560,15 @@ async def handle_dialog(self, accept: bool, prompt_text: Optional[str] = None):
554560 PageCommands .handle_javascript_dialog (accept = accept , prompt_text = prompt_text )
555561 )
556562
557- async def execute_script (self , script : str , element : Optional [WebElement ] = None ):
563+ @overload
564+ async def execute_script (self , script : str ) -> EvaluateResponse : ...
565+
566+ @overload
567+ async def execute_script (self , script : str , element : WebElement ) -> CallFunctionOnResponse : ...
568+
569+ async def execute_script (
570+ self , script : str , element : Optional [WebElement ] = None
571+ ) -> Union [EvaluateResponse , CallFunctionOnResponse ]:
558572 """
559573 Execute JavaScript in page context.
560574
@@ -565,17 +579,17 @@ async def execute_script(self, script: str, element: Optional[WebElement] = None
565579 Examples:
566580 await page.execute_script('argument.click()', element)
567581 await page.execute_script('argument.value = "Hello"', element)
582+
583+ Raises:
584+ InvalidScriptWithElement: If script contains 'argument' but no element is provided.
568585 """
586+ if 'argument' in script and element is None :
587+ raise InvalidScriptWithElement ('Script contains "argument" but no element was provided' )
588+
569589 if element :
570- script = script .replace ('argument' , 'this' )
571- script = f'function(){{ { script } }}'
572- object_id = element ._object_id
573- command = RuntimeCommands .call_function_on (
574- object_id = object_id , function_declaration = script , return_by_value = True
575- )
576- else :
577- command = RuntimeCommands .evaluate (expression = script )
578- return await self ._execute_command (command )
590+ return await self ._execute_script_with_element (script , element )
591+
592+ return await self ._execute_script_without_element (script )
579593
580594 @asynccontextmanager
581595 async def expect_file_chooser (
@@ -693,6 +707,46 @@ async def callback_wrapper(event):
693707 event_name , function_to_register , temporary
694708 )
695709
710+ async def _execute_script_with_element (self , script : str , element : WebElement ):
711+ """
712+ Execute script with element context.
713+
714+ Args:
715+ script: JavaScript code to execute.
716+ element: Element context (use 'argument' in script to reference).
717+
718+ Returns:
719+ The result of the script execution.
720+ """
721+ if 'argument' not in script :
722+ raise InvalidScriptWithElement ('Script does not contain "argument"' )
723+
724+ script = script .replace ('argument' , 'this' )
725+
726+ if not is_script_already_function (script ):
727+ script = f'function(){{ { script } }}'
728+
729+ command = RuntimeCommands .call_function_on (
730+ object_id = element ._object_id , function_declaration = script , return_by_value = True
731+ )
732+ return await self ._execute_command (command )
733+
734+ async def _execute_script_without_element (self , script : str ):
735+ """
736+ Execute script without element context.
737+
738+ Args:
739+ script: JavaScript code to execute.
740+
741+ Returns:
742+ The result of the script execution.
743+ """
744+ if has_return_outside_function (script ):
745+ script = f'(function(){{ { script } }})()'
746+
747+ command = RuntimeCommands .evaluate (expression = script )
748+ return await self ._execute_command (command )
749+
696750 async def _refresh_if_url_not_changed (self , url : str ) -> bool :
697751 """Refresh page if URL hasn't changed."""
698752 current_url = await self .current_url
0 commit comments