[Library] Chrome.ahk - Automate Google Chrome using native AutoHotkey. No IE!

Post your working scripts, libraries and tools for AHK v1.1 and older
serzh82saratov
Posts: 137
Joined: 01 Jul 2017, 03:04

Re: [Library] Chrome.ahk - Automate Google Chrome using native AutoHotkey. No Selenium!

08 Dec 2020, 15:52

Thanks!
For some reason, this line brings the window to the foreground.

Code: Select all

Page.Call("Target.createTarget", {url : url2})
I try this, but nothing changes.

Code: Select all

Page.Call("Target.createTarget", {url : url2, background: Chrome.Jxon_True()})
Tre4shunter
Posts: 139
Joined: 26 Jan 2016, 16:05

Re: [Library] Chrome.ahk - Automate Google Chrome using native AutoHotkey. No Selenium!

08 Dec 2020, 19:27

I think your object keys need to be strings -

Code: Select all

Page.Call("Target.createTarget", {"url" : url2, "background": Chrome.Jxon_True()})
teadrinker
Posts: 4325
Joined: 29 Mar 2015, 09:41
Contact:

Re: [Library] Chrome.ahk - Automate Google Chrome using native AutoHotkey. No Selenium!

08 Dec 2020, 20:06

I don't think there is a difference between {url and {"url".
serzh82saratov
Posts: 137
Joined: 01 Jul 2017, 03:04

Re: [Library] Chrome.ahk - Automate Google Chrome using native AutoHotkey. No Selenium!

09 Dec 2020, 00:57

That is, it is impossible to convey true or false.
malcev
Posts: 1769
Joined: 12 Aug 2014, 12:37

Re: [Library] Chrome.ahk - Automate Google Chrome using native AutoHotkey. No Selenium!

09 Dec 2020, 01:53

