I am working on MacOS application and I would like to know if there is a way to get url of active browser from it. Application is done in C++.
I would like to get it without having to use AppleScript.
Is that possible?
Thanks
I am working on MacOS application and I would like to know if there is a way to get url of active browser from it. Application is done in C++.
I would like to get it without having to use AppleScript.
Is that possible?
Thanks
This isn't an easy answer but the good news is "yes, it's possible to do this without having to use AppleScript" and the bad news is "you'll have to use AppleScript to begin with".
Let me elaborate a bit: browser apps typically have an Applescript dictionary (which you can see by using the "Script Editor" application found in your /Applications/Utilities
folder. Here's what the dictionary looks like for Google Chrome:
You'll see I've found the "tab" class and in there you'll see the URL property.
So what you need to do is work up an AppleScript first to fetch the windows for the browser you're targeting. Then, when that's working, you need to convert the AppleScript to the underlying, raw AppleEvents (which is what AppleScripts compile to). Both AppleScripts and AppleEvents can be done in code.
The answer you're really looking for (e.g. "can I use some top secret API or send a URL from my code into some open port on my local machine to query the browser?") doesn't exist, as far as I know. AppleScript and AppleEvents are the longtime ways Apple provides to automate most apps and you'll need to leverage that.
If you decide to go ahead with using AppleScript and possibly converting them to AppleEvents, here's what I would do.
1) Find publicly available AppleScripts to do the work you want.
2) Convert the AppleScript to AppleEvents
3) Code up the AppleEvents.
For 1, here's a script that goes through every window in Safari, which I got from this tutorial:
tell application "Safari"
--Variables
set windowCount to number of windows
set docText to ""
--Repeat for Every Window
repeat with x from 1 to windowCount
set tabCount to number of tabs in window x
--Repeat for Every Tab in Current Window
repeat with y from 1 to tabCount
--Get Tab Name & URL
set tabName to name of tab y of window x
set tabURL to URL of tab y of window x
end repeat
end repeat
end tell
For step 2, I think you can use the Accessory View Pane in Script Editor to see the raw events and results produced.
For step 3, this ancient code that I wrote in C will get the URL for a browser window given a window ID.
OSStatus CreateTheAppleEventForGetURL( OSType appSignature, long windowid, char **retval)
{
OSErr err = noErr;
AEAddressDesc targetAddress;
AEDescList replyDesc = { typeNull, nil };
AEKeyword keyword;
DescType desiredClass;
AEDesc replySingle, theOptionalAttributeDesc, theSeldDesc,theObjSpec,theThirdObjSpec,theSecondObjSpec,theFormDesc,theNullDesc;
AppleEvent theEvent, reply;
AEIdleUPP theIdleProc;
Boolean gotReply = false;
long errNumber=0;
long buffer;
Size actualSize;
char *result = NULL;
theIdleProc = NewAEIdleUPP((AEIdleProcPtr)&TheIdleFunction );
if( NULL != theIdleProc )
{
err = AECreateDesc( typeApplSignature, &appSignature, sizeof( appSignature ), &targetAddress );
if( noErr == err )
{
err = AECreateAppleEvent( 'core', 'getd', &targetAddress, kAutoGenerateReturnID, kAnyTransactionID, &theEvent );
buffer = 0x10000;
err = AECreateDesc('magn', &buffer, 4, &theOptionalAttributeDesc);
if( noErr == err )
{
err = AECreateDesc(typeNull, nil, 0, &theNullDesc);
desiredClass = 'cwin';
buffer = 'ID ';
err = AECreateDesc('enum',&buffer,4,&theFormDesc);
buffer = windowid;
err = AECreateDesc(typeLongInteger,&buffer,4,&theSeldDesc);
err = CreateObjSpecifier(desiredClass,&theNullDesc,'ID ',&theSeldDesc,true,&theThirdObjSpec);
buffer = 'docu';
err = AECreateDesc(typeType,&buffer,4,&theSeldDesc);
desiredClass = 'prop';
err = CreateObjSpecifier(desiredClass,&theThirdObjSpec,'prop',&theSeldDesc,true,&theSecondObjSpec);
buffer = 'prop';
err = AECreateDesc('enum',&buffer,4,&theFormDesc);
err = AECreateDesc(typeNull, nil, 0, &theObjSpec);
buffer = 'pURL';
err = AECreateDesc(typeType,&buffer,4,&theSeldDesc);
err = CreateObjSpecifier(desiredClass,&theSecondObjSpec,'prop',&theSeldDesc,true,&theObjSpec);
err = AEPutAttributeDesc(&theEvent,'csig',&theOptionalAttributeDesc);
err = AEPutParamDesc(&theEvent,keyDirectObject, &theObjSpec);
}
if( noErr == err )
{
err = AESend( &theEvent, &reply, kAEWaitReply + kAENeverInteract, kAENormalPriority, 120, theIdleProc, NULL );
if( noErr == err )
{
gotReply = true;
}
else
{
gotReply = false;
}
err = AEDisposeDesc(&theObjSpec);
err = AEDisposeDesc(&theOptionalAttributeDesc);
err = AEDisposeDesc(&theSeldDesc);
err = AEDisposeDesc(&theSecondObjSpec);
err = AEDisposeDesc(&theThirdObjSpec);
err = AEDisposeDesc(&theFormDesc);
err = AEDisposeDesc(&theNullDesc);
}
}
err = AEGetParamPtr(&reply, keyErrorNumber, typeLongInteger, NULL, &errNumber, sizeof(errNumber), &actualSize);
if(true == gotReply )
{
err = AEGetParamDesc( &reply, keyDirectObject, typeAEList, &replyDesc );
keyword = typeAEList;
err = AEGetNthDesc( &replyDesc, 1, typeUnicodeText, &keyword, &replySingle);
if( noErr == err)
{
OSStatus status;
Size theSize;
UnicodeMapping iUnicodeMapping;
UnicodeToTextInfo theInfo;
UniChar theName[512];
unsigned char crapola[512]; // a.k.a. a pstring
iUnicodeMapping.unicodeEncoding = kTextEncodingUnicodeDefault;
iUnicodeMapping.otherEncoding = kTextEncodingMacRoman;
iUnicodeMapping.mappingVersion = kUnicodeUseLatestMapping;
status = CreateUnicodeToTextInfo(&iUnicodeMapping,&theInfo);
theSize = AEGetDescDataSize(&replySingle);
err = AEGetDescData(&replySingle,&theName,512);
if( noErr == err)
{
err = ConvertFromUnicodeToPString(theInfo,theSize,theName,crapola);
if( noErr == err )
{
result = malloc( theSize * sizeof( char ));
if( NULL != result )
{
p2cstrcpy(result,crapola);
printf( "URL returned is %s\n", result);
}
}
}
status = DisposeUnicodeToTextInfo(&theInfo);
}
}
err = AEDisposeDesc( &targetAddress );
err = AEDisposeDesc( &replySingle );
DisposeAEIdleUPP( theIdleProc );
}
if( NULL != retval )
*retval = result;
return(err);
}
I'm sure it won't compile, since a number of Carbon API's have been updated and renamed since macOS 10.8, but you get the idea.
Hopefully this long essay helps you out!