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

Post your working scripts, libraries and tools
gregster
Posts: 3855
Joined: 30 Sep 2013, 06:48

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

31 Oct 2019, 12:31

@extra: Ok, I see, thank you. I read this instead for visibilityOfElementLocated:
https://selenium.dev/selenium/docs/api/java/org/openqa/selenium/support/ui/ExpectedConditions.html#visibilityOfElementLocated-org.openqa.selenium.By- wrote:An expectation for checking that an element is present on the DOM of a page and visible. Visibility means that the element is not only displayed but also has a height and width that is greater than 0.
It wasnt clear why it was mentioned so i linked what you used for reference.
Actually, I didn't see a relation here between iterateNext() and visibilityOfElementLocated. I just asked about the latter because I didn't address it in my code so far, and DanielToward13 called it the next step:
For the next step, It would be perfect if I can have 2 different callback functions for each tab to implement the equivalent of wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath("//input[@id='someID']"))); in Chrome DevTools Protocol.
So, I wanted to clarify what he exactly needs... :) I made an edit now that hopefully clarifies it a bit better.

I guess, we just misunderstood. But is there a better day to be cryptic than Halloween :D ?
DanielToward13
Posts: 71
Joined: 18 May 2017, 10:56

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

01 Nov 2019, 04:09

Thanks @gregster. How to define a callback function to always track the existence of an element in the DOM, even after the user has clicked on a button and the page/DOM is changed? I've tried a simple loop but that doesn't work after the page is changed.

Code: Select all

Loop
{
    ShowText := Page1.evaluate("document.evaluate('/html/body/div/p[2]/a', document).iterateNext().textContent").value
    ToolTip, %ShowText%
    Sleep, 500
}
gregster
Posts: 3855
Joined: 30 Sep 2013, 06:48

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

01 Nov 2019, 15:49

DanielToward13 wrote:
01 Nov 2019, 04:09
Thanks @gregster. How to define a callback function to always track the existence of an element in the DOM, even after the user has clicked on a button and the page/DOM is changed? I've tried a simple loop but that doesn't work after the page is changed.
I guess it depends what actually changes in the DOM - that's why I asked what you are exactly looking for with visibilityOfElementLocated - which I have no experience with.

The chrome debugging protocol itself has some DOM events, but I find them difficult to handle. I'd rather inject some javascript via Chrome.ahk to watch for DOM changes and let it write to the browser console which I would then monitor with a callback function.

State-of-the-art javascript (and more flexible) for this seem to be so-called mutationobservers.

Since I am no js expert and I don't have your webpage available, I created a quick, minimal website via a code sandbox website to be able to test my code. See here: https://2vlwl.csb.app/ (it uses javascript to change the DOM elements, when you click).

So, I am looking here for a a hidden input element that originally has the style="display:none" attribute and a width and height of "auto" (I assume that this means no visibility in the sense of visibilityOfElementLocated). As soon, as the style gets removed, the input element will get visible and width and height will have some px values.
website start.png
website start.png (4.49 KiB) Viewed 3427 times
At first, you'll just see the text: Click here to show input element. Click it and a (so far) hidden input box will be shown; the text changes, too. If you click into the input box, it disappers again and the text changes once again. You can then toggle the visibility of the input box by clicking again the text/box.

Every time the input box (dis-)appears, the tooltip shown, created by the callback function, will reflect that change:
box visible.png
box visible.png (11.03 KiB) Viewed 3427 times
To make it even more confusing, I am also showing in this example how you can check for the changing text via a timer. The timer will check every 10 seconds, if the text has changed since the last check (the timer only starts, if you confirm the first msgbox). Of course, you could check in much shorter intervals.

In practice, I probably wouldn't combine these techniques. I would either use a mutationobserver or a timer/loop.

Code: Select all

#NoEnv
SetBatchLines, -1
SetTitleMatchMode 2
#Include Chrome.ahk											; https://www.autohotkey.com/boards/viewtopic.php?f=6&t=42890

url := "https://2vlwl.csb.app/"

; js to inject
JS =
(
		var blockedinput = document.querySelector("#blocked");
		var inputstyle = window.getComputedStyle(blockedinput);
		
		var observer = new MutationObserver(function(mutations) { mutations.forEach(function(mutation) {
			if ( mutation.attributeName === "style" &&  inputstyle.getPropertyValue("display") !== "none" 
					&&  inputstyle.width !== "auto" &&  inputstyle.height !== "auto" ) 
			{
				console.log("visible (w x h):   " + inputstyle.width + " x " + inputstyle.height );
			}
			else 
				console.log("hidden")
		  });
		});

		observer.observe(blockedinput, {
			attributes: true
		});
)

; --- Create a new headless Chrome instance ---
FileCreateDir, ChromeProfile										; creates a profile subdirectory in current folder
ChromeInst := new Chrome("ChromeProfile")
winwait, Chrome 	

; --- Connect to the page ---
if !(Page1 := ChromeInst.GetPage( , ,  "fnCallback" ))		; connect to callback function 
{
	MsgBox, Could not retrieve page!
	ChromeInst.Kill()
}
else
	Page1.WaitForLoad()

Page1.Call("Page.navigate", {"url": url})			; Navigate to url
Page1.WaitForLoad()
Page1.Call("Console.enable")								; enable console events

Page1.Evaluate(JS)												; Inject js from above

; Check with timer, if text changed
OldText :=  Page1.evaluate("document.evaluate('//*[@id=""trigger""]', document).iterateNext().textContent").value
msgbox % "Old text: `n" OldText "`n`nPlease press OK"							; timer will be started after you press OK
SetTimer, checkElement, 10000
return

checkElement:						; called every 10000 ms by timer
ShowText := Page1.evaluate("document.evaluate('//*[@id=""trigger""]', document).iterateNext().textContent").value
if (OldText != ShowText)
{
	msgbox, Text changed -- New text:`n`n %ShowText%
	OldText := ShowText
}
else
	msgbox Text didn't change!
return

Esc::ExitApp

fnCallback(Event){
	if (Event.Method == "Console.messageAdded")					; check for console messages
		ToolTip, % Event.params.message.text, 150, 150			;Clicked %Text% times on page %PageNum%
}
I hope this helps!

Edit: Of course, the CDP's DOM events might help as well (you could use again a callback function), depending on what you are looking for exactly. Perhaps I'll add an example later.
gregster
Posts: 3855
Joined: 30 Sep 2013, 06:48

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

11 Nov 2019, 08:17

wbpbu wrote:
11 Nov 2019, 08:13
Is there a version of this for AHK v2?
https://github.com/G33kDude/Chrome.ahk/issues/12
Not yet, afaik. You might be better off using Selenium, if you are in a hurry and are focused on AHK v2.
Tre4shunter
Posts: 54
Joined: 26 Jan 2016, 16:05

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

27 Nov 2019, 09:10

Hey guys -

Been using this class for a while now - but recently it seems like my footer is no longer including the pageNumber Values...it worked for quite a while, but now it just shows empty values for pageNumber and totalPages classes.

