This is a proposed standard for the Newton Standards Mailing List. It is based on "URL-AE Standard" and the Internet Config application by John Norstad, Peter Lewis, Quinn the Eskimo, et al.

Draft 0.3.0, 10/15/96 [Editing in progress.]

Changed method name to RegisterURLHandler, and changed number of arguments to two. (The old method name will still be supported.)

Summary:

Applications which encounter a URL string of a type which they cannot handle should call GetRoot().urlCop:?GetURL(string, nil), in order to display the item to the user. If the application wants the value of the item returned instead of displayed, it should call GetRoot().urlCop:?FetchURL(string, nil).

Applications which can handle URL types can declare their ability to do so in two ways. The first is simply to implement at their base level methods called GetURL and/or FetchURL, each taking two arguments; the first is a URL string, the second is an options frame which may be ignored. The GetURL method should display to the user the item denoted by the URL string; the FetchURL method should return the value of the item, but not display it.

The second thing the application should do to make its ability known is to call in a delayed action, when installed,

GetRoot().urlCop:?RegisterURLHandler(appSymbol, {types: array, GetURL: true, FetchURL: true, version: n})

appSymbol: The application's symbol.

types: An array of strings or symbols, indicating which URL types the registering app is offering to handle, e.g., ['http, 'ftp].

version: An integer.

An application which cannot handle the FetchURL (or GetURL) method should omit the corresponding slot from the fourth argument.


Application Methods

URL-Aware applications (or transports) should implement the following two methods. For backward compatibility, and for apps which do not know which internet apps are available, I've implemented a generic GetURL and FetchURL method in a package; these methods simply read a registry of which applications are available to handle the specified URL type, and then call the appropriate method in the appropriate app.
There are some unresolved issues involving transports which result in some awkwardness in when a transport is handled like an app, and when it isn't.


GetRoot().(yourAppSymbol):?GetURL(string, optionsFrame)
Get a URL and display it in a view or save it as an entry in a soup (normally as an attachment in the InBox soup).
Result: true if successful, nil otherwise.

string: the URL to get. Developers who call this method are encouraged to make this string of class '|string.url|, or '|string.url.urlType| but this is not required, and the method must be able to handle untyped strings. Developers who implement this method are encouraged, but not required, to accept strings in non-canonical format, and to have an intelligent default behavior or URL type. (E.g., An html browser might resolve a string which does not begin with a URL type as if it began with "http:".)

optionsFrame: This argument may be a frame or nil. All the slots in this argument are optional, and the precise interpretation of a slot is up to the implementor. Implementors are free to invent other options slots, e.g., for passing state information.
Slot Name Type Meaning
Save boolean or string If true, save to the default location, normally as an attachment in the InBox. If a string, save to the soup with the specified name on the default store. If nil, do not save, but display if possible.
Autoconnect boolean If true, connect without further user intervention.
Autodisconnect boolean If true, disconnect after resource is obtained, or after failure.
Age integer If the URL is cached and its age is less than this number of minutes, get the cached URL instead. If this is missing or nil, the implementor may always use the cached URL. To ensure that the implementor always gets a fresh copy, make this value -1.
ScrollPercentage integer (0 to 100) If present and supported by the receiver, the initial display should be as though the user had scolled scroll% through the resource. (This is definitely optional, but many existing web pages have no significant information until the second Newton screen-full.)
Worksite Worksite to use for connection.
Parts an array of symbols, indicating the only parts that are of interest, e.g., 'title, 'links. 'header.


For a mailto URL, open a mail message window addressed to the email address in
the URL. The Save slot in the optionsFrame parameter has no meaning for a mailto URL, and must be ignored if it is specified.

For ftp URLs, if the path in the URL resolves to a directory, display the directory contents in a window. If Save is non-nil and the path resolves to a non-text file, save the file to the specified soup. If the path resolves to a text file and Save is non nil, behavior is up to the application; it might display the file, save it as a Notepad entry, or save it in some other format. For ftp URLs, the implementor may do further processing, or call additional handlers.