Just dont use buggy and slow json class by Coco.
teadrinker`s json class works ok with this command.
serzh82saratov
Posts: 137
Joined: 01 Jul 2017, 03:04

Re: [Library] Chrome.ahk - Automate Google Chrome using native AutoHotkey. No Selenium!

09 Dec 2020, 02:24

There is an example with this class, but it seems like there are many versions json.
teadrinker
Posts: 4325
Joined: 29 Mar 2015, 09:41
Contact:

Re: [Library] Chrome.ahk - Automate Google Chrome using native AutoHotkey. No Selenium!

09 Dec 2020, 05:13

Later I'll try to create the light version of my JSON class for use with Chrome.ahk
User avatar
Xtra
Posts: 2750
Joined: 02 Oct 2015, 12:15

Re: [Library] Chrome.ahk - Automate Google Chrome using native AutoHotkey. No Selenium!

09 Dec 2020, 06:36

teadrinker wrote:
09 Dec 2020, 05:13
Later I'll try to create the light version of my JSON class for use with Chrome.ahk
:thumbup:
teadrinker
Posts: 4325
Joined: 29 Mar 2015, 09:41
Contact:

Re: [Library] Chrome.ahk - Automate Google Chrome using native AutoHotkey. No Selenium!

09 Dec 2020, 09:25

Class LightJson:

Code: Select all

json = {"key": "value"}
Obj := LightJson.Parse(json)                     ; json to AHK object
MsgBox, % Obj.key

; set boolean value
Obj.bool := LightJson.true
MsgBox, % LightJson.Stringify(obj, "   ")        ; AHK object to json

unescapedStr = 
(
text%A_Tab%abc
new line
)
escapedStr := LightJson.Stringify(unescapedStr)  ; escape spec characters
MsgBox, % escapedStr

class LightJson
{
   static JS := LightJson.GetJS(), true := {}, false := {}, null := {}
   
   Parse(json, _rec := false) {
      if !_rec
         obj := this.Parse(this.JS.JSON.parse(json), true)
      else if !IsObject(json)
         obj := json
      else if this.JS.Object.prototype.toString.call(json) == "[object Array]" {
         obj := []
         Loop % json.length
            obj.Push( this.Parse(json[A_Index - 1], true) )
      }
      else {
         obj := {}
         keys := this.JS.Object.keys(json)
         Loop % keys.length {
            k := keys[A_Index - 1]
            obj[k] := this.Parse(json[k], true)
         }
      }
      Return obj
   }
   
   Stringify(obj, indent := "") {
      if indent|1 {
         for k, v in ["true", "false", "null"]
            if (obj = this[v])
               Return v

         if IsObject( obj ) {
            isArray := true
            for key in obj {
               if IsObject(key)
                  throw Exception("Invalid key")
               if !( key = A_Index || isArray := false )
                  break
            }
            for k, v in obj
               str .= ( A_Index = 1 ? "" : "," ) . ( isArray ? "" : this.Stringify(k, true) . ":" ) . this.Stringify(v, true)

            Return str = "" ? "{}" : isArray ? "[" . str . "]" : "{" . str . "}"
         }
         else if !(obj*1 = "" || RegExMatch(obj, "^-?0|\s"))
            Return obj
         
         for k, v in [["\", "\\"], [A_Tab, "\t"], ["""", "\"""], ["/", "\/"], ["`n", "\n"], ["`r", "\r"], [Chr(12), "\f"], [Chr(8), "\b"]]
            obj := StrReplace( obj, v[1], v[2] )

         Return """" obj """"
      }
      sObj := this.Stringify(obj, true)
      Return this.JS.eval("JSON.stringify(" . sObj . ",'','" . indent . "')")
   }
   
   GetJS() {
      static Doc, JS
      if !Doc {
         Doc := ComObjCreate("htmlfile")
         Doc.write("<meta http-equiv=""X-UA-Compatible"" content=""IE=9"">")
         JS := Doc.parentWindow
         ( Doc.documentMode < 9 && JS.execScript() )
      }
      Return JS
   }
}
Using with class Chrome:

Code: Select all

class Chrome
{
	static DebugPort := 9222
	
	/*
		Escape a string in a manner suitable for command line parameters
	*/
	CliEscape(Param)
	{
		return """" RegExReplace(Param, "(\\*)""", "$1$1\""") """"
	}
	
	/*
		Finds instances of chrome in debug mode and the ports they're running
		on. If no instances are found, returns a false value. If one or more
		instances are found, returns an associative array where the keys are
		the ports, and the values are the full command line texts used to start
		the processes.
		
		One example of how this may be used would be to open chrome on a
		different port if an instance of chrome is already open on the port
		you wanted to used.
		
		```
		; If the wanted port is taken, use the largest taken port plus one
		DebugPort := 9222
		if (Chromes := Chrome.FindInstances()).HasKey(DebugPort)
			DebugPort := Chromes.MaxIndex() + 1
		ChromeInst := new Chrome(ProfilePath,,,, DebugPort)
		```
		
		Another use would be to scan for running instances and attach to one
		instead of starting a new instance.
		
		```
		if (Chromes := Chrome.FindInstances())
			ChromeInst := {"base": Chrome, "DebugPort": Chromes.MinIndex(), PID: Chromes[Chromes.MinIndex(), "PID"]}
		else
			ChromeInst := new Chrome(ProfilePath)
		```
	*/
	FindInstances()
	{
		Out := {}
		for Item in ComObjGet("winmgmts:").ExecQuery("SELECT * FROM Win32_Process WHERE Name = 'chrome.exe'")
			if RegExMatch(Item.CommandLine, "i)chrome.exe""?\s+--remote-debugging-port=(\d+)", Match)
				Out[Match1] := {cmd: Item.CommandLine, PID: Item.ProcessId}
		return Out.MaxIndex() ? Out : False
	}
	
	/*
		ProfilePath - Path to the user profile directory to use. Will use the standard if left blank.
		URLs        - The page or array of pages for Chrome to load when it opens
		Flags       - Additional flags for chrome when launching
		ChromePath  - Path to chrome.exe, will detect from start menu when left blank
		DebugPort   - What port should Chrome's remote debugging server run on
	*/
	__New(ProfilePath:="", URLs:="about:blank", Flags:="", ChromePath:="", DebugPort:="")
	{
		; Verify ProfilePath
		if (ProfilePath != "" && !InStr(FileExist(ProfilePath), "D"))
			throw Exception("The given ProfilePath does not exist")
		this.ProfilePath := ProfilePath
		
		; Verify ChromePath
		if (ChromePath == "")
			FileGetShortcut, %A_StartMenuCommon%\Programs\Google Chrome.lnk, ChromePath
		if (ChromePath == "")
			RegRead, ChromePath, HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\chrome.exe
		if !FileExist(ChromePath)
			throw Exception("Chrome could not be found")
		this.ChromePath := ChromePath
		
		; Verify DebugPort
		if (DebugPort != "")
		{
			if DebugPort is not integer
				throw Exception("DebugPort must be a positive integer")
			else if (DebugPort <= 0)
				throw Exception("DebugPort must be a positive integer")
			this.DebugPort := DebugPort
		}
		
		; Escape the URL(s)
		for Index, URL in IsObject(URLs) ? URLs : [URLs]
			URLString .= " " this.CliEscape(URL)
		
		Run, % this.CliEscape(ChromePath)
		. " --remote-debugging-port=" this.DebugPort
		. (ProfilePath ? " --user-data-dir=" this.CliEscape(ProfilePath) : "")
		. (Flags ? " " Flags : "")
		. URLString
		,,, OutputVarPID
		this.PID := OutputVarPID
	}
	
	/*
		End Chrome by terminating the process.
	*/
	Kill()
	{
		Process, Close, % this.PID
	}
	
	/*
		Queries chrome for a list of pages that expose a debug interface.
		In addition to standard tabs, these include pages such as extension
		configuration pages.
	*/
	GetPageList()
	{
		http := ComObjCreate("WinHttp.WinHttpRequest.5.1")
		http.open("GET", "http://127.0.0.1:" this.DebugPort "/json")
		http.send()
		return LightJson.Parse(http.responseText)
	}
	
	/*
		Returns a connection to the debug interface of a page that matches the
		provided criteria. When multiple pages match the criteria, they appear
		ordered by how recently the pages were opened.
		
		Key        - The key from the page list to search for, such as "url" or "title"
		Value      - The value to search for in the provided key
		MatchMode  - What kind of search to use, such as "exact", "contains", "startswith", or "regex"
		Index      - If multiple pages match the given criteria, which one of them to return
		fnCallback - A function to be called whenever message is received from the page
	*/
	GetPageBy(Key, Value, MatchMode:="exact", Index:=1, fnCallback:="")
	{
		Count := 0
		for n, PageData in this.GetPageList()
		{
			if (((MatchMode = "exact" && PageData[Key] = Value) ; Case insensitive
				|| (MatchMode = "contains" && InStr(PageData[Key], Value))
				|| (MatchMode = "startswith" && InStr(PageData[Key], Value) == 1)
				|| (MatchMode = "regex" && PageData[Key] ~= Value))
				&& ++Count == Index)
				return new this.Page(PageData.webSocketDebuggerUrl, fnCallback)
		}
	}
	
	/*
		Shorthand for GetPageBy("url", Value, "startswith")
	*/
	GetPageByURL(Value, MatchMode:="startswith", Index:=1, fnCallback:="")
	{
		return this.GetPageBy("url", Value, MatchMode, Index, fnCallback)
	}
	
	/*
		Shorthand for GetPageBy("title", Value, "startswith")
	*/
	GetPageByTitle(Value, MatchMode:="startswith", Index:=1, fnCallback:="")
	{
		return this.GetPageBy("title", Value, MatchMode, Index, fnCallback)
	}
	
	/*
		Shorthand for GetPageBy("type", Type, "exact")
		
		The default type to search for is "page", which is the visible area of
		a normal Chrome tab.
	*/
	GetPage(Index:=1, Type:="page", fnCallback:="")
	{
		return this.GetPageBy("type", Type, "exact", Index, fnCallback)
	}
	
	/*
		Connects to the debug interface of a page given its WebSocket URL.
	*/
	class Page
	{
		Connected := False
		ID := 0
		Responses := []
		
		/*
			wsurl      - The desired page's WebSocket URL
			fnCallback - A function to be called whenever message is received
		*/
		__New(wsurl, fnCallback:="")
		{
			this.fnCallback := fnCallback
			this.BoundKeepAlive := this.Call.Bind(this, "Browser.getVersion",, False)
			
			; TODO: Throw exception on invalid objects
			if IsObject(wsurl)
				wsurl := wsurl.webSocketDebuggerUrl
			
			wsurl := StrReplace(wsurl, "localhost", "127.0.0.1")
			this.ws := {"base": this.WebSocket, "_Event": this.Event, "Parent": this}
			this.ws.__New(wsurl)
			
			while !this.Connected
				Sleep, 50
		}
		
		/*
			Calls the specified endpoint and provides it with the given
			parameters.
			
			DomainAndMethod - The endpoint domain and method name for the
			endpoint you would like to call. For example:
			PageInst.Call("Browser.close")
			PageInst.Call("Schema.getDomains")
			
			Params - An associative array of parameters to be provided to the
			endpoint. For example:
			PageInst.Call("Page.printToPDF", {"scale": 0.5 ; Numeric Value
			, "landscape": LightJson.true ; Boolean Value
			, "pageRanges: "1-5, 8, 11-13"}) ; String value
			PageInst.Call("Page.navigate", {"url": "https://autohotkey.com/"})
			
			WaitForResponse - Whether to block until a response is received from
			Chrome, which is necessary to receive a return value, or whether
			to continue on with the script without waiting for a response.
		*/
		Call(DomainAndMethod, Params:="", WaitForResponse:=True)
		{
			if !this.Connected
				throw Exception("Not connected to tab")
			
			; Use a temporary variable for ID in case more calls are made
			; before we receive a response.
			ID := this.ID += 1
			this.ws.Send(LightJson.Stringify({"id": ID
			, "params": Params ? Params : {}
			, "method": DomainAndMethod}))

			if !WaitForResponse
				return
			
			; Wait for the response
			this.responses[ID] := False
			while !this.responses[ID]
				Sleep, 50
			; Get the response, check if it's an error
			response := this.responses.Delete(ID)
			if (response.error)
				throw Exception("Chrome indicated error in response",, LightJson.Stringify(response.error))
			
			return response.result
		}
		
		/*
			Run some JavaScript on the page. For example:
			
			PageInst.Evaluate("alert(""I can't believe it's not IE!"");")
			PageInst.Evaluate("document.getElementsByTagName('button')[0].click();")
		*/
		Evaluate(JS)
		{
			response := this.Call("Runtime.evaluate",
			( LTrim Join
			{
				"expression": JS,
				"objectGroup": "console",
				"includeCommandLineAPI": LightJson.true,
				"silent": LightJson.false,
				"returnByValue": LightJson.false,
				"userGesture": LightJson.true,
				"awaitPromise": LightJson.false
			}
			))
			
			if (response.exceptionDetails)
				throw Exception(response.result.description,, LightJson.Stringify(response.exceptionDetails))
			
			return response.result
		}
		
		/*
			Waits for the page's readyState to match the DesiredState.
			
			DesiredState - The state to wait for the page's ReadyState to match
			Interval     - How often it should check whether the state matches
		*/
		WaitForLoad(DesiredState:="complete", Interval:=100)
		{
			while this.Evaluate("document.readyState").value != DesiredState
				Sleep, Interval
		}
		
		/*
			Internal function triggered when the script receives a message on
			the WebSocket connected to the page.
		*/
		Event(EventName, Event)
		{
			; If it was called from the WebSocket adjust the class context
			if this.Parent
				this := this.Parent
			
			; TODO: Handle Error events
			if (EventName == "Open")
			{
				this.Connected := True
				BoundKeepAlive := this.BoundKeepAlive
				SetTimer, %BoundKeepAlive%, 15000
			}
			else if (EventName == "Message")
			{
				data := LightJson.Parse(Event.data)
				
				; Run the callback routine
				fnCallback := this.fnCallback
				if (newData := %fnCallback%(data))
					data := newData
				
				if this.responses.HasKey(data.ID)
					this.responses[data.ID] := data
			}
			else if (EventName == "Close")
			{
				this.Disconnect()
			}
			else if (EventName == "Error")
			{
				throw Exception("Websocket Error!")
			}
		}
		
		/*
			Disconnect from the page's debug interface, allowing the instance
			to be garbage collected.
			
			This method should always be called when you are finished with a
			page or else your script will leak memory.
		*/
		Disconnect()
		{
			if !this.Connected
				return
			
			this.Connected := False
			this.ws.Delete("Parent")
			this.ws.Disconnect()
			
			BoundKeepAlive := this.BoundKeepAlive
			SetTimer, %BoundKeepAlive%, Delete
			this.Delete("BoundKeepAlive")
		}
		
		class WebSocket
		{
			__New(WS_URL)
			{
				static wb
				
				; Create an IE instance
				Gui, +hWndhOld
				Gui, New, +hWndhWnd
				this.hWnd := hWnd
				Gui, Add, ActiveX, vWB, Shell.Explorer
				Gui, %hOld%: Default
				
				; Write an appropriate document
				WB.Navigate("about:<!DOCTYPE html><meta http-equiv='X-UA-Compatible'"
				. "content='IE=edge'><body></body>")
				while (WB.ReadyState < 4)
					sleep, 50
				this.document := WB.document
				
				; Add our handlers to the JavaScript namespace
				this.document.parentWindow.ahk_savews := this._SaveWS.Bind(this)
				this.document.parentWindow.ahk_event := this._Event.Bind(this)
				this.document.parentWindow.ahk_ws_url := WS_URL
				
				; Add some JavaScript to the page to open a socket
				Script := this.document.createElement("script")
				Script.text := "ws = new WebSocket(ahk_ws_url);`n"
				. "ws.onopen = function(event){ ahk_event('Open', event); };`n"
				. "ws.onclose = function(event){ ahk_event('Close', event); };`n"
				. "ws.onerror = function(event){ ahk_event('Error', event); };`n"
				. "ws.onmessage = function(event){ ahk_event('Message', event); };"
				this.document.body.appendChild(Script)
			}
			
			; Called by the JS in response to WS events
			_Event(EventName, Event)
			{
				this["On" EventName](Event)
			}
			
			; Sends data through the WebSocket
			Send(Data)
			{
				this.document.parentWindow.ws.send(Data)
			}
			
			; Closes the WebSocket connection
			Close(Code:=1000, Reason:="")
			{
				this.document.parentWindow.ws.close(Code, Reason)
			}
			
			; Closes and deletes the WebSocket, removing
			; references so the class can be garbage collected
			Disconnect()
			{
				if this.hWnd
				{
					this.Close()
					Gui, % this.hWnd ": Destroy"
					this.hWnd := False
				}
			}
		}
	}
}