Any ideas?

Heres the code:

Code: Select all

footer := "<style>h1 {font-size:7px; margin-left:30px; padding:0; margin-top:0; margin-bottom:0;} </style>"
footer .= "<h1>PO# " Paths["po"] " - Page <span class=""pageNumber""></span> of <span class=""totalPages""></span></h1>"
Base64PDF := PageInstP.Call("Page.printToPDF", {"printBackground":Chrome.Jxon_True(),"displayHeaderFooter":Chrome.Jxon_True(),"headerTemplate":header,"footerTemplate":footer}).data	
The footer does show up on my PDF - but the pagenumber values are blank? Doesnt looks like Chrome has changed anything internally, so, not quite sure whats going on - any ideas are appreciated!

Thanks
Tre4shunter
Posts: 54
Joined: 26 Jan 2016, 16:05

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

27 Nov 2019, 13:55

Looks like it was some CSS in a @media print class

I ended up commenting this out:

Code: Select all

  /*
  @page {
  margin: .8cm;
  }
  */
  
Now my PageNumbers seem to be fine...not sure why it was causing the issue though.

Thanks!
bembem
Posts: 1
Joined: 14 Dec 2019, 02:17

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

14 Dec 2019, 02:21

hi grepster and everyone ! Can you teach me how to get the result from javascript function? I want to write a script to wait until an element exist.Thanks
gregster
Posts: 3855
Joined: 30 Sep 2013, 06:48

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

14 Dec 2019, 03:47

bembem wrote:
14 Dec 2019, 02:21
hi grepster and everyone ! Can you teach me how to get the result from javascript function? I want to write a script to wait until an element exist.Thanks
Welcome to the forum!
Do you have AHK and Chrome.ahk already running and looked through the included examples and this thread ? What have you tried so far?

Sounds doable, but a few more details would be nice (and ideally, if possible, a link to the targeted website). At least, the type and the html source code of the element would be useful.
It's not necessarily beginner stuff and if I make wild assumptions about what you need, it might not help you at all.

Edit:
This is a related example that watches for certain, specific changes in the DOM: https://www.autohotkey.com/boards/viewtopic.php?f=6&t=42890&p=299221#p299221
It shows two alternatives: a simpler one with a timer that checks in certain intervals (that you might have to combine with try in your case) and a more advanced one with a javascript-mutationobserver.
I could think of more options - it just depends on what you need exactly.
DanielToward13
Posts: 71
Joined: 18 May 2017, 10:56

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

18 Dec 2019, 04:15

The injected JavaScript code is removed from the console (DOM) after refreshing the Chrome tab. The socket connection to the debug interface will stay alive but the injected JS code will be removed after refreshing the page. Is there a way to keep alive the injected JavaScript code in the console even after refreshing it? Something like Greasemonkey extension but using AHK.

Move your mouse to the menu:

Code: Select all

#Persistent
#SingleInstance Force
#NoEnv
SetBatchLines, -1
SetTitleMatchMode 2
#Include Chrome.ahk											; https://www.autohotkey.com/boards/viewtopic.php?f=6&t=42890

url := "https://www.autohotkey.com/docs/AutoHotkey.htm"


JS_MutationObserver =
(
function init() {
  if (window.MutationObserver) {
    startMutationObserver();
  } else {
    startMutationEvents();
  }
}

function startMutationObserver() {
  // target node to be observed
  var target = document.querySelector('body');
  // mutation observer config object with the listeners configuration
  var config = listenOnlyAttributeChanges();

  // mutation observer instantiation
  var mutationObs = new MutationObserver(callbackAttributeChange);

  // observe initialization
  mutationObs.observe(target, config);
}

function listenOnlyAttributeChanges() {
  return {
    attributes: true,
    childList: true,
    subtree: true
  };
}

function callbackAttributeChange(mutations, mutationObs) {
  for (var i = 0, length = mutations.length; i < length; i++) {
    var mutation = mutations[i];
    if (mutation.type === 'attributes' && mutation.attributeName === 'style') {
      var target = mutation.target;
      console.log(target.id + target.style.display);
      console.log("---" + target.id + target.class); 
    }
  }
}

var addedNodes = 0;
var colorpage = false;
init();
)




FileCreateDir, ChromeProfile										; creates a profile subdirectory in current folder
ChromeInst := new Chrome("ChromeProfile")
winwait, Chrome 

if !(Page1 := ChromeInst.GetPage( , ,  "fnCallback" ))		; connect to callback function 
{
	MsgBox, Could not retrieve page!
	ChromeInst.Kill()
}
else
	Page1.WaitForLoad()
sleep, 1000
Page1.Call("Page.navigate", {"url": url})			; Navigate to url
Page1.WaitForLoad()
Page1.Call("Console.enable")								; enable console events




Page1.Evaluate(JS_MutationObserver)


Esc::ExitApp
fnCallback(Event){
	if (Event.Method == "Console.messageAdded")					; check for console messages
		if (Event.params.message.text == "---undefined") {
			global elementChangedFlag
			elementChangedFlag := true
			MsgBox, "Something has changed!"
		}
}
Stavencross
Posts: 82
Joined: 24 May 2016, 16:42

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

21 Dec 2019, 12:15

Tre4shunter wrote:
09 Sep 2019, 13:55
A project ive been working on which is fully built around Geekdudes Chrome.ahk Class. Its amazing!

The interface is fully designed with HTML/CSS/Javascript. I use AHK to tie in Excel - where the data is populated from, as well as other resources like internal databases, config files, and do things like attach the pdf's to Emails automatically etc, etc for a fully automated order entry workflow.

I owe you a Beer my man! Thanks again! :clap: :clap:

https://imgur.com/a/pPM06JN

I'd love to see your ahk script and look at how you're running all this stuff, but understandable if it's not shareable
Tre4shunter
Posts: 54
Joined: 26 Jan 2016, 16:05

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

23 Dec 2019, 10:30

Hey @Stavencross,

I have no problem showing you, but there are just quite a few pieces to it. Here is the main Script.
Obviously, i wrote alot of CSS/HTML for it as well.

The ahk is really just displaying my HTML/CSS and using Chrome.ahk to display and attach to it so i can 'Control' Everything with AHK. Most of the page logic is actually done with javascript.

AHK is only used to trigger external functions. Function call back to main ahk script -> Calls external ahk script, connects to chrome instance [Does stuff] -> return.

Main AHK:

Code: Select all

SetBatchLines, -1
#persistent
#SingleInstance, Off
#NoEnv

#Include, %A_ScriptDir%\Includes\pArr.ahk
#include, %A_ScriptDir%\Includes\XL.ahk
#Include, %A_ScriptDir%\chrome\Chrome.ahk

Onexit, GarbageCollect
gui, message:new, hwndMyhwnd

;Global Variable Definitions
GLOBAL port, profile, UpdateData, XL, GUID, PageInst ;gPageInst
GLOBAL Manufacturer, RepOrDist, OrderEntryDetails, OrderPrin

