Page 1 of 10

Get the URL of the current (active) browser tab

Posted: 08 Jun 2014, 13:19
by atnbueno
Hello everyone.

Short version: The code below finds out the URL of the current (active) browser tab for most of the modern browsers.

Long version:
How to get the URL of the browser tab I'm using? The problem is quite common and there have been scripts to tackle it here and in the old forum.

The best solution for quite sometime (IMHO) was Sean's DDE code but unfortunately Google Chrome doesn't support DDE. Recent changes to Chrome invalidated the Chrome_OmniboxView workaround so I didn't knew how to get it until I saw uname's use of the ACC library (Sean strikes again :D). uname's code depended on the language used in Windows (I use Win7 x64 in Spanish) but I found out a workaround. I even managed to extend my solution to cover most of the Chromium-based browsers (all but Vivaldi), and even got it to work in Maxthon.

Anyway, the code below finds out the URL of the current (active) browser tab. I've tested successfully (on Win7 x64 Spanish) with the following browsers:
  • Google Chrome
  • Mozilla Firefox
  • Internet Explorer 11
  • Opera
  • Microsoft Edge
  • Opera 12
  • Maxthon
  • Iron
  • Coowon
  • Slimjet
  • Vivaldi (if run with the --force-renderer-accessibility switch)
I'd appreciate any feedback, particularly if it DOESN'T work :P

Code: Select all

; AutoHotkey Version: AutoHotkey 1.1
; Language:           English
; Platform:           Win7 SP1 / Win8.1 / Win10
; Author:             Antonio Bueno <user atnbueno of Google's popular e-mail service>
; Short description:  Gets the URL of the current (active) browser tab for most modern browsers
; Last Mod:           2016-05-19

Menu, Tray, Icon, % A_WinDir "\system32\netshell.dll", 86 ; Shows a world icon in the system tray

ModernBrowsers := "ApplicationFrameWindow,Chrome_WidgetWin_0,Chrome_WidgetWin_1,Maxthon3Cls_MainFrm,MozillaWindowClass,Slimjet_WidgetWin_1"
LegacyBrowsers := "IEFrame,OperaWindowClass"

