Version 72 of Invoking browsers

Updated 2007-09-04 08:38:42 by GordonS

This page describes how to call upon an external browser to display a page, given the URL. - DL

It also discussed how to do other things with browsers, such as the opposite (what page is the browser currently viewing?).

Why doesn't someone put the 'best of breed' into tklib?

Answer: because no one cares enough to - a "tragedy of the commons".


I use this on Windows 95 and 98.

  proc url x {
    set x [regsub -all -nocase {htm} $x {ht%6D}]
    exec rundll32 url.dll,FileProtocolHandler $x &
  }

Internet Explorer (actually rundll32 according to the links here) doesn't like .htm [L1 ] [L2 ] that's why I work around it.

--ro

Thanks Vince for suggesting the rundll method ;) I found the htm problem the hard way ;(


There was a potential problem with embedded spaces in the path in the unix branch. 'auto_execok' returns a list, but I assume that the value of $::env(BROWSER) should be a simple string. I've changed the code to reflect that assumption, but it may cause trouble in the unlikely event that 'auto_execok' actually returns a list of length greater than 1. - DGP

The windows branch can be simplified in Tcl interpreters of release 8.3 or later. The command

        [eval exec [auto_execok start] [list $url]]

should launch the browser on both NT and 95/98. -DGP

NOTE: The following code example will not work in Tcl releases 8.1 and later, due to Tcl Bug 219372 "Cannot set env array element via upvar #0" [L3 ]. -- DGP

 proc browser::findExecutable {progname varname} {
     upvar 1 $varname result
     set progs [auto_execok $progname]
     if {[llength $progs]} {
         set result [lindex $progs 0]
     }
     return [llength $progs]
 }

 proc browser::urlOpen {url} {
    global env tcl_platform
    switch $tcl_platform(platform) {
        "unix" {
            expr {
                [info exists env(BROWSER)] ||
                [findExecutable netscape       env(BROWSER)] ||
                [findExecutable iexplorer      env(BROWSER)] ||
                [findExecutable $env(NETSCAPE) env(BROWSER)] ||
                [findExecutable lynx           env(BROWSER)]
            }
            # lynx can also output formatted text to a variable
            # with the -dump option, as a last resort:
            # set formatted_text [ exec lynx -dump $url ] - PSE
            if {[catch {exec $env(BROWSER) -remote $url}]} {
                # perhaps browser doesn't understand -remote flag
                if {[catch {exec $env(BROWSER) $url &} emsg]} {
                    error "Error displaying $url in browser\n$emsg"
                    # Another possibility is to just pop a window up
                    # with the URL to visit in it. - DKF
                }
            }
        }
        "windows" {
            if {$tcl_platform(os) == "Windows NT"} {
                set rc [catch {exec $env(COMSPEC) /c start $url &} emsg]
            } else {
               # Windows 95/98
               set rc [catch {exec start $url} emsg]
            }
            if {$rc} {
                error "Error displaying $url in browser\n$emsg"
            }
        }
        "macintosh" {
            if {0 == [info exists env(BROWSER)]} {
                set env(BROWSER) "Browse the Internet"
            }
            if {[catch {
                AppleScript execute\
                    "tell application \"$env(BROWSER)\"
                         open url \"$url\"
                     end tell
                "} emsg]
            } then {
                error "Error displaying $url in browser\n$emsg"
            }
        }
    } ;## end of switch
 }

Fixed two things in Windows NT section: changed tcl_platform to ::tcl_platform and added & to end of cmd.exe invocation. Now it works on NT.

Tero

Here's an example from my Tcl/Tk Programmer's Reference [L4 ] which uses the registry on Win32. This example can browse to URLs with anchors (foo.html#bar). It can be adapted to open any file based on its association by replacing ".html" with the desired extention in the first registry get command.

(perSub does percent substitutions like those in event bindings. It's available in the regexp example at the site noted above.)

Chris Nelson

  package require registry
  proc showHtml { htmlFile } {
      # Look for the application under
      # HKEY_CLASSES_ROOT
      set root HKEY_CLASSES_ROOT

      # Get the application key for HTML files
      set appKey [registry get $root\\.html ""]

      # Get the command for opening HTML files
      set appCmd [registry get \
           $root\\$appKey\\shell\\open\\command ""]

      # Substitute the HTML filename into the
      # command for %1
      set appCmd [perSub $appCmd %1 $htmlFile]

      # Double up the backslashes for eval (below)
      regsub -all {\\} $appCmd  {\\\\} appCmd

      # Invoke the command
      eval exec $appCmd &
  }
  showHtml C:/foobar.html

Surely the line

        set result [lindex $progs]

in browser::findExecutable should be

        set result [list $progs]

BHT

We verified this to work on a variety of Windows platforms:

        # On Win95, even if there is a web browser installed, it cannot
        # open an internet address, only a local .html file.

        # Win98 doesn't like "/"
        if {[lindex [array get ::tcl_platform] 1]=="4.10"} {
            regsub -all "/" $file "\\\\" file
        }
        }
        # Substitute & with ^&
        # (Example: http://www.ideogramic.com?a=1&b=1 => http://www.ideogramic.com?a=1^&b=1
        #  Otherwise, it will only open http://www.ideogramic.com?a=1)
        regsub -all "&" $file "^&" file
        regsub -all "&" $file "^&" file
        # Open file
        eval exec [auto_execok start] [list $file] &

Trust me :-)

Klaus Marius Hansen

Brian Theado 13May03 - How does the ^& trick work? Is it something within Tcl that is treating the ^ as an escape?

If I try

 eval exec [auto_execok start] [list http://ats.nist.gov/cgi-bin/cgi.tcl/echo.cgi?hello=1&hi=2]

I get

 'hi' is not recognized as an internal or external command, operable program or batch file.

and hi=2 doesn't make it through to the browser. If I try:

 eval exec [auto_execok start] [list http://ats.nist.gov/cgi-bin/cgi.tcl/echo.cgi?hello=1^&hi=2]

Both hello=1 and hi=2 makes it through to the browser. This happens for me on both WinXP and NT 4.0.

BR 15May03 - On NT, START is an internal command to CMD.EXE. ^ is an escape in CMD.EXE command syntax. You can do line continuation with ^, and you can do several commands in one line with &. You use ^^ and ^& if you really want to pass ^ or & to a command. You can also quote using double quotation marks as in

  eval exec [auto_execok start] {"http://ats.nist.gov/cgi-bin/cgi.tcl/echo.cgi?hello=1&hi=2"}

All this is probably different on W9x/Me where START.EXE is an external command and the command interpreter is different (COMMAND.COM).

MG April 1st 2004 - On Win 98SE, at least, the '^' isn't needed; including it causes 'hello' to equal '1^' rather than just '1' on the webpage.

Gordon Scott September 4th 2007. In addition to the above ampersand substitution, I found I also had to substitute the space character with percent 20 so my application could open it's own help files in C:/Program Files/MyApp/html/

    regsub -all " " $uri "%20" uri

For UNIX users that want to use ONE Netscape window to display various content, here's what I do:

Create a default html file that establishes an initial title for the Netscape window that starts.

    <title>
    foobar.20001005232400     <-- This is a date time string
    </title>

Then start Netscape with this default file:

    exec netscape -geometry 600x800 default.html 2> /dev/null &

Then get the X-windows id for the Netscape window just started:

    set windowID [exec xlswins | grep foobar.20001005232400]

        xlswins is a UNIX command used to query the X-windows which
        are open.

Now that we have a window id, that particular Netscape window can be used for dedicated content display for your application:

    exec netscape -id $windowID -remote openFile(somefile.html)

Of course I've presented the bare bones approach that isn't very practical or 'clean' by itself, but it has the basics. Just wish I could do this in Windows also. Maybe Microsoft's Explorer has similar capability?

Marty Backe


The rough windows equivalent of the above is:

 if {[catch {dde request $browser WWW_OpenURL $url}]} {
     regsub %1 $appCmd $url appCmd
     regsub -all {\\} $appCmd {\\\\} appCmd
     eval exec $appCmd $url &
 }

$browser is "NETSCAPE" or "IExplore" (as determined by the appCmd extracted from the registry), $appCmd is the application invocation command extracted from the registry as in showHtml above


here a more dynamic version of the above, simply try starting the browser with a remote command and check for errors, if no browser instance is running, you get something like "... not running .." and you have to start the browser. This works on unix and with mozilla, netscape would need some modifications.

 set rcmdbrowser /usr/local/mozilla/mozilla
 set browser /usr/local/mozilla/run-mozilla.sh
 # use new-tab or new-window - whatever you prefer
 catch {exec $rcmdbrowser -remote "OpenURL($url,new-tab)"} resp
 if {[string match "*running*" $resp]} {
   exec $browser /usr/local/mozilla/mozilla-bin $url &
 }

Gerhard Hintermayer


Here's a procedure I use to ask the browser what page it is currently viewing. It currently needs 'browserSig' to point to the browser, and on MacOS relies on the AETcl 'apple-events' extension to Tcl.

    proc url::browserWindow {} {
        global tcl_platform browserSig browserSigs
        switch -- $tcl_platform(platform) {
            "macintosh" {
                # If several different browsers are running, we should
                # really pick the frontmost, somehow.
                if {![app::isRunning $browserSigs name sig]} {
                    error "No browser running."
                }
                if {![regexp {\[([0-9]+)} [AEBuild -r '$sig' WWW! LSTW] "" winnum]} {
                    error "No browser window."
                }
                # returns window info
                regexp {\[([^ ]+)} [AEBuild -r '$sig' WWW! WNFO ---- $winnum] "" winurl
                set winurl [string trim $winurl ","]
                if {$winurl == "'TEXT'()"} {
                    error "Empty browser window."
                }
                return $winurl
            }
            "windows" {
                if {[info exists browserSig]} {
                    set root [string tolower [file rootname [file tail $browserSig]]]
                } else {
                    set root iexplore
                }
                set root [string trim $root ".0123456789"]
                # If multiple iexplore instances are running, this seems
                # to pick the first?  This should work for 'iexplore' and
                # 'netscape' names.
                set info [dde request $root WWW_GetWindowInfo 1]
                set url [lindex [split $info \"] 1]
                return $url
            }
            "unix" {
                if {$tcl_platform(os) == "Darwin"} {
                    if {![app::isRunning $browserSigs name sig]} {
                        error "No browser running."
                    }
                    if {![regexp {\[([0-9]+)} [AEBuild -r '$sig' WWW! LSTW] "" winnum]} {
                        error "No browser window."
                    }
                    # returns window info
                    regexp {\[([^ ]+)} [AEBuild -r '$sig' WWW! WNFO ---- $winnum] "" winurl
                    set winurl [string trim $winurl "??,"]
                    if {$winurl == "'TEXT'()"} {
                        error "Empty browser window."
                    }
                    return $winurl
                } else {
                    error "Sorry, this is unimplemented.  Please contribute\
                      a suitable implementation!"
                }
            }
        }
    }

This doesn't need to be so complicated on windows, IMO.

Get my winutils extension @ http://sourceforge.net/project/showfiles.php?group_id=1616&release_id=51105

Inside it is a command [winutils::shell]. exec was intended to launch stdio apps and communicate over pipes. Windows does not have this concept for GUI programs. [winutils::shell] is a fire and forget approach and like cmd /c start will use associations.

 % package require winutils
 0.2
 % winutils::shell http://cvs.sourceforge.net/cgi-bin/viewcvs.cgi/tcl/tcl/generic/tcl.h?rev=1.103.2.1&content-type=text/vnd.viewcvs-markup&only_with_tag=macosx-8-4-branch

DG


Also on Windows you can use the Internet Explorer ActiveX component to display a web page in a Tk frame. See optcl for more information and examples of embeddeding ActiveX components into a Tk frame.

MPJ


See also "Advanced browser management", "Using Tcl to write WWW client side applications", "Tcl/Tk Tclet Plugin", "BrowseX", "sh8", ....


Almost all you say here is about MS Windows. However, on Unix (and sometimes even Windows), Tcl can interact with browsers in a "text-mode" way, too. w3m provides one example.


Linux

The following works nicely for most Linux distributions out there (and probably other Unix)

    proc invokeBrowser {url} {
        foreach browser {htmlview mozilla konqueror netscape} {
            set binary [lindex [auto_execok $browser] 0]
            if {[string length $binary]} {
                catch {exec $binary $url &}
                break
            }
        }
    }

MacOS X

An answer of Derk Gwen to Brian Toby, on c.l.t:

  [email protected] (Brian Toby) wrote:
  # The web page http://wiki.tcl.tk/557 shows how to open a browser to view a
  # URL in various OS's, but is there an equivalent "nice" way do the same
  # from inside Tcl/Tk running on Mac OS X?

  Applescript provides a command to open a URL in the user's default browser.

    open location "type:location"

  You can run Applescripts by exec osascript

  exec osascript -e "open location \"file://127.0.0.1/~buffy/.profile\""

  --
  Derk Gwen http://derkgwen.250free.com/html/index.html
  The little stoner's got a point.

Bryan Oakley 09-Nov-2003 I don't think applescript needs to be involved. This seems to work just fine on my box:

    exec open http://wiki.tcl.tk/557

While CL entirely agrees that's the most standard way to automate browser automation on a modern Macintosh, he records that typical locations for a standard installation include

    /Applications/Safari.app/Contents/MacOS/Safari
    /usr/local/bin/lynx
 [[what are the executables for Netscape, IE, Opera, ...?]]




CL, greatly amused by the extraordinary variety of approaches, offers the one he's using as of mid-2004:

    proc _launchBrowser url {
      global tcl_platform

    # It *is* generally a mistake to switch on $tcl_platform(os), particularly
    # in comparison to $tcl_platform(platform).  For now, let's just regard it
    # as a stylistic variation subject to debate.
      switch $tcl_platform(os) {
        Darwin {
          set command [list open $url]
        }
        HP-UX -
        Linux  -
        SunOS {
          foreach executable {firefox mozilla netscape iexplorer opera lynx
                       w3m links epiphany galeon konqueror mosaic amaya
                       browsex elinks} {
            set executable [auto_execok $executable]
            if [string length $executable] {
    # Do you want to mess with -remote?  How about other browsers?
              set command [list $executable $url &]
              break
            }
          }
        }
        {Windows 95} -          
        {Windows NT} {
          set command "[auto_execok start] {} [list $url]"
        }
      }
      if [info exists command] {
        if [catch {exec {expand}$command} err] {
          tk_messageBox -icon error -message "error '$err' with '$command'"
        }
      } else {
        tk_messageBox -icon error -message \
          "Please tell CL that ($tcl_platform(os), $tcl_platform(platform)) is not yet ready for browsing."
      }
    }

[ CL has examples of COM management of IE and Navigator he'll include here sometime.]


More ideas appear in this [L5 ] page, which happens to be expressed in Python.


RA I added "Epiphany", the default gnome browser before galeon and put firefox in front of mozilla because of number of firefox users.


[Category Internet|Category Tutorial]