;Connect to the calling Excel Workbook
XL := XL_Check().Activeworkbook
If(!isObject(XL))
{
	msgbox, Order form cannot connect to Excel.
	exitapp
}

;Global References to Quote page and Qform
Q 		:= xla.sheets("Quote")
QF 		:= xla.sheets("Qform")

ThisScriptsHWND := Format("{:i}", Myhwnd)
XL.OFhwnd := ThisScriptsHWND 

;Get the Workbook name so we can pull custom configs for each principal (Config.ini and /Manufacturer/xxx.txt files)
xlname 	:= StrSplit(XL.name,".").1
If (SubStr(xlname,StrLen(xlname),1) < "9")
	xlname:=SubStr(xlname,1,StrLen(xlname)-1)

IniRead, RepOrDist, %A_ScriptDir%\Config.ini, %xlname%, Rep
IniRead, Manufacturer, %A_ScriptDir%\Config.ini, %xlname%, Manufacturer
IniRead, OrderPrin, %A_ScriptDir%\Config.ini, %xlname%, Principal

;Update CSS Files in users temp folder
IniRead, NewVer, %A_ScriptDir%\Config.ini, OrderVer, Ver
IniRead, CurVer, %A_Temp%\ver.ini, OrderVer, Ver

if(NewVer > CurVer)  OR (CurVer = "ERROR"){
	fileread, css, % A_ScriptDir "\form2css.css"
	filedelete, % A_Temp "\form2css.css"
	fileappend, % css, % A_Temp "\form2css.css"

	fileread, css, % A_ScriptDir "\form2Ack.css"
	filedelete, % A_Temp "\form2Ack.css"
	fileappend, % css, % A_Temp "\form2Ack.css"

	IniWrite, % NewVer, % A_Temp "\ver.ini", OrderVer, Ver
}

OrderEntryDetails := GetEntryDetails(Manufacturer)
If(OrderEntryDetails = -1){
	msgbox, Order Entry Failed (Exit Code -15)
	return
}

; --- Create a new Chrome instance ---
path := A_Scriptdir "\form2.html"
Port := 9222

if (Chromes := Chrome.FindInstances()).HasKey(Port){
	port := chromes.maxIndex() + 1
	profile := A_Temp "\ChromeProfile" Port
}else{
	profile := A_Temp "\ChromeProfile"
}
;Create a chrome profile directory if one doesnt exist
if !FileExist(profile)
	FileCreateDir, % profile

ChromeInst := new Chrome(profile,"--app=" path, "--window-size=970,1000 --disable-web-security --bwsi --disable-component-extensions-with-background-pages",,port)
BoundCallback := Func("Callback").Bind()

WinWaitActive, Order Form,, 10
if ErrorLevel
	goto, GarbageCollect

if !(PageInst := ChromeInst.GetPageByTitle("", "contains",,BoundCallBack))
{
	MsgBox, Could not retrieve page ""!
	ChromeInst.Kill()
	ExitApp
}

;On first load - fill out document title, and user information
PageInst.WaitForLoad()
PageInst.Call("Console.enable",,false)
PageInst.WaitforLoad()
PageInst.Evaluate("document.title = '" XL.name "';")

LogoPath := A_scriptdir "\Images\" Manufacturer ".png"
stringreplace, LogoPath, LogoPath, \, \\, All


rdy := WaitEval("document.getElementById('prinIMG').src")
if(rdy = -1)
{
	PageInst.Evaluate("alert('Order form will close - please reopen.  If you continue to get this message multiple times, let MD know.')")
	ExitApp
}

PageInst.Evaluate("document.getElementById('prinIMG').src = '" LogoPath "';")

;Pull Information from Excel initially to populate Order Form
FillQformData()
PopulateItemList(Manufacturer)
return

ESC::
GarbageCollect:
	try{
		ChromeInst.Kill()
		PageInst.Disconnect()
	}
	xl.OFhwnd := 0
exitapp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;BELOW HERE IS FOR THE CHROME INSTANCE FUNCTIONS AND PAGES
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

