Sunday, July 29, 2018

TenFourFox FPR9b1 available

Before we begin: if you haven't seen it, check out the newly updated and refurbished TenFourFox FAQ, and consider this sobering thought on the state of web monitoring advertising.

Also, for those of you who may be unaware, long-time Mozillian Gervase Markham passed away, surrounded by his family. He was ever a professional to the end. I didn't know him as well as some, but I'll always remember him, not least of which for his unwavering faith in the face of adversity and leaving us far too soon. Go with God.

TenFourFox Feature Parity Release 9 beta 1 is now available (downloads, hashes, release notes). There are several new features and many bug fixes in this version. FPR9 is also the first TenFourFox release to pull from the new extended support release, Firefox 60ESR, and we have updated the extended validation certificate roots and our certs'n'pins import script to pull from that source instead of the shortly-to-be-decommissioned 52ESR. All relevant security and stability patches on 60ESR so far have also been backported.

On the bug fix side, there is a crash fix for media tracks from Raphael, updates to the ATSUI font blacklist (mostly for certain Japanese fonts) that can now block incompatible fonts through the CSS Font Loading API as well, updated timezone data for the ICU internationalization library, ICU security fixes, fixes for button sensing in events which should get around some weird glitchy things where the mouse buttons get ignored, and a dumb old bug with WiFi scanning. I also added a layout fix for button subelements that keep getting split apart, fixing problems on sites like Twitch and GoDaddy.

There are also a number of performance related changes. First, the idle observer minimal interval has been increased to be a better fit for our old machines, since doing this every second (potentially) robs CPU time and possibly instruction cache space away from user interaction. Do note, however, that while this reduces work being run while you use the browser the work is only delayed and not eliminated, meaning improvements in responsiveness and animation will be at the cost of memory being occasionally held longer and taking longer to release, and possibly longer GC pauses (though less often), so advise if you don't think the tradeoff is worth it (this is adjustable, though it's hardcoded within TenFourFox and is not a pref). This release also has some tuning to Cocoa events handling to slightly reduce scroll wheel latency and reduce some overhead with custom events, and additional code in JavaScript to make it more nimble about cancelling and recompiling since most of our systems are uniprocessor.

The other big change is to array handling. I backported a number of changes that landed in Firefox 55 which increase the performance of certain array operations such as splices and shifts by anywhere from 35 to 80 times. This does not translate into as dramatic a boost as you might think because in small numbers these types of operations were already reasonably swift, nor are they typically executed in large numbers or in tight loops back to back, but for those sites that do, they will run quite a bit quicker.

Of the new features, CSS column properties are now unprefixed and tested, which will help improve site compatibility. Unfortunately one big new feature that I planned for FPR9 won't make this release: deferred idle callbacks. The requestIdleCallback DOM API was enabled in Firefox in Fx55 and allows browsers to schedule work in the idle time between animation frames. Rarely do any of our old systems have such idle time available, and the 50ms or less usually granted (and actually checked for by the W3C test suite) isn't much time to do much work anyway. However, we can also infer from the timeout that is (optionally) passed that the website is able to tolerate the work being deferred up to that long, so deferred idle callbacks deferred the work up to that long. For example, on YouTube this would delay comments and other pieces of the UI so that the CPU could concentrate more on decoding and blitting frames. My initial thought was to try to run the callback when the system was not under load, but it turned out to be simpler and more reliable to just run it at the maximum timeout interval (or after a default interval if one was not specified). Unfortunately, after a lot of idle callbacks had run test builds started suffering random stalls even after the callbacks had completed, indicating something wasn't getting cleaned up, so it is pref'ed off in this version while I try to figure out what I did wrong. You can play with it by turning tenfourfox.dom.requestIdleCallback.enabled to true, but you'll have to restart the browser if you change that setting, so remember that any assessment you make about performance should keep in mind whether the browser is "fresh" or not. This is something I want to get working but it's not a major issue right now because most of the sites that depend on it (including YouTube) have polyfills.

