Danp2 / au3WebDriver

Web Driver UDF for AutoIt
MIT License
107 stars 23 forks source link

List all frames #354

Closed mlipok closed 2 years ago

mlipok commented 2 years ago

Feature request

Is your feature request related to a problem? Please describe

Very often, automation concerns websites with frames. Mostly novice users (AutoIt novice scripts) do not understand them. Even for the more advanced ones, a diagnostic tool would be useful.

Describe the solution you'd like

I would like to add new feature to _WD_FrameEnter - list all frames.

_WD_FrameEnter($sSession, Default)

which should use my own JavaScript:

FramesEnumerator()

function FramesEnumerator() {
    console.log('===PROCESSING START===');
  var result = GetFrames(window.top, '', -1, 'null', 0);
    console.log('===PROCESSING END===');
  console.log('RESULT:');
  console.log(result);
}

function GetFrames(window_current, frames_html, level_current, level_string, counter) {
  counter = counter + 1
  console.log('======== STEP = ' + counter)
  console.log('currently processing level = ' + level_current)
  console.log('currently processing "window_current":');
  console.log(window_current.location.href);

  const windows_len = window_current.length;
  const windows_max = windows_len -1
  console.log('windows_len = ' + windows_len);
  if (frames_html === '') {
      frames_html = frames_html + level_string + '|' + window_current.location.href + '|' + '\n'
  };

  for (let i = 0; i < windows_len; i++) {
      frames_html = frames_html + level_string + '/' + i + '|' + window_current[i].location.href + '|' + window_current.document.querySelectorAll('iframe')[i].outerHTML + '\n'
      console.log('processing sub frames =   "' + level_string + '/' + i + '"  :');
    frames_html = GetFrames(window_current[i], frames_html, i, level_string + '/' + i, counter);
  }
  return frames_html
}

Describe alternatives you've considered

Manually check the webpage

Additional context

Example: using for testing this website: https://www.w3schools.com/

I get

null|https://www.w3schools.com/|
null/0|https://www.w3schools.com/howto/tryhow_js_slideshow_ifr.htm|<iframe src="/howto/tryhow_js_slideshow_ifr.htm" id="howto_iframe"></iframe>
null/1|about:blank|<iframe name="__tcfapiLocator" style="display: none;"></iframe>
null/2|about:blank|<iframe name="__uspapiLocator" style="display: none;"></iframe>

and of course it can be easily transform to AutoIt array:

frame position / Identifier | HREF |  element -- | -- | -- null | https://www.w3schools.com/ |   null/0 | https://www.w3schools.com/howto/tryhow_js_slideshow_ifr.htm | null/1 | about:blank | null/2 | about:blank |

Question

Do you agree to add such feature to UDF ?

Example usage:

Local $aFrames = _WD_FrameEnter($sSession, Default)
_ArrayDisplay($aFrames)
Danp2 commented 2 years ago
mlipok commented 2 years ago

of course sometimes it will fire: Uncaught DOMException: Permission denied to access property "document" on cross-origin object

for example here: https://www.w3schools.com/html/tryit.asp?filename=tryhtml_iframe_height_width

But I think that I can do this in another aproach.

mlipok commented 2 years ago
  • I don't agree with adding this functionality to _WD_FrameEnter. It would need to become a new, standalone function (ie: _WD_FrameList)

Ok.

  • I'll have to try out your code on some different sites to better understand how nested frames are handled / represented

Waiting for your test results.

Danp2 commented 2 years ago

@mlipok Please post your Autoit code that implements the above so that I can perform some tests.

mlipok commented 2 years ago

As for now I only have the JS code which I already posted in opening post. Just try it on different websites.

mlipok commented 2 years ago

If the JS code will be it will be inspiring (for you), I will try to translate it more into AutoIt, because it will be required to go to a given Frame and check its data from the frame level.

Because using only JS we will hit this following restriction: Uncaught DOMException: Permission denied to access property "document" on cross-origin object

Side note:

I studied JS and this was one of my test scripts that I had on my study list. For this reason, I only have JS and not AutoIt code. But anyway, I want to move it to AutoIt just waiting for your recommendation.

mlipok commented 2 years ago

so I had such code:

Func _WD_FrameList($sSession)
    Local $sResult = __WD_FrameList_Internal($sSession, 'null', '')
    Return SetError(@error, @extended, $sResult)
EndFunc   ;==>_WD_FrameList