class LightJson
{
   static JS := LightJson.GetJS(), true := {}, false := {}, null := {}
   
   Parse(json, _rec := false) {
      if !_rec
         obj := this.Parse(this.JS.JSON.parse(json), true)
      else if !IsObject(json)
         obj := json
      else if this.JS.Object.prototype.toString.call(json) == "[object Array]" {
         obj := []
         Loop % json.length
            obj.Push( this.Parse(json[A_Index - 1], true) )
      }
      else {
         obj := {}
         keys := this.JS.Object.keys(json)
         Loop % keys.length {
            k := keys[A_Index - 1]
            obj[k] := this.Parse(json[k], true)
         }
      }
      Return obj
   }
   
   Stringify(obj, indent := "") {
      if indent|1 {
         for k, v in ["true", "false", "null"]
            if (obj = this[v])
               Return v

         if IsObject( obj ) {
            isArray := true
            for key in obj {
               if IsObject(key)
                  throw Exception("Invalid key")
               if !( key = A_Index || isArray := false )
                  break
            }
            for k, v in obj
               str .= ( A_Index = 1 ? "" : "," ) . ( isArray ? "" : this.Stringify(k, true) . ":" ) . this.Stringify(v, true)

            Return str = "" ? "{}" : isArray ? "[" . str . "]" : "{" . str . "}"
         }
         else if !(obj*1 = "" || RegExMatch(obj, "^-?0|\s"))
            Return obj
         
         for k, v in [["\", "\\"], [A_Tab, "\t"], ["""", "\"""], ["/", "\/"], ["`n", "\n"], ["`r", "\r"], [Chr(12), "\f"], [Chr(8), "\b"]]
            obj := StrReplace( obj, v[1], v[2] )

         Return """" obj """"
      }
      sObj := this.Stringify(obj, true)
      Return this.JS.eval("JSON.stringify(" . sObj . ",'','" . indent . "')")
   }
   
   GetJS() {
      static Doc, JS
      if !Doc {
         Doc := ComObjCreate("htmlfile")
         Doc.write("<meta http-equiv=""X-UA-Compatible"" content=""IE=9"">")
         JS := Doc.parentWindow
         ( Doc.documentMode < 9 && JS.execScript() )
      }
      Return JS
   }
}
Last edited by teadrinker on 04 Oct 2021, 05:19, edited 4 times in total.
serzh82saratov
Posts: 137
Joined: 01 Jul 2017, 03:04

Re: [Library] Chrome.ahk - Automate Google Chrome using native AutoHotkey. No Selenium!

09 Dec 2020, 10:40

Excellent! So it worked.

Code: Select all

 Page.Call("Target.createTarget", {url: url2, background: LightJson.true})
But it turned out that this opens a tab in the background, and my problem is that createTarget activates the window.
And another question - how to initially start chrome minimized?
Natitoun
Posts: 6
Joined: 09 May 2019, 06:57

Re: [Library] Chrome.ahk - Automate Google Chrome using native AutoHotkey. No Selenium!

11 Dec 2020, 10:51

evilmanimani wrote:
02 Dec 2020, 21:49
Natitoun wrote:
12 Nov 2020, 10:14
Hi all !

I have one issue that I can't fix, maybe one of you have an idea on how to figure it out :)

While automating Chrome thanks to Chrome.ahk, I am facing an issue with a small popup window, the kind you get with a "Javascript: alert('Hello World!')" command.
This window will appear when I am sending a wrong request to my website.

I'd like to detect this window and close it ("ok" button).
Should be simple enough just with a regular timer running in the background, either at the start of the script, or at the start of the function.

Code: Select all

CloseAlert:
If (WinExist("Hello World!")) {
	WinActivate
	Send, {enter}
}
Return

SetTimer, CloseAlert, 100
Hi @evilmanimani !

Thanks for the feedback. Unfortunately, I tried but I could not make it work, are you sure these alerts can be detected by WinExists ? I tried to make it work with
WinExist("Hello World!")
and
WinExist("www.google.com says")

Thank,s
evilmanimani
Posts: 29
Joined: 24 Jun 2020, 16:42

Re: [Library] Chrome.ahk - Automate Google Chrome using native AutoHotkey. No Selenium!

15 Dec 2020, 16:38

Natitoun wrote:
11 Dec 2020, 10:51
Hi @evilmanimani !

Thanks for the feedback. Unfortunately, I tried but I could not make it work, are you sure these alerts can be detected by WinExists ? I tried to make it work with
WinExist("Hello World!")
and
WinExist("www.google.com says")

Thank,s

I've got the same sort of routine running in one of my scripts, and it seems to work fine, maybe double check the title using window spy and make sure it matches.


serzh82saratov wrote:
09 Dec 2020, 10:40
Excellent! So it worked.

Code: Select all

 Page.Call("Target.createTarget", {url: url2, background: LightJson.true})
But it turned out that this opens a tab in the background, and my problem is that createTarget activates the window.
And another question - how to initially start chrome minimized?

Are you attempting to open a url in a background window, so it doesn't appear visible to the user, somewhat like a headless instance?
Vaggeto
Posts: 24
Joined: 14 Dec 2020, 23:52

Re: [Library] Chrome.ahk - Automate Google Chrome using native AutoHotkey. No Selenium!

16 Dec 2020, 16:02

I've got this working great as an #include but one issue I have is that while stepping through code in debug mode, it seems like this script triggers extremely often even when not being referenced. Has anyone else experienced this and is this potentially a cause for concern on a scripts performance?
evilmanimani
Posts: 29
Joined: 24 Jun 2020, 16:42

Re: [Library] Chrome.ahk - Automate Google Chrome using native AutoHotkey. No Selenium!

17 Dec 2020, 16:49

Vaggeto wrote:
16 Dec 2020, 16:02
I've got this working great as an #include but one issue I have is that while stepping through code in debug mode, it seems like this script triggers extremely often even when not being referenced. Has anyone else experienced this and is this potentially a cause for concern on a scripts performance?
I find it will if you don't properly disconnect your page instances (I E. with PageInst.Disconnect() ), otherwise it'll keep calling it intermittently.
Vaggeto
Posts: 24
Joined: 14 Dec 2020, 23:52

Re: [Library] Chrome.ahk - Automate Google Chrome using native AutoHotkey. No Selenium!

17 Dec 2020, 22:43

evilmanimani wrote:
17 Dec 2020, 16:49
Vaggeto wrote:
16 Dec 2020, 16:02
I've got this working great as an #include but one issue I have is that while stepping through code in debug mode, it seems like this script triggers extremely often even when not being referenced. Has anyone else experienced this and is this potentially a cause for concern on a scripts performance?
I find it will if you don't properly disconnect your page instances (I E. with PageInst.Disconnect() ), otherwise it'll keep calling it intermittently.
Hmmm... In my case the webpage is still active and is being worked on. So I'm gathering data and collecting it to be placed into Excel, and then navigating on the site. When I step through the steps that deal with placing the data into excel, (already loaded in variables and not dealing with the chrome data anymore) If I step through the code it will repeatedly go through this script before returning to the main script. In this case I don't think it would make sense to disconnect from the page between the various steps of dealing with the site and also performing other code, or is that recommended?
Vaggeto
Posts: 24
Joined: 14 Dec 2020, 23:52

Re: [Library] Chrome.ahk - Automate Google Chrome using native AutoHotkey. No Selenium!

19 Dec 2020, 03:22

Does anyone have an advice for triggering console commands to DOM that are part of an I-Frame? In the console you can manually change the javascript context and the commands work, but is it possible using Chrome.ahk to direct commands at a specific context? From what I can tell firefox supports a cd() command which changes the context but I'm not having luck finding something similar with Chrome.

If this isn't possible, am I wrong that that basically leaves Iframes essentially untouchable?

Thanks!
evilmanimani
Posts: 29
Joined: 24 Jun 2020, 16:42

Re: [Library] Chrome.ahk - Automate Google Chrome using native AutoHotkey. No Selenium!

20 Dec 2020, 18:27

Vaggeto wrote:
17 Dec 2020, 22:43
evilmanimani wrote:
17 Dec 2020, 16:49
Vaggeto wrote:
16 Dec 2020, 16:02
I've got this working great as an #include but one issue I have is that while stepping through code in debug mode, it seems like this script triggers extremely often even when not being referenced. Has anyone else experienced this and is this potentially a cause for concern on a scripts performance?
I find it will if you don't properly disconnect your page instances (I E. with PageInst.Disconnect() ), otherwise it'll keep calling it intermittently.
Hmmm... In my case the webpage is still active and is being worked on. So I'm gathering data and collecting it to be placed into Excel, and then navigating on the site. When I step through the steps that deal with placing the data into excel, (already loaded in variables and not dealing with the chrome data anymore) If I step through the code it will repeatedly go through this script before returning to the main script. In this case I don't think it would make sense to disconnect from the page between the various steps of dealing with the site and also performing other code, or is that recommended?
If you're actually just sticking to a single instance, then that's fine, however you might want to check that you're not doing PageInst := ChromeInst.GetPage() at all elsewhere in the script while it's running, or at least assign a different variable for a second instance if you are, otherwise it'll overwrite it and be unable to disconnect the first instance. Otherwise you can expect it to keep calling back to the class repeatedly while that instance is active. Not sure what editor you're using, but it's definitely annoying when trying to step through, I'm using VSCode and I'm not sure if there's a way to have exclude some methods or routines while debugging.
Vaggeto
Posts: 24
Joined: 14 Dec 2020, 23:52

Re: [Library] Chrome.ahk - Automate Google Chrome using native AutoHotkey. No Selenium!

21 Dec 2020, 04:03

evilmanimani wrote:
20 Dec 2020, 18:27
Vaggeto wrote:
17 Dec 2020, 22:43
evilmanimani wrote:
17 Dec 2020, 16:49
Vaggeto wrote:
16 Dec 2020, 16:02
I've got this working great as an #include but one issue I have is that while stepping through code in debug mode, it seems like this script triggers extremely often even when not being referenced. Has anyone else experienced this and is this potentially a cause for concern on a scripts performance?
I find it will if you don't properly disconnect your page instances (I E. with PageInst.Disconnect() ), otherwise it'll keep calling it intermittently.
Hmmm... In my case the webpage is still active and is being worked on. So I'm gathering data and collecting it to be placed into Excel, and then navigating on the site. When I step through the steps that deal with placing the data into excel, (already loaded in variables and not dealing with the chrome data anymore) If I step through the code it will repeatedly go through this script before returning to the main script. In this case I don't think it would make sense to disconnect from the page between the various steps of dealing with the site and also performing other code, or is that recommended?
If you're actually just sticking to a single instance, then that's fine, however you might want to check that you're not doing PageInst := ChromeInst.GetPage() at all elsewhere in the script while it's running, or at least assign a different variable for a second instance if you are, otherwise it'll overwrite it and be unable to disconnect the first instance. Otherwise you can expect it to keep calling back to the class repeatedly while that instance is active. Not sure what editor you're using, but it's definitely annoying when trying to step through, I'm using VSCode and I'm not sure if there's a way to have exclude some methods or routines while debugging.

I'm using Scite4AutoHotkey. This is the only place I reference pageinst := (or in my case page :=).
I essentially check if it's already active based on a matching URL. If it isn't I open a new tab with that URL and then try again. If there is a better approach to this, please let me know.

But after this I only reference page.Evaluate to trigger console code.

Code: Select all

Function_Name(var1,var2,var3,var4,ShortName,URL)
{
	;Check if website is already loaded
	page := Chrome.GetPageByURL(URL)

	If !IsObject(page)
	{
		;msgbox, initial check of page not found
		;Initialize Website
		Run chrome.exe %URL%  " --new-tab "
		Sleep, 1500
		;Establish ChromeAHK Connection
		page := Chrome.GetPageByURL(URL)
	}

	;Verify Page is Active
	If !IsObject(page)
	{
		msgbox, 2nd check of page still not Found: %ShortName% : %URL%
		; --- Close the Chrome instance ---
		Chrome.Kill()
		page.Disconnect()
		ExitApp
	}
	
	;Do things with page
	
} ; end function 
Vaggeto
Posts: 24
Joined: 14 Dec 2020, 23:52

Re: [Library] Chrome.ahk - Automate Google Chrome using native AutoHotkey. No Selenium!

21 Dec 2020, 04:05

Vaggeto wrote:
19 Dec 2020, 03:22
Does anyone have an advice for triggering console commands to DOM that are part of an I-Frame? In the console you can manually change the javascript context and the commands work, but is it possible using Chrome.ahk to direct commands at a specific context? From what I can tell firefox supports a cd() command which changes the context but I'm not having luck finding something similar with Chrome.

If this isn't possible, am I wrong that that basically leaves Iframes essentially untouchable?

Thanks!
So I think i found a solution and man did it take many hours and eventually luck to find the right suggestion:

I've got it working in the console. Now I just need to see if I can run multi-lines of code in chrome.ahk which I think should be fine with pageInst.Evaluate(js).value etc.

Code: Select all

var iframe = document.getElementById('iframe-id');
iframe.contentDocument.getElementById('test2');
burque505
Posts: 1732
Joined: 22 Jan 2017, 19:37

Re: [Library] Chrome.ahk - Automate Google Chrome using native AutoHotkey. No Selenium!

21 Dec 2020, 09:06

hi @Vaggeto, for multi-line JS I prefer continuation sections (keeps me from trashing the syntax with double-quotes inline), though it appears lots of other people do not. Will something like this work for you?

Code: Select all

iframeJs =
(
var iframe = document.getElementById('iframe-id');
var test2 = iframe.contentDocument.getElementById('test2');
)
PageInstance.Evaluate(iframeJs)
Regards,
burque505

Return to “Scripts and Functions (v1)”

Who is online

Users browsing this forum: No registered users and 53 guests