However, the other big new feature I wanted in FPR9 did land: native HTML5 time and date input controls. On my list of future features likely to be used by sites, these scored very high because for those sites that don't need custom clock/calendar UIs this can eliminate them having to load and maintain one of their own just to select intervals. Date and time input controls were not implemented in Firefox until Fx60, and the implementation Firefox uses is a cross-platform one with a fair bit of JavaScript and other support code for vagaries in date handling and localization. I really didn't want to backport all of that because it would be slower and bloatier and especially because we already have all of this special handling built into Mac OS X itself through NSDate. So I took a tip from the Android version and, in a like manner to the colour picker and file picker, which use native Cocoa widgets, implemented date and time pickers using the built-in NSDatePicker widget. This, in turn, handles all the calendar and date manipulations so we don't have to, displays the calendar and time and date using the user's own locale, and does it all at native speed to boot. Genius!

For simplicity I chose to implement this as a modal window rather than the drop-down Firefox 60 uses, which solved a lot of synchronization issues even though it's not as slick. Ordinarily you'd implement a simple modal window in Cocoa with an NSAlert, and if you want to add something to the alert box, you do so by creating an NSView with the widgets you want to splice in and inserting it as an accessory view.

There's just one problem: NSAlert didn't implement setAccessoryView: until 10.5, so you can't do that in Tiger. Or can you?

When looking for undocumented functionality in Cocoa, the best tools are either class-dump (3.1.2 works with Tiger), or my personal favourite GUI tool, MagicHat. Let's see what MagicHat comes up with for NSAlert:

// Framework:      AppKit
// Header:        NSAlert.h
// Documentation: Unknown

@interface NSAlert : NSObject
{
 int             _alertStyle;
 float           _buttonPadding;
 float           _buttonSpacing;
 float           _buttonSpacingMaxX;
 float           _buttonSpacingY;
 NSArray*        _buttons;
 NSSize          _defaultPanelSize;
 id              _delegate;
 SEL             _didDismissSelector;
 SEL             _didEndSelector;
 NSWindow*       _docWindow;
 id              _first;
 id              _helpAnchor;
 id              _helpButton;
 id              _imageView;
 NSTextField*    _informationField;
 id              _messageField;
 float           _messagePadding;
 NSSize          _minButtonSize;
 id              _modalDelegate;
 NSPanel*        _panel;
 id              _second;
 char            _showsHelp;
 id              _third;
 NSImage*        _unbadgedImage;
 char            _useNSLayout;
 [?]             reserved;
 id              reserved1;
 id              reserved2;
}
+ (id) alertWithError: (id) parameter1;
+ (id) alertWithMessageText: (id) parameter1 defaultButton: (id) parameter2 alternateButton: (id) parameter3 otherButton: (id) parameter4 informativeTextWithFormat: (id) parameter5;
+ (void) stopAllTimersForSpeaking;

- (char) _dontWarnAgain;
- (void) _setDontWarnMessage: (id) parameter1;
- (char) _showsDontWarnAgain;
- (id) addButtonWithTitle: (id) parameter1;
- (int) alertStyle;
- (void) beginSheetModalForWindow: (id) parameter1 modalDelegate: (id) parameter2 didEndSelector: (SEL) parameter3 contextInfo: (void*) parameter4;
- (id) buildAlertStyle: (int) parameter1 title: (id) parameter2 formattedMsg: (id) parameter3 first: (id) parameter4 second: (id) parameter5 third: (id) parameter6 oldStyle: (char) parameter7;
- (id) buildAlertStyle: (int) parameter1 title: (id) parameter2 message: (id) parameter3 first: (id) parameter4 second: (id) parameter5 third: (id) parameter6 oldStyle: (char) parameter7 args: (char*) parameter8;
- (id) buttonPressed: (id) parameter1;
- (id) buttons;
- (void) dealloc;
- (id) delegate;
- (void) didEndAlert: (id) parameter1 returnCode: (int) parameter2 contextInfo: (void*) parameter3;
- (void) didEndSheet: (id) parameter1 returnCode: (int) parameter2 contextInfo: (void*) parameter3;
- (void) finalize;
- (id) helpAnchor;
- (id) icon;
- (id) informativeText;
- (id) init;
- (id) messageText;
- (float) messageWidthForMessage: (id) parameter1;
- (void) placeButtons: (int) parameter1 firstWidth: (float) parameter2 secondWidth: (float) parameter3 thirdWidth: (float) parameter4;
- (void) prepare;
- (char) rememberChoice;
- (int) runModal;
- (int) runModalSheetForWindow: (id) parameter1;
- (void) setAlertStyle: (int) parameter1;
- (void) setDelegate: (id) parameter1;
- (void) setHelpAnchor: (id) parameter1;
- (void) setIcon: (id) parameter1;
- (void) setInformativeText: (id) parameter1;
- (id) setMessage: (id) parameter1;
- (void) setMessageText: (id) parameter1;
- (void) setRemberChoiceMessage: (id) parameter1;
- (void) setShowsHelp: (char) parameter1;
- (id) setTitle: (id) parameter1 andMessage: (id) parameter2;
- (char) showsHelp;
- (void) startSpeaking: (id) parameter1;
- (void) startTimerForSpeaking;
- (void) stopTimerForSpeaking;
- (id) window;
- (void) windowDidBecomeKey: (id) parameter1;