Func __WD_FrameList_Internal($sSession, $s_level_string, $s_Path)
    #WARRNING some kind of remark in the header should be aded first proposal below:
    ; changing frames may affect your further coding by changing contex in browser to different frames
    Local Const $sFuncName = "_WD_FrameList"
    Local $iErr = $_WD_ERROR_Success
    Local $sResult = '', $s_URL = '', $sMessage = ''
    If Not @compiled Then ConsoleWrite('TESTING=' & $s_level_string & '|' & $s_Path & @CRLF)

    Local $a_Level = StringSplit($s_level_string, '/')
    For $i =1 To $a_Level[0]
        If $a_Level[$i] = 'null' Then
            _WD_FrameEnter($sSession, Null)
        Else
            _WD_FrameEnter($sSession, Int($a_Level[$i]))
        EndIf
        If @error Then ExitLoop
    Next

    If @error Then
        $sMessage = 'Error occured on "' & $s_level_string & '" level when trying to entering frame'
    Else
        $s_URL = _WD_ExecuteScript($sSession, "return window.location.href", Default, Default, $_WD_JSON_Value)
        If @error Then
            $sMessage = 'Error occured on "' & $s_level_string & '" level when checking URL'
        Else
            $sResult = $s_level_string & ' | ' & $s_URL & ' | ' & $s_Path & @CRLF
        EndIf
    EndIf
    $iErr = @error
    If Not @error Then
        Local $iFrameCount = _WD_GetFrameCount($sSession)
        $iErr = @error
        If @error Then
            $sMessage = 'Error occured on "' & $s_level_string & '" level when trying to check frames count'
        Else
            For $iFrame = 0 To $iFrameCount - 1
                $s_Path = _WD_ExecuteScript($sSession, "return document.querySelectorAll('iframe')[" & $iFrame & "].outerHTML", Default, Default, $_WD_JSON_Value)
                $iErr = @error
                If @error Then
                    $sMessage = 'Error occured on "' & $s_level_string & '" level when trying to check atributes child frames #' & $iFrame
                Else
                    $sResult &= __WD_FrameList_Internal($sSession, $s_level_string & '/' & $iFrame, $s_Path)
                    $iErr = @error
                EndIf
                If @error Then ExitLoop
            Next
        EndIf
    EndIf

    Return SetError(__WD_Error($sFuncName, $iErr, $sMessage), 0, $sResult)
EndFunc   ;==>__WD_FrameList_Internal

test it with:

Func UserTesting() ; here you can replace the code to test your stuff before you ask on the forum

    _WD_Navigate($sSession, 'https://www.w3schools.com')
    _WD_LoadWait($sSession)
    Local $sFrameList = _WD_FrameList($sSession)
    ConsoleWrite($sFrameList & @CRLF)
    Return
EndFunc

The only difference is that with AutoIt I get:

null | https://www.w3schools.com/ | 
null/0 | https://www.w3schools.com/howto/tryhow_js_slideshow_ifr.htm | <iframe src="/howto/tryhow_js_slideshow_ifr.htm" id="howto_iframe"></iframe>

when JavaScript version (from opening post) shows:

null|https://www.w3schools.com/|
null/0|https://www.w3schools.com/howto/tryhow_js_slideshow_ifr.htm|<iframe src="/howto/tryhow_js_slideshow_ifr.htm" id="howto_iframe"></iframe>
null/1|about:blank|<iframe name="__tcfapiLocator" style="display: none;"></iframe>
null/2|about:blank|<iframe name="__uspapiLocator" style="display: none;"></iframe>

and this is related that the two other frames are not visible.

mlipok commented 2 years ago

but it is related to

_WD_ExecuteScript ==> Javascript Exception [21] : Error occurred when trying to ExecuteScript
_WD_FrameList ==> Javascript Exception [21] : Error occured on "null" level when trying to check atributes child frames #1

wchich mean that _WD_GetFrameCount is getting numbers of frame in wrong way. Working on.

mlipok commented 2 years ago

I found reason must find solution

mlipok commented 2 years ago

now you can check:

#Region - UserTesting
Func UserTesting() ; here you can replace the code to test your stuff before you ask on the forum
    _WD_Navigate($sSession, 'https://www.w3schools.com/tags/tryit.asp?filename=tryhtml_iframe')
    _WD_LoadWait($sSession)

    ; get frame list as string
    ConsoleWrite(_WD_FrameList($sSession, False) & @CRLF)

    ; get frame list as array
    Local $aFrameList = _WD_FrameList($sSession)
    _ArrayDisplay($aFrameList)
    Return
EndFunc   ;==>UserTesting

Func _WD_FrameList($sSession, $bReturnAsArray = True)
    Local $a_Result[0][3]
    Local $vResult = __WD_FrameList_Internal($sSession, 'null', '')
    If @error = $_WD_ERROR_Success Then
        $vResult = StringTrimRight($vResult, 2) ; last @CRLF
        If $bReturnAsArray then
            _ArrayAdd($a_Result, $vResult)
            $vResult = $a_Result
        EndIf
    EndIf
    Return SetError(@error, @extended, $vResult)
EndFunc   ;==>_WD_FrameList