^+!u::
	nTime := A_TickCount
	sURL := GetActiveBrowserURL()
	WinGetClass, sClass, A
	If (sURL != "")
		MsgBox, % "The URL is """ sURL """`nEllapsed time: " (A_TickCount - nTime) " ms (" sClass ")"
	Else If sClass In % ModernBrowsers "," LegacyBrowsers
		MsgBox, % "The URL couldn't be determined (" sClass ")"
	Else
		MsgBox, % "Not a browser or browser not supported (" sClass ")"
Return

GetActiveBrowserURL() {
	global ModernBrowsers, LegacyBrowsers
	WinGetClass, sClass, A
	If sClass In % ModernBrowsers
		Return GetBrowserURL_ACC(sClass)
	Else If sClass In % LegacyBrowsers
		Return GetBrowserURL_DDE(sClass) ; empty string if DDE not supported (or not a browser)
	Else
		Return ""
}

; "GetBrowserURL_DDE" adapted from DDE code by Sean, (AHK_L version by maraskan_user)
; Found at http://autohotkey.com/board/topic/17633-/?p=434518

GetBrowserURL_DDE(sClass) {
	WinGet, sServer, ProcessName, % "ahk_class " sClass
	StringTrimRight, sServer, sServer, 4
	iCodePage := A_IsUnicode ? 0x04B0 : 0x03EC ; 0x04B0 = CP_WINUNICODE, 0x03EC = CP_WINANSI
	DllCall("DdeInitialize", "UPtrP", idInst, "Uint", 0, "Uint", 0, "Uint", 0)
	hServer := DllCall("DdeCreateStringHandle", "UPtr", idInst, "Str", sServer, "int", iCodePage)
	hTopic := DllCall("DdeCreateStringHandle", "UPtr", idInst, "Str", "WWW_GetWindowInfo", "int", iCodePage)
	hItem := DllCall("DdeCreateStringHandle", "UPtr", idInst, "Str", "0xFFFFFFFF", "int", iCodePage)
	hConv := DllCall("DdeConnect", "UPtr", idInst, "UPtr", hServer, "UPtr", hTopic, "Uint", 0)
	hData := DllCall("DdeClientTransaction", "Uint", 0, "Uint", 0, "UPtr", hConv, "UPtr", hItem, "UInt", 1, "Uint", 0x20B0, "Uint", 10000, "UPtrP", nResult) ; 0x20B0 = XTYP_REQUEST, 10000 = 10s timeout
	sData := DllCall("DdeAccessData", "Uint", hData, "Uint", 0, "Str")
	DllCall("DdeFreeStringHandle", "UPtr", idInst, "UPtr", hServer)
	DllCall("DdeFreeStringHandle", "UPtr", idInst, "UPtr", hTopic)
	DllCall("DdeFreeStringHandle", "UPtr", idInst, "UPtr", hItem)
	DllCall("DdeUnaccessData", "UPtr", hData)
	DllCall("DdeFreeDataHandle", "UPtr", hData)
	DllCall("DdeDisconnect", "UPtr", hConv)
	DllCall("DdeUninitialize", "UPtr", idInst)
	csvWindowInfo := StrGet(&sData, "CP0")
	StringSplit, sWindowInfo, csvWindowInfo, `" ;"; comment to avoid a syntax highlighting issue in autohotkey.com/boards
	Return sWindowInfo2
}

GetBrowserURL_ACC(sClass) {
	global nWindow, accAddressBar
	If (nWindow != WinExist("ahk_class " sClass)) ; reuses accAddressBar if it's the same window
	{
		nWindow := WinExist("ahk_class " sClass)
		accAddressBar := GetAddressBar(Acc_ObjectFromWindow(nWindow))
	}
	Try sURL := accAddressBar.accValue(0)
	If (sURL == "") {
		WinGet, nWindows, List, % "ahk_class " sClass ; In case of a nested browser window as in the old CoolNovo (TO DO: check if still needed)
		If (nWindows > 1) {
			accAddressBar := GetAddressBar(Acc_ObjectFromWindow(nWindows2))
			Try sURL := accAddressBar.accValue(0)
		}
	}
	If ((sURL != "") and (SubStr(sURL, 1, 4) != "http")) ; Modern browsers omit "http://"
		sURL := "http://" sURL
	If (sURL == "")
		nWindow := -1 ; Don't remember the window if there is no URL
	Return sURL
}

; "GetAddressBar" based in code by uname
; Found at http://autohotkey.com/board/topic/103178-/?p=637687

GetAddressBar(accObj) {
	Try If ((accObj.accRole(0) == 42) and IsURL(accObj.accValue(0)))
		Return accObj
	Try If ((accObj.accRole(0) == 42) and IsURL("http://" accObj.accValue(0))) ; Modern browsers omit "http://"
		Return accObj
	For nChild, accChild in Acc_Children(accObj)
		If IsObject(accAddressBar := GetAddressBar(accChild))
			Return accAddressBar
}

IsURL(sURL) {
	Return RegExMatch(sURL, "^(?<Protocol>https?|ftp)://(?<Domain>(?:[\w-]+\.)+\w\w+)(?::(?<Port>\d+))?/?(?<Path>(?:[^:/?# ]*/?)+)(?:\?(?<Query>[^#]+)?)?(?:\#(?<Hash>.+)?)?$")
}

; The code below is part of the Acc.ahk Standard Library by Sean (updated by jethrow)
; Found at http://autohotkey.com/board/topic/77303-/?p=491516

Acc_Init()
{
	static h
	If Not h
		h:=DllCall("LoadLibrary","Str","oleacc","Ptr")
}
Acc_ObjectFromWindow(hWnd, idObject = 0)
{
	Acc_Init()
	If DllCall("oleacc\AccessibleObjectFromWindow", "Ptr", hWnd, "UInt", idObject&=0xFFFFFFFF, "Ptr", -VarSetCapacity(IID,16)+NumPut(idObject==0xFFFFFFF0?0x46000000000000C0:0x719B3800AA000C81,NumPut(idObject==0xFFFFFFF0?0x0000000000020400:0x11CF3C3D618736E0,IID,"Int64"),"Int64"), "Ptr*", pacc)=0
	Return ComObjEnwrap(9,pacc,1)
}
Acc_Query(Acc) {
	Try Return ComObj(9, ComObjQuery(Acc,"{618736e0-3c3d-11cf-810c-00aa00389b71}"), 1)
}
Acc_Children(Acc) {
	If ComObjType(Acc,"Name") != "IAccessible"
		ErrorLevel := "Invalid IAccessible Object"
	Else {
		Acc_Init(), cChildren:=Acc.accChildCount, Children:=[]
		If DllCall("oleacc\AccessibleChildren", "Ptr",ComObjValue(Acc), "Int",0, "Int",cChildren, "Ptr",VarSetCapacity(varChildren,cChildren*(8+2*A_PtrSize),0)*0+&varChildren, "Int*",cChildren)=0 {
			Loop %cChildren%
				i:=(A_Index-1)*(A_PtrSize*2+8)+8, child:=NumGet(varChildren,i), Children.Insert(NumGet(varChildren,i-8)=9?Acc_Query(child):child), NumGet(varChildren,i-8)=9?ObjRelease(child):
			Return Children.MaxIndex()?Children:
		} Else
			ErrorLevel := "AccessibleChildren DllCall Failed"
	}
}
EDIT 2014-06-17:
- Faster in Chromium browsers: After the first use, successive queries are instantaneous (faster than DDE!) Thanks to XeroByte for the nudge ;-)
- Improved error messages in the hotkey example
- Tested without problems in all AutoHotkey versions (U32, A32, U64) and also in Windows 8.1

EDIT 2015-10-18:
- Now it works with Microsoft Edge (only tested via VM) and Slimjet (both suggestions by Kudos)
- Better URL detection (thanks LarryC for the feedback)
- Added workaround for runtime errors suggested by Kipp a year ago
- Removed support for Google's failed Origin Chip experiment

EDIT 2016-05-15:
- Better support for Microsoft Edge
- DDE is now used only for legacy browsers (specifically, Internet Explorer and Opera 12)
- Identified two browsers where the script doesn't work (it can't reach the address bar): Pale Moon and Vivaldi :think:

EDIT 2016-05-19:
- Better detection of the address bar
- Vivaldi now supported as long as is run with the --force-renderer-accessibility switch on (and optional, but recommended, check the option "Address Bar/Show Full URL")
- Pale Moon won't be supported as they explicitly disabled the accessibility features: https://www.palemoon.org/technical.shtml#features (thanks qwerty12 for the info)


Regards from Spain,
Antonio

Re: Get the URL of the current (active) browser tab

Posted: 09 Jun 2014, 11:56
by Johnny R
Wonderful! The script works with AutoHotkeyA32 too.

Re: Get the URL of the current (active) browser tab

Posted: 15 Jun 2014, 19:34
by LarryC
Hey, anybody noticed, this script stopped working on Chrome 35??
It works of FireFox.
It worked well last few days with Chrome 35, but today, no dice??

Re: Get the URL of the current (active) browser tab

Posted: 16 Jun 2014, 14:03
by LarryC
Hmm, sorry, now its working again. No explanation.

Re: Get the URL of the current (active) browser tab

Posted: 17 Jun 2014, 16:02
by atnbueno
Hello again.

Slightly improved version in the OP:
- Faster in Chromium browsers: After the first use, successive queries are instantaneous (faster than DDE!) Thanks to XeroByte (in the old forum) for the nudge ;-)
- Improved error messages in the hotkey example
- Tested without problems in all AutoHotkey versions (U32, A32, U64) and also in Windows 8.1


Regards,
Antonio

Re: Get the URL of the current (active) browser tab

Posted: 18 Oct 2015, 17:41
by atnbueno
Hello again.

It's been more than a year (!), so it's time for an updated version in the OP:
- Now it works with Microsoft Edge (only tested via VM) and Slimjet (both suggestions by Kudos)
- Better URL detection (thanks LarryC for the feedback)
- Added workaround for runtime errors suggested by Kipp (in the old forum) a year ago
- Removed support for Google's failed Origin Chip experiment


Regards from Spain,
Antonio

Re: Get the URL of the current (active) browser tab

Posted: 19 Oct 2015, 11:26
by ahk7
Thanks for the update, seems to work well.

There are two non-mainstream browsers where it doesn't work (yet): Note: I'm posting this on autohotkey.com/boards/ (the phbbb3 forum) to see if it works OK

Re: Get the URL of the current (active) browser tab

Posted: 19 Oct 2015, 16:52
by joedf
Vivaldi, with Atle Mo! He maintains Subtle patterns! :D

Re: Get the URL of the current (active) browser tab

Posted: 25 Nov 2015, 05:21
by SlowPoster[1H+]
Ola. Nice handy script you got there, been using Internet Explorer URL-detection for a while but
since we recently moved up a notch with Edge I had to drop IE as my 2nd/3rd browser. Can't bare it's slowness anymore, lol
Sadly...

:terms: Here URL-detection fails with Microsoft Edge 20.10240.16384.0 - ApplicationFrameWindow class
Tried several tweaks to no avail... Got any ideas? I'm on Windows 10 x64, AHK v1.1.22.09 Unicode 32-bit

By the way, here are some ideas for (fast and secure) browsers to extend support with (or to use):
Comodo Dragon (Chromium-based)
Comodo IceDragon (Mozilla-based)
Comodo Chromodo (Chromium-based - newest addition) :thumbup:

Re: Get the URL of the current (active) browser tab

Posted: 27 Nov 2015, 11:28
by JoeSchmoe
Thanks so much for writing this. Just knowing I have a library that will work with all of the big browsers is incredibly helpful.

Re: Get the URL of the current (active) browser tab

Posted: 28 Nov 2015, 01:50
by Johnny R
@atnbueno, Thank You! I had to rename your function IsUrl() once more to IsUrlString() cause I have another function IsUrl() (see below), which checks not only the form of a url, but also the content, the real existence of the target.

Code: Select all

IsUrl(Url) {
   ComObjError(false) ; Verhindert einen ERROR, wenn die Url nicht existiert
   WebRequest := ComObjCreate("WinHttp.WinHttpRequest.5.1")
   WebRequest.Open("GET", Url)
   WebRequest.Send()
   Zwi := WebRequest.ResponseText
   If StrLen(Zwi)=0 ; Wenn nichts geladen werden konnte.
      Return false
   Return true
}
Your new RegEx-Formula

Code: Select all

RegExMatch(sURL, "^(?<Protocol>https?|ftp)://(?<Domain>(?:[\w-]+\.)+\w\w+)(?::(?<Port>\d+))?/?(?<Path>(?:[^:/?# ]*/?)+)(?:\?(?<Query>[^#]+)?)?(?:\#(?<Hash>.+)?)?$")
in your (new) function IsUrl() (Line 90) is faulty. It does not accept the url

Code: Select all

http://anonymouse.org/cgi-bin/anon-www_de.cgi/http://www.mittelbayerische.de
as a url. Your former RegEx-Formula

Code: Select all

RegExMatch(sURL, "^(?<Protocol>https?|ftp)://(?:(?<Username>[^:]+)(?::(?<Password>[^@]+))?@)?(?<Domain>(?:[\w-]+\.)+\w\w+)(?::(?<Port>\d+))?/?(?<Path>(?:[^/?# ]*/?)+)(?:\?(?<Query>[^#]+)?)?(?:\#(?<Hash>.+)?)?$")
works fine.

Re: Get the URL of the current (active) browser tab

Posted: 29 Nov 2015, 17:41
by atnbueno
@ahk7: Thanks for the links. I'll update the script soon with a fix for Firefox x64 (and Waterfox). For Vivaldi is too soon (is doesn't appear to support neither the old DDE nor the MSAA API).

@SlowPoster: I only have Windows 10 in a virtual machine right now, but in it my script works fine with Edge if you add "ApplicationFrameWindow" to the If sClass In line in GetActiveBrowserURL().

@Johnny R: You're right, but there's no need to go back to the old RegEx. This one should do the work:

Code: Select all

RegExMatch(sURL, "^(?<Protocol>https?|ftp)://(?<Domain>(?:[\w-]+\.)+\w\w+)(?::(?<Port>\d+))?/?(?<Path>(?:[^/?# ]*/?)+)(?:\?(?<Query>[^#]+)?)?(?:\#(?<Hash>.+)?)?$")
As mentioned before I have a new version of the script with significant changes. I'll publish it once I've polished and tested it properly (hopefully soon).


Regards,
Antonio

Re: Get the URL of the current (active) browser tab

Posted: 30 Nov 2015, 09:08
by Johnny R
@Antonio, the new regex-formula works fine now. Thank You!

Re: Get the URL of the current (active) browser tab

Posted: 30 Nov 2015, 21:17
by SlowPoster[1H+]
Script works fine :bravo: Maybe I was too sleepy to fiddle around with it last time... lol
Now I can finally start to manage my browsing madness again :dance: Thanks !

Re: Get the URL of the current (active) browser tab

Posted: 19 Dec 2015, 09:26
by rg_software
Sorry for trouble, but... how to use this script properly? :)
If I just run GetActiveBrowserURL.ahk, it says "error at line 64 -- this line does not contain a recognized action".

Re: Get the URL of the current (active) browser tab

Posted: 20 Dec 2015, 03:44
by atnbueno
@rg_software, I don't know what to tell you :eh: That shouldn't happen :wtf:

:think: What version of AutoHotkey are you using?

Re: Get the URL of the current (active) browser tab

Posted: 21 Dec 2015, 07:17
by rg_software
atnbueno wrote: :think: What version of AutoHotkey are you using?
Oh my, sorry for this false alarm! It looks like my installation was really outdated, now everything works fine :)

Re: Get the URL of the current (active) browser tab

Posted: 19 Jan 2016, 18:31
by mc-lemons
Need some help here as I am not much of a coder. I have downloaded GetActiveBrowserURL.ahk, brought it into my script with #Include, but can't figure out how to get it to work the way I want to.

I basically want it to work the same way as #IfWinActive, but instead "If URL Active". That way, a set hotkeys can be set to do one thing when on a certain webpage, and something different when another webpage is active.

Re: Get the URL of the current (active) browser tab

Posted: 27 Jan 2016, 10:25
by atnbueno
Hello mc-lemons.

Would something like this serve you? (untested code)

Code: Select all

#a::
sURL := GetActiveBrowserURL()
IfInString, sURL, autohotkey.com
	MsgBox, Welcome!
Else
	MsgBox, 4, , Do you want to go to autohotkey.com? ; 4=Yes/no
Return
Doing it like #IfWinActive would require something to check the URL of any browser either continuously or everytime each particular address bar content changes. I actually have such code, but it's quite complex. I think the code above may be functionally equivalent in your case, and it's significantly easier to understand, reuse and adapt.


Regards,
Antonio

Re: Get the URL of the current (active) browser tab

Posted: 07 Feb 2016, 14:43
by mc-lemons
Antonio,

I appreciate the response. Your code worked (the code I tried on my own was very close, but had one small difference that was causing it to not work). I would also be interested in your code that acts just like #IfWinActive. I could just save it as a separate script and use #Include to bring it into my scripts, correct?

Thanks again,
Michael