@end

Most of these are documented. Unfortunately there is no obvious analogue to setAccessoryView:, so there's no easy way that way. However, there is an interesting selector called buildAlertStyle:. This is not documented, but the name suggests that it gets called at some point to construct the NSAlert, presumably out of a generic NSWindow. Unfortunately because it's undocumented it's not at all clear what those parameters actually do.

I ruminated over this for a couple of days until by dumb luck I ran across an old library called NSAlertCheckbox. This library, though fragmentary and appearing to hail from around the days of Jaguar 10.2, does exactly what the name implies: it adds a checkbox control to an NSAlert. Well, said I, if it can add an NSButton, it can add a NSDatePicker. I cleaned up and expanded the library and wrote a little model (modal?) application to test my theory. It looked like this:

(Here is the source code for this example. You can compile it with Xcode 2.5 and the Mac OS X 10.4 SDK. Note in this and the below examples, because the model is being run from the command line and not within a regular NSApplication, it cannot intercept keystrokes. Just roll with this for the explanations below.)

The way the library works is by hooking those mysterious functions, which do indeed get called during window construction. Happily it turns out we don't need to know anything about what those opaque parameters are; we can modify the window without them (and just pass them along) by iterating through the components within the NSWindow that makes up the NSAlert. Once the code finds the second text field (the explanatory text, here articulately represented by the deep and meaningful words "Blah blah"), it then inserts a subview after that containing the date picker, et voilá.

This is not enough, however. While we can easily select the date and emit it (using NSDateFormatter), we can't type it, so selecting years and arbitrary months can sometimes require a lot of mousing around. Indeed, if you go to System Preferences to set the date and time, you have the choice of either using a stepper to manually enter a date or selecting one off the calendar. We can support that here too ... by adding another NSDatePicker!

(Source code for this example.)

The trick here was to keep them in sync so that you could type a date and have the calendar widget reflect it. By using a delegate on each date picker's cell, we can make modifications in one picker be synchronized to the other.

That suffices for dates. How about times? Straightforward enough: we just change the style to make the twin NSDatePickers into a clock and time stepper instead, and alter our template to NSDateFormatter to emit the time (in hours and minutes, and optionally seconds). Since the HTML spec also requires that we allow sites to specify minimums and maximums, let's implement that here too:

(Source code for this example.)

Because of the time interval constraint we implemented, when you run it you can only pick a time between 7:30am and 5:45pm; the pickers won't let you select anything else. Exactly what we want.

Before you upgrade to the beta, open either the MDN time or date picker pages and click in one of the demonstration input fields. In FPR8, this is just a text box, and you just enter text. (Same for Safari.) In FPR9, however, when you click or select that field the appropriate picker will pop open and the field will be autofilled for you in the proper format. If you press delete in the field, the date or time will be cleared. If you click Cancel, no change will occur. All of the standard constraints are supported, along with defaults (if no default is specified, the current time and/or date is used). Easy. As a safety measure, though, I implemented a basic filter to prevent NSDateFormatter from being exposed directly to web content since who knows what crap is lurking in there; it's only used as a secondary validator once the web-provided date is known to be in a non-pathological format.

Still to be implemented are month (easily done by just returning the month, not the full date), local date and time (needing a quadruple date picker) and week (harder, but possible). With the exception of local date'n'time, however, these are much less likely to be common and I'll defer those to later versions of the browser.