Func __WD_FrameList_Internal($sSession, $s_level_string, $s_Path)
    #WARRNING some kind of remark in the header should be aded first proposal below:
    ; changing frames may affect your further coding by changing contex in browser to different frames
    Local Const $sFuncName = "_WD_FrameList"
    Local $iErr = $_WD_ERROR_Success
    Local $vResult = '', $s_URL = '', $sMessage = ''

    Local $a_Level = StringSplit($s_level_string, '/')
    For $i = 1 To $a_Level[0]
        If $a_Level[$i] = 'null' Then
            _WD_FrameEnter($sSession, Null)
        Else
            _WD_FrameEnter($sSession, Int($a_Level[$i]))
        EndIf
        If @error Then ExitLoop
    Next

    If @error Then
        $sMessage = 'Error occured on "' & $s_level_string & '" level when trying to entering frame'
    Else
        $s_URL = _WD_ExecuteScript($sSession, "return window.location.href", Default, Default, $_WD_JSON_Value)
        If @error Then
            $sMessage = 'Error occured on "' & $s_level_string & '" level when checking URL'
        Else
            $vResult = $s_level_string & '| ' & $s_URL & '|' & $s_Path & @CRLF
        EndIf
    EndIf
    $iErr = @error
    If Not @error Then
        Local $iFrameCount = _WD_GetFrameCount($sSession)
        $iErr = @error
        If $iErr Then
            $sMessage = 'Error occured on "' & $s_level_string & '" level when trying to check frames count'
        Else
            For $iFrame = 0 To $iFrameCount - 1
                $s_Path = _WD_ExecuteScript($sSession, "return document.querySelectorAll('iframe')[" & $iFrame & "].outerHTML;", Default, Default, $_WD_JSON_Value)
                $iErr = @error
                If @error Then
                    $sMessage = 'Error occured on "' & $s_level_string & '" level when trying to check atributes child frames #' & $iFrame
                Else
                    $vResult &= __WD_FrameList_Internal($sSession, $s_level_string & '/' & $iFrame, $s_Path)
                    $iErr = @error
                    If Not @error Then
                        _WD_FrameLeave($sSession)
                        $iErr = @error
                        If @error Then
                            $sMessage = 'Error occured on "' & $s_level_string & '" level when trying to leave frames #' & $iFrame
                        EndIf
                    EndIf
                EndIf
                If @error Then ExitLoop
            Next
        EndIf
    EndIf
    Return SetError(__WD_Error($sFuncName, $iErr, $sMessage), 0, $vResult)
EndFunc   ;==>__WD_FrameList_Internal
#EndRegion - UserTesting

my results are:

null| https://www.w3schools.com/tags/tryit.asp?filename=tryhtml_iframe|
null/0| about:blank|<iframe id="iframeResult" name="iframeResult" allowfullscreen="true" frameborder="0"></iframe>
null/1| about:blank|<iframe style="display: none;" name="__tcfapiLocator"></iframe>
null/2| https://www.w3schools.com/tags/tryit.asp?filename=tryhtml_iframe|<iframe style="display: none;" name="__uspapiLocator"></iframe>
null/2/0| https://www.w3schools.com/|<iframe src="https://www.w3schools.com" title="W3Schools Free Online Web Tutorials">
</iframe>
null/2/0/0| https://www.w3schools.com/howto/tryhow_js_slideshow_ifr.htm|<iframe src="/howto/tryhow_js_slideshow_ifr.htm" id="howto_iframe"></iframe>
null/2/0/1| about:blank|<iframe style="display: none;" name="__tcfapiLocator"></iframe>
null/2/0/2| about:blank|<iframe style="display: none;" name="__uspapiLocator"></iframe>

image

mlipok commented 2 years ago

I wonder if it will be easier for You to test this FeatureRequest ISSUE ... if I make PR related to this ISSUE ?

Danp2 commented 2 years ago

No, that shouldn't be necessary. I just haven't been able to test it yet due to other priorities. I would like you to explain your thinking on the column with contents of "null", "null/0", etc.

These are my current questions / thoughts about this --

mlipok commented 2 years ago

No, that shouldn't be necessary. I just haven't been able to test it yet due to other priorities.

ok

I would like you to explain your thinking on the column with contents of "null", "null/0", etc.

These are my current questions / thoughts about this --

  • Why is the first line with "null" even in the list? IMO, it doesn't belong because it isn't a frame.

because the first is window.top which is the main document and is not frames as such, but it is needed, to know how to use _WD_FrameEnter()

  • I don't see the benefit of preceding each entry with "null"

It is very important because it shows the full path you need to walk to achieve particular context.

For example if you want to go to this frame: image

you need to perform such steps:

_WD_FrameEnter($sSession, Null)
_WD_FrameEnter($sSession, 2)
_WD_FrameEnter($sSession, 0)
_WD_FrameEnter($sSession, 0)

Ultimately I am thinking to be possible of something like this:

_WD_FrameEnter($sSession, "Null/2/0/0")
mlipok commented 2 years ago

I also want to retrive each window.document.outerHTML as a additionall column. And also want to add speciall parameter to filter results, which will allow you to easily find the frame you want to use with your target DOM element.