GetRoot().(yourAppSymbol):?FetchURL(string, optionsFrame)
Get a URL and return it, if possible, without displaying it.
Result: non-nil value of the URL if successful, nil otherwise.
string: the URL to get.
optionsFrame: This argument may be a frame or nil.
In addition to the slots listed for GetURL, implementors may support an additional slot, converted. If non-nil, this method may return an application dependent version of the resource, rather than the unconverted value of the URL. If this slot is missing or nil, and the implementor is unable to return the raw resource, the method should return nil.
(The converted slot is a kludge. But, for instance, a browser which talks to an intermediary client on a Mac may never see the raw html; it may get a newton-specific version instead. This version of the html might still nonetheless be of some use to the caller, e.g., if it's just looking for text.)


URL Handler Registry


If the caller does not know where it should send a GetURL or FetchURL method, it should send it to the URL Traffic Cop, a package which is a generic URL handler. The URL Traffic Cop will consult a database of registered apps (and transports) to determine which app and which method to pass the call to. If it ever becomes possible to register a transport and an application with the same appSymbol, the registry will call the transport instead of the application.


GetRoot().urlCop:?RegisterURLHandler(appSymbol, {types: typesArray, suitability: nil, integer, or array, GetURL: boolean, symbol, or function, FetchURL: boolean, symbol, or function})
appSymbol: a symbol, normally your application's symbol.
types: An array of strings or symbols, indicating which URL types the registering app is offering to handle.
suitability: An integer, indicating how suitable the app denoted by appSymbol is for handling one of the URL types. Larger is better.

This may also be missing or nil, which has the same effect as 0, the default suitability. In future versions it may also be an array of integers, in which case each integer represents the suitability of the corresponding type in the Types array.
handlersFrame: Obsolete.


:PreferredApp(URLString, method, optionsFrame)
Returns the symbol for the preferred application or transport for the given URL and method.

The following two methods are now deprecated:

:RegInetConnected(appSymbol)
:RegInetDisconnected(appSymbol)

Instead, add InetConnected:TRUE or InetDisconnected:TRUE to your registration frame. The application or transport denoted by appSymbol will be sent the InetConnected or InetDisconnected method when the Internet Enabler connects or disconnects.

:InetConnected()
:InetDisconnected()

_______


Where to look for and store URL's


The only out-of-box solution for a user who wants to store a URL is a custom slot in Names. The naming of this slot was discussed in comp.sys.newton.misc, and the consensus was that the name of the custom slot should be "URL"; this has to be looked up using the bcCustomFields method. Other places to store URL's may emerge.

Internal API

The following information is for the use of implentors of URLCop and associated utilities only. I probably should not have documented it in the first place, and implementors of future URL dispatchers are free to break it or ignore it.

The registry will be stored in a soup. The idea behind the data structure is to make it easy, first to find all entries which can handle the requested type and the GetURL (respectively FetchURL) method, and then to go to the app with the highest suitability.
For efficiency, the implementation will signal its ability to handle a URL type and the GetURL (or FetchURL) method using a soup tag, which must be a symbol. (This should be transparent to the caller and receiver, though.) The Newton object store only allows a single tags array, so both items must be stored in the same array, but it may be clearer to think of them separately. (This would cause a conflict if a URL type had the same name as a handler method, but this seems unlikely.)
A typical entry might look like this:

{
types: ['http, 'shttp, 'GetURL],
appSymbol: '|NetSurfer:WebLizards|,
Suitability: -42
FetchURL: nil,
GetURL: 'MyURLMethod
}




When an app sends the GetURL method to the generic handler:

If Method is a symbol, then GetRoot().(AppSymbol) is opened, and GetRoot().(AppSymbol).(Method) is looked up. If GetRoot().(AppSymbol).(Method) takes two arguments, the method is called with the original string and optionsFrame arguments. For backwards compatibility, if the arg count is one, only the URL string is passed to the method.
If Method is TRUE, then the arguments are passed to GetRoot().(AppSymbol).GetURL.
If Method is a function, then the value of the function slot is performed in the context of the entry with the original two arguments.

If AppSymbol is a registered transport, instead of the above, the following happens:
If Method is a symbol, then the transport is sent the method Method with the original string and optionsFrame arguments, via the TransportNotify global function. (The single-argument backward-compatibility hack is not supported for transports.)
If Method is TRUE, then the arguments are sent to GetURL method of the transport.
If Method is a function, then the value of the function slot is called with the original two arguments.


If multiple apps have registered to handle type, then the app with the highest Suitability value gets the call.
The FetchURL slot in the description frame is treated similarly. If the entry has a non-nil slot entitled 'NoAutoOpen, the app is not opened before the message is sent.
Jim Bailey suggested being able to send a method to a transport; I've revised urlCop to handle this.

[examples deleted]



An app can then be looked up as follows:

mySoup := GetUnionSoup("urlCop");

myCursor := mySoup:query(
{indexpath: 'suitability,
tagSpec: {all:['http, 'GetURL]},
validTest: func(e) GetRoot().(e.appSymbol)
});

myCursor:entry();

This will give the app with highest suitability which can handle method GetURL and type http. Calling myCursor:next() will move to the second-most suitable app.





urlCop internal methods and slots: Should these be documented? (They should be over-rideable.)
ParseURLForType(string, optionsFrame)
defaultURLType (a symbol)
URLRegistryQuery(theType, theMethod, optionsFrame)


The URL Traffic Cop will be free; I'll either post the source, or make it available to anyone who wants to implement a superset of its features. The current version should be on my home page, http://pobox.com/~flasheridn.


I'm not speaking officially, and I did this on my own time.
Flash Sheridan
FlaSheridn@pobox.com

  • Flash Sheridan's home page
  • URLCop