FPR9 will come out officially parallel to Firefox 62 and 60.2 on September 4, but I wanted a nice long beta test period since there is a lot of stuff in this release and to provide a sufficient timeframe for additional betas if required, so you're getting what I've got now. Bang on it and post your impressions in the comments.

9 comments:

  1. The http://www.tenfourfox.com/start/en-US/firefox/45.18.0/whatsnew/from/45.17 URL is landing me on a URL Shortener page. Is that expected behavior?

    ReplyDelete
    Replies
    1. GoDaddy screws us again. I've made some changes to the DNS to redirect through Floodgap, which I probably should have just done in the first place, pending us getting off them all together. This is the third time in as many months they've wrecked our domain name.

      Delete
    2. Cameron, I've been using these guys for several years…

      Tigertech: https://tigertech.net/

      They are Mac oriented (used to design Mac apps).

      And their customer service is superb.

      I don't normally promote these kinds of things, but GoDaddy generally sucks (I used them a long time ago).

      Just putting that out there…

      Delete
    3. I'm thinking about Gandi, actually, since I've worked with them before and gotten other recommendations from people, but I'll look into Tigertech as well. Thanks for the suggestion.

      Delete
  2. Cameron, again many thanks for your work in this release of TFF. As I stated before I use my G5 as a daily driver. So your work towards browser parity is critical to me since Webkit and variants are not dependable nor desirable. I also maintain TFF on multiple G5s. To me, the gold standard is how it performs on banking and/or brokerage websites. So far they have not nagged that FPR8 needs to be "upgraded". Once again, thank you for the great work you do!

    ReplyDelete
  3. VERY ZIPPY. Also the new AdBlock is still working GREAT.

    Am amazed by how much of new computing is just bloat. Even new Linux Distros like Mint Mate want 1GB just for the OS?!?!?!? To be frank OS9 was as posh as Mate, and Tiger simply stomps it for polish.

    Further, while the web and video/3D are understandably getting heavier, it's ridiculous to listen to guys like Linus Tech Tips on youtube talking about 16GB minimums for production work of any kind (even Audio). How did we do anything in the G4 days??

    Picked up Adobe CS4 Master Edition last year for cheap on eBay and with the exception of Premier (that's Intel only) and Dreamweaver that must be written from Osmium 1 & 0s, it's so slow and heavy, the rest has run GREAT on my MDD, despite stating a minimum of a G5 and on a recent output of a 200p pdf from InDesign it was remarkable to realize that it operates/exports to pdf at close to the same speed as the new Intel iMac running Sierra I used in a contract job for a local real estate shop doing magazine work (within 25%).

    Further it multi-tasks better.

    Consumerism is the Devil.

    ReplyDelete
    Replies
    1. I'm a Graphic Designer by profession. I work for a weekly community newspaper publisher and we used a 450mhz G4 running Leopard and a 1.8Ghz G5 running Leopard from 2005 to 2013 when the G5 died. I replaced the logicboard and that went to the other designer while I got the new MacPro.

      The G4 serves as my print server for the MP while another G4 is our Applescript server.

      We've used CS4 from about 2008 on and up until early this year I was still using it on Yosemite on the MP.

      All of that said…I have to keep up. I use all this older stuff on my G5s at home but at work I often have to repurpose PDFs that someone else sends us. It's necessary for me then to upgrade periodically.

      What I do now in 2018 I could not accomplish with Acrobat 6, Photoshop 7 and QuarkXPress 6 - the tools I used in 2004 when I was first hired.

      CS4 is still relevant, which is a testament to that specific version, but I updated to CC17 earlier this year. I also ditched Acrobat 9 for Acrobat DC.

      But yeah, all these older versions are still usable.

      Delete
    2. Acrobat DC is the ONLY item I lament not having in CS4. You can finally edit text-in line (we can all die happy now). But Libre' Office 4 has been able to do that for me in a perfectly satisfactory way for over 2 years on the MDD, so I basically ONLY upgrade now when absolutely necessary - as the shortcomings of modern software often outweigh the benefits I.M.O.

      Any anything I can't do on the Apple, LXLE Linux does on my 2006 Core 2 Duo (like RAW Therapee 5).

      Delete

Due to an increased frequency of spam, comments are now subject to moderation.