mlipok commented 2 years ago

Please check it with this version:

#Region - UserTesting
Func UserTesting() ; here you can replace the code to test your stuff before you ask on the forum
    _WD_CheckContext($sSession, False)
    If @error Then ; return if session is NOT OK
        ConsoleWrite("! ---> @error=" & @error & "  @extended=" & @extended & _
                " : _WD_CheckContext reported a problem with the session" & @CRLF)
        Return
    EndIf

    _WD_Navigate($sSession, 'https://www.w3schools.com/tags/tryit.asp?filename=tryhtml_iframe')
    _WD_LoadWait($sSession)

    ; Example 1 - from 'https://www.w3schools.com' get frame list as string
    ConsoleWrite(_WD_FrameList($sSession, False) & @CRLF)

    ; Example 2 - from 'https://www.w3schools.com' get frame list as array
    Local $aFrameList = _WD_FrameList($sSession)
    _ArrayDisplay($aFrameList, @ScriptLineNumber)

    _WD_Navigate($sSession, 'https://stackoverflow.com/questions/19669786/check-if-element-is-visible-in-dom')
    _WD_LoadWait($sSession)

    ; Example 3 - from 'https://stackoverflow.com' get frame list as string
    ConsoleWrite(_WD_FrameList($sSession, False) & @CRLF)

    ; Example 4 - from 'https://stackoverflow.com' get frame list as array
    $aFrameList = _WD_FrameList($sSession, True)
    _ArrayDisplay($aFrameList, @ScriptLineNumber)

    ; Example 5 - from 'https://stackoverflow.com' get frame list as array - but only this one which contain id="question-header"
    $aFrameList = _WD_FrameList($sSession, True, '(?i)id=.question-header.')
    _ArrayDisplay($aFrameList, @ScriptLineNumber)

    ; go thru the Frames

EndFunc   ;==>UserTesting

Func _WD_FrameList($sSession, $bReturnAsArray = True, $sFilter = '')
    Local $a_Result[0][4]
    Local $vResult = __WD_FrameList_Internal($sSession, 'null', '', $sFilter)
    If @error = $_WD_ERROR_Success Then
        $vResult = StringTrimRight($vResult, 2) ; last @CRLF
        If $bReturnAsArray Then
            _ArrayAdd($a_Result, $vResult)
            $vResult = $a_Result
        EndIf
    EndIf
    Return SetError(@error, @extended, $vResult)
EndFunc   ;==>_WD_FrameList

Func __WD_FrameList_Internal($sSession, $s_level_string, $s_Path, $sFilter)
    #WARRNING some kind of remark in the header should be aded first proposal below:
    ; changing frames may affect your further coding by changing contex in browser to different frames
    Local Const $sFuncName = "_WD_FrameList"
    Local $iErr = $_WD_ERROR_Success
    Local $vResult = '', $s_URL = '', $s_HTML = '', $sMessage = ''

    Local $a_Level = StringSplit($s_level_string, '/')
    For $i = 1 To $a_Level[0]
        If $a_Level[$i] = 'null' Then
            _WD_FrameEnter($sSession, Null)
        Else
            _WD_FrameEnter($sSession, Int($a_Level[$i]))
        EndIf
        If @error Then ExitLoop
    Next

    If @error Then
        $sMessage = 'Error occured on "' & $s_level_string & '" level when trying to entering frame'
    Else
        $s_URL = _WD_ExecuteScript($sSession, "return window.location.href", Default, Default, $_WD_JSON_Value)
        If @error Then
            $sMessage = 'Error occured on "' & $s_level_string & '" level when checking URL'
        Else
            $s_HTML = _WD_GetSource($sSession)
            If @error Then
                $sMessage = 'Error occured on "' & $s_level_string & '" level when getting HTML Source'
            Else
                If $sFilter = '' Or StringRegExp($s_HTML, $sFilter, $STR_REGEXPMATCH) Then
                    $vResult = $s_level_string & '|' & $s_Path & '| ' & $s_URL & '|' & StringToBinary($s_HTML, $SB_UTF8) & @CRLF
                EndIf
            EndIf
        EndIf
    EndIf
    $iErr = @error
    If Not @error Then
        Local $iFrameCount = _WD_GetFrameCount($sSession)
        $iErr = @error
        If $iErr Then
            $sMessage = 'Error occured on "' & $s_level_string & '" level when trying to check frames count'
        Else
            For $iFrame = 0 To $iFrameCount - 1
                $s_Path = _WD_ExecuteScript($sSession, "return document.querySelectorAll('iframe')[" & $iFrame & "].outerHTML;", Default, Default, $_WD_JSON_Value)
                $iErr = @error
                If @error Then
                    $sMessage = 'Error occured on "' & $s_level_string & '" level when trying to check atributes child frames #' & $iFrame
                Else
                    $vResult &= __WD_FrameList_Internal($sSession, $s_level_string & '/' & $iFrame, $s_Path, $sFilter)
                    $iErr = @error
                    If Not @error Then
                        _WD_FrameLeave($sSession)
                        $iErr = @error
                        If @error Then
                            $sMessage = 'Error occured on "' & $s_level_string & '" level when trying to leave frames #' & $iFrame
                        EndIf
                    EndIf
                EndIf
                If @error Then ExitLoop
            Next
        EndIf
    EndIf
    Return SetError(__WD_Error($sFuncName, $iErr, $sMessage), 0, $vResult)