FillQformData() {
	;PageInst := gPageInst
	Q 		:= xl.sheets("Quote")
	QF 		:= xl.sheets("Qform")
	
	envget, firstlast, first.last
	envget, testmail, testmail
	envget, testphon, testphon

	rTime := 
	rTime += 7, Days

	FormatTime, fTime, A_Now, ShortDate
	FormatTime, rTimeFinal, % rTime, ShortDate

	bCitStaZip := trim(qf.range("_bCitStaZip").text)
	sCitStaZip := trim(qf.range("_sCitStaZip").text)
	
	bZipPos := RegExMatch(bCitStaZip, "i)([0-9]{0,5}(?:-[0-9]{4})?$)", bZip)
	bCitSta_NoZip := trim(StrReplace(bCitStaZip,bZip))
	bStatePos := RegExMatch(bCitSta_NoZip, "i)(\b\w{2}\b$)", bState)
	bCity := Trim(StrReplace(bCitSta_NoZip,bState))
	
	sZipPos := RegExMatch(sCitStaZip, "i)([0-9]{0,5}(?:-[0-9]{4})?$)", sZip)
	sCitSta_NoZip := trim(StrReplace(sCitStaZip,sZip))
	sStatePos := RegExMatch(sCitSta_NoZip, "i)(\b\w{2}\b$)", sState)
	sCity := Trim(StrReplace(sCitSta_NoZip,sState))
	
	OnGList = 4600,4920
	MiningList = 1241,1000,1221,1400,2895,3532,3200
	EnvironList = 4300,4941,4950,4953
	ChemList = 2813,2800,2821,2834,2879,2891,2895,2900,2910,3000
	FnBList = 2000,2010,2040,2100
	try
	{
		Sic := qf.range("_sicCode").text
		If Sic in %OnGList%
			BJSText := "document.getElementById('indType').value = 'Industrial';"
		else if Sic in %MiningList% 
			BJSText := "document.getElementById('indType').value = 'Mining and Aggregates';"
		else if Sic in %EnvironList% 
			BJSText := "document.getElementById('indType').value = 'Environmental';"
		else if Sic in %ChemList% 
			BJSText := "document.getElementById('indType').value = 'Industrial';"
		else if Sic in %FnBList% 
			BJSText := "document.getElementById('indType').value = 'Food & Beverage';"
		else 
			BJSText := "document.getElementById('indType').value = 'Industrial';"
	}
	
	cpnum := (qf.range("_slsnum").value < 80) AND (qf.range("_slsnum").value > 69) ? "831" : "931"
	ph := RegexMatch(Format("{:T}",qf.range("_bPhone").Text),"[2-9]\d{2}-\d{3}-\d{4}",onlyphone)

	bJSText .= "document.getElementById('eDate').value = '" fTime "'; document.getElementById('submitName').value = '" firstlast "'; document.getElementById('submitPhone').value = '" testphon "'; document.getElementById('submitEmail').value = '" testmail "';"
	. "document.getElementById('bName').value 		=	 '" trim(Format("{:T}",qf.range("_bName").Text)) "';"
	. "document.getElementById('bAdd1').value 		=	 '" trim(Format("{:T}",qf.range("_bAdd1").Text)) "';"
	. "document.getElementById('bAdd2').value 		=	 '" trim(Format("{:T}",qf.range("_bAdd2").Text)) "';"
	. "document.getElementById('bAdd3').value 		=	 '" trim(Format("{:T}",qf.range("_bAdd3").Text)) "';"
	. "document.getElementById('bCit').value 		=	 '" Format("{:T}",bCity) "';"
	. "document.getElementById('bSta').value 		=	 '" Format("{:U}",bState) "';"
	. "document.getElementById('bZip').value 		=	 '" SubStr(bZip,1,5) "';"
	. "document.getElementById('bContact').value 	=	 '" Format("{:T}",qf.range("_bContact").Text) "';"
	. "document.getElementById('bPhone').value 		=	 '" onlyphone "';"
	. "document.getElementById('bEmail').value 		=	 '" Format("{:T}",qf.range("_bEmail").Text) "';"
	. ""
	. "document.getElementById('sName').value 		=	 '" trim(Format("{:T}",qf.range("_sName").Value)) "';"
	. "document.getElementById('sAdd1').value 		=	 '" trim(Format("{:T}",qf.range("_sAdd1").Text)) "';"
	. "document.getElementById('sAdd2').value 		=	 '" trim(Format("{:T}",qf.range("_sAdd2").Text)) "';"
	. "document.getElementById('sAdd3').value 		=	 '" trim(Format("{:T}",qf.range("_sAdd3").Text)) "';"
	. "document.getElementById('sCit').value 		=	 '" Format("{:T}",sCity) "';"
	. "document.getElementById('sSta').value 		=	 '" Format("{:U}",sState) "';"
	. "document.getElementById('sZip').value 		=	 '" SubStr(sZip,1,5) "';"
	. "document.getElementById('sContact').value 	=	 '" Format("{:T}",qf.range("_bContact").Text) "';"
	. "document.getElementById('sPhone').value 		=	 '" onlyphone "';"
	. "document.getElementById('sEmail').value 		=	 '" Format("{:T}",qf.range("_bEmail").Text) "';"
	
	BJSText.= "document.getElementById('AccNum').value 		=	 '" Format("{:T}",trim(qf.range("_gesAccNum").text)) "';"
	. "document.getElementById('acct1').value 		=	 '" Format("{:T}",trim(qf.range("_gesAccNum").text)) "';"
	. "document.getElementById('ContactID').value 	=	 '" StrReplace(qf.range("_contactID").text,"\","\\") "';"
	. "document.getElementById('sman').value 		=	 '" Format("{:T}",qf.range("_slsinitials").Text) "';"
	. "document.getElementById('smanNumber').value 	=	 '" Format("{:T}",qf.range("_slsnum").text) "';"
	. "document.getElementById('qNumber').value 	=	 '" Format("{:T}",qf.range("_qNum").text) "';"
	. "document.getElementById('qRef').value 		=	 '" Format("{:T}",q.range("_qRef").text) "';"
	. "document.getElementById('cpRef').value 		=	 '" Format("{:T}",qf.range("_qNum").text) "';"
	. "document.getElementById('billComp').value 		=	 '" Format("{:T}",qf.range("_bName").text) "';"
	. "document.getElementById('prinNum').value 	=	 '" OrderPrin "';"
	. "document.getElementById('repID').value 		=	 '" cpnum "';"
	. "document.getElementById('sls1').value		= 	 '" Format("{:T}",qf.range("_slsnum").text) "';"
	. "document.getElementById('csrNum').value		= 	 '" Format("{:T}",qf.range("_CSR").text) "';"
	. "document.getElementById('sprNum').value		= 	 '" Format("{:T}",qf.range("_SPR").text) "';"
	. "document.getElementById('Manufact').value	= 	 '" Manufacturer "';"
	. "document.getElementById('appReview').value	= 	 '" Format("{:T}",qf.range("A308").text) "';"
	. "document.getElementById('sameShip').checked = true;"	
	. "document.getElementById('rDelivery').value = '" rTimeFinal "';"
	. "sameAsShip(document.getElementById('sameShip'));"
	. "readTextFile();"
	PageInst.Evaluate(bJSText)
	
}

PopulateItemList(Manufacturer,AdditionalFuncs:="0")
{
	local col1,col2,col3,col4,col5,col6,col7
	
	ChromeObjn := {"base": Chrome, "DebugPort": Port}
	nPageInst := ChromeObjn.GetPageByurl("file:///" StrReplace(Path,"\","/"), "startswith")
	
	try {
		DeleteLines := "var table = document.getElementById('tablelineItems');for(var i = table.rows.length - 3; i > 0; i--){table.deleteRow(i);}"
		nPageInst.Evaluate(DeleteLines)
	}
	AddLineItemsJS := "var table = document.getElementById('tablelineItems').getElementsByTagName('tbody')[0];"

	lineitems := []
	Items := XL_RangeFindAll(XL,"Qform","N1:N299","1", "N299", xlValues, xlwhole, xlbyrows, xlnext,"Row")	
	For i,Row in Items
	{

		description := 
		Index := A_Index
		
		itemrow := XL_RangeToObj(xl,"qform","E" Row ":Q" Row)
		
		Qty := itemrow["O"][Row]
		Desc := itemrow["E"][Row]
		DiscList := itemrow["M"][Row]
		Disc := itemrow["P"][Row]
		Comm := itemrow["Q"][Row]
		
		try {
			ItemDesc := XL_RangeToObj(xl,"qform","E" Row+1 ":N" items[A_Index + 1] - 1)
			for k,v in ItemDesc["E"] {
				if(ItemDesc["N"][Row + A_Index] = "")
					description .= v "`n"
			}
		}catch{
			ItemDesc :=
			ItemDesc := XL_RangeToObj(xl,"qform","E" Row+1 ":N" xl_getlast(xl,"qform","E299",xlup,"Row"))
			for k,v in ItemDesc["E"] {
				if(ItemDesc["N"][Row + A_Index] = "")
					if(v = "Suitability and or performance of product:") or (v = "Net Total of All Items:")
						break
					description .= v "`n"
			}
		}
		
		description := StrReplace(description,"'","\'")
		
		if(trim(StrReplace(description,"`n","")) = trim(Desc))
			description := ""
		
		col1 := Index
		col2 := Format("{:1d}",Qty)
		col3 := trim(StrReplace(Desc,"'","\'"))
		col4 := "$" Format("{:.02f}",DiscList/((100-Disc)/100))
		if(RepOrDist = "1"){
			;Rep Line Cost/Commision Calucations
			col5 := "$" Format("{:.02f}",DiscList*Qty)
			col6 := Format("{:.03f}",Disc) "%"
			col7 := "$" Format("{:.02f}",strReplace(col5,"$") * Comm)
		}else{	
			;Distributor Line Cost/Commision Calculations
			GilsonCost := "$" Format("{:.02f}",(DiscList*(1-Comm))*qty)
			CustomerCost := Format("{:.02f}",DiscList*Qty)
			Profit := StrReplace(CustomerCost,"$","") - StrReplace(GilsonCost,"$","") 	
			
			col5 := GilsonCost
			col6 := "$" Format("{:.02f}",CustomerCost/Qty)
			col7 :=  "$" CustomerCost

			gComm := Format("{:.02f}",(1-((StrReplace(GilsonCost,"$","")/col2)/StrReplace(col4,"$",""))) * 100) "%"
			Comm := Format("{:.02f}",Disc)
			xtra := "SetCellProp();"
		}
		
		AddLineItemsJS 	.= "var row = table.insertRow(-1);"
						. "var tArea = document.createElement('textarea');"
						. "tArea.style.display = 'block';"
						. "var eInput = document.createElement('input');"
						. "var textdata, inputdata;"
						. "var cell1 = row.insertCell(0);"
						. "var cell2 = row.insertCell(1);"
						. "var cell3 = row.insertCell(2);"
						. "var cell4 = row.insertCell(3);"
						. "var cell5 = row.insertCell(4);"
						. "var cell6 = row.insertCell(5);"
						. "var cell7 = row.insertCell(6);"
						. "var cell8 = row.insertCell(7);"			
						. "var cell9 = row.insertCell(8);"	
						. "var cell10 = row.insertCell(9);"
						. ""
						. "cell1.innerHTML = '<img src=""Images\\RemoveItem.png"" id=""Row"" A_Index "">';"
						. ""
						. "cell2.innerText = '" col1 "';"
						. ""
						. "inputdata = cell3.appendChild(eInput);"
						. "inputdata.setAttribute(""type"",""text"");"	
						. "inputdata.setAttribute(""value"",""" col2 """);"	
						. "inputdata.addEventListener(""change"", function() { QtyEdit(this);});"
						. ""
						. "var inputdata,eInput = document.createElement('textarea');"
						. "inputdata = cell4.appendChild(eInput);"
						. "inputdata.textContent = '" strreplace(Rtrim(col3,"`n"),"`n","\n") "';"
						. "textdata = cell4.appendChild(tArea);textdata.textContent = '" strreplace(Rtrim(description,"`n"),"`n","\n") "';"
						. ""
						. "var inputdata,conformButton,eInput = document.createElement('input');"
						. "var dConform = document.createElement('input');"
						. "dConform.setAttribute(""type"",""button"");"
						. "dConform.setAttribute(""value"",""*"");"
						. "inputdata = cell5.appendChild(eInput);"
						. "inputdata.setAttribute(""type"",""text"");"	
						. "inputdata.setAttribute(""value"",""" col4 """);"	
						. "conformButton = cell5.appendChild(dConform);"
						. "conformButton.addEventListener(""click"", function() { DtoConform(this);});"
						. "inputdata.addEventListener(""change"", function() { QtyEdit(this);});"
						. ""
						. "var inputdata,eInput = document.createElement('input');"
						. "inputdata = cell6.appendChild(eInput);"
						. "inputdata.setAttribute(""type"",""text"");"
						. "inputdata.setAttribute(""readonly"",""true"");"						
						. "inputdata.setAttribute(""value"",""" col5 """);"	
						. ""
						. "var inputdata,eInput = document.createElement('input');"
						. "inputdata = cell7.appendChild(eInput);"
						. "inputdata.setAttribute(""type"",""text"");"	
						. "inputdata.setAttribute(""readonly"",""true"");"	
						. "inputdata.setAttribute(""value"",""" col6 """);"	
						. "inputdata.addEventListener(""dblclick"", function() { ShowDConformModal(this);});"
						. ""
						. "cell8.innerHTML = '<input type=""text"" value=""" Comm """>';"
						. ""
						. "var inputdata,eInput = document.createElement('input');"
						. "inputdata = cell9.appendChild(eInput);"
						. "inputdata.setAttribute(""type"",""text"");"	
						. "inputdata.setAttribute(""readonly"",""true"");"
						. "inputdata.setAttribute(""value"",""" col7 """);"	
						. "inputdata.addEventListener(""dblclick"", function() { ShowDConformModal(this);});"
						. ""
						. "cell10.style.display = 'none';"
						. "cell10.innerHTML = '<input type=""text"" value=""" gComm """>';"	
	}
	
	AddLineItemsJS 	.= "var row = table.insertRow(-1);"
					. ""
					. "var cell1 = row.insertCell(0);"
					. "cell1.colSpan = 8;"
					. "cell1.innerHTML = '<input type=""button"" value=""New Line"">';"
					. "cell1.addEventListener(""click"", function() { NewLineItemRow();});"
					. Xtra
	

	AddLineItemsJS .= "var buttons = document.querySelectorAll('#tablelineItems tbody tr td:nth-of-Type(1)');for(i=0; i < buttons.length; ++i){buttons[i].addEventListener(""click"", function() { DeleteRow(this);});} var tareas = document.querySelectorAll('#tablelineItems tbody tr td textarea:nth-of-type(1)');for (i=0;i<tareas.length;++i){tareas[i].style.display = 'block';autoExpand(tareas[i]);}var tareas = document.querySelectorAll('#tablelineItems tbody tr td textarea:nth-of-type(2)');for (i=0;i<tareas.length;++i){autoExpand(tareas[i]);tareas[i].style.display = 'none';} document.getElementById('termsSelect').addEventListener(""change"", function() { ShowCreditCardFields('" Manufacturer "');});"
	
	If(!AdditionalFuncs)
	{
		ExtraFormFunctions := StrSplit(OrderEntryDetails["FormFunctions"],"|")
		for k,v in ExtraFormFunctions
		{
			fileread,file, % A_ScriptDir "\Scripts\FormTexts\" v
			AddLineItemsJS .= file
		}
	}

	nPageInst.Evaluate(AddLineItemsJS "subTotal();")
	nPageInst.Disconnect()
}


SaveOrder(){
	EmailOrder := OrderEntryDetails["EmailOrder"]
	Functions := StrSplit(OrderEntryDetails["OrderFunctions"],",")
	for k,v in Functions
	{
		funcname := StrReplace(StrSplit(v,"{").1,"}")
		paramlist := StrReplace(StrSplit(v,"{").2,"}")
		eParams := []
		Params := StrSplit(paramlist,";")
		for k,v in Params
			eParams[A_Index] := %v%
		ret := %Funcname%(eParams)

		if(ret = -1)
			break
	}
}

BuyResell(){
	runwait, % "F:\userfolders\md\autohotkey\autohotkey.exe " A_ScriptDir "\scripts\BuyResell.ahk """ XL.Name """ " A_ScriptDir " " Port,,UseErrorLevel
	return % ErrorLevel
}
CheckFields(params*){
	runwait, % "F:\userfolders\md\autohotkey\autohotkey.exe " A_ScriptDir "\scripts\CheckFields.ahk """ XL.Name """ " A_ScriptDir " " Manufacturer " " Port,,UseErrorLevel
	return % ErrorLevel
}
Soap(params*){
	runwait, % "F:\userfolders\md\autohotkey\autohotkey.exe " A_ScriptDir "\scripts\Soap.ahk """ XL.Name """ " A_ScriptDir " " Port,,UseErrorLevel
	return % ErrorLevel
}
Print(params*){
	runwait, % "F:\userfolders\md\autohotkey\autohotkey.exe " A_ScriptDir "\scripts\Print.ahk """ XL.Name """ " A_ScriptDir " " Port,,UseErrorLevel
	return % ErrorLevel
}
OrderFile(params*){
	runwait, % "F:\userfolders\md\autohotkey\autohotkey.exe " A_ScriptDir "\scripts\Ordertst.ahk """ XL.Name """ " A_ScriptDir " " Port,,UseErrorLevel
	return % ErrorLevel	
}
UpdateMcs(params*){
	runwait, % "F:\userfolders\md\autohotkey\autohotkey.exe " A_ScriptDir "\scripts\UpdateMCS.ahk """ XL.Name """ " A_ScriptDir " " Port,,UseErrorLevel
	return % ErrorLevel	
}
JGBFiles(params*){
	runwait, % "F:\userfolders\md\autohotkey\autohotkey.exe " A_ScriptDir "\scripts\PrintGilco.ahk """ XL.Name """ " A_ScriptDir " " Port,,UseErrorLevel
	return % ErrorLevel		
}
GetTaxCertData(Params*){
	runwait, % "F:\userfolders\md\autohotkey\autohotkey.exe " A_ScriptDir "\scripts\GetTaxCert.ahk """ XL.Name """ " A_ScriptDir " " Port,,UseErrorLevel
	return % ErrorLevel			
}
CheckTemco(Params*){
	runwait, % "F:\userfolders\md\autohotkey\autohotkey.exe " A_ScriptDir "\scripts\CheckTemco.ahk """ XL.Name """ " A_ScriptDir " " Port,,UseErrorLevel
	return % ErrorLevel			
}
Email(Params*){
	runwait, % "F:\userfolders\md\autohotkey\autohotkey.exe " A_ScriptDir "\scripts\Email.ahk """ XL.Name """ " A_ScriptDir " " Params[1].2 " " Params[1].3 " " Port,,UseErrorLevel
	return 	
}


RunWaitOne(command) {
    shell := ComObjCreate("WScript.Shell").Exec(ComSpec " /C " command)
    return shell.StdOut.ReadAll()
}

Callback(Event) {
	; Filter for console messages starting with "AHK:"
	
	if (Event.Method = "Console.messageAdded" && InStr(Event.params.message.text, "AHK:Print") == 1)
	{
		SaveOrder()		
	} else if(Event.Method = "Console.messageAdded" && InStr(Event.params.message.text, "AHK:buyresell") == 1){
		BuyResell()
	}else if(Event.Method == "Console.messageAdded" && InStr(Event.params.message.text, "AHK:notes") == 1){
		msgbox, hello from callback AHK:notes
	}else if (Event.Method == "Console.messageAdded" && InStr(Event.params.message.text, "AHK:FieldEdit") == 1){
		If(InStr(Event.params.message.text, "CitStaZip"))
		{
			Parsed := StrSplit(StrReplace(Event.params.message.text,"AHK:FieldEdit:CitStaZip:"),"|")
			qf := xl.sheets("qform")
			If(SubStr(Parsed.1,1,1) = "b")
				CitStaZip := qf.range("_bCitStaZip").text
			else
				CitStaZip := qf.range("_sCitStaZip").text
			
			
			ZipPos := RegExMatch(CitStaZip, "i)([0-9]{0,5}(?:-[0-9]{4})?$)", Zip)
			CitSta_NoZip := trim(StrReplace(CitStaZip,Zip))
			StatePos := RegExMatch(CitSta_NoZip, "i)(\b\w{2}\b$)", State)
			City := Trim(StrReplace(CitSta_NoZip,State))
			
			If(InStr(Parsed.1, "Cit")){
				If(City <> "")
					NewStr := RegExReplace(CitStaZip, City,parsed.2)
				else
					NewStr := Parsed.2 " " State " " Zip
			}else if(InStr(Parsed.1, "Sta")){
				If(State <> "")
					NewStr := RegExReplace(CitStaZip, state,parsed.2)
				else
					NewStr := City " " Parsed.2 " " Zip
			}else if(InStr(Parsed.1, "Zip")){
				If(Zip <> "")
					NewStr := RegExReplace(CitStaZip, Zip,parsed.2)
				else
					NewStr := City " " State " " Parsed.2
			}			
			xl.sheets("qform").range("_" SubStr(Parsed.1,1,1) "CitStaZip") := NewStr
		}else{
			Parsed := StrSplit(StrReplace(Event.params.message.text,"AHK:FieldEdit:"),"|")
			xl.sheets("qform").range("_" Parsed.1) := Parsed.2
		}

	}else if(Event.Method == "Console.messageAdded" && InStr(Event.params.message.text, "AHK:itemRefresh") == 1){
		PopulateItemList(Manufacturer,"1")
	}else if(Event.Method == "Console.messageAdded" && InStr(Event.params.message.text, "AHK:ReqdFields") == 1){
		hwndval:= StrSplit(Event.params.message.text,":").3
		winget, sPID, PID, % "ahk_id" hwndval
		process, close, % sPID
	}else if(Event.Method == "Console.messageAdded" && InStr(Event.params.message.text, "AHK:SoapFail") == 1){
		hwndval:= StrSplit(Event.params.message.text,":").3
		winget, sPID, PID, % "ahk_id" hwndval
		process, close, % sPID
	}else if(Event.Method == "Console.messageAdded" && InStr(Event.params.message.text, "AHK:SiemensCC") == 1){
		LaunchCCWindow()
	}else if(Event.Method = "Inspector.Detached"){
		ExitApp
	}
}

LaunchCCWindow() {
	;mainInst := gPageInst

	if !FileExist(A_Temp "\ChromeProfileCC")
		FileCreateDir, % A_Temp "\ChromeProfileCC"
	
	CC_ChromeInst := new Chrome(A_Temp "\ChromeProfileCC", "--app=https://redacted.com"," --window-size=505,400",,"9600")
	CCBoundCallBack := Func("CC_Callback").Bind(CC_ChromeInst)
	
	WinWaitActive, Siemens CC Tokenization - Entry,,20
	if ErrorLevel
	{
		PageInst.Evaluate("alert('Could not contact Siemens Credit Card Tokenization Form');")
		CC_ChromeInst.Kill()	
	}
	if !(CCPageInst := CC_ChromeInst.GetPageByTitle("Siemens CC Tokenization - Entry", "exact",,CCBoundCallBack))
	{
		PageInst.Evaluate("alert('Could not contact Siemens Credit Card Tokenization Form');")
		CC_ChromeInst.Kill()
	}
	CCPageInst.Call("Console.enable",,false)
	CCPageInst.WaitForLoad()
	CCPageInst.Evaluate("document.querySelector('#iframewrapper > div > button').addEventListener(""click"", function() { CCSubmitClicked();});  function CCSubmitClicked(){console.log(""AHK:CC_Clicked"");}")
	CCPageInst.WaitForLoad()
}

CC_Callback(Chrome,Event) {
	
	if(Event.Method = "Inspector.Detached"){
		try
		{
			Chrome.Kill()
		}
	}else if(Event.Method = "Console.messageAdded" && InStr(Event.params.message.text, "AHK:CC_Clicked") == 1){
		;PageInst := gPageInst
	
		WinWaitActive, Siemens CC Tokenization - Status,,5
		if ErrorLevel
		{
			PageInst.Evaluate("alert('Could not contact Siemens Credit Card Tokenization Form');")
			Chrome.Kill()	
		}
		page := Chrome.getPageByTitle("Siemens CC Tokenization - Status", "exact")
		
		JS = 
		(
			var tokenstr = document.querySelector('span  #CreditToken').value
			var token = document.querySelector('table #CreditToken').value;
			var Expr = document.getElementById('ExpirationDate').value;
			var card = document.getElementById('CardType').value;

			var returnstr = tokenstr + '|' + token + '|' + Expr + '|' + card

			el = document.createElement('textarea');
			document.body.appendChild(el);
			el.textContent = returnstr;

			document.getElementsByTagName('textarea')[0].value;
		)
		TokenFields := StrSplit(page.Evaluate(JS).value,"|")
		Chrome.Kill()

		etoken := TokenFields.1
		type := TokenFields.4
		ctoken := TokenFields.2
		expr := TokenFields.3
		JS = 
		(
			document.getElementById('eToken').value = '%etoken%';
			document.getElementById('tokencardType').value = '%type%';
			document.getElementById('cToken').value = '%ctoken%';
			document.getElementById('cExpire').value = '%expr%';
			document.getElementById('cEDate').value = '%A_Now%';
		)
		PageInst.Evaluate(JS)
	}
}
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;ABOVE HERE IS FOR THE CHROME INSTANCE FUNCTIONS AND PAGES
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
;
;--------------------------------
;ORDER ENTRY FUNCTIONS BELOW HERE
;--------------------------------
GetEntryDetails(ManufactName){
	If !ManufactName
		return, -1
	
	OutArr := []
	Loop, read, % A_ScriptDir "\Manufacturers\" ManufactName ".txt"
	{
		FieldData := StrSplit(A_LoopReadLine,"|")
		OutArr[FieldData.1] := FieldData.2
	}
	return % OutArr
}


/*
    ObjRegisterActive(Object, CLSID, Flags:=0)
    
        Registers an object as the active object for a given class ID.
        Requires AutoHotkey v1.1.17+; may crash earlier versions.
    
    Object:
            Any AutoHotkey object.
    CLSID:
            A GUID or ProgID of your own making.
            Pass an empty string to revoke (unregister) the object.
    Flags:
            One of the following values:
              0 (ACTIVEOBJECT_STRONG)
              1 (ACTIVEOBJECT_WEAK)
            Defaults to 0.
    
    Related:
        http://goo.gl/KJS4Dp - RegisterActiveObject
        http://goo.gl/no6XAS - ProgID
        http://goo.gl/obfmDc - CreateGUID()
*/
ObjRegisterActive(Object, CLSID, Flags:=0) {
    static cookieJar := {}
    if (!CLSID) {
        if (cookie := cookieJar.Remove(Object)) != ""
            DllCall("oleaut32\RevokeActiveObject", "uint", cookie, "ptr", 0)
        return
    }
    if cookieJar[Object]
        throw Exception("Object is already registered", -1)
    VarSetCapacity(_clsid, 16, 0)
    if (hr := DllCall("ole32\CLSIDFromString", "wstr", CLSID, "ptr", &_clsid)) < 0
        throw Exception("Invalid CLSID", -1, CLSID)
    hr := DllCall("oleaut32\RegisterActiveObject"
        , "ptr", &Object, "ptr", &_clsid, "uint", Flags, "uint*", cookie
        , "uint")
    if hr < 0
        throw Exception(format("Error 0x{:x}", hr), -1)
    cookieJar[Object] := cookie
}

CreateGUID()
{
    VarSetCapacity(pguid, 16, 0)
    if !(DllCall("ole32.dll\CoCreateGuid", "ptr", &pguid)) {
        size := VarSetCapacity(sguid, (38 << !!A_IsUnicode) + 1, 0)
        if (DllCall("ole32.dll\StringFromGUID2", "ptr", &pguid, "ptr", &sguid, "int", size))
            return StrGet(&sguid)
    }
    return ""
}

WaitEval(JS,timeout:=10) {
	GLOBAL
	tVar := ""
	
	Start := A_TickCount
	While(tVar = "")
	{
		tVar :=PageInst.Evaluate("try{" JS "}catch{var el = ''};").Value
		If((A_TickCount - Start) > (timeout*1000))
			return, -1
	}
return
}


Markway1
Posts: 1
Joined: 24 Dec 2019, 13:49

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

24 Dec 2019, 17:27

Hello everyone, i would like to say that the library is amazing, thank you for sharing it with us!
I do apologize in advance if i don't use the correct terms for what i am about to ask,hopefully it is understandable

I would like to ask if there is a solution to my problem, i have an element without an id or name, the class varies depending on the state of the site, but it does have an angular attribute.
Here is the element:

<div ng-show="citydata.contacts" class="ng-hide">

My question would be, is there any way to get the class value of this element, by searching the angular element and then the value of the class, similar to how it can be done via id.

somevar := PageInst.Evaluate("document.getElementById('someid').className;").Value

I would really appreciate if anybody could help me with this, thank you!
yezilaoda
Posts: 3
Joined: 25 Mar 2019, 01:19

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

02 Jan 2020, 03:11

i want a "Automate IE using native AutoHotkey" like this
is it easy?
Stavencross
Posts: 82
Joined: 24 May 2016, 16:42

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

02 Jan 2020, 12:13

Tre4shunter wrote:
23 Dec 2019, 10:30
Hey @Stavencross,

I have no problem showing you, but there are just quite a few pieces to it. Here is the main Script.
Obviously, i wrote alot of CSS/HTML for it as well.

The ahk is really just displaying my HTML/CSS and using Chrome.ahk to display and attach to it so i can 'Control' Everything with AHK. Most of the page logic is actually done with javascript.

AHK is only used to trigger external functions. Function call back to main ahk script -> Calls external ahk script, connects to chrome instance [Does stuff] -> return.
@Tre4shunter
I have a question for you, since you, like me, are using chrome.ahk to run an HTML/CSS/JS GUI.

When you trigger events via AHK (evaluate JS on the page from AHK, or navigate to a new page from AHK), do you get stuck in a sleep loop? https://github.com/G33kDude/Chrome.ahk/issues/11 this has been a huge challenge for me, as the fix posted here doesn't work, and I'm hoping you overcame it somehow.
Tre4shunter
Posts: 54
Joined: 26 Jan 2016, 16:05

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

02 Jan 2020, 13:15

I have actually run into that - but to be honest I'm not 100% certain what i actually did to overcome it. I make sure i trigger PageInst.WaitForLoad() after doing any sort of javascript eval which modifies the page, etc - and have the interval set to 100(Not sure if i changed it from what it is originally).

I definitely don't run into those issues normally - only if i run the program on a system which is very slow, then occasionally i will see it again. Another thing i've done is just run a Try{}catch{} in a loop until a certain element exists on the page, to make sure the page is fully loaded. A bit hacky, but seemed to help in certain instances where my JS wouldnt eval right because an element was available/ready.
Stavencross
Posts: 82
Joined: 24 May 2016, 16:42

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

02 Jan 2020, 13:26

Tre4shunter wrote:
02 Jan 2020, 13:15
I have actually run into that - but to be honest I'm not 100% certain what i actually did to overcome it. I make sure i trigger PageInst.WaitForLoad() after doing any sort of javascript eval which modifies the page, etc - and have the interval set to 100(Not sure if i changed it from what it is originally).

I definitely don't run into those issues normally - only if i run the program on a system which is very slow, then occasionally i will see it again. Another thing i've done is just run a Try{}catch{} in a loop until a certain element exists on the page, to make sure the page is fully loaded. A bit hacky, but seemed to help in certain instances where my JS wouldnt eval right because an element was available/ready.
@Tre4shunter
Did you modify your chrome.ahk at all? If so, can you post it? Maybe I can figure it out between the script you posted & your chrome.ahk.
gregster
Posts: 3855
Joined: 30 Sep 2013, 06:48

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

03 Jan 2020, 04:24

yezilaoda wrote:
02 Jan 2020, 03:11
i want a "Automate IE using native AutoHotkey" like this
is it easy?
It already exists. Internet Explorer can be automated via its COM interface - there are plenty of examples on the forum.
No third-party programs or additional AHK libraries needed (in AHK 1.1.x) - it's built-in in MS Windows and can easily be accessed via native AHK. Very basic example in the docs: https://www.autohotkey.com/docs/commands/ComObjCreate.htm#ExIE

There are even YT videos, created by forum member Joe Glines: see Web Scraping with AutoHotkey and IE
Stavencross
Posts: 82
Joined: 24 May 2016, 16:42

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

04 Jan 2020, 16:11

Consider the following:
HTML PAGE

Code: Select all

	<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<button onclick="console.log('AHK:getState')" id="TEST">TEST ME</button>
</body>
</html>
AUTOHOTKEY SCRIPT

Code: Select all

#Persistent
#include Chrome.ahk

FileCreateDir, ChromeProfile 
global ChromeInst := new Chrome("ChromeProfile", "http://localhost/test.html") 
sleep, 3000 

global pageInst:= ChromeInst.GetPageByURL("http://localhost/test.html",,,"event_callBack")
pageInst.WaitForLoad()
pageInst.Call("Console.enable") 

event_callBack(Event)
{	
	if (Event.Method == "Console.messageAdded"  && InStr(Event.params.message.text,"AHK:") >=1) 
	{	
		Text := Event.params.message.text 
		msgbox, % text
		if(InStr(Text, "{") && inStr(Text, "}")) {	
			Split := StrSplit(Text, "|", "AHK")
		} else {
			Split := StrSplit(Text, ",","AHK:") 			
		}
		fnName := Split[1]  
		;Split.RemoveAt(1)  
		%fnName%() 
		;printArray(Split,1)
 	} else if(Event.Method == "Inspector.detached") { 
		
	}
}
getState() ;This will collect the readystate and msgbox "complete"
return

getState() {
	global pageInst
	readyState:= pageInst.Evaluate("document.readyState").value
	msgbox, % readyState
}
When auto-executed within the first return, getState() will correctly return the document.readyState of the current window. However, when you click the button in the HTML page, you end up caught in a sleep loop at line 241 of chrome.ahk

Code: Select all

; Wait for the response
			this.responses[ID] := False
			while !this.responses[ID]
				Sleep, 50
Now, I can comment out this code, or break this loop, but the issue here is that I'm completely and totally unable to get values of items within the page because of this.

Code: Select all

str:=pageInst.Evaluate("document.getElementById('test').innerHTML).value	
msgbox, % str
This is another example where, if executed before the return (outside of a function), it works perfectly. However, as a function

Code: Select all

getValue() {
str:=pageInst.Evaluate("document.getElementById('test').innerHTML).value	
msgbox, % str
}
We end up in another sleep loop.

Please for the love of god if someone has the answer let me know.
Stavencross
Posts: 82
Joined: 24 May 2016, 16:42

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

04 Jan 2020, 18:53

Thanks to @Tre4shunter for helping out with this! Discovered that somehow, the PageInst is loosing its reference, so we add reconnectToPage() with each callback to ensure it doesn't die. This should help solve the endless sleeps on response. (again it was because you couldn't connect to the page and were therefore unable to get the response from the callback)

Code: Select all

#Persistent
#include %A_ScriptDir%\Lib\Chrome2.ahk

FileCreateDir, ChromeProfile 
global ChromeInst := new Chrome("ChromeProfile", "http://localhost/test.html") ;connect to page
sleep, 3000 

global pageInst:= ChromeInst.GetPageByURL("http://localhost/test.html",,,"event_callBack")
pageInst.WaitForLoad()
pageInst.Call("Console.enable") 

getState() ;load complete
ESC::
ExitApp
return ;END AUTOEXECUTE

event_callBack(Event)
{	
	if (Event.Method == "Console.messageAdded"  && InStr(Event.params.message.text,"AHK:") >=1) 
	{	
		global PageInst:=reconnectToPage() ;<----------------- reconnect on each func call
		Text := Event.params.message.text 		
		Split := StrSplit(Text, ",","AHK:") 			
		fnName := Split[1]  
		%fnName%() ;Allows you to call a func 
 	} else if(Event.Method == "Inspector.detached") { 
		
	}
}

getState() {	
	readyState:= pageInst.Evaluate("document.readyState").value
	trayTip,Load, % readyState,2
}

reconnectToPage() {
	global ChromeInst	
	url := Chrome.GetPageList()[1].url ;get the URL of the first page in the debug port - hopefully this is our app.
	global pageInst:= ChromeInst.GetPageByURL(url,,,"event_callBack") ;re-attach 
	return pageInst
}

Return to “Scripts and Functions”

Who is online

Users browsing this forum: swub and 60 guests