Skip navigation
This discussion is archived

Getting Safari document title/location via Scripting Bridge fails

2965 Views 6 Replies Latest reply: Mar 12, 2010 10:11 AM by hhas RSS
Rico_1 Calculating status...
Currently Being Moderated
Mar 10, 2010 12:25 PM
I'm trying to get the URL and document title from the topmost Safari document/tab. I have an AppleScript and an objective-c version using the Scripting Bridge framework.

Both versions work fine for most web pages, however when I open a Youtube video in full-screen mode, the Scripting Bridge based version fails (error below). The Apple Script works fine for "normal" and full-screen Safari windows.

Can anyone see what is wrong with the Scripting Bridge code below to cause it to fail for full-screen Safari windows?

Here the code (I omitted error checking for brevity):

AppleScript:

tell application "Safari"
# Give us some time to open video in full-screen mode
delay 10
do JavaScript "document.title" in document 0
end tell


Scripting Bridge:

SafariApplication* safari = [SBApplication applicationWithBundleIdentifier:@"com.apple.Safari"];

SBElementArray* windows = [safari windows];
SafariTab* currentTab = [[windows objectAtIndex: 0] currentTab];

// This fails when in full-screen mode:
id result = [safari doJavaScript: @"document.title" in: currentTab];
NSLog(@"title: %@", result);

Scripting Bridge error (with added line breaks):

Apple event returned an error. Event = 'sfri'\'dojs'{
'----':'utxt'("document.title"),
'dcnm':'obj '{ 'want':'prop',
'from':'obj '{ 'want':'cwin',
'from':'null'(),
'form':'indx',
'seld':1 },
'form':'prop',
'seld':'cTab' }
}
Error info = {
ErrorNumber = -1728;
ErrorOffendingObject = <SBObject @0x175c2de0:
currentTab of SafariWindow 0 of application "Safari" (238)>;
}

I could not find details about the given error code. It complains about 'currentTab' which shows that the JavaScript event at least made it all the way to Safari. I assume that the current tab receives the event, but refuses to run the JS code, because it is in full-screen mode. However, why does this work for an AppleScript? Don't they use the same code path eventually?

Thanks!
Rico
Mac OS X (10.5.8)
  • etresoft Level 7 Level 7 (23,880 points)
    If playing in full screen, it probably considers it in a new window with no tabs. You should probably check the number of tabs and only use the tab setting if there is more than one tab, otherwise, just use the window.

    Scripting bridge isn't the most well-documented interface in the world. You might have to play around with it a bit.
    MacBook 2007 (white), Mac OS X (10.6.2), 2.0 Ghz/4GB Ram/200 HD
  • etresoft Level 7 Level 7 (23,880 points)
    There is also a "doJavaScriptIn" in the SafariText class.
    MacBook 2007 (white), Mac OS X (10.6.2), 2.0 Ghz/4GB Ram/200 HD
  • hhas Level 2 Level 2 (190 points)
    You AppleScript code is not the same as your ObjC code which may or may not have something to do with it.

    Your AppleScript specifies 'document 0' [1] for the 'do JavaScript' command's 'in' parameter. Your ObjC code specifies a tab object; unfortunately, I suspect you're kinda stuck there since SB was designed with little regard to how Apple event interfaces, application dictionaries, etc. actually work in the real world, so will have typed that parameter as requiring a SafariTab instance, even though Safari happily accepts a document reference here.

    So if you want to match the command sent by your AppleScript, your options would be:

    1. futz about with raw Apple event codes (painful)

    2. call out to an NSAppleScript (klunky)

    3. use objc-appscript, which is more capable than SB and much less prone to application compatibility problems (third-party framework, so you need to include it in your .app bundle).

    Which is best for you will depend on your particular needs.

    e.g. Here's your AppleScript command after I ran it through appscript's ASTranslate tool:


    #import "SFGlue/SFGlue.h"
    SFApplication *safari = [SFApplication applicationWithName: @"Safari"];
    SFDoJavaScriptCommand *cmd = [[safari doJavaScript: @"document.title"] in: [[safari documents] at: 0]];
    id result = [cmd send];



    [1] I suspect you meant 'document 1', since AppleScript and the underlying Apple events use 1-based indexing, but I think Cocoa apps tend to treat 'document 0' as the same thing anyway, so you might get away with it here.
  • hhas Level 2 Level 2 (190 points)
    1) I have not yet looked into Apple events, but have read elsewhere that it is not the most fun thing to deal with. I might give it a shot after having tried 2 + 3


    Not really, but your command isn't too complicated so it'd be quite doable using the AEBuild* functions/NSAppleEventDescriptor and AESendMessage. If you go the AEBuild* route, you can use AEDebug to sniff Apple events sent from AppleScript for clues - the AEPrint syntax is similar to that used by AEBuild* functions. Mostly it's just tedious; first learning to use the lower-level APIs, then writing the code for them.

    2) Using NSAppleScript was actually my first approach, but having to compile each script before I run it seems a waste. Maybe I can try a pre-compiled script and hand in the JavaScript as a parameter.


    That's the safe and efficient way to parameterize AppleScripts. Although unless you're doing this to support user-supplied scripts, by the time you've packed what you need into an NSAppleEventDescriptor, a bit more code and you could probably send it directly to Safari yourself.

    3) I had tried ASTranslate in the past. I have not actually compiled the generated code, but seeing this makes me wonder about the difference between the Apple Script and Scripting Bridge.


    I've written about that in various places, e.g.:

    http://stackoverflow.com/questions/1309958/avoiding-applescript-through-ruby-rb- appscript-or-rubyosa/1316563#1316563

    Basically, Apple event IPC is RPC plus first-class queries, not OO as a lot of folks assume. AppleScript's OO-like syntax is a bit of a red herring, and the AS interpreter uses various tricks (e.g. implict gets) to further this illusion. So it looks and feels like OO, but only up to a point, and beyond that folks get totally confused when it does something decidedly un-OO. Hence AppleScript's reputation amongst professional programmers as being confusing and unpredictable. (It's actually reasonably straightforward and predictable one you know how it really works; it's just that figuring it out for yourself takes a lot of time and effort, since it's completely messed with your preconceptions by then. Ugh.)

    Apple had an opportunity to learn their lessons from AS's approach, but whether due to politics, hubris or naivety they decided to dress up SB to look even more Cocoa-/OO-like. Which just means there's a bigger impedance mismatch between the SB API and the AE API hidden beneath the surface. So the abstractions are thicker and leakier, and the obfuscations more impenetrable when you do run into problems. By comparison, objc-appscript minimizes the syntactic sugar and wears much of Apple events' inherent weirdness on its sleeve. So while it takes a bit of getting used to if you're from an OO background, once you do get the hang of it it (nearly always) just works.

Actions

More Like This

  • Retrieving data ...

Bookmarked By (0)

Legend

  • This solved my question - 10 points
  • This helped me - 5 points
This site contains user submitted content, comments and opinions and is for informational purposes only. Apple disclaims any and all liability for the acts, omissions and conduct of any third parties in connection with or related to your use of the site. All postings and use of the content on this site are subject to the Apple Support Communities Terms of Use.