EndFunc   ;==>__WD_FrameList_Internal

; if necessary, add any additional function required for testing within this region here
#EndRegion - UserTesting
Danp2 commented 2 years ago

because the first is window.top which is the main document and is not frames as such, but it is needed, to know how to use _WD_FrameEnter()

IMO, if the function is going to list all frames, then the Null should be implied and the first entry should be excluded. The results would then look like this --

0| about:blank|<iframe id="iframeResult" name="iframeResult" allowfullscreen="true" frameborder="0"></iframe>
1| about:blank|<iframe style="display: none;" name="__tcfapiLocator"></iframe>
2| https://www.w3schools.com/tags/tryit.asp?filename=tryhtml_iframe|<iframe style="display: none;" name="__uspapiLocator"></iframe>
2/0| https://www.w3schools.com/|<iframe src="https://www.w3schools.com" title="W3Schools Free Online Web Tutorials"></iframe>
2/0/0| https://www.w3schools.com/howto/tryhow_js_slideshow_ifr.htm|<iframe src="/howto/tryhow_js_slideshow_ifr.htm" id="howto_iframe"></iframe>
2/0/1| about:blank|<iframe style="display: none;" name="__tcfapiLocator"></iframe>
2/0/2| about:blank|<iframe style="display: none;" name="__uspapiLocator"></iframe>

It is very important because it shows the full path you need to walk to achieve particular context.

I think it is a matter of perspective. Your version forces a return to the top level by including the Null. I would recommend that the user make this decision instead. Perhaps the solution is to add an optional parameter that controls if "null" is generated.

Ultimately I am thinking to be possible of something like this: _WD_FrameEnter($sSession, "Null/2/0/0")

That could be interesting indeed. But the Null should be optional. 😛

I also want to retrive each window.document.outerHTML as a additionall column.

Why? That could add a bunch of unnecessary overhead. If you add this, it should be optional and it should not be included by default.

mlipok commented 2 years ago

Why? That could add a bunch of unnecessary overhead. If you add this, it should be optional and it should not be included by default.

As already get HTML to check it with $sFilter then why not return them ?

_WD_FrameEnter($sSession, "Null/2/0/0")

That could be interesting indeed.

I'm happy to hear that.

But the Null should be optional.

Why ?

In my opinion the string parameter "Null/2/0/0"

_WD_FrameEnter($sSession, "Null/2/0/0")

should contain all the action necessary to do

For example if you need to switch between:

_WD_FrameEnter($sSession, "Null/2/0/0")
_WD_FrameEnter($sSession, "Null/1/2/2")
_WD_FrameEnter($sSession, "Null/2/1/1")

normally, doing this with multpile _WD_FrameEnter calls, you must 3 times goes thru Null.

I want to keep the string parameter Null/2/1/1 consistent with the path of conduct as it is now when you must pass 'Null' each time when you want back to top window

mlipok commented 2 years ago

If you add this, it should be optional and it should not be included by default.

Ok. $bReturnHTML added in my background work.

Func _WD_FrameList($sSession, $bReturnAsArray = True, $sFilter = '', $bReturnHTML = False)
Danp2 commented 2 years ago

As already get HTML to check it with $sFilter then why not return them ?

If you aren't filtering the result, then no need to retrieve the HTML, right? 😉

But the Null should be optional.

Why ?

By prepending "Null" at the beginning, you force a potential extra step of switching to the top level browsing context when it isn't needed if the browsing context is already at the top level. Look at it from the POV of other developers, who may have different needs than your own.

mlipok commented 2 years ago

This string Null/2/0/0 as for now is only informationall to show the path that allways fit no matter on what level of the document (in what context / frame) you are currently..

I know that If you know that you are on top then you do not must back to top. But for this is _WD_IsWindowTop($sSession) and can be used internally in:

_WD_FrameEnter($sSession, "Null")

because as for now it is not used.

And as far as I understand it should be checked ?

mlipok commented 2 years ago

If you aren't filtering the result, then no need to retrieve the HTML, right?

yeah, agree, already fixed in my background code.

mlipok commented 2 years ago

current version:

#Region - UserTesting
Func UserTesting() ; here you can replace the code to test your stuff before you ask on the forum
    _WD_CheckContext($sSession, False)
    If @error Then ; return if session is NOT OK
        ConsoleWrite("! ---> @error=" & @error & "  @extended=" & @extended & _
                " : _WD_CheckContext reported a problem with the session" & @CRLF)
        Return
    EndIf

    _WD_Navigate($sSession, 'https://www.w3schools.com/tags/tryit.asp?filename=tryhtml_iframe')
    _WD_LoadWait($sSession)

    ; Example 1 - from 'https://www.w3schools.com' get frame list as string
    ConsoleWrite(_WD_FrameList($sSession, False) & @CRLF)

    ; Example 2 - from 'https://www.w3schools.com' get frame list as array, with HTML content of each document
    Local $aFrameList = _WD_FrameList($sSession, True, '', True)
    _ArrayDisplay($aFrameList, 'Example 2', 0, 0, Default, '_WD_FrameEnter Identifiers|IFRAME attributes|URL|HTML')

    _WD_Navigate($sSession, 'https://stackoverflow.com/questions/19669786/check-if-element-is-visible-in-dom')
    _WD_LoadWait($sSession)

    ; Example 3 - from 'https://stackoverflow.com' get frame list as string
    ConsoleWrite(_WD_FrameList($sSession, False) & @CRLF)

    ; Example 4 - from 'https://stackoverflow.com' get frame list as array
    #TODO this following line - NOT WORKS - should be checked
;~  $aFrameList = _WD_FrameList($sSession, True)
    $aFrameList = _WD_FrameList($sSession, True, '', False)
    _ArrayDisplay($aFrameList, 'Example 4', 0, 0, Default, '_WD_FrameEnter Identifiers|IFRAME attributes|URL|HTML')

    ; Example 5 - from 'https://stackoverflow.com' get frame list as array - but only this one which contain id="question-header"
    $aFrameList = _WD_FrameList($sSession, True, '(?i)id=.question-header.')
    _ArrayDisplay($aFrameList, 'Example 5', 0, 0, Default, '_WD_FrameEnter Identifiers|IFRAME attributes|URL|HTML')

EndFunc   ;==>UserTesting

Func _WD_FrameList($sSession, $bReturnAsArray = True, $sFilter = '', $bReturnHTML = False)
    Local $a_Result[0][4]
    Local $vResult = __WD_FrameList_Internal($sSession, 'null', '', $sFilter, $bReturnHTML)
    If @error = $_WD_ERROR_Success Then
        $vResult = StringTrimRight($vResult, 2) ; last @CRLF
        If $bReturnAsArray Then
            _ArrayAdd($a_Result, $vResult)
            $vResult = $a_Result
        EndIf
    EndIf
    Return SetError(@error, @extended, $vResult)
EndFunc   ;==>_WD_FrameList

Func __WD_FrameList_Internal($sSession, $s_level_string, $s_Path, $sFilter, $bReturnHTML)
    #WARRNING some kind of remark in the header should be aded first proposal below:
    ; changing frames may affect your further coding by changing contex in browser to different frames
    Local Const $sFuncName = "_WD_FrameList"
    Local $iErr = $_WD_ERROR_Success
    Local $vResult = '', $s_URL = '', $s_HTML = '', $sMessage = ''

    Local $a_Level = StringSplit($s_level_string, '/')
    For $i = 1 To $a_Level[0]
        If $a_Level[$i] = 'null' Then
            _WD_FrameEnter($sSession, Null)
        Else
            _WD_FrameEnter($sSession, Int($a_Level[$i]))
        EndIf
        If @error Then ExitLoop
    Next

    If @error Then
        $sMessage = 'Error occured on "' & $s_level_string & '" level when trying to entering frame'
    Else
        $s_URL = _WD_ExecuteScript($sSession, "return window.location.href", Default, Default, $_WD_JSON_Value)
        If @error Then
            $sMessage = 'Error occured on "' & $s_level_string & '" level when checking URL'
        Else
            If $bReturnHTML Or $sFilter <> '' Then $s_HTML = _WD_GetSource($sSession)
            If @error Then
                $sMessage = 'Error occured on "' & $s_level_string & '" level when getting HTML Source'
            Else
                If $sFilter = '' Or StringRegExp($s_HTML, $sFilter, $STR_REGEXPMATCH) Then
                    $s_HTML = ($bReturnHTML) ? (StringToBinary($s_HTML, $SB_UTF8)) : ('')
                    $vResult = $s_level_string & '|' & $s_Path & '| ' & $s_URL & '|' & $s_HTML & @CRLF
                EndIf
            EndIf
        EndIf
    EndIf
    $iErr = @error
    If Not @error Then
        Local $iFrameCount = _WD_GetFrameCount($sSession)
        $iErr = @error
        If $iErr Then
            $sMessage = 'Error occured on "' & $s_level_string & '" level when trying to check frames count'
        Else
            For $iFrame = 0 To $iFrameCount - 1
                $s_Path = _WD_ExecuteScript($sSession, "return document.querySelectorAll('iframe')[" & $iFrame & "].outerHTML;", Default, Default, $_WD_JSON_Value)
                $iErr = @error
                If @error Then
                    $sMessage = 'Error occured on "' & $s_level_string & '" level when trying to check atributes child frames #' & $iFrame
                Else
                    $vResult &= __WD_FrameList_Internal($sSession, $s_level_string & '/' & $iFrame, $s_Path, $sFilter, $bReturnHTML)
                    $iErr = @error
                    If Not @error Then
                        _WD_FrameLeave($sSession)
                        $iErr = @error
                        If @error Then
                            $sMessage = 'Error occured on "' & $s_level_string & '" level when trying to leave frames #' & $iFrame
                        EndIf
                    EndIf
                EndIf
                If @error Then ExitLoop
            Next
        EndIf
    EndIf
    Return SetError(__WD_Error($sFuncName, $iErr, $sMessage), 0, $vResult)
EndFunc   ;==>__WD_FrameList_Internal

; if necessary, add any additional function required for testing within this region here
#EndRegion - UserTesting

image

image

image

mlipok commented 2 years ago

Look at it from the POV of other developers, who may have different needs than your own

POV ?

Danp2 commented 2 years ago

POV ?

Point of view

But for this is _WD_IsWindowTop($sSession) and can be used internally in:

_WD_FrameEnter($sSession, "Null")

because as for now it is not used.

And as far as I understand it should be checked ?

Why do you believe that _WD_FrameEnter should check this? Please explain your reasoning.

mlipok commented 2 years ago

Why do you believe that _WD_FrameEnter should check this? Please explain your reasoning.

because of your statement

By prepending "Null" at the beginning, you force a potential extra step of switching to the top level browsing context when it isn't needed if the browsing context is already at the top level. Look at it from the POV of other developers, who may have different needs than your own.

and from this my resoning is that we have two possible way how UDF should behaves:

  1. if UDF should protect user from doing this internally it must be also done in _WD_FrameEnter() not only in _WD_FrameList(), changing by adding such protection/check to _WD_FrameEnter() we also protect _WD_FrameList()
  2. if UDF should not protect user from doing this internally in _WD_FrameEnter() then this is user case/care to parse result form _WD_FrameList()

_WD_FrameList() in my intention was not to give to user relative path from current frame level to any other level, but always relative to Null in the meaning of top window.

IMHO: If user wants to calculate relative steps:

then he always must to keep current level and recalculate them manually.

btw. After reflection, the following questions arose for me, which I would ask you to reflect on.

Why calling

_WD_FrameEnter($sSession, Null)

from top window is so bad ?

Won't storing the current level information be a more difficult task taking more CPU time and user intervention (on his own code) ?

What user should do if he is not on top window ?

How he should check it ? And how to check his current level if it is no top window ?

Why checking of this should not be performed by _WD_FrameEnter() ?

How many switching between frames will be performed in each minutes of processing that would affect speed ?

If we remove the Null how user should be awer that he must to use them ?

Finnaly to sum up:

My intention is to add the full path required by _WD_FrameEnter() to each correct transition to a given frame regardless of its current position to ensure that the result given is always reliable and effective with no additional processing beyond the UDF.

If the user wants to control it in a different way, no problem, he can just parse the result even with a regular

StringReplace($sPath, 'Null', '')
Danp2 commented 2 years ago

because of your statement

You are twisting my words to suit your POV.

and from this my resoning is that we have two possible way how UDF should behaves:

It's been a long day and there is currently a guy jack hammering outside my room, so my brain isn't "absorbing" at full capacity. I didn't completely understand the two scenarios you described. However, I don't believe we need to be "protecting the user" in this scenario.

_WD_FrameList() in my intention was not to give to user relative path from current frame level to any other level, but always relative to Null in the meaning of top window.

Why? Maybe the script writer is only interested in the relative path from the current browsing context. This should be an option IMO.

Why calling _WD_FrameEnter($sSession, Null) from top window is so bad ?

I never said it was bad. I said that by prepending Null to the results you are causing extra work (either calling _WD_FrameEnter($sSession, Null) unnecessarily or having to check _WD_IsWindowTop($sSession))

Won't storing the current level information be a more difficult task taking more CPU time and user intervention (on his own code) ?

You really don't want to go there (unless I completely misunderstood your point). I can virtually guarantee that your argument is invalid as there's no way that storing the current level information (ie: update a variable) will take anywhere close to the amount of time / cycles it will take to return to perform all of your actions.

Why checking of this should not be performed by _WD_FrameEnter() ?

Because it's the responsibility of the calling script.

mlipok commented 2 years ago

Maybe the script writer is only interested in the relative path from the current browsing context. This should be an option IMO.

maybe in the future it will be possible but for now let stick with current solution that is workable.

the only way that I can see is to use:

Func _WD_FrameList($sSession, $sCurrentLoaction = Default, $bReturnAsArray = True, $sFilter = '', $bReturnHTML = False)
    #forceref $sCurrentLoaction ; not supported yet

I never said it was bad. I said that by prepending Null to the results you are causing extra work (either calling _WD_FrameEnter($sSession, Null) unnecessarily or having to check _WD_IsWindowTop($sSession))

so somebody must to check.

I ask again: Who should to check UDF or user (in calling script) ?

I was asking (in a very convoluted way) about this here:

my resoning is that we have two possible way how UDF should behaves:

  1. if UDF should protect user from doing this internally it must be also done in _WD_FrameEnter() not only in _WD_FrameList(), changing by adding such protection/check to _WD_FrameEnter() we also protect _WD_FrameList()
  2. if UDF should not protect user from doing this internally in _WD_FrameEnter() then this is user case/care to parse result form _WD_FrameList()

......

Why checking of this should not be performed by _WD_FrameEnter() ?

Because it's the responsibility of the calling script.

and simply I'm saying the same about result from _WD_FrameList : result should always shows full path including Null and this is resposibility of calling script to parse or not full path to relative path.

mlipok commented 2 years ago

way that I can see is to use:

Func _WD_FrameList($sSession, $sCurrentLoaction = Default, $bReturnAsArray = True, $sFilter = '', $bReturnHTML = False)
  #forceref $sCurrentLoaction ; not supported yet

I just had an enlightenment and I have an idea how to support it

Danp2 commented 2 years ago

You've misspelled location 😆

mlipok commented 2 years ago

something like this:

If $sCurrentLocation <> Default Then
    If StringRight($sCurrentLocation, 1) <> '/' Then $sCurrentLocation &= '/'
    For $i = 0 To UBound($a_Result) - 1
        If $a_Result[$i][0] = StringTrimRight($sCurrentLocation, 1) Then $a_Result[$i][0] = ''
        $a_Result[$i][0] = StringRegExpReplace($a_Result[$i][0], '\A' & $sCurrentLocation, '')
    Next
EndIf

as you can see it is easy to implement but it is user responsibility to store current location in null/2/0/2 format

WIP:

    ; Example 2 - from 'https://www.w3schools.com' get frame list as array, with HTML content of each document
    Local $aFrameList = _WD_FrameList($sSession, Default, True, '', True)
    _ArrayDisplay($aFrameList, 'Example 2', 0, 0, Default, '_WD_FrameEnter Identifiers|IFRAME attributes|URL|HTML')

image

    ; Example 3 - from 'https://www.w3schools.com' get frame list as array, with
    Local $aFrameList = _WD_FrameList($sSession, 'null/2', True, '', False)
    _ArrayDisplay($aFrameList, 'Example 3', 0, 0, Default, '_WD_FrameEnter Identifiers|IFRAME attributes|URL|HTML')

image

is this what you are requesting ?

mlipok commented 2 years ago

@Danp2 I plan to make PR today to continue posting code changes over PR and not as a snippet in conversation.

btw. I also plan to modify DemoFrames()

Danp2 commented 2 years ago

I ask again: Who should to check UDF or user (in calling script) ?

My belief is that the responsibility lies with the calling script, not the UDF. The UDF should simply perform that actions requested by the calling script. If there's a null present, then the calling script wants the browsing context reset to the top level. Otherwise, all actions should occur from the current browsing context.

is this what you are requesting?

No. I never suggested passing a string representing the current location into _WD_FrameList. When I said this --

Why? Maybe the script writer is only interested in the relative path from the current browsing context. This should be an option IMO.

I was thinking of a boolean flag that would determine if the path was full vs relative.

mlipok commented 2 years ago

No. I never suggested passing a string representing the current location into _WD_FrameList. I was thinking of a boolean flag that would determine if the path was full vs relative.

Ok I understand you resoning about boolean paramater instead string parameter.

As you know It is simple to check is it on TOP or not. But do you have any idea how to check the current level to have possibility to pass relative path ?

I need to ask you about the way I represents data in relative way :

image

Is this result fit to what you was expecting ?

Danp2 commented 2 years ago

Thinking about it some more, I'm not sure that the boolean parameter is even needed. I think that the results from _WD_FrameList should always be relative to the current browsing context. If _WD_IsWindowTop() is True, then prepend null to the results to indicate that it's a fullpath.

mlipok commented 2 years ago

Thinking about it some more, I'm not sure that the boolean parameter is even needed

Yes it is needed. But it can be as default set to RELATIVE mode and the option to retunr FULL path will be set as not default.

mlipok commented 2 years ago

What this EYES mean ? (in this particular case)

Danp2 commented 2 years ago

That I'm watching you. 😆

mlipok commented 2 years ago

So be careful because the bell is about to ring.

mlipok commented 2 years ago

Is this PR#362 _FrameList() fits to what you was requesting ? If so you can close this ISSUE and we can start continue in PR.

mlipok commented 2 years ago

Ultimately I am thinking to be possible of something like this:

_WD_FrameEnter($sSession, "Null/2/0/0")

Please take a look on: https://github.com/Danp2/au3WebDriver/pull/364