Surfin' Safari
Dave Hyatt's Weblog
June 30, 2005
Moving Time

Now that WebKit has its own web site on OpenDarwin, it's time for this blog to change. For starters, Surfin' Safari has now moved to here. Another big change is that I will no longer be the only person talking to you about Safari and WebKit changes. Some of the other members of the team will be posting about what they're working on as well.

I will leave this blog up here so that all the links remain valid, but all subsequent posts will be to the new blog. Update your bookmarks. :)

Comments (4) | TrackBack (1)
June 13, 2005
June 07, 2005
The Improved Web Kit

We've already received and committed several patches from external contributors and the repository has only been live for a few hours!

As some of you have already noticed (those of you that built), the new Web Kit not only passes Acid2, but it's also substantially faster at loading Web pages and at handling JavaScript. It contains a number of additional performance improvements that went in post-Tiger.

One question people have asked is "Does this have to replace my system frameworks?" The answer is "No." You can run this custom version of Web Kit with a particular instance of Safari without replacing your system frameworks. The run-safari script we provided does this for you. If you study what it does, you'll see that you can easily try out your own WebKit apps with the new frameworks as well. We in fact encourage you to do this so that you can make sure your apps are functioning properly with the latest WebKit.

Comments (91) | TrackBack (7)
Say Hello to WebKit!

As some of you may have heard at WWDC Monday, the Safari team is proud to announce that we are making significant changes in the way we operate, and these changes start today.

Here is what we are launching:

1. webkit.opendarwin.org, the new web site for WebKit, WebCore and JavaScriptCore.
2. Full CVS access to WebCore and JavaScriptCore, our frameworks based on khtml and kjs. This repository includes the complete history of the project, so all patches past and present can be viewed.
3. WebKit, the Objective-C API that wraps WebCore, is also being open sourced. It is in the same CVS repository.
4. This repository is live. You can pull and build it today. As we improve the frameworks you can pull and run the latest and greatest. If you want to run a version of Safari that passes the Acid2 test, now you can.
5. This repository is open. We welcome contributions.
6. From now on bugs in these frameworks will be tracked in public at bugzilla.opendarwin.org. You can submit bugs in the open, view the status of our work, attach patches to bugs, and test code fixes to those bugs.
7. A new public mailing list, webkit-dev@opendarwin.org, for development discussion of WebKit, WebCore, and JavaScriptCore.
8. A new public IRC channel - #webkit on irc.freenode.net.

And finally, going forward we will be engaging actively with the community. Find us on IRC and on the mailing list, jump in, and get involved!

Build. Run. Test. It's live!

Comments (91) | TrackBack (20)
May 02, 2005
Implementing CSS (Part 1)

One of the most interesting problems (to me at least) in browser layout engines is how to implement a style system that can determine the style information for elements on a page efficiently. I worked on this extensively in the Gecko layout engine during my time at AOL and I've also done a lot of work on it for WebCore at Apple. My ideal implementation would actually be a hybrid of the two systems, since some of the optimizations I've done exist only in one engine or the other.

When dealing with style information like font size or text color, you have both the concept of back end information, what was specified in the style rule, and the concept of front end information, the computed result that you'll actually use when rendering. The interesting problem is how to compute this front end information for a given element efficiently.

Back end information can be specified in two different ways. It can either be specified using CSS syntax, whether in a stylesheet or in an inline style attribute on the element itself, or it is implicitly present because another attribute on the element specified presentational information. An example of such an attribute would be the color attribute on the font tag. Both WebCore and Gecko use the term mapped attribute to describe an attribute whose value (or even mere presence) maps to some implicit style declaration.

A rule in CSS consists of two pieces. There is the selector, that bit of information that says under what conditions the rule should match a given element, and there is the declaration, a list of property/value pairs that should be applied to the element should the selector be matched.

All back end information can ultimately be thought of as supplying a declaration. A normal rule in a stylesheet that is matched has the declaration specified as part of the rule. An inline style attribute on an element has no selector and is simply a declaration that always applies to that element. Similarly each individual mapped attribute (like the color and face attributes on the font tag) can be thought of as supplying a declaration as well.

Therefore the process of computing the style information for an element can be broken down into two phases. The first phase is to determine what set of declarations apply to an element. Once that back end information has been determined, the second phase is to take that back end information and quickly determine the information that should be used when rendering.

WebCore (in upcoming Safari releases) has a really cool optimization that I came up with to avoid even having to compute the set of declarations that apply to an element. This optimization in practice results in not even having to match style for about 60% of the elements on your page.

The idea behind the optimization is to recognize when two elements in a page are going to have the same style through DOM (and other state) inspection and to simply share the front end style information between those two elements whenever possible.

There are a number of conditions that must be met in order for this sharing to be possible:
(1) The elements must be in the same mouse state (e.g., one can't be in :hover while the other isn't)
(2) Neither element should have an id
(3) The tag names should match
(4) The class attributes should match
(5) The set of mapped attributes must be identical
(6) The link states must match
(7) The focus states must match
(8) Neither element should be affected by attribute selectors, where affected is defined as having any selector match that uses an attribute selector in any position within the selector at all
(9) There must be no inline style attribute on the elements
(10) There must be no sibling selectors in use at all. WebCore simply throws a global switch when any sibling selector is encountered and disables style sharing for the entire document when they are present. This includes the + selector and selectors like :first-child and :last-child.

The algorithm to locate a shared style then goes something like this. You walk through your previous siblings and for each one see if the above 10 conditions are met. If you find a match, then simply share your style information with the other element. Such a system obviously assumes a reference counting model for your front end style information.

Where this optimization kicks into high gear, however, is that it doesn't have to give up if no siblings can be located. Because the detection of identical style contexts is essentially O(1), nothing more than a straight pointer comparison, you can easily look for cousins of your element and still share style with those elements.

The way this works is that if you can't locate a sibling, you can go up to a parent element and attempt to find a sibling or cousin of the parent element that has the same style pointer. If you find such an element, you can then drill back down into its children and attempt to find a match.

This means that for HTML like the following:

<table>
<tr class='row'>
<td class='cell' width=300 nowrap>Cell One</td>
</tr>
<tr class='row'>
<td class='cell' width=300 nowrap>Cell Two</td>
</tr>

In the above example, not only do the two rows share the same style information, but the two cells do as well. This optimization works extremely well for both old-school HTML (in which many deprecated presentational tags are used) and newer HTML (in which class attributes might figure more prominently).

Once the engine determines that a style can't be shared, i.e., that no pre-existing front end style pointer is available, then it's time to figure out the set of declarations that match a given element. It is obvious that for inline style attributes and mapped attributes that you can find the corresponding declaration quickly. The inline style declaration can be owned by the element, and the mapped attributes can be kept in a document-level hash. WebCore has a bit of an edge over Gecko here in that it treats each individual mapped attribute on an element as a separate declaration, whereas Gecko hashes all of the mapped attributes on an element as a single "rule." This means that Gecko will not be able to share the mapped attribute declaration for the following two elements:

<img width=300 border=0>
<img width=500 border=0>

WebCore creates three unique declarations and hashes them, one for a width of 300, one for a width of 500, and one for a border of 0. Gecko creates two different "rules," one for (width=300,border=0) and another for (width=500,border=0). As you can see in such a system, you will frequently not be able to treat the identical border attributes as the same.

Aside from this difference in mapped attribute handling, the two engines employ a similar optimization for quickly determining matching stylesheet rules called rule filtering. All rules that are potentially matchable by any element (i.e., that have the correct media type) are hashed based on the contents of the rightmost simple selector in the rule.

A selector in CSS can be either simple (meaning that all of the contents of that selector apply only to a single element) or compound (meaning that you may examine multiple elements like parents or siblings of that element). A compound selector is essentially a chain of simple selectors, so the following rule:

tr > td { color: blue }

has two simple selectors, tr and td. The rightmost simple selector in the rule is the one that we will use for the rule filtering optimization.

The rightmost simple selector falls into four categories.

(1) The selector uses an ID. (Example: #foo)
(2) The selector doesn't have an ID but uses a class. (Example: .foo)
(3) The selector has no class or ID but specifies a tag name. (Example: div)
(4) The selector specifies none of these things. (Example: *[disabled])

The rule is placed into one of four hashtables depending on which category it falls into. The idea behind these categorizations is to always filter out more specific information first. For example, if an element has a specific ID, then obviously any rules whose rightmost selector uses a different ID cannot match. Technically the last category can just be a list and not a hashtable, since those rules must always be examined by all elements.

Each hashtable, therefore, consists of a mapping from a given atomic string to a set of rules that match. The class attribute is exceptional in that you must put the rule into the hashtable multiple times if multiple class attributes are used.

When determining the set of rules that match a given element, you only examine rules that correspond to the correct hash entry based off your ID, classes and tag name. This optimization basically eliminates 95+% of the rules up front so that they need not even be considered during the matching process.

Each rule is then examined in detail, with all selectors being checked, to determine if it is a match, and the set of matches is collected. The set of matches can then be sorted by priority and specificity such that all the declarations are in the proper application order.

This brings us to the final phase of the style computation, which is taking the set of matches and quickly computing the appropriate front end style information. It is here that Gecko really shines. What I implemented in Gecko was a data structure called the rule tree for efficient storing of cached style information that can be shared *even when* two elements are not necessarily the same.

The idea behind the rule tree is as follows. You can think of the universe of possible rules in your document as an alphabet and the set of rules that are matched by an element as a given input word. For example, imagine that you had 26 rules in a stylesheet and you labeled them A-Z. One element might match three rules in the sheet, thus forming the input word "C-A-T" or another might form the input word "D-O-G."

There are several important observations one can make once you formulate the problem this way. The first is that words that are prefixes of a larger word will end up applying the same set of rules. All additional letters in the word do is result in the application of more declarations. Thus the rule tree is effectively a lexicographic tree of nodes, with each node in a tree being created lazily as you walk the tree spelling out a given word.

This system allows you to cache style information at each node in the tree. This means that once you've looked up the word "C-A-T-E-R-W-A-U-L", and cached information at all of the nodes, then looking up the word "C-A-T" becomes more efficient.

In order to make the caching efficient, properties can be grouped into categories, with the primary criterion for categorization being whether the property inherits by default. It's also important to group properties together that would logically be specified together, so that when a fault occurs and you have to make a copy of a given struct, you do so knowing that the other values in the struct were probably going to be different anyway.

Once you have the properties grouped into categories like the border struct or the background struct, then you can either store these structs in the rule tree or as part of a style tree that more or less matches the structure of the document. Inheritance has to apply down the style tree and tends to force a fault, whereas non-inherited properties can usually be cached in the rule tree for easy access.

WebCore doesn't contain a rule tree, but it is smart enough to refcount the structs and share them as long as no properties have been set in the struct. In practice this works pretty well but is not as ideal as the rule tree solution.

Comments (141) | TrackBack (7)
April 30, 2005
Safari and KHTML

KHTML developers respond to my posting of the WebCore Acid2 patches here and here.

For what it's worth, the patches I posted are to WebCore, which consists of both KHTML and KWQ (our port of Qt). They are posted to illustrate all the WebCore bugs that had to be fixed in Safari to pass the Acid2 test. They are not solely KHTML patches. The antialiasing bug was in KWQ, and so doesn't even apply to KHTML. The better object element support necessarily involves KWQ as well, since the plugin code is (obviously) platform-specific.

What do you think Apple could be doing better here? Comment or trackback. I'll read it all.

Comments (71) | TrackBack (6)
April 27, 2005
Safari Passes the Acid2 Test (Updated)

acid-victory.png Safari now passes the Acid2 test. There were two issues left that needed to be resolved.

The first issue involved implementing a few enhancements to the object element. I needed to support fallback content when invalid MIME types were specified or when bad status codes were returned for HTTP requests (like 404). After fixing these bugs and a couple of other problems with intrinsic sizing of plugins, the eyes of the face showed up.

The second issue involved improper antialiasing of the border corners. Antialiasing was enabled for the drawing of the corner polygons, and this resulted in a bleed-through of the background. Because the two corners were drawn separately, the antialiasing was actually incorrect, since it was disrupting the join of the corners by letting the background show.

Here are the patches for all of the problems fixed in Safari to make the test pass.

Fix parsing of the REL attribute on links.

Disallow TABLE inside P in strict mode.

Add support for min/max-width/height for positioned elements.

Fix the rendering glitch that causes the reference page to paint garbage.

Make sure that percentages that go to auto don't mess up the self-collapsing block check.

Implement SGML-style comment parsing for HTML in strict mode.

Make sure empty tables honor CSS-specified height in strict mode.

Fix baseline alignment within table cells to use the bottom of empty blocks. Fix floats to not grow if child floats overhang but the height of the outer float is auto.

Make sure percentage min-height goes to 0 and not auto when the percentage does not apply.

Implement fallback content for the object element and fix intrinsic sizing to work properly when images are specified in the object element.

Disable antialiasing for the drawing of polygons.

Victory!

Comments (251) | TrackBack (40)
April 23, 2005
Acid2: Version 1.1 Posted

The Acid2 test has been updated to version 1.1 in order to fix the bug I outlined in my previous blog entry. Here is how the Safari build with all of my fixes renders version 1.1. As you can see now we're just down to better object element handling.

acid2-6.png

Comments (52) | TrackBack (11)
April 20, 2005
Acid2: Lopping Off the Sideburns

Astute viewers pointed out that there was still a rendering glitch in row ten. It turns out this was actually a problem with rows six to nine. The block above ended up being too tall because min-height specified using a percentage was resolving to auto instead of to 0 when the containing block had no specified height. I fixed this problem, but check out the rendering now.

acid2-5.png

Observe how the smile is now positioned too high relative to the reference rendering. I spent a very long time investigating this problem and determined that it is in fact a bug in the test. At this point I am halting work on Acid2 until a revised test has been posted.

Comments (56) | TrackBack (3)
April 18, 2005
Regression Roundup

The purpose of this blog entry is for you to track back to it with regressions you have discovered going from 1.2 to 1.3. It would be especially helpful if you can test up front for whether this is a user agent bug (by spoofing as another browser), since changes in browser version numbers often cause regressions even when nothing is wrong with the browser itself.

The more clear and concise the reduction, the better the chances are that we can address the issue quickly (thus increasing the odds it can make it into a software update sooner rather than later).

Please include in these trackbacks only regressions from 1.2. If you included something in the earlier blog entry's comments section, please post it again as a trackback. Thanks!

TrackBack (26)
Response to Some 1.3 Comments

(1) The feed URL dialog that tells you 10.4 must be installed to view RSS feeds is simply a bug and not part of a master plan for global domination.
(2) The View Source shortcut was changed to match Mail.app.
(3) The default bookmarks reappearing after being removed won't happen going forward now that the way this is handled has been changed. See (1) above re: global domination.
(4) The selection extends to the edges of lines in the new Safari just as it does in other Mac apps like TextEdit. This change had to be made so that editing selection would behave like NSTextView. It was a challenge translating this to the Web space, but I will blog more about this in a future entry.
(5) When saving links to the desktop from the context menu, you can hold down Option to change the menu item so that you can pick a location.

Comments (127) | TrackBack (7)
The Acid2 Test: The Smile and Row Fourteen

Even though I consider row 14 to be ambiguous, I went ahead and modified the Safari code to yield the correct expected behavior. It isn't so much that the test is wrong as that it is testing unspecified behavior.

I also noticed thanks to Ian Hickson that the smile was not in fact rendering correctly. The reason is that Safari will expand floats to encompass overhanging child floats even when the outer float has specified a height explicitly in CSS. I changed the code so that this is now only done when height is auto, and the smile now renders as it should.

Updated screenshot below. All that I have left is fixing the object element's behavior in order to completely pass the test.

acid2-4.png

Comments (73) | TrackBack (4)
April 15, 2005
Safari 1.3

Those of you running Panther can now update to 10.3.9. This update includes Safari 1.3 and new versions of WebKit, WebCore, and JavaScriptCore that contain thousands of improvements we've made to the engine since Safari 1.2.

What you are getting is all of the new standards support, new WebKit capabilites, site compatibility fixes and performance optimizations that are also present in Safari 2.0 for Tiger. The layout engines for the two are virtually identical.

Here are some of the highlights:

Page Load Performance
Safari 1.3 loads pages overall 35% faster than 1.2 as measured by IBench. In addition to improving the overall page load, Safari 1.3 will display content sooner than 1.2 did, so that subresources don't hold up the initial display of the page.

JavaScript Performance
We have substantially improved the performance of the JavaScript engine in Safari. I encourage you to check out Safari 1.3 on this benchmark for example to see the improvement relative to 1.2.

HTML Editing
Safari 1.3 supports HTML editing, both at the Objective-C WebKit API level and using contenteditable and designMode in a Web page. The new Mail app in Tiger uses WebKit for message composition. You can write apps that make use of WebKit's editing technology and deploy them on Panther and Tiger.

Compatibility and Security
Compatibility and security are our number one priority in WebCore, and Safari 1.3 has many important compatibility fixes. For example, percentage heights on blocks, tables and cells now work much better in Safari 1.3. min/max-width/height support has been added. More of the table-related CSS properties are now supported. DOM methods like getComputedStyle are now supported.

The DOM Exposed
The entire level 2 DOM has been exposed a public API in Objective-C. This means various holes have been filled in Safari's DOM level 2 support. In addition to exposing the DOM to Objective-C, the JS objects that wrap DOM objects can also be accessed from Objective-C, allowing you to examine and edit the JS objects themselves to inject properties onto them that can then be accessed from your Web page.

XSLT
Safari 1.3 on Panther now supports XSLT. 10.3.9 includes libxslt, and Safari uses this excellent library to handle XSLT processing instructions it encounters in Web pages.

Plugin Extensions
For those of you writing WebKit apps, a new Objective-C WebKit plugin API is supported that lets you put Cocoa widgetry into the Web page more easily. In addition enhancements to the Netscape Plugin API (made in conjunction with Mozilla Foundation) have been implemented for plugins that require cross-browser compatibility.

Did I mention it's really really fast? :)

In case you're curious about differences between the Tiger and Panther versions of the engine, they mostly have to deal with frameworks that changed underneath WebKit. For example we have new faster image decoders on Tiger (that also handle PNGs correctly), so you'll find that Tiger fixes some of the PNG gamma issues that will still exist on Panther. In addition the new decoders are incredibly fast and are now run on a separate thread on multi-processor machines on Tiger.

The network layer has also been improved on Tiger, so this may be another source of differences in behavior between the two operating systems. Overall, however, it's likely that content and applications you develop with WebKit will behave identically on the two operating systems.

Let us know what you think.

Comments (308) | TrackBack (29)
Acid2: Row 14

I believe row 14 is ambiguous and needs to be amended. Safari makes this row too tall for the same reasons Firefox does.

See https://bugzilla.mozilla.org/show_bug.cgi?id=289480#c14 for more details.

Comments (155) | TrackBack (6)
Acid2: Rows 6-9 Revisited

Earlier I asserted that Safari passed rows 6-9. Now I'm not so sure. As someone in the comments pointed out, Safari has a 1px golden ring around the black nose that is not there in the reference rendering. I will have to figure out what causes this to see if it's a bug in Safari.

Comments (114) | TrackBack (4)
Acid2: Row Thirteen Revisited

Ok, I now pass row thirteen. In strict mode I just made comment parsing flip a canClose bit as it encountered pairs of --. For an even number of --, you can close when you encounter a >. That fixes the rendering. I'm not sure this is correct, but hopefully it is. :)

acid2-3.png

Comments (106) | TrackBack (4)
The Acid2 Test, Row Thirteen

I must admit that I am completely baffled by row thirteen. The row includes a comment that Safari appears to be handling incorrectly, but I have no idea why. There is apparently something I don't know about comment handling. Can someone from WaSP clarify and explain what the heck I'm supposed to do with this line?

<!-- -- --->ERROR<!- ------ ></div></div> <!-- two dashes is what delimits a comment, so the text "->ERROR<!-" earlier on this line is actually part of a comment -->

This particular comment test seems particularly esoteric and pointless to me. It's clear that I'll have to limit this parsing rule to strict mode anyway, since I doubt other browsers make you count hyphens when constructing comments.

I'm a bit perplexed that a test that is supposedly testing features that WaSP considers "essential" for the future Web contains data URLs and esoteric SGML comment rule tests. What about inline-block? I consider that far more essential than whether or not a browser can handle some obscure comment parsing rule.

Comments (143) | TrackBack (4)
The Acid2 Test, Between the Rows

I spent some time studying rows four and five of the test and realized it would take a while to fix. Those rows are testing using the object element without specifying a MIME type and making sure that if the types are unsupported that you fallback to nested objects. I have ideas for how to fix this, but I decided to save this part of the test for last.

Instead I focused next on why there was an odd six pixel gap between rows nine and ten in Safari. It turns out that my check for whether or not a block could collapse its own margins together was missing a case, and the Acid2 test had a nice comment explaining what I was missing.

.empty { margin: 6.25em; height: 10%; /* computes to auto which makes it empty per 8.3.1:7 (own margins) */ }

Basically I needed to improve my self-collapsing block code to be smarter about noticing when percentage heights end up turning into auto. Once I fixed that, the gap went away. Below is the current rendering after the bug fix.

acid2-2.png

I also noticed that the test and the reference rendering apply overflow:hidden to a root element and expect that to be applied to the viewport. Safari 2.0 does this for HTML documents with the body element but did not ever do this with the root element. I fixed Safari to actually behave according to the latest internal draft of CSS2.1 and now the documents don't have scrollbars.

Comments (12) | TrackBack (6)
April 13, 2005
The Acid2 Test, Row One

I have finally completed the implementation of the CSS properties min-width, max-width, min-height and max-height in WebCore. Safari 2.0 (and Safari 1.3) both support these properties on everything but positioned elements. It figures then that the use of these properties on the Acid2 test would occur on positioned elements (thanks Hixie!).

Included below is a screenshot of the rendering in Safari with this bug fixed.

acid1.png

As you can see, rows 1-3 and rows 6-9 all render correctly. I'm going to work on rows 4 and 5 next and see if I can get the eyes of the face to show up.

Comments (103) | TrackBack (9)
April 12, 2005
The Acid2 Test

The Web standards project has released the Acid2 test for Web browsers. It is a pretty crafty HTML+CSS test designed to ensure that browsers are properly implementing support for those standards.

Every browser fails it spectacularly. :)

I started work today on making Safari pass the test, and I thought I'd blog my progress as I fix bugs in the test. This will be a fairly slow process as whole features may have to be added simply to make one row of the test render correctly.

For those using Safari, I have fixed two bugs so far. The ugly red background that covers the test is caused by a bug in Safari's parsing of the rel attribute on links. Safari had some hard-coded checks for keywords rather than doing proper space-based tokenization of the attribute. I fixed this and now the red background is gone.

Row one in Safari mis-renders for two reasons. The first is that the test wants to make sure table elements are not allowed inside paragraph elements in strict mode. Safari did not have a tight check here, and allowed tables inside paragraphs in all modes. You have to allow this in quirks mode because other browsers do, but I went ahead and tightened the check for strict mode. With this bug fixed, the first row's maroon line became black as it should.

The second problem is that the first row's black line is the wrong height and the wrong width. This is because Safari 2.0 supports min/max-width/height on everything but positioned elements. I will have to add this support to make row one finally render correctly, so that's what I'll be working on next.

Comments (36) | TrackBack (20)
January 07, 2005
Float Nightmares

Sometimes trying to support the standards can be a real pain.

While trying to improve our CSS2 compliance, I recently did a big cleanup of our block layout code, including the code for handling floats. I made what I believed to be a fairly innocuous correction to follow the CSS2 specification. Here's the scenario.

Lets say you have a div that is set to 300 pixels in CSS. You then put a 250 pixel wide float inside that div. Immediately after that you have a 100 pixel wide overflow:hidden div. All sizes have been specified in CSS.

Now here's the pop quiz. What do you think the layout should be? Should the overflow div:
(a) Be on the same line with the float and spill out of the enclosing 300 pixel div
(b) Be placed underneath the float, automatically clearing it because there is insufficient space for
the overflow div next to the float

Before I give an answer, lets see what the CSS specification has to say on this issue. Section 9.5 on floats, fifth paragraph.

"The margin box of a table or an element in the normal flow that establishes a new block formatting context (such as an element with 'overflow' other than 'visible') must not overlap any floats in the same block formatting context as the element itself. If necessary, implementations should clear the said element by placing it below any preceding floats, but may place it adjacent to such floats if there is sufficient space."

My interpretation of this language is that there must be sufficient space for the table or overflow:hidden element to fit within the containing block. If not, you should clear. That's what I implemented. So in my opinion the correct answer to the question above is (b).

I decided to see what other browsers did. I started with Gecko. Gecko chose (a). Gecko always does (a). It is at least consistent if - in my humble opinion - incorrect. Gecko chooses (a) regardless of whether you pick strict, almost strict or quirks mode.

Next I tried WinIE, and this is the part that blew my mind. Depending on whether the float was an image or a table, the float was left or right aligned, the table specified that it floated via the align attribute or the float CSS property, and on whether or not the normal flow element was declared as a sibling or not of the float, I could get completely different results! The level of inconsistency was astonishing.

I was able to watch WinIE do clipping in one case, to wrap in a second case, to not wrap in a third case, to overwrite content in a fourth case, all by just tweaking the parameters outlined above. It's no wonder Web designers have no idea how to code a page to standards when they have to deal with a layout engine that is so horribly inconsistent and buggy.

Naively I opted to implement (b) and to hope for the best. Unfortunately the bugs immediately started pouring in. finance.yahoo.com was broken for example because it used an old-style align table and relied on it not wrapping underneath the float. Undaunted, I simply added a strict mode/quirks mode check and opted to do (a) in quirks mode and (b) in strict mode.

The bugs kept coming in though. Next was versiontracker.com, a page that is actually in strict mode and relies on an overflow:hidden div to spill out of a containing block rather than wrapping.

So now I really have no choice. This is an example of where the CSS2 standard simply can't be followed because buggy layout engines have set a bad precedent that the rest of us have no choice but to follow.

It's a shame that Gecko does not do the right thing in strict mode at least, but I suppose they had no choice in the matter either.

Comments (67) | TrackBack (17)
September 15, 2004
Z-Index

Over on webstandards.org I found a link to a guide to z-index according to the CSS2.1 specification. Click here to see this guide.

I mention it because it accuses both the Gecko and KHTML engines of violating the CSS spec, but in reality the author simply does not understand how auto z-index works in CSS2.1.

Stacking contexts are established in three ways in modern browsers:

(1) The root element gets a context.
(2) Elements with opacity < 1.0 establish a stacking context (and a z-index of auto is changed to 0).
(3) A positioned element with a z-index other than auto establishes a stacking context.

In other words, being positioned (absolute/fixed/relative) does not mean you establish a stacking context! You must also have a non-auto z-index.

The normal flow contents of positioned elements with auto z-index are sorted in an enclosing stacking context as though they had z-index 0. Document order breaks ties. This is specified in section 9.9.1 of the CSS2.1 specification.

... a stacking level for positioned descendants with 'z-index: auto', and any descendant stacking contexts with 'z-index: 0'...

So in this example you have 4 blocks, two of them relatively positioned and two absolutely positioned.

The two relative positioned blocks each contain absolute positioned descendants. Because the two RP blocks have the same z-index (auto), they are sorted in document order. RP1 is therefore below RP2. AP1, the absolute positioned block inside RP1, has a z-index of 1. This guarantees it will be above both RP1 and RP2, since it is sorted in the root element's stacking context at a z-index of 1. RP1 and RP2 render just above the normal flow (z-index 0) level of the root stacking context, and therefore will always be below AP1 if it has a positive z-index defined.

Therefore the assertion that AP1 should render below RP2 is false, and Mozilla and Safari have the correct rendering.

In this example, Safari 1.2 on Panther does violate the spec regarding the positioning of AP2. AP2 should render above RP1 and RP2 in the example but below AP1. Internal Safaris actually render this correctly (we fixed this bug a long time ago actually), so future releases of Safari will be correct.

In the next example, both Mozilla and Safari have the correct rendering. The claim that only IE gets it right is false. IE gets it wrong. The RP blocks have a z-index of auto, and so they are below all of the absolute positioned blocks with positive z-indices in the root's stacking context.

The following quote should additionally make this clear. It's also from the CSS2.1 spec, section 9.9.1.

The root element forms the root stacking context. Other stacking contexts are generated by any positioned element (including relatively positioned elements) having a computed value of 'z-index' other than 'auto'.

In other words, when a block has auto z-index, it does not act as a stacking context for other positioned descendants.

So in the next example, Mozilla and Safari are once more correct. Because RP1 now establishes a stacking context, AP1 is sorted only within RP1's stacking context. RP1 is then sorted with AP2 in the root's stacking context. RP1 has a z-index of 11 and AP2 has a z-index of 20, so RP1 is below AP2. RP1 and AP2 are both above RP2 of course because RP2 has an auto z-index, and therefore is just above normal flow content in the root stacking context (at the 0 z-index level).

I hope this helps clarify how z-index works in modern browsers. As usual, track back if you have questions.

Comments (62) | TrackBack (25)
August 15, 2004
XSLT in Safari

Some time ago we switched over to libxml in Safari for the processing of XML (and XHTML) files. I'm happy to report that we now have basic XSLT support working in Safari using libxslt. You can style your XML using xml-stylesheet processing instructions. I don't yet have a programmatic JS API working for transforming documents, but that shouldn't be too difficult to add. What I really need are XSLT test cases that use xml-stylesheet. Track back or comment if you know of some good test cases online that I can use, or just generally have suggestions to make regarding XSLT support.

Comments (70) | TrackBack (20)
July 12, 2004
Ian Hickson Weighs In

Ian Hickson comments on Apple's HTML extensions and points out that all of the solutions suggested so far are inappropriate. I'll be eagerly awaiting the responses of both Tim and Eric to this entry.

Ian also takes Apple to task for inventing new tags without discussing them in a standards forum first. I think Ian has gotten his wires crossed a bit. For whatever reason, he (and others) are equating a beta release of Tiger with a shipping release of Tiger. Until Apple actually ships Tiger, then we have not yet extended HTML. What you see in the Tiger beta is a proposal. The syntax of these tags is not frozen, as is evidenced by the fact that I'm willing to move them into a completely different namespace! The documentation delivered to Dashboard authors during WWDC even warns that the syntax of these tags has not yet been finalized.

To criticize Apple for not discussing the syntax of tags prior to releasing a piece of beta software is unfair. We are willing to discuss these elements in an open forum like the WHAT-WG. You can see our proposal for canvas and for the new form controls, since we released the complete source of these extensions in WebCore-146, and anyone can download it to view the current APIs. I believe we are being quite open in our process regarding these extensions.

Comments (0) | TrackBack (16)
The Composite Attribute

Several people questioned the presentational aspect of the composite attribute. In an earlier blog entry I had mentioned we would like to ultimately standardize our extensions. This attribute is an exception. We are aware that this functionality should be in CSS rather than in the HTML itself.

We didn't want to introduce a new CSS property, however, because how to composite should be specifiable anywhere you use an image in CSS properties. Examples of such properties are content, background-image, and list-style-image. However, normal specification of the foreground URL of an img tag is done in the HTML itself... so there was no consistent solution that could be employed.

Ideally you'd like the composite operation to be part of the image value, just as you might like cropping (indicating a sub-region of the image to use) to be part of the image value, but you are prevented from doing this with the img tag by the fact that a portion of the image value, the URL, is specified in the HTML itself.

It made little sense to invent a new CSS property that would only apply to this tag just to specify the compositing operation when the better way to do this from a purist CSS perspective would be more fundamental, affecting any place where a URL value could be used to represent an image.

I invented -moz-image-region in CSS to solve a similar problem in Mozilla (the sub-region problem), and this property was largely derided by the CSS WG, and so I knew better than to repeat the same mistake with a -khtml-composite property.

Comments (0) | TrackBack (8)
July 09, 2004
Implementation of HTML Extensions Proposal

Ok, here is what I have implemented to handle the four HTML extensions that Apple has made so far. I implemented Eric Meyer's default namespace proposal.

In HTML, in order for Safari or other Web Kit applications to recognize the new extensions, you must declare a default namespace on your root html element. This namespace's value at the moment is:

http://www.apple.com/2004/xhtml-extended/

Feedback on the above namespace identifier is welcome, if you think there's a better choice. Note that in HTML this namespace support isn't real. The default namespace declaration is all that will be checked, and it will act as a global switch that will just turn on all the HTML extensions.

However, the benefit comes when you switch to real XML. In the XML implementation, the namespace is completely real and effectively maps to a new language, Apple Extended XHTML, that has all the functionality of XHTML plus the extensions. In other words, in XML you can declare the namespace for real, use it with prefixes, use it only on canvas tags to keep the rest of the doc pure XHTML, etc.

I have not yet committed this solution, because I want to see if this meets with public approval first. Please trackback with suggestions and/or amendments.

Comments (0) | TrackBack (15)
July 08, 2004
More On Extending HTML

I received more feedback on our HTML extensions, and some people made some good suggestions for how Safari should handle extensions to HTML. There were essentially three good ideas that were pointed out to me (along with a host of really bad ones).

Tim Bray suggested namespacing the extensions we've made to HTML. Eric Meyer suggested this as well. The idea would be that you could feed your HTML with the namespace declaration to an HTML parser and it would essentially have namespace support and understand how to handle the namespaced content. This is my favorite of the suggestions, since the namespace could effectively be hacked and only allowed on the root element. This seems like a minor cut-and-paste requirement to impose on Dashboard authors that want to use the new tags and attributes.

A second suggestion was to make a special DTD. I don't like this suggestion as much, since doctypes are used for setting browser modes, and I don't want to impose a particular mode on Dashboard widget authors.

A third suggestion was to restrict these tags and attributes only to Dashboard. This seems reasonable on the surface but would be difficult to do in practice, and besides, as I stated before, we actually are submitting these extensions to WHAT-WG for review anyway. This means the intent is for them to find their way into HTML eventually.

I'll look into what it would take to implement the first suggestion. It sounds to me like people will be satisfied with such a solution. I do wonder what to do with the new values to the type attribute on the input element. Search and range are new values to an already-existing attribute, and so I'm not sure how to mollify people on this one. Breaking those out into new attributes not only makes little sense to me, since it allows for a contradictory type clash (by specifying multiple attributes), but it also would complicate the code in WebCore that routinely switches on the type of the input element.

Going forward, I'm curious what the reaction will be as WHAT-WG works to further extend HTML. Assuming that the W3C has really decreed HTML4 to be obsolete, what happens when a proposal is made by multiple browser vendors to extend it? If the W3C rejects it, should the browser vendors be forced to keep their content namespaced forever? I guess we'll cross that bridge when we come to it.

Comments (0) | TrackBack (16)
July 07, 2004
On Extending HTML

A few people have written me expressing concern over the extensions that Apple has made to HTML in order to support Safari RSS and Dashboard. I wanted to explain what we've done and hopefully clear up any confusion.

Let's start by talking about the contenteditable attribute and drag and drop. I bring these up first because what we implemented is exactly compatible with WinIE. In the case of contenteditable, we have no choice regarding syntax. We have received many bugs to support already-deployed systems that use contenteditable, and so we are constrained syntactically. Had we gone our own route, we still wouldn't work with the Web pages that use it, and it would be unrealistic to expect all of those Web sites to modify their systems simply to support Safari. This is especially true if you consider that Web sites frequently deploy systems that they didn't write in the first place, and so they wouldn't know how to modify them anyway.

Drag and drop is a similar situation. Web sites use it, and so we need to support it. We already support dozens of WinIE-invented properties, many of which are incredibly useful and well-specified, so I'm a bit confused as to why contenteditable and drag and drop are creating any stir at all. These attributes are no different from innerHTML or offsetWidth and offsetHeight or innerText or oncontextmenu or any one of the other WinIE extensions that Safari has supported since its first beta 18 months ago.

We have a phrase we like to use here on the Safari team, and that's "real-world standards compliance." What that means is that where possible we attempt to be fully compatible with the W3C standards, but we also want to support the real-world standards, i.e., extensions that for better or worse have become de facto standards. If you really do believe we should not have implemented contenteditable, then you are simply out of touch with reality.

As for the Dashboard extensions that involve changing HTML, there are exactly four of them. We've tried to keep the number to a minimum, but this functionality was required in order to build the gadgets. Let me outline them again:

(1) Slider controls. This is not only used by Dashboard but also by Safari RSS, and so this feature cannot be restricted only to the Dashboard.
(2) Search fields. Again, this feature is used by Dashboard and Safari RSS.
(3) The new composite attribute on the img tag. This feature is used only by Dashboard.
(4) The canvas tag. This feature is used only by Dashboard.

The principal complaint seems to be that we should not be polluting HTML. However, I'm not sure what we should have done instead. I can outline some of your suggestions and explain why we discarded them.

First, it was suggested that the widgets be written in XML rather than HTML and that all of the new tags and attributes be namespaced. However, this would have dramatically increased the complexity of crafting Dashboard widgets. People know how to write HTML, but most of those same people have never written an XML file, and namespaces are a point of confusion.

In addition there are technical hurdles to the use of XML. Every modern browser, including Mozilla and Safari, is much worse at XHTML than at HTML. People tend to foolishly gloss over the transition from one to the other, thinking that code you write for one will "just work" when you switch to XHTML. That simply isn't true. If you look at XHTML in both Mozilla and Safari and compare it to HTML, you'll see that it's slower, non-incremental, and generally buggier than HTML.

An example of a feature that won't "just work" when moved from HTML to XHTML is editing. The serialization model is totally different for XHTML, and HTML elements that have to be written out when you get the raw markup must know to do so using XML-style syntax in XHTML documents. Editing must be able to serialize namespaces, and ideally even preserve the namespace prefixes that were used at various points in the document as well as the use of default namespaces as set up by the author. Right off the bat I've outlined a challenging editing feature that only exists in the XHTML world. There are many more examples of these kinds of problems.

The perfect example of a widget that combines editing with HTML extensions is the Stickies widget. We simply could not have moved this widget to XHTML without doing an enormous amount of XML work.

A second complaint leveled against us was over the canvas tag, namely that it should have been done using SVG. My response to this is simple. Go to the w3c Web site and print out the SVG specification. Twenty minutes later, after you've killed a few dozen trees, then maybe you'll have an appreciation for why this wasn't practical.

Remember that SVG would have forced the use of XHTML, which had all the problems outlined above. Now add to that time the amount of work that would be required to get even a rudimentary SVG implementation going. Now factor in the time it would have taken to make that implementation perform well enough when compared with a programmatic counterpart like the canvas. Canvas only took a handful of days to implement. SVG would take months to implement.

In other words, in an ideal world where we had two years to craft Dashboard, maybe we could have used XHTML and SVG, but we aren't living in that ideal world. We can basically manage only one "huge" layout engine feature in a development cycle, and given our developer feedback the choice of HTML editing as the feature to focus on this cycle was clear. We would still love to implement SVG and XSLT and other great technologies in the future, but we simply can't do everything at once.

Finally we have submitted all of our extensions to the WHAT-WG for review. The slider in particular is already in the Web Forms draft. It is our hope that these HTML extensions will ultimately be standardized by a working group, but I wanted to emphasize that we are working with other browser vendors such as Opera and Mozilla to ensure that these extensions are implementable in those browsers and that these extensions can be standardized. We are not simply off "doing our own thing."

Comments (0) | TrackBack (32)
July 05, 2004
Introducing the Canvas

Another extension we made to HTML is a new element called the canvas. This element is essentially an image element that supports programmatic drawing. The way it works is that you can invoke a method called getContext on the canvas and then you have access to a whole range of 2d drawing calls. This element is how the hands of the Dashboard clock are drawn in HTML. The canvas fully participates in CSS styling too, so you can give it borders or background images while still painting the foreground content programmatically.

In addition to the canvas element, we've also introduced a new attribute onto the img element. The composite attribute allows you to control how an image gets composited. The default is source-over, which is how images are normally composited on the Web, but now we've introduced other values for more complex compositing operations. Especially in the Dashboard with its transparent windows, these other compositing operations become useful, since you can use this feature to "punch holes" to the desktop, allowing for all sorts of wild irregular shapes.

Comments (0) | TrackBack (201)
July 03, 2004
HTML Editing

The new WebCore also supports HTML editing. You can specify editable regions in a page using the contenteditable attribute (which maps to a CSS property behind the scenes, so you can even set that property in your user stylesheets if you want to get crazy). We've also added a selection object so that you can get the current selection, and of course support for execCommand so that you can perform operations on the selection. And yes, so that you can actually tell what you're doing stylistically, getComputedStyle is now also supported.

Comments (0) | TrackBack (32)
July 02, 2004
Plugin Scriptability

One reader of my previous entry (in the trackbacks) expressed concern that only Dashboard widget apps could execute native code. Well, we have an answer to that as well. You see, we're working with other browser vendors to extend the plugin API to add scriptability. See this link for more information. This means you can obviously execute native code from within any WebKit application's Web pages through the use of scriptable plugins.

Comments (0) | TrackBack (13)
Dashboard III

Todd Dominey writes about Dashboard in his blog and asks some questions that I'd like to clear up.

A Dashboard widget is a bundle that contains a principal HTML file and any supporting code that the widget requires (be it CSS, JS, images, or native code). A widget can add an optional interface to native code, written in Objective-C, that can be bound into JavaScript and made accessible from the HTML document's JS window object.

In other words, an address book widget could inject a property called "addressBook" into the JS window object of an address book widget's HTML document, and then expose methods and properties on that object that can be invoked from the JS. This effectively allows you to execute native code through the use of this special type of plugin.

The "native code as a service accessible from JS" model should be familiar to anyone who has used XPCOM with XUL. It's essentially the same idea. Extensions to the Firefox browser that contain native code can expose that native code to script as an XPCOM service, and then that object can be obtained from JS and have methods/properties invoked.

Again, when viewed from a certain perspective, this is a competitive Web browser feature that has been fused with Expose. These widgets that might otherwise have had to be inside the browser window as sidebar panels or toolbars have been set free by the brilliant idea of using Expose.

Anyway, some points about this model.
(1) The native plugin code must be owned by root. This means that in order for a Dashboard widget that contains one of these special types of plugins to execute that code, you have to enter a root account password (to chown the plugin code). This plugin code cannot execute, therefore, without the widget being "blessed" just as an application that you might install on your system must be.
(2) This plugin will not be present in Safari or other WebKit applications, and is only accessible from Dashboard.
(3) The dashboard object is also exposed on the JS window object of the HTML document and has methods for "meta-functions" that the Dashboard can execute.

As for many of the animations, fades, slides, etc in the widgets themselves., they simply look so damn cool because of Safari's rich support for CSS3 used in conjunction with DHTML. Do you know what I talked about at WWDC? Image replacement. Sliding doors. Using opacity to create fade effects. CSS3 text truncation. Web standards. All of which are being used to full effect in Dashboard widgets. Our standards support has grown so rich and our engine has become so smooth at effects that people are constantly mistaking pure JS/DHTML/CSS stuff that people are doing for something fancier. I've heard "That's HTML?!" several times in the past week.

Now it is true that we have made many extensions to WebCore, but only in places where there are holes in HTML that must be filled. And even then, we have tried to implement compatible models or to design so that our enhancements could be standardized in the future.

For example, the new WebCore supports all of WinIE's drag events, and that's how drag and drop is done in the Dashboard. So at the same time we added this rich support to WebCore, we also added support for a feature that can now be used in Web pages in a compatible fashion with WinIE. We started with a compatible base and enhanced drag and drop to allow you to dynamically set the drag image and even enhanced CSS with a new drag pseudo-class so that you could re-resolve style on the element while it's being dragged, but at the core, we made sure to pick a practical starting point.

In other examples, we added support for new slider widgets and search field widgets (wrapping NSSlider and NSSearchField respectively). HTML is missing these widgets, and so we had to add them so that Dashboard widgets could use them. But even there we did so in a way that is designed to be compatible with other browsers.

Comments (0) | TrackBack (13)
July 01, 2004
Safari RSS

I haven't had a chance to talk about this, but I thought I'd again start by briefly clearing up a point of confusion. Safari RSS is not the name of the entire Safari browser on Tiger. It is the name of the RSS/Atom feature in Safari itself. If you pull down the About information in Safari on Tiger, you'll see that the version is 2.0.

Comments (0) | TrackBack (10)
The Search Field

One of the new features added to WebCore is an HTML wrapper for the Cocoa NSSearchField, which means you will be able to use this control in your Web pages and Dashboard widgets.

The syntax is:

<input type="search">

The incremental attribute can be used to control whether or not the search is performed as you type.

The placeholder attribute can be used to set the greyed placeholder text in the control.

The autosave attribute allows you to specify an autosave name for your search results in the dropdown. What's cool about the autosave is that it's global, which means that two Web sites could put up a search field with an autosave name of google and then they would share the search results. (Since the actual result values are not accessible to the page there is no privacy violation.)

The results attribute allows you to specify how many results should be displayed in the dropdown.

The search DOM event fires at the precise time the search should be performed and obeys the heuristic for the control, so you don't have to trap key and click events at all. You can use the onsearch attribute on the control to respond when the search happens.

When submitted, the search control will save the submitted result, so that the user can select it from the dropdown when returning to the page.

In addition the search control is a subclass of NSTextField and also supports all of the functionality of the text input element, so you can make it readonly, set a maxlength, etc.

The search control will also even degrade gracefully in other browsers, since they won't recognize the unknown type and so will just use a text field instead.

If you have the 1.3 preview for Panther or are playing with Tiger, you can try this feature out. It works in both.

Comments (0) | TrackBack (202)
June 30, 2004
Dashboard II

I've seen plenty of opinions on what Dashboard is, most from people who haven't even used it yet. :)

Just to prove a point that there are many ways to think about this new feature, here's another take on what Dashboard is. From a browser geek's perspective, the Dashboard is a collection of HTML sidebar panels liberated from the browser window and placed anywhere on your screen. The "Web pages as widgets" concept is really just a logical extension of the Web sidebar panel metaphor fused with Expose.

In a Web browser like Mozilla, for example, the sidebar can be toggled with a key, the panels inside can be viewed, and individual panels can be selected, reordered, managed, and added/deleted. Custom panels can be installed into the sidebar and people have written panels for Mozilla (and Opera) that do everything from FedEx package tracking to HTML validation.

In other words, like the desk accessories of yore, the sidebar panels in Web browsers are web page accessories that perform basic functions. When Netscape 6 came out many of these panels were downloadable from netscape.com. People wrote Bugzilla widgets for checking Mozilla bugs, thesaurus and dictionary widgets, widgets for using lxr, FedEx package trackers, and so on. There were a lot of these panels made, written both in XUL and in HTML, and this was done a long time ago... which brings me to my point.

The concept of small "Web pages as accessories" inside a browser has existed for years.

However the sidebar metaphor suffers from usability problems, such as the inability to scale up to many panels as well as being constrained by the browser's window width. It's also hard to view multiple panels at once. The panels are also tied to a particular application (the browser) despite frequently having no connection to the application itself.

A logical way of solving these sidebar panel usability problems is to free those panels from the browser window and make them accessible anywhere on the screen (both invokable and dismissable with the touch of a key). This gives you the real estate you need to really make the widgets useful, lets you show multiple widgets at once, and makes the UI for panel configuration easier, since you have more room to represent that user interface on-screen.

Comments (0) | TrackBack (37)
Dashboard

I haven't blogged in a long time, primarily because I've been so busy preparing for WWDC (working frantically on my presentation as well as fixes to WebCore to support Safari RSS and Dashboard of course). I'll be talking about both Dashboard and Safari RSS a lot more in depth (primarily from the perspective of all the new open source WebCore features that were added to support these two new features) once I've gotten some sleep. :)

I wanted to blog briefly to clear up what the widgets actually are written in. They are Web pages, plain and simple (with extra features thrown in for added measure). Apple's own web site says "build your own widgets using the JavaScript language", but that's sort of misleading. The widgets are HTML+CSS+JS. They are not some JS-only thing.

In other words, each widget is just a web page, and so you have the full power of WebKit behind each one... CSS2, DOM2, JS, HTML, XMLHttpRequest, Flash, Quicktime, Java, etc. I'll have a lot more to say later on, but I thought it important to clear that up right up front, since a lot of people were asking me about it in email and such.

Comments (0) | TrackBack (38)
June 08, 2004
iTunes and WebKit

Just to clear up a common misconception, iTunes does not use WebKit to render the music store. What you see when you visit the iTunes music store may look "web-like", but it isn't HTML, and it isn't rendered by WebKit.

Comments (107) | TrackBack (7)
May 16, 2004
Testing Page Load Speed

One of the most problematic tasks when working on a Web browser is getting an accurate measurement of how long you're taking to load Web pages. In order to understand why this is tricky, we'll need to understand what exactly browsers do when you ask them to load a URL.

So what happens when you go to a URL like cnn.com? Well, the first step is to start fetching the data from the network. This is typically done on a thread other than the main UI thread.

As the data for the page comes in, it is fed to an HTML tokenizer. It's the tokenizer's job to take the data stream and figure out what the individual tokens are, e.g., a start tag, an attribute name, an attribute value, an end tag, etc. The tokenizer then feeds the individual tokens to an HTML parser.

The parser's job is to build up the DOM tree for a document. Some DOM elements also represent subresources like stylesheets, scripts, and images, and those loads need to be kicked off when those DOM nodes are encountered.

In addition to building up a DOM tree, modern CSS2-compliant browsers also build up separate rendering trees that represent what is actually shown on your screen when painting. It's important to note two things about the rendering tree vs. the DOM tree.

(1) If stylesheets are still loading, it is wasteful to construct the rendering tree, since you don't want to paint anything at all until all stylesheets have been loaded and parsed. Otherwise you'll run into a problem called FOUC (the flash of unstyled content problem), where you show content before it's ready.

(2) Image loads should be kicked off as soon as possible, and that means they need to happen from the DOM tree rather then the rendering tree. You don't want to have to wait for a CSS file to load just to kick off the loads of images.

There are two options for how to deal with delayed construction of the render tree because of stylesheet loads. You can either block the parser until the stylesheets have loaded, which has the disadvantage of keeping you from parallelizing resource loads, or you can allow parsing to continue but simply prevent the construction of the render tree. Safari does the latter.

External scripts must block the parser by default (because they can document.write). An exception is when defer is specified for scripts, in which case the browser knows it can delay the execution of the script and keep parsing.

What are some of the relevant milestones in the life of a loading page as far as figuring out when you can actually reliably display content?

(1) All stylesheets have loaded.
(2) All data for the HTML page has been received.
(3) All data for the HTML page has been parsed.
(4) All subresources have loaded (the onload handler time).

Benchmarks of page load speed tend to have one critical flaw, which is that all they typically test is (4). Take, for example, the aforementioned cnn.com. Frequently cnn.com is capable of displaying virtually all of its content at about the 350ms mark, but because it can't finish parsing until an external script that wants to load an advertisement has completed, the onload handler typically doesn't fire until the 2-3 second mark!

A browser could clearly optimize for only overall page load speed and show nothing until 2-3 seconds have gone by, thus enabling a single layout and paint. That browser will likely load the overall page faster, but feel literally 10 times slower than the browser that showed most of the page at the 300 ms mark, but then did a little more work as the remaining content came in.

Furthermore benchmarks have to be very careful if they measure only for onload, because there's no rule that browsers have to have done any layout or painting by the time onload fires. Sure, they have to have parsed the whole page in order to find all the subresources, and they have to have loaded all of those subresources, but they may have yet to lay out the objects in the rendering tree.

It's also wise to wait for the onload handler to execute before laying out anyway, because the onload handler could redirect you to another page, in which case you don't really need to lay out or paint the original page at all, or it could alter the DOM of the page (and if you'd done a layout before the onload, you'd then see the changes that the onload handler made happen in the page, such as flashy DHTML menu initialization).

Benchmarks that test only for onload are thus fundamentally flawed in two ways, since they don't measure how quickly a page is initially displayed and they rely on an event (onload) that can fire before layout and painting have occurred, thus causing those operations to be omitted from the benchmark.

i-bench 4 suffers from this problem. i-bench 5 actually corrected the problem by setting minimal timeouts to scroll the page to the offsetTop of a counter element on the page. In order to compute offsetTop browsers must necessarily do a layout, and by setting minimal timers, all browsers paint as well. This means i-bench 5 is doing an excellent job of providing an accurate assessment of overall page load time.

Because tests like i-bench only measure overall page load time, there is a tension between performing well on these sorts of tests and real-world perception, which typically involves showing a page as soon as possible.

A naive approach might be to simply remove all delays and show the page as soon as you get the first chunk of data. However, there are drawbacks to showing a page immediately. Sure, you could try to switch to a new page immediately, but if you don't have anything meaningful to show, you'll end up with a "flashy" feeling, as the old page disappears and is replaced by a blank white canvas, and only later does the real page content come in. Ideally transitions between pages should be smooth, with one page not being replaced by another until you can know reliably that the new page will be reasonably far along in its life cycle.

In Safari 1.2 and in Mozilla-based browsers, the heuristic for this is quite simple. Both browsers use a time delay, and are unwilling to switch to the new page until that time threshold has been exceeded. This setting is configurable in both browsers (in the former using WebKit preferences and in the latter using about:config).

When I implemented this algorithm (called "paint suppression" in Mozilla parlance) in Mozilla I originally used a delay of 1 second, but this led to the perception that Mozilla was slow, since you frequently didnt see a page until it was completely finished. Imagine for example that a page is completely done except for images at the 50ms mark, but that because you're a modem user or DSL user, the images aren't finished until the 1 second mark. Despite the fact that all the readable content could have been shown at the 50ms mark, this delay of 1 second in Mozilla caused you to wait 950 more ms before showing anything at all.

One of the first things I did when working on Chimera (now Camino) was lower this delay in Gecko to 250ms. When I worked on Firefox I made the same change. Although this negatively impacts page load time, it makes the browser feel substantially faster, since the user clicks a link and sees the browser react within 250ms (which to most users is within a threshold of immediacy, i.e., it makes them feel like the browser reacted more or less instantly to their command).

Firefox and Camino still use this heuristic in their latest releases. Safari actually uses a delay of one second like older Mozilla builds used to, and so although it is typically faster than Mozilla-based browsers on overall page load, it will typically feel much slower than Firefox or Camino on network connections like cable modem/modem/DSL.

However, there is also a problem with the straight-up time heuristic. Suppose that you hit the 250ms mark but all the stylesheets haven't loaded or you haven't even received all the data for a page. Right now Firefox and Camino don't care and will happily show you what they have so far anyway. This leads to the "white flash" problem, where the browser gets flashy as it shows you a blank white canvas (because it doesn't yet know what the real background color for the page is going to be, it just fills in with white).

So what I wanted to achieve in Safari was to replicate the rapid response feel of Firefox/Camino, but to temper that rapid response when it would lead to gratuitous flashing. Here's what I did.

(1) Create two constants, cMinimumLayoutThreshold and cTimedLayoutDelay. At the moment the settings for these constants are 250ms and 1000ms respectively.

(2) Don't allow layouts/paints at all if the stylesheets haven't loaded and if you're not over the minimum layout threshold (250ms).

(3) When all data is received for the main document, immediately try to parse as much as possible. When you have consumed all the data, you will either have finished parsing or you'll be stuck in a blocked mode waiting on an external script.

If you've finished parsing or if you at least have the body element ready and if all the stylesheets have loaded, immediately lay out and schedule a paint for as soon as possible, but only if you're over the minimum threshold (250ms).

(4) If stylesheets load after all data has been received, then they should schedule a layout for as soon as possible (if you're below the minimum layout threshold, then schedule the timer to fire at the threshold).

(5) If you haven't received all the data for the document, then whenever a layout is scheduled, you set it to the nearest multiple of the timed layout delay time (so 1000ms, 2000ms, etc.).

(6) When the onload fires, perform a layout immediately after the onload executes.

This algorithm completely transforms the feel of Safari over DSL and modem connections. Page content usually comes screaming in at the 250ms mark, and if the page isn't quite ready at the 250ms, it's usually ready shortly after (at the 300-500ms mark). In the rare cases where you have nothing to display, you wait until the 1 second mark still. This algorithm makes "white flashing" quite rare (you'll typically only see it on a very slow site that is taking a long time to give you data), and it makes Safari feel orders of magnitude faster on slower network connections.

Because Safari waits for a minimum threshold (and waits to schedule until the threshold is exceeded, benchmarks won't be adversely affected as long as you typically beat the minimum threshold. Otherwise the overall page load speed will degrade slightly in real-world usage, but I believe that to be well-worth the decrease in the time required to show displayable content.

Comments (89) | TrackBack (19)
May 15, 2004
clear: none bug

The bad news is that as reported here, Safari 1.2 does ignore clear: none. The good news is that this bug was fixed months ago, so look for the fix in a future release. :)

Comments (149) | TrackBack (8)
April 25, 2004
min-height

I have implemented support for min-height and max-height. The footers example on alistapart works now. I've also been fixing bugs with table cells and percentage height children, so that elements inside cells with percentage heights will flex properly.

I still haven't done min/max-width/height for positioned elements though. That's all that remains, and then support for it will be complete.

Comments (82) | TrackBack (13)
March 29, 2004
Soft Hyphen Support

This excellent blog entry showed up in my trackback a while ago. It basically explains how soft hyphens are supposed to work on the Web and points out that Safari always renders soft hyphens even when they don't end up breaking a line.

I thought about just taking the Mozilla route and punting on soft hyphens (i.e., changing KHTML to always hide them), but it turned out to be no harder to just make KHTML work properly with them the way IE6 and Opera do, so I'm pleased to report that KHTML's soft hyphen behavior now matches those browsers.

Comments (170) | TrackBack (93)
February 11, 2004
Regressions from 1.1

There have been some excellent discoveries of some minor regressions from 1.1 to 1.2. For those who are curious, here are some of the highlights.

(1) A bug with display:block in generated content. Reported here. This particular bug was caused by a rearchitecture to unify anonymous blocks created by the render tree when wrapping normal inline flows and the anonymous blocks created for generated content. I forgot to patch the code that coalesced inlines within anonymous blocks to ignore pseudo-element anonymous blocks, and so all the inline children end up *inside* the anonymous :before block. I have a fix.

(2) A regression from incremental repainting. If you change the position of an object from absolute/fixed/relative to static, and if you have descendant positioned elements for whom the object is acting as a containing block, Safari will try to repaint those elements, but only after the containing block chain has been updated... meaning that repaints happen in the wrong place. I don't have a fix yet, but it should be pretty easy to patch. This is honestly kind of a weird thing to do in DHTML, so our odds of catching it were pretty slim. Typically position changes like this only happen on alternate stylesheet re-resolves, which change so much that the whole page would repaint anyway, masking the bug.

(3) Classic image rollovers (e.g., preloads using "new Image") are busted, resulting in a flash on initial rollover, even when the images should have been pre-loaded. This is the truly weird one. I looked at this for a couple of hours tonight, and the images do get pre-loaded into the WebCore cache, but then for some reason they get punted out at rollover time. Not sure why.

I feel bad for not catching this. The problem is that I patched CSS rollovers so that they would load lazily in order to avoid loading unused images, and so when I saw this image flashing, I just assumed I was seeing the results of my CSS change. In reality some other unrelated and as-yet-unknown change caused the regression.

Have any other regressions from 1.1 to report? If so, please post trackbacks in this entry with bug descriptions and (ideally) test pages.

Comments (0) | TrackBack (31)
February 09, 2004
Safari 1.2 Released

Safari 1.2 has been released for Panther (OS 10.3). Here are some of the technical highlights:

LiveConnect - LiveConnect is now supported for Java applets, allowing for bi-directional communication between Javascript and Java. Many Java sites that didn't work in earlier versions of Safari will now work properly in 1.2.

Personal Certificate Support - Personal certificates are now supported, so sites that were previously inaccessible are now available in the latest Safari.

keygen Implementation - The keygen element is now supported, so you can now generate key pairs from e.g., VeriSign.

Full Keyboard Access - You can now tab to all controls (and optionally links) on a page. There has been much confusion over this feature, since the ability to tab to all controls honors the OS setting.

In order to tab to popup menus, you need to go to your system preferences, select the Keyboard and Mouse panel, and then select the Keyboard tab. At the bottom of the tab is a checkbox next to the words "Turn on full keyboard access." Check that box to enable full keyboard access, and you'll find that you'll now be able to tab to popups all over the operating system (including Safari).

Another complaint I've seen on forums was that you couldn't type letters to have the popup jump directly to a selected item (e.g., typing "U" to jump to "United States"). Again, we obey the OS behavior, which does allow this, but only after you hit the spacebar when the control has the keyboard focus. Multi-letter typing is supported to complete to a specific item. Try it. You'll like it. :)

Improved Downloads - A download halted by the user or stalled due to network troubles can now be resumed in the Download Manager. You'll also find a number of other improvements to downloads, including the ability to select individual downloads to e.g., delete them, the ability to save images to specific locations via the context menu, and the removal of the 4-connection limitation when downloading while browsing.

Printing Improvements - The "huge margin" problem for printing has been fixed, and Safari is also smarter now about scaling the page when it contains long unwrappable lines. In addition, the CSS2 page break properties are now supported (for values of "always") as per the CSS2.1 Paged Media specification. The speed of printing has been improved dramatically, and you can also now disable backgrounds when printing.

International Domain Name Support - Safari 1.2 supports the IDN standard, which allows for non-ASCII characters in host names.

RTL and International Text Improvements - Handling of RTL and international text has been improved for better Hebrew, Arabic and Hindi support.

Accessibility Improvements - The title attribute is now supported as a tooltip, and 1.2 also supports the accesskey attribute for accessing specific objects in the Web page via the keyboard. In addition, minimum font size is now supported and exposed in Safari's preferences.

Mini Form Controls - Safari 1.2 now analyzes the font size specified by a Web page for form controls and swaps in the mini and small versions as needed. Sites like Travelocity will now render properly with mini form controls in place.

XMLHTTPRequest - XMLHttpRequest is now supported, which means that those of you subscribed to Orkut can now rate your friends. ;)

CSS2 Table Support - Table support has been improved, with border-spacing now fully supported, empty-cells supported, and border collapsing supported.

DHTML Performance Improvements - Safari 1.2 is light years ahead of 1.1 in terms of DHTML performance. When objects change size or position, Safari 1.2 will only repaint the affected areas (whereas older versions would repaint the entire visible area every time).

hover/active improvements - Safari 1.2 has a faster (and more correct) implementation of :hover and :active, so it will no longer get into "stuck hover" states or mistakenly put multiple overlapping objects into :hover simultaneously.

Generated Content Support - 1.2 supports the positioning and floating of generated content as per the CSS2.1 spec, and many bugs have been fixed in generated content, particularly with first-letter and first-line. First-letter is now fully dynamic, and first-line styles will now be inherited properly into the descendants of the line. Both styles will even work across nested block-level children (something I believe that no other browser can yet do).

Marquee Support - All forms of marquees are supported, and the behavior is designed to match Internet Explorer for windows. The start() and stop() methods are also supported, so that marquee animations can be paused and resumed. Safari supports marquees using a special overflow value in conjunction with the CSS3 draft properties, and so it's easy to disable the animation while still allowing access to the content (all via a user stylesheet).

Small-caps Support - Safari 1.2 supports small-caps variants for fonts. It does not support true variants but instead synthesizes the font using the 70% heuristic employed by other browsers (like Mozilla).

Stability - Many crashes and hangs have been addressed.

Performance - Safari has added smarts when transitioning between pages (e.g., preserving the vertical scrollbar to avoid an extra layout), so that pages load more quickly on fast networks. This is just one example of several performance enhancements we made to speed up browsing since 1.1.

Caching Improvements - Safari's WebCore cache was not honoring expiration time, and this led to stale content remaining in the cache. This issue has been addressed.

HTTPS Speed Improvements - HTTPS pages load more quickly in Safari 1.2, thanks to bug fixes and improvements.

CSS Load Improvements - Safari no longer aggressively fetches images specified in CSS files but instead waits until the image is used in the Web page before loading it. This reduces the load time on sites that use generic cross-site CSS files with lots of rules that might never apply on many pages. (Translation: SprintPCS is fast now.)

Comments (142) | TrackBack (34)
January 24, 2004
CSS @namespace support

I just finished implementing support for the @namespace directive in stylesheets. The exercise got me thinking about clever ways to represent namespaces, element names, and attribute names efficiently.

Right now KHTML stores the namespace + the element/attribute name as two 16-bit quantities jammed into a single 32-bit value. The high 16 bits represent the namespace, and the low 16 bits represent the element/attribute name.

For HTML elements, no space is consumed at all for this 32-bit value for element names, since a virtual id() method is implemented in all the HTML element subclasses to return the appropriate element id (the upper 16 bits where the namespace would be are just 0).

For XML elements, the element name is a member variable of the XMLElementImpl object. As elements in a document are constructed and new namespace URIs, element names, and attribute names are encountered, the strings get registered and corresponding ids get handed out.

There are several drawbacks to this approach. The first is that the qualified name (including the prefix) gets lost. Technically when you ask for the qualified name via the DOM you should get back the original prefix that was specified.

Second, this element/attribute/namespace URI cache is per-document, which creates a dependency between a stylesheet and the document it's found in, thus defeating the ability to cache a stylesheet in memory for use across multiple documents.

Basically I'm trying to come up with a space and time-efficient solution that doesn't degrade the current performance of HTML elements, but that still performs well for XML elements. Ideally I would not have to have special methods for getting an HTML element name vs. getting an XML element name.

Comments (43) | TrackBack (106)
XML Error Reporting III

Thanks to those of you who answered my question regarding how much of an invalid page should be rendered. It turns out that the XML spec is clear on this issue, and that I must stop building up the page DOM after the first fatal error is encountered.

With that in mind I now tell libxml to continue the processing, but I start ignoring all of the callbacks. That way I get a list of all the errors, but properly stop the DOM tree buildup after the first error.

For those of you who suggested that WebKit needs some sort of error reporting API, I agree, and if it had one, these errors would obviously be reported to it. However, these errors still have to be reported aggressively so that WebKit clients can't mask these mistakes.

I don't believe in showing a sheet or a dialog as an intermediate step prior to displaying a rendering of the page. The reason I dislike this idea is that this error reporting is primarily a Web developer feature, and they're just going to want to load the page, see the errors, maybe correct some CSS at the same time, and then reload with changes until the error report has been eliminated.

The end user isn't ever going to see this report, since anyone who makes an invalid XML file right now ends up with something that won't display in any browser. Thus it seems to me that the report should be easy to access (in terms of # of clicks), always visible, and included with the page rendering.

I have polished the look of the report a bit based off suggestions. Here's another screenshot.

Comments (15) | TrackBack (5)
January 22, 2004
XML Error Reporting II

Responding to comments in the previous blog entry:

(1) Some people thought this was a hacked expat. Darin actually switched Safari over to libxml2, so the error messages you're seeing (as well as the ability to continue parsing) are all built in to libxml2.

(2) Do you think it's better to show the page only up to the first error or to try to display the entire page (with the understanding that what follows the first error could be very badly mangled)?

(3) Often there are a lot of meaningless errors after the first. I could put a cap on the number of displayed errors to deal with this problem or just not worry about it. What do people think?

(4) Those of you who suggested drawers for errors, remember a drawer is a UI element in Safari and not WebKit. This feature should just work out of the box for WebKit clients, so I'm inclined not to use drawers or sheets, but to just display the errors at the top of the page.

Comments (61) | TrackBack (9)
Obtrusive XML Error Reporting

I spent some time tinkering with XML today and decided to try out a non-draconian approach to XML error recovery. Point the browser of your choice at the following XHTML URL:

http://www.faireal.net/soft/browser/XHTML-Invalidator?Content-Type=application/xhtml+xml

If you try this in Mozilla, you should get something like this:

Screenshot

In current versions of Safari, you get something even worse, since you don't even get any line/col information.

What I implemented in my build (it's still just at the tinkering stage remember, so be gentle) is error recovery for non-fatal errors, i.e., the XML parser continues and attempts to recover from the error, and then I still build the DOM for the XML.

Once the parser is finished, I then display the Web page, but with a badge of shame, namely an error report at the top that lists all of the discovered errors. This is not a halt-at-first-error system, which is cool, since it means you'll see *all* the errors in your page and not just one.

Here's a screenshot of what I have so far. The error report is just XHTML as well (shoehorned in at the top using DOM calls), so if you have any ideas of how I could style it to make it look really cool, show me your screenshots.

Let me know what you think of this idea. Do you like it better than draconian error handling? If you dislike it, let me know why!

Comments (67) | TrackBack (9)
January 20, 2004
More on XML Error Handling

I thought I'd respond to a few of the comments I received:

Many people suggested that there be a built-in validator in the browser that could show the errors to the developer. The validators basically break down into two types: obtrusive validators and unobtrusive validators.

If the validator is unobtrusive, then I would argue that it won't receive sufficient usage to make a difference. If the browser doesn't impose a penalty of some kind, then there will be no incentive for the author to correct mistakes.

I can see the value of an obtrusive validator, as long as the obtrusive part was only checking well-formedness (i.e., really basic mistakes).

(2) Some people pointed out that my own blog was not valid. I have two responses to that:

(a) I am not arguing for perfectly valid XML documents. I am arguing for well-formed XML documents. There is a difference. I think asking that the page be well-formed is setting the bar fairly low. For example, one of the current errors on this blog is that I have two elements with the same id. While this makes the blog invalid, it does not have any effect on the blog being well-formed. At least I don't think it does. :)

(b) I'm illustrating a point, namely that I have no reason to make the blog valid, given that browsers will display the blog anyway.

(3) People complained that I wasn't serving up XHTML. I can't actually serve up XHTML if I want the blog to be displayable in all browsers, including Safari, which still has sufficient issues with XHTML that I can't make that switch yet.

(4) My comments on HTML error handling were largely misinterpreted.

Some people thought I was attacking WinIE for its permissive handling of HTML. I was not, and I'm glad others appreciated that fact. Back in the 90s WinIE had to emulate the permissive error handling of the then-dominant browser Netscape. They had no choice if they wanted Web sites to be viewable as the designer intended. They were in the same position then that Safari is in now.

Nor am I suggesting that WinIE should become less tolerant of malformed HTML, or that they are at fault for not doing so. That is simply not a logical conclusion to have drawn from my previous comments. You can't take a Web site (even a malformed one) that works a certain way and suddenly refuse to render it or even render it radically differently than before.

For HTML, this issue was resolved long ago in favor of permissive error handling and recovery, and no modern browser is to blame for that situation.

Others said a browser that handles malformed HTML is better than one that does not, and if Safari doesn't handle all this malformed HTML, then it's simply not as capable a browser.

What amused me about this comment is that there is no definition of what it means to handle malformed HTML. As long as a browser shows you something and doesn't crash, it has handled the malformed HTML. What people don't understand is that you don't simply have to handle the malformed HTML. You have to handle it in exactly the same way as the Web browser that the site author designed for.

If you do not, you'll end up with different renderings of the same page, which as I said before, constitute the largest set of rendering differences between Web browsers. Perfect emulation is what makes error recovery so difficult. If you allow grossly malformed pages, then most XML on the Web will end up being grossly malformed (as is the case with HTML today).

Once you have a Web full of grossly malformed XML, there will be one dominant browser that designers will check to see if the site looks ok. They will then make assumptions that other browsers will recover from the malformation errors in precisely the same way and will simply assume that it is the fault of the other browsers if they don't.

Right now it is the responsibility of alternate browsers to emulate the dominant browser's error recovery strategies, but there's simply no reason to do that for XML as well.

Comments (35) | TrackBack (6)
January 19, 2004
XML, Not HTML

Because enough people seem to be getting confused by my previous blog entry, let me clarify that I am talking about XML error handling and not HTML error handling. Obviously given the current state of the Web, a browser must be extremely good at handling malformed HTML and emulate WinIE as much as possible. We of course are actively working on that with Safari.

The reason I brought up HTML error handling while talking about XML error handling was to point out how much time and effort it costs developers simply to handle malformed content. Also for those who mistakenly interpreted this as an attack on WinIE, of course it isn't. WinIE had to emulate Netscape's error handling, so if you want to blame anyone, blame Netscape.

Right now the browsers that handle XML *are* draconian in their handling, and I see no reason why that would change in the future (unless WinIE weighs in with a tolerant XML parser). In effect as far as Web browsers are concerned we draconians have already won. :)

Comments (20) | TrackBack (6)
January 18, 2004
XML Error Handling in Web Browsers

I've been following the topic of XML error handling on Mark Pilgrim's blog with great interest. Go read this blog entry. Done? Good. Now go read this blog entry.

Safari has draconian XML error handling. If the file isn't well-formed, Safari won't display it. Mozilla does the same, which should come as no surprise, since the two browsers use the same open-source XML parser (expat).

I fall squarely into the draconian camp and agree with Tim Bray. Fully half of the bugs I receive in WebCore are not bugs at all, but are essentially differences in error handling and error recovery between Safari and the dominant Web browser, WinIE. None of these issues occur with XML.

If we lived in a world where browsers could refuse to display malformed content (with useful error notification of course so that authors could easily repair their content), then all of these "bugs" would simply disappear. I could focus my efforts on real DOM and CSS bugs, and not have to waste my time emulating the behavior of WinIE.

Relaxing restrictions on well-formedness is a slippery slope, and where does it end? Consider all the "helpful" rules that exist in HTML today thanks to early versions of Netscape and WinIE. Did you know that any h1-h6 tag can close any other h1-h6 tag? Try it. Open an h1, type some text and then put in a close h2. It will close up the h1 in WinIE and Mozilla. (I haven't yet fixed this "bug" in Safari.) Try specifying a close tag for a paragraph by itself. You'll get an empty paragraph in Safari, Mozilla, and WinIE.

Of course the most complicated error recovery problem is residual style, which I have blogged about at length. This "helpful feature" (note the sarcasm) allows you to accidentally mis-nest style tags like the italic and bold tags and basically treat HTML more like a stream of "on/off" states than an actual tree structure. This feature is more a by-product of primitive browsers from the 90s that didn't have true DOMs than an actual intended error recovery system.

There's also the missing quotes problem, e.g., leaving a close quote off a link href. Browsers employ complicated heuristics to try to match up unclosed quotes that depend on the number of quotes in the document, their positions, and other factors. Safari doesn't really handle this problem that well yet, and it shouldn't have to.

The whole reason nearly all Web pages on the Internet are malformed is because browsers let Web page authors get away with it. As long as browsers are permissive in their error handling and recovery, Web authors will continue to produce invalid Web pages, because they won't even have any idea the pages they are authoring are invalid!

People in the error recovery camp then suggest ideas like icons in the status bar, or error messages dumped to some obscure console, but the average Web designer isn't going to know or care about validation as long as WinIE displays the Web site adequately. The only way you can make the average Web designer care is to get in his face with the obvious errors. The browser has to make a face and refuse to eat the swill that is being force-fed to it, or the average designer is simply going to shrug and say, "Well, close enough."

The crux of the problem with implementing true error recovery is that it must be unambiguous. Every Web browser has to recover from malformed content in precisely the same way. This means that in order for browsers to be tolerant of malformed content, there would have to be a specification regarding how to handle all possible malformations. This is virtually impossible to specify, so why waste time and energy on it when creating well-formed XML files is so ridiculously simple?

I think people who don't work on Web browsers for a living have no concept of just how malformed the Web really is, so let me state this as clearly as I can:

The #1 reason that HTML pages render incorrectly in alternate browsers is because of differences in error handling and recovery.

Comments (39) | TrackBack (27)
January 09, 2004
Redesign *Still* in Progress

Yes, yes, I'm still working on it. A few of the designs aren't uploaded yet, but you will find that the default "Clean" look is very similar to the previous Safari design (for those of you who objected to the other designs).

Comments (5) | TrackBack (10)
December 16, 2003
Redesign in Progress

Pardon the mess. Redesign currently in progress.

Update: Redesign is still ongoing, with files left to upload, etc. I'm putting the redesign on hold, though, until after Return of the King. I'm sure you understand. :)

Comments (3) | TrackBack (24)
December 12, 2003
CSS Caching Issue

In my earlier blog entry on caching problems, the issue of CSS being overly aggressive about fetching background-images was mentioned. I have fixed the issue now so that background images will only be fetched when first used. Sites like SprintPCS load much more quickly with this fix.

Comments (2) | TrackBack (5)
Page Breaks

WebCore now supports values of always for page-break-before and page-break-after. I followed the CSS2.1 specification very closely, which says you can only apply these properties to block level elements in the root's block formatting context. I suspect other browsers may be more lax in their acceptance of these properties, so if anyone has real-world sites I can test, post them here.

Comments (1) | TrackBack (21)
December 10, 2003
Caching Issues

It sounds like there are two primary caching complaints that come up over and over in my blog comments. The first is that Safari caches subresources too aggressively, and the second is that Safari fetches subresources too aggressively. :)

We found the first problem. The WebCore cache was not actually making use of the proper expiration dates, which meant the WebCore memory cache would keep using stale objects even though they should have expired. We have fixed the problem, and we believe this will solve the vast majority of the stale content issues.

Comments (8) | TrackBack (16)
December 08, 2003
Progress Report

We've made fantastic progress in WebCore since 1.1. Here are a few of the highlights:
(1) Border collapsing in tables
(2) DHTML performance improvements (including optimized layout paths for positioned elements and smart repainting when objects move).
(3) Support for tabbing to all controls in a web page
(4) Support for the css2 outline property
(5) Support for the css2 cursor property
(6) Support for tabbing to links in a web page
(7) Tabindex support
(8) The title attribute is now displayed in a tooltip
(9) Small-caps support (synthesized as in other browsers)
(10) Marquees (flame away)
(11) Support for the DOM click() method
(12) Support for window.print()
(13) DOM lvl 2 (and even 3) event handling improvements
(14) better support for malformed HTML
(15) Minimum font size fixed to be like it was in Safari 1.0
(16) Smart line breaking so that long URLs don't screw up page content
(17) The two bugs listed here are fixed
(18) Major improvements to :before/:after handling, including support for floating and positioning :before/:after content (css2 did not allow this, css2.1 does). Many of the bugs described here are fixed.
(19) The first-letter/text-transform "text doubling" bug is fixed.
(20) :first-letter now works even when applied across spans, so the letter T in Thoughts from Eric on meyerweb.com now shows up properly.
(21) :hover/:active have been rewritten to be faster and to avoid bugs related to hovering over floats and relative positioned elements. Fixes the problems on diveintomark and mezzoblue.com.
(22) Many of the crashes/hangs reported via the Safari bug button have been addressed.

Keep the feedback coming!

Comments (18) | TrackBack (29)
November 12, 2003
The Reality of Bugs

As some comments in my previous blog entry illustrate, I think people simply don't grasp the magnitude of the Web. There are (conservatively) 10 million Web sites on the Web. Let's say (conservatively) that each Web site has 50 unique Web pages. That's 500 million Web pages that the Web browser has to work perfectly on.

Let's imagine that the browser has done a fantastic job of emulating all the quirks of WinIE and Netscape 4, and that it is really good at laying out malformed HTML. An awesome browser would be (conservatively) 95% compliant, which means that it would have some sort of bug or problem on 5% of those 500 million Web pages.

5% of 500 million Web pages is 25 million malfunctioning Web pages. Let's now assume that only 10% of those Web pages are even seen by someone using Safari itself. Now we're down to 2.5 million pages seen by Safari users.

If only 10% of those users even bother to report a bug, that's 250,000 unique bugs that have to be screened.

This is the reality of the Web. People are constantly shocked and amazed that their pet bug hasn't been fixed in subsequent releases (e.g., in Mozilla or Safari), but those people simply don't understand how many hundreds of thousands of bugs their particular problem is competing with.

Comments (0) | TrackBack (16)
Bug Guilt Trips

I love the tactics some people use when filing bugs. In particular the tactic of saying something inflammatory in order to goad the receiver of the bug into fixing it. You see this a lot in Bugzilla, and also in reported Safari bugs.

Here are some of my favorite phrases (for your enjoyment). Let X = the browser of your choice. Let Y = any other browser.

(1) The Promise - "The lack of this feature is the one thing that keeps me from switching to X."
(2) "I can't work under these conditions. I'll be in my trailer." - "I can't believe you broke this! That's it! I'm going back to Y!"
(3) Playing the EOMB Card - "How can this be broken? Every other modern browser gets this right."
(4) Impatience - "Months have passed, and this bug still hasn't been fixed! What's the holdup?"
(5) Overeagerness - "Still broken." (2 days later.) "Still broken." (2 days later.) "Feature still doesn't work. (2 days later.) "Broken in build from mm/dd/yy."

The Safari team has actually started using the term EOMB as a way of referring to all other modern browsers. ;)

Comments (1) | TrackBack (16)
October 30, 2003
<marquee> Support

I just implemented complete <marquee> support! Let the flames begin!

Comments (7) | TrackBack (24)
October 25, 2003
XUL

Looks like XUL and XAML have become the topic of an interesting cross-blog conversation recently. It started with a blog entry from Ryan Dawson, who mentioned XUL and XAML together. Eric Meyer responded here. Robert Scoble then clarified that Ryan does not work for Microsoft here.

More responses followed from Dave Shea and from Simon Willison.

I know nothing about XAML yet, so I think comparisons with XUL are perhaps premature. I can, however, talk about XUL itself, since people are starting to ask questions about both Mozilla XUL and about the "XUL-like" features that have been implemented in Safari on Panther.

So what is XUL anyway? It is an XML language whose tags consist of:

(1) layout primitives (tags like hbox, vbox, grid and stack)
(2) widgets (tags like menulist, menubar, toolbar, and button)
(3) commands, keyboard accelerators (tags like command and keyset)
(4) xul templates (for UI binding to back-end data, represented as RDF)

Rendering a XUL file in Mozilla is just like displaying an HTML Web page, albeit without any surrounding Window chrome, in that the XML is parsed, a tree representation of the tags (called the Document Object Model or DOM for short) is constructed, associated CSS is used to define the presentation for the tags, layout objects that represent the visual on-screen boxes are built, and the result is rendered on your screen as a dialog or window.

In other words, XUL at its core is actually pretty simple. It's nothing more than an XML description of the GUI elements in your dialog or window, along with presentational hints for how to position those elements.

A whole slew of acronyms come into play in Mozilla to bring the XUL to life. They are (in no particular order): CSS, DOM, RDF, XML, XBL, JS, and XPCOM.

Loading a XUL file goes something like this:
(1) XML is parsed into a DOM tree.
(2) CSS is used to define the rendering of the DOM tree.
(3) XBL is attached via CSS to define implementations of the widgets. Some widgets are defined in C++, others are defined purely in XBL/JS, and still others a mixture of the two. XBL is essentially an implementation detail (unless you're designing your own custom widgets).
(4) JS can be used in the XUL file to attach event handlers and execute scripts. Many services are available in JS via XPCOM wrappers. This is where the "core" of the Mozilla toolkit is defined, in APIs for file i/o, network loading, drag and drop, etc.
(5) RDF provides the means of binding back end data to user interface. Think of it as a glue layer between information you might store in a database (or anywhere you want) and the user interface.

Mozilla has a binary representation of both XML and scripts that avoids reparsing the raw XUL across launches, CSS style resolution is extremely quick (and at this point highly optimized), and JS is nice and speedy, primarily being used as glue to connect the UI to back-end services implemented in C++ anyway.

Of course one problem with using an XML+CSS layout engine is that people expect GUI windows to be available instantly, while they're more tolerant of Web pages taking a second or two to load. At this point, however, machines have gotten fast enough (and layout engines have improved enough) to handle the additional cost of rendering user interface this way.

Also, don't let the issue of native widgets sidetrack you when considering the "new window time" cost of XUL. Using native widgets or non-native widgets really has no effect on this time. The cost is in the construction of the XML, the DOM, the style resolution, and the layout, and native widgets don't really do anything to obviate that cost. (CSS still has to tell that native widget how big to be, or whether its visible, etc. The DOM still has to bind event handlers that say what to do for that widget, etc. The DOM still has a tree structure that has to be mapped into the widget itself.)

So to implement XUL, you have to:
(a) build an XML+CSS+DOM+JS layout engine
(b) implement additional layout primitives (like a spring and strut model and popups) that interoperate with the standard CSS-defined layout primitives
(c) implement a binary format cache for the XML and JS so that it can be loaded really quickly even across launches of the app
(d) implement a component/tag extension model like XBL to allow XML tags to be defined as components and reused easily in different pages, windows, and dialogs
(e) implement XML tag support for all the remaining OS widgets that HTML has been missing for years (tree widget anyone?)
(f) provide a binding from the GUI XML to backend data, via some form of data binding (Mozilla chose RDF)
(g) expose an entire SDK for file I/O, networking, etc to JS in addition to your preferred native language formats
(h) implement support for a command infrastructure for command execution and command updating
(i) implement very smart memory caching for CSS and XML that allows lightweight prototypes to be cloned and shared (with copy-on-write semantics)

Then all you have to do is put it in a blender for three years. See? It's easy. :)

The piece of XUL that Safari implements is "(b) implement some additional layout primitives." XUL basically introduces four new layout primitives to CSS: the flexible box, the grid (flexible boxes in 2 dimensions), rich popups/tooltips, and stacks. Safari in Panther has implemented the first (and most useful) of those layout primitives, the flexible box model. Since the box layout primitives are defined via CSS, you can even use them in HTML (in either Safari or Mozilla).

Comments (1) | TrackBack (14)
Safari 1.1, Part 2

Responding to some of the trackbacks from the previous blog entry...

The first mentions a bug in 1.1, and the test page is found here. In Safari post-Panther, the rendering is actually different (but still broken). I'm not sure what the problem is at first glance, but I'll take a look.

The second trackback asks for complete navigation of bookmarks from the keyboard. Since that isn't part of WebCore, I can't comment. Several trackbacks also ask about Safari 1.1 on Jaguar. As I've mentioned in previous blog entries, I can't comment on future Safari releases.

I can whet your appetite with more WebCore stuff that we've implemented since Safari 1.1: small-caps support, fixes for first-letter and text-transform (the ugly doubling text effect is gone), fixes to first-line, and speed improvements to DHTML.

Comments (0) | TrackBack (120)
Safari 1.1

Safari 1.1 is here. Those of you who picked up Panther can take it for a spin. This release is big step forward from 1.0, chock full of bugs fixes, improvements and UI refinements.

As far as new WebCore features, here's a few highlights:
(1) Better standards support. You'll find fixes for positioning bugs, overflow bugs, floats, tables, gzip support, generated content using ::before and ::after, DHTML. You name it, we've improved it.
(2) Speed. We're still fast, and we're only going to get faster.
(3) CSS2 support. In addition to all of the bug fixes to be more standards-compliant, we also added support for CSS2 properties like text-shadow and new display values like inline-block. Try using text-shadow in conjunction with ::selection. It's cool. :)
(3) Safari on Panther supports rgba values in CSS for specifying border, background, foreground and shadow colors.
(4) Support for the CSS3 opacity (using -khtml-opacity) property. Make entire blocks and inlines transparent without resorting to transparent PNGs.
(5) A complete implementation of the XUL box model. Safari on Panther supports the complete XUL box model, including horizontal and vertical boxes, the ability to flex, and the ability to reorder content and reverse content. If you're building canned content that you control using WebKit, you'll find a whole new range of layout possibilities at your disposal. Need to create dynamically sized headers and footers and flexible center content? The XUL box model can do that. Need to center an object within the viewport? The XUL box model can do that too.

And in case you're curious, here's what we've already got working post 1.1 in WebCore that you can look forward to:
(1) Support for the title attribute using tooltips
(2) The ability to tab to all controls in a Web page and to manipulate them from the keyboard.
(3) Support for table border collapsing.
(4) Support for the CSS cursor property.
... and a whole lot more ...

Enjoy the upgrade and as always send us your feedback (trackbacks preferred). We're listening.

Comments (0) | TrackBack (46)
October 08, 2003
Lists and Margins II

First read the previous blog entry, and then read this response.

Responding to that trackback, the horizontal overlap when using list-style-position: inside with ordered lists has been fixed in Safari on Panther, and so I wasn't really concerned with that problem.

The real rendering problem I was referring to was the negative vertical margin being treated differently by Safari. To make a long story short (too late) I'm wrong. I totally missed the list-style-position: inside style in the test case.

The inside style causes the marker box to generate an inline box, a box that then has to be wrapped in an anonymous block. Safari generates the marker box within the first nested line box it can find, a behavior that - although incorrect according to CSS2.1 and the CSS3 draft - actually makes more sense to me. Nevertheless, to match the specification, it looks like I'll need to fix Safari's behavior. :)

In other words, Safari is "smart enough" (ha ha) to keep your bullet properly aligned vertically even when you place block elements as children of the list item. It will drill down as far as it needs to in order to find the correct first line, and it will generate the marker box there. Note that this is essentially how outside list bullets are positioned vertically, so IMO it's actually more consistent (albeit wrong according to the spec) to do this for inside bullets as well.

Ignoring the spec for a second, which behavior is better? Having to put an artificial negative margin on blocks in a list item in order to compensate for a line generated by the list bullet (other browsers) or having the marker just place itself correctly automatically (Safari)? You can tell by now which behavior I favor. ;)

Maybe it's not too late to change the spec. :)

Comments (0) | TrackBack (28)
October 07, 2003
Lists and Margins

In the trackback for my previous entry, Anton complains about Safari's misrendering of the list items at this URL.

As far as I can tell, Safari's rendering is correct. Mozilla seems to be ignoring the negative margin placed on the divs with class="title". If you change the display of the list items to block, Mozilla stops ignoring the negative margin and behaves like Safari.

This appears to be a bug in Mozilla to me, and I believe Safari's rendering is correct.

Comments (0) | TrackBack (15)
October 05, 2003
DHTML Goodness

I've been working on implementing incremental repainting in Safari. With the exception of some hacks that I threw in for overflow:hidden elements, Safari 1.0 will repaint the entire visible area of a Web page whenever any object changes size. (Turn on paint flashing using Quartz debug and you'll see what I mean.)
This is obviously a big problem for DHTML sites.

Anyway, I've fixed this problem in my own build (which makes DHTML easily 5-10 times faster for affected pages), and now I'm looking for tests. What I'm looking for are any pages/tests you've got that do really cool dynamic stuff with DHTML and CSS. I want to know about pages that mostly work in Safari now. The point of this exercise is not to hear about current DHTML bugs, but to make sure I haven't regressed any behavior, so please stay on topic.

Post links to cool demos that do anything from dynamically changing clip to sliding elements around on screen to animating using all sorts of different techniques (changing margins, position, overflow, background, etc.).

Comments (54) | TrackBack (9)
September 26, 2003
Bug Roundup

Walter Ian Kaye writes about a bug in Safari 1.0. I really appreciate the reduction for this issue and will take a look. It should be easy to fix.

Update: I have fixed the bug. Thanks for the great reduction, Walter! I've added your page to our list of regression tests.

Comments (0) | TrackBack (35)
September 10, 2003
Safari Response: daringfireball.net

John Gruber writes about a bug in Safari where a white stripe appears. The table is definitely too short in the iframe referenced, although I have no idea why. I wrote a simple example of a table inside an iframe in which both had the same specified height and it worked fine. I'm not sure what about that table is special. Further reduction would be useful.

For what it's worth, in the current WebCore, iframes will now properly be transparent. So now instead of a mildly annoying white stripe, I see a really annoying red stripe. The iframe in the enclosing document has a background color of red specified on it. A bit of visual debugging perhaps? :) Anyway, to work around the problem in the Panther WebCore, you can presumably wrap the iframe in a div whose background matches the background of the table.

Comments (0) | TrackBack (17)
August 28, 2003
A Nifty Trick

I have a nifty trick I wanted to share, since I think Web designers might find it useful when making reductions of bugs to send to browser developers. Basically I had this bug I was working on where content would sometimes shift too far to the right in Safari (don't worry, this bug isn't in 1.0). The bug was timing-related, and it only occurred intermittently when you reloaded the page.

The bug would only happen if layout occurred at a particular time, and usually this happened when a script was stalled loading long enough for the browser to decide it needed to show the user what it had so far.

If a script stalls, it's blocking the parsing of the rest of the content, so if a layout does occur, you're basically guaranteed that you're going to lay out the content of the page exactly up to the opening script tag and no more.

The bug in question would only happen if Safari had to do a layout before the script had loaded. Maciej (the JS guru on our team) came up with a clever idea: instead of pointing the script at an external resource, just make the script do something that would force a synchronous layout. Then you could basically construct test cases that made layout happen at precise chokepoints that you set up yourself! How cool is that?

A trick that works with both Safari and Mozilla is to try to access a property like offsetHeight, so here's an example:

<script>var v = document.body.offsetHeight;</script>

The layout engine basically has to lay out in order to give you a decent answer, so the handy little snippet above can be inserted anywhere in a document to force the layout of a page up to that point!

Armed with this trick I was able to easily fix the bug, since I now had a test case that demonstrated the problem 100% of the time. Thanks, Maciej!

Comments (0) | TrackBack (20)
August 26, 2003
Scripts and Slashes

In an earlier blog entry, I mentioned how Safari was having to emulate a quirk in Mozilla in order to make a particular Web site work. When Mozilla encounters a <script/> element with an XML-style self-closing /, it actually closes the tag, even in HTML pages. This is - I believe - incorrect behavior, since in HTML the / is invalid and should simply be discarded, leaving you with an open tag instead.

If you try this in WinIE and Safari 1.0, you'll see that they behave identically. They both ignore the / and leave the script open. Both Mozilla and Opera, however, honor the / and close up the script tag.

With my "fix" for this bug, the latest Safari will now honor the / as well. I wonder if this behavior was deliberate on the part of Mozilla and Opera or just some accidental fallout from their XML implementations... whatever the case, we're all stuck with it now, since Web sites are (amazingly) writing to it.

Comments (0) | TrackBack (14)
mezzoblackandblue.com and diveintopain.org

Let me share with you the pain and suffering that has been the last 10 hours of my life. It all started innocently enough. A few days ago I was visiting Dave Shea's site and I noticed that the three parallelograms at the top (zen garden, blue spark, and modernalus) didn't line up properly in Safari.

I quickly reduced the HTML+CSS and discovered that the problem was really elementary. Safari didn't support relative positioning of floats. It would just ignore any relative position you specified on a float. Oops. Given that the code in question literally looked like this:

if (style()->position() == RELATIVE)
    setRelPositioned(true);
else if (style()->floating() != FNONE)
    setFloating(true);

... the fix was a no-brainer...

if (style()->position() == RELATIVE)
    setRelPositioned(true);
else if (style()->floating() != FNONE)
    setFloating(true);

I made the fix, fired up Dave Shea's site, and the problem was solved. One quick code review later, the code was checked in to the Safari code base, and I moved on to other bugs.

Then today Don was idly going over the list of issues at Mark Pilgrim's site to see what we'd fixed since v85. We were chatting on the phone and he said, "Hey, the tabs are mispositioned on Mark's site. There's a vertical gap underneath them."

"No way," I said confidently (haha). I pulled up the site in the latest Safari, and sure enough, Mark's tabs were busted. I rolled back to a version of Safari a day or two earlier, and the tabs were fine.

Sighing, I began reducing the Web site. It seemed simple enough. The top background was a div with an id of logo that ostensibly was supposed to contain the title banner and the tabs. The tabs themselves were list items in an unordered list. I looked at the CSS for the tabs and - aha! - saw that the tabs were floating *and* they were relative positioned!

Since the tabs had a translation offset of -1.32em, they had been pulled up, and that's why a gap had been left. "This isn't a bug!" I stated confidently.

Then I pulled up the site in Mozilla and saw that it was using the same CSS. No gap. "What the hell?" I thought, irritated, and I broke out the trusty DOM inspector to dissect the layout in Mozilla. This is the point at which I began my descent into CSS hell.

You see, the CSS in question tripped over enough unspecified and ambiguous layout behavior to make a grown man cry.

Breaking it down, the first problem I noticed was with an innocent little span that followed the unordered list. Basically the structure was:


<div>
<ul>...</ul>
<span/>
</div>

The span in question had clear:both on it, which meant it was supposed to be pushed underneath the floats. Now in Safari the floats were about 14 pixels tall, and the span cleared them, which meant it was offset 14 pixels from the top of the enclosing div. The assumption Safari made was that the block itself should grow to enclose the span. This meant that the div itself was 14 pixels tall.

Not so in Mozilla. In the latest Mozilla, if a block had no bottom border and the last item in the block was a zero-height block child that cleared the floats, then even though the child itself was offset 14 pixels, the enclosing block had zero height. Make sense? Didn't think so. Welcome to my world.

Now remember that the tabs were relatively positioned at a negative offset, so this meant that there was now a 14 pixel gap in Safari. I tried a simple test in WinIE and discovered that WinIE behaved like Safari! Here's the little test case that shows the difference between Mozilla and WinIE/Safari.

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">

<div style="background-color:#dddddd">
<div style="float:left; width: 2em; height:2em;background-color:lime"></div>
<div style="clear:both"></div>
</div>

So who was right? I had no frickin' clue, but Safari's behavior sure made more sense to me. I did notice that if you added a border to the enclosing div in the above test case, that then Mozilla would increase the height of the div.

What made this behavior potentially ambiguous was the definition of clear. In the CSS2 specification, clearing was specified as an increase in an object's top margin. Now because the span on Mark's site had no content and zero-height, it was considered a self-collapsing block. Self-collapsing blocks collapsed their top and bottom margins together, since the margins were adjacent.

I tried to rationalize Mozilla's behavior as follows: you could think of the span as having no margins initially, but then when it clears the float, it gets a top margin of 14px. That top margin collapses with its bottom margin, and then the 14px margin collapses with the bottom of the block.

I went and hacked Safari to have the same clear hack that Mozilla had, hoping that it would somehow fix the problem. Then I wrote more tests and discovered that Mozilla was just plain out to lunch.


<div style="background-color:#dddddd">
<div style="float:left; width: 2em; height:2em;background-color:lime"></div>
<div style="clear:both"></div>
</div>
<div style="border-top: 2px solid red"></div>

In the above example, you'd expect the red line to be below the float, but it ended up at the very top of the float, and the fact that a clear was specified was just completely lost.

I tried this interpretation of clear anyway (despite it matching no browser on the planet as far as I could tell) to see if it would fix the problem. When I made the change, suddenly the tabs ended up *outside* of the banner background.

Ok, so now I was officially irritated. I started looking at the render tree dumps in Safari to try to see what was going on. Then I figured it out. Above the div that enclosed the list of floats was a header tag (an h1). This tag had a bottom margin on it.

Since the ul had zero-height and contained only floating content, Safari collapsed the bottom margin of the h1 *through* the ul. This did not match Mozilla's behavior.

The problem I was running into now was that what constitutes a self-collapsing block has never been clarified. Web site developers know that when you have a tag like p by itself that it self-collapses, i.e., its top and bottom margins collapse together. But what if the same p contains a float? That float wouldn't be part of the normal flow, so why shouldn't the p still collapse its margins together? After all, the p itself is zero-height, so its margins are technically adjacent.

So this was my problem. With my hack for clear, all the siblings following the h1 were self-collapsing, so the bottom margin of the h1 actually collapsed into the banner's bottom margin, and so the banner itself ended up being smaller than was obviously expected.

I then tried rewriting Safari's definition of self-collapsing blocks so that objects with floats were no longer considered self-collapsing. Once I had this new definition of self-collapsing blocks, the site now rendered correctly.

Now I had two hacks, neither of which I was happy with, neither of which I considered correct behavior, but they made the site work. I was most concerned about this clear hack, since it didn't match any other browsers, so I decided I'd back it out.

After backing out the clear behavior, the tabs started mis-rendering again. Without both hacks I didn't have a fix. The problem was - from a behavioral standpoint - I didn't agree with any of the code I'd just spent 8 hours writing.

Now I was really confused. I could see why Mozilla rendered the tabs "correctly", since it had the buggy clear behavior combined with the non-self-collapsing block behavior, but why did the other browsers, who didn't have the clear hack render the tabs correctly?

I tried Opera first, and then I noticed that its rendering matched Safari's old rendering pre-relative positioning! I then thought that perhaps Opera didn't honor relative positioning on floats. Then I stumbled onto the use of the *7 hack in Mark's CSS, and it became clear why Opera rendered the tabs correctly. Basically Safari and Opera had the same behavior, but Opera had been given some special CSS love to make it render correctly!

I now believe that Safari's rendering (gap and all) is correct, so until a spec tells me otherwise, I'm changing nothing. NOTHING.

It took me 10 hours just to decide that what I did in the first place was correct. Aren't layout engines fun? ;)

Comments (0) | TrackBack (26)
August 22, 2003
The Overflow "Quirk"

In my previous blog I complained about a behavior in Mozilla where overflow:hidden on the body element is actually applied to the viewport rather than to the body element itself. This is done even in strict mode.

I thought this was a quirk, but then Ian Hickson pointed me to this section in the CSS2.1 specification. It contains the following highlighted green text (presumably this means a proposed change to CSS2).

"HTML UAs may apply the overflow property from the BODY or HTML elements to the viewport."

I think this is ridiculous on several levels. First of all, the language says HTML UAs, which means XHTML UAs are not allowed to do this. So the CSS spec is in effect implying that this quirky behavior in HTML UAs is acceptable by adding this sentence.

I also fail to see the point of using language like "may" when WinIE and Mozilla both implement this quirk. It's obvious that browser makers have no choice but to do this for HTML in order to be compatible with WinIE.

I don't see why the spec should be amended to condone this obviously quirky behavior, especially if it isn't going to apply for XHTML.

Comments (0) | TrackBack (5)
August 21, 2003
Quirks and the Uselessness of Modes

I got into an interesting discussion with Don and Darin yesterday about quirks in browsers. What instigated the discussion was a bug where Safari fails to render a particular Web site because it was given invalid HTML by the Web site.

The site in question has a pretty decent browser check. It uses document.all to detect IE, document.layers to detect NS4, and all other browsers are considered standards-compliant.

Interestingly, though, Opera supports document.all, so it ends up in the IE path. Konqueror also supports document.all, so it too ends up in the IE path. Mozilla and Safari don't support document.all or document.layers, and so end up being the two browsers that take the standards-compliant path.

What's interesting about this site is that it then spews out some invalid code that Mozilla (even in strict mode) honors. This code is NOT honored by WinIE, and Safari's behavior when given this invalid HTML matches the behavior of WinIE as well as the specified behavior according to the rules for HTML parsing. Clearly this is desirable.

This is not the first instance of quirks in other standards-compliant browsers biting Safari (and of course Safari has plenty of these bugs as well). There are plenty of other examples of unintentional strict mode quirks in Mozilla (its overflow:hidden quirk on body, designed to match WinIE's odd behavior, is actually enabled in strict mode) that bite Safari, since it follows the same path.

Now we're left with a dilemma. Do we start implementing quirks for browsers other than WinIE? The problem is that for some Web site designers, the standards compliance path has become "the Mozilla path," and these designers don't run their sites through other standards-compliant browsers like Opera and Safari. Thus they may end up making false assumptions as they stumble over hidden bugs in Mozilla or valid differences in browsers' rendering behavior.

The solution for Web designers is clear:
(1) Test your sites in multiple browsers!!
(2) Do not assume that just because a browser misrenders your site that the browser is wrong. Think the problem through, read the spec, consult other designers, and try to determine who is at fault. Remember that it's unfortunately very easy to trip over ambiguities in the specs as well, and you may be running into a situation where there are multiple valid interpretations.

For browser makers, the solution is not so clear. Mac IE introduced the concept of doctype switching, and browsers were quick to jump on board. Safari and Mozilla have identical doctype switching, and the same three modes (quirks, almost strict, and strict).

But if designers still have code paths in strict mode (e.g., .all, .layers, .getElementById) but then only test their code in one browser along the .getElementById path, then other browsers will potentially end up in trouble.

The conclusion that we came to was that we may very well have to introduce quirks into Safari to match bugs in Mozilla as well as bugs in WinIE. In some cases we may have to introduce these quirks despite the fact that they don't even occur in WinIE! What a no-win situation.

Whatever makes the sites work though... that's what we've gotta do. :)

Comments (0) | TrackBack (14)
August 19, 2003
Safari Response: scripting.com

Dave Winer writes about a problem Safari has with Chris Lydon's weblog. This particular bug has been the subject of discussion on other sites as well.

The bug in question was really caused by poor CSS, and, although Safari certainly should have handled this poor CSS in a better fashion, I felt compelled to blog about this in more detail so that Web designers can avoid this coding error in the future.

What causes Safari to get confused is that float:left is specified on a td. This is a rather nonsensical thing to do, and the browser really has two choices regarding how it can handle this scenario.

Choice One, the WinIE way, is to just drop the float property completely and treat the td like a table cell. This results in the rendering that Chris Lydon clearly expects.

Choice Two, the Mozilla way (and definitely the correct way according to the spec), is to honor the float property, which means the td is no longer a table cell but a float, and so the float gets wrapped in an anonymous table cell that encloses it. The specified width is no longer used to participate in table layout, and so that's why Mozilla "misrenders" the links on the right-hand-side of Lydon's weblog.

On to Safari. Safari was trying to give Web designers the best of both worlds. In Safari, I implemented a quirk that was supposed to give you the WinIE behavior in quirks mode, but give you the Mozilla behavior in strict mode. Chris Lydon's weblog (based off the transitional doctype) is in quirks mode.

The problem is that I didn't get the floating bit cleared in all of the places that checked for it, so the table cell ended up being quasi-floating, and thus event handling got broken on the cell.

Anyway, the quirk has been improved so that Lydon's blog will render as WinIE does in quirks mode, and if he decides to ever shift to strict mode it will render like Mozilla does.

In the mean time, the float:left should clearly be removed so that Mozilla can start rendering the site as the designer expected as well.

Comments (0) | TrackBack (9)
August 08, 2003
Selection Bug Fixed

I fixed the bug where selection copies the raw HTML source and not the rendered source. I know that will make a lot of people very happy. It's especially satisfying fixing this bug, since I'm the one who caused it in the first place. ;)

Comments (0) | TrackBack (32)
July 24, 2003
What's New

Here's what I've fixed lately.

(1) The Outlook Web Access bug with objects overlapping one another has been fixed. Turned out to be a 1-line patch to render_table.cpp. I checked this into the KDE code at the same time I landed it in Safari, so the fix should be in that tree as well.
(2) I implemented onload for iframes and frames. I think this should allow the DOM test suite to run more easily.

I'm working now on fixing selection so that it doesn't just copy text from the source HTML. I know that bug has been bothering a lot of people.

Comments (0) | TrackBack (10)
July 12, 2003
More WebCore Fixes

:before/:after content that changed dynamically didn't update properly, e.g., across alternate stylesheet switches or when printing. This is now fixed. Also, problems with text encoding in forms when going back have been fixed.

In the CSS3 department, I have an implementation of opacity working in WebCore on Panther (using new public APIs for transparency in CoreGraphics on Panther). It works like a charm except for native widgets. We used lots of clever hackery in order to get Cocoa NSViews to work properly with z-index, so we'll probably need a bit more hackery in order to get them to work properly with opacity.

CG also has a public API for shadowing, so I may end up implementing text-shadow and box-shadow in WebCore on Panther as well. It's nice having all the hard drawing code written already. All I have to do is link CSS up with it. :)

Comments (0) | TrackBack (20)
July 09, 2003
WebCore Update

So... a couple of problems that I've fixed in the last week that should hopefully cheer some people up. :)

First I have implemented a strategy for minimum font size that I think will work quite well. I have restored Safari's pref value to 9px, but I've changed the meaning of minimum font size (so that it does not match other browsers). With this new patch, the minimum font size is only applied in two cases:

(1) When the specific font size according to CSS is relative to the user agent default
(2) When the text zoom factor alone pushes the font below the minimum size

This means that you will never get tiny text as a result of changing your default font size in Safari's Preferences. The only way you will get tiny text now is if the page explicitly specifies an absolute size that is tiny, e.g., 2px. When the Web page does this, Safari will honor it, thus avoiding the layout disruptions that led to the removal of the minimum font size in the first place.

I believe that this interpretation of minimum font size will actually result in better rendering than in other browsers, since the introduction of the minimum won't disrupt page layouts that rely on absolute pixel font sizes.

The drawback of course is that minimum font size can't be used as a "permanent zoom", e.g., for someone with poor vision, like it can in other browsers, but I think a better solution for a problem like that would be a full page zoom anyway.

The second bug I fixed (just today) was the dreaded background bug. Safari 1.0 has a regression that causes backgrounds to be clipped to the height of the html element. If the html element had content that spilled out of it, then the background didn't paint in that area. Although this sizing of the html element is correct (according to the spec, even though this doesn't match other browsers), not painting the background is not correct. The problem has been corrected.

Comments (0) | TrackBack (36)
July 08, 2003
Safari on Panther

Some of the bug reports I've been seeing about Safari are Panther-specific. It's important to realize that there *are* rendering regressions in Safari on Panther that are outside Safari's domain, e.g., bugs in CG, bugs in image rendering, etc. Because of this, make sure that you mention specifically whether you're running on Jaguar or on a Panther beta when you post bugs here. If you have access to both Jaguar and Panther, check it out on Jaguar first to see if the problem is Panther-specific.

Here are some known Panther beta issues:
(1) 1px dotted borders draw as solid
(2) Fixed backgrounds treat the NSImage as unflipped, resulting in rendering errors (e.g., complexspiral on meyerweb is hosed in both its devolved and distorted variants)
(3) Thick underlines appear under links when scrolling. Detritus left behind on screen when scrolling.

So make sure you mention what version of the OS you're using from now on when you report bugs via trackbacks. Thanks.

Comments (0) | TrackBack (5)
July 03, 2003
Top Ten Lists

In order to get some good feedback about the state of WebKit (including its sub-frameworks WebCore and JavaScriptCore) and the new Foundation code, I'm inviting you to use trackback to link to your "Top Ten Problems With Safari's Web Page Display." Please leave UI out of your lists. I'm interested specifically in any problems that occur inside the Web page display area, be it networking, layout, cookies, proxies, whatever. I'm looking forward to seeing what you come up with. If there's a bug that we didn't fix that you've been clamoring for in every beta, feel free to mention it in your lists. I will read all trackbacks and look for the most popular requests.

Comments (0) | TrackBack (72)
June 24, 2003
Comments

I have decided to permanently disable comments in my Safari blog. Despite my repeated insistent attempts to explain that my blog is not a bug database, people still come here complaining about every random Web site that doesn't work. When I blog about a topic like "Standards Charts" instead of getting relevant feedback, I get more bug reports. In the real world this would be clearly impolite, somewhat akin to interrupting someone in the middle of a conversation in order to babble about a completely different subject, so what makes you think it's any less rude to do it in my blog comments?

Since most of the useful bug reports I receive are through Trackbacks anyway (and those people usually provide reductions and test cases as well), I'm going to leave them enabled, and I encourage people to report interesting WebCore bugs/problems to me that way.

Finally, I'd like to make yet another plea for my readers to stop bombarding me with email asking why some random Web site doesn't work flawlessly in 1.0. If you actually thought all 10 million Web sites would render flawlessly in 1.0, then you simply had unrealistic expectations.

About the best we can do from release to release is demonstrate forward progress, and I think it's clear that we have accomplished this. Go look at how many bugs have been fixed on diveintomark's Safari list from 74 to 85 for example, or take note of the fact that we've built an entire embedding framework (WebKit) for you to use in your Cocoa applications. Are we moving forward at an insanely fast clip? I think so.

Browsers take years to mature, and I know you'd like it all to be perfect right this instant, but that's simply an impossibility. Have patience. Safari will continue to improve, and it's likely that your particular issue will be addressed somewhere down the line. Also have faith that we know what we're doing. Do we know about the issue where popups sometimes set the default new window size? Of course. Do we know about the multiple animated GIFs pointing to the same URL issue? Yes. Do we know we could have better TITLE support? Yes. Do we know that we need to implement more accessibility features and options? Absolutely.

Can we magically implement all of the thousands of features and requests that we're being bombarded with in a single release? No. Of course not.

Comments (0) | TrackBack (130)
June 23, 2003
Safari Download Size

Another common question I'm seeing is, "Why is the Safari download bigger than previous betas?" The simple answer is that the WebFoundation framework code merged into the Foundation framework. That means that the 1.0 installer actually has to give you a new version of Foundation.

Comments (0) | TrackBack (4)
Fonts in Safari

Safari 1.0 has set the default font to Times 16. For those of you upgrading, this is why text now looks bigger. The old default used to be Lucida Grande 14 (in case you're one of those people who is interested in switching back).

You can read this earlier blog entry for the reasoning behind this decision.

Previous betas of Safari also instituted a minimum font size pref, never allowing fonts to shrink below 9 pixels in size. It turns out that this caused sites to misrender, since sites commonly use small font size spans as spacers. These sites would misrender (quite badly) in Safari, and so the minimum font size restriction was lifted. You can still set this as a hidden preference however.

All of these behaviors (the lack of a minimum font size and the default size/face of 16/Times) now match other browsers (Mac IE, Camino, and Mozilla).

Comments (0) | TrackBack (28)
June 22, 2003
Example: Margin Collapsing

I thought I'd put my money where my mouth is and produce an example of the kind of standards reporting I'd like to see. Let's take Ian Hickson's margin collapsing tests as an example.

http://www.hixie.ch/tests/adhoc/css/box/block/margin-collapse/

We have 48 tests here just testing one feature. This is fantastic. Here's an example of a feature that frequently doesn't even show up in standards charts but is essential to proper vertical layout of blocks.

These tests are all extremely well-constructed, in that it's very obvious whether a browser passes or fails a given test. So what we can do is award a browser 1 point for each PASS and no points for failures, and then compute the browser's overall score as a percentage.

Safari's performance on these tests is as follows: 1-6 (PASS), 7-9 (FAIL), 10-45 (PASS), 46 (FAIL), 47-48 (PASS). (Test 36 is discarded, since it is testing an as-yet-unclarified part of the specification.)

So out of 47 possible tests, Safari scores a 43/47, or ~91%.

A developer who sees this score now knows that Safari has excellent margin collapsing support and probably needn't worry about using even complex margin collapsing rules on his site.

This is the kind of reporting I'd love to see, but it involves having a bunch of tests for every feature, and that's the hard part. That's where IMO the W3C working groups (with the aid of browser developers) need to come through for Web site designers.

One final note: I hope I did not offend anyone with my own personal opinion about standards charts. They're about as good as one could reasonably expect them to be without the intervention of the browser developers or W3C working group members, and so it was not my intent to take potshots at anyone in particular. As an interim measure, the charts are obviously providing a useful service.

Comments (14) | TrackBack (6)
June 21, 2003
Standards Charts

Standards charts really really bug me. I won't link to any, since I'm not terribly interested in picking on anyone in particular, but let's take CSS as an example. A typical chart will have entries like so:

                             Browser A       Browser B         Browser C
text-decoration              Yes             No                Buggy

Features are usually listed as rows in the chart. Browsers are listed as columns, so you can find out if browser X supports feature Y by looking at column X and row Y.

What really bothers me about these charts is the range of values they usually allow. Typical values are usually "Yes", "No", "Partial", and "Buggy." These do very little to convey the actual level of browser support.

"Yes" simply means, "Well, in the 3 test cases I happened to try, the browser passed all of them. Therefore it must work flawlessly!"

"No" is about the only value that you can reliably assume is correct. In all probability, a "No" in the chart really does mean the value is not supported at all.

"Partial" means "Well, in the 3 test cases I tried it passed 2 and failed 1."

"Buggy" is often conflated with "partial" and just means something like "Well, in the 3 test cases I tried it passed 1 and failed 2 in some way that really bothered me."

What irritates me about these charts is how inaccurate they frequently end up being. They are inherently biased towards feature breadth and not feature depth, and in the real world feature depth is so much more important.

With these charts, a vendor is even encouraged to do a hack job implementing every CSS property, since the browser engine will then end up looking like it's the best out there. "Haha! I support 59 properties, and you only support 56! I win!" (All vendors are guilty of this too. I'm not singling anyone out here.)

There are plenty of features, for example, where some browsers have absolutely spectacular depth, but this isn't tested at all. All you get from the chart is a "Yes" or a check. We all know that Mozilla's support for the float property (and hundreds of other features) is vastly superior to WinIE's, so why do we persist in applying such sloppy evaluations to browsers? Why do both browsers just get a "Yes"?

It's depth (not breadth) that is going to reveal whether or not a Web design will blow up in your face when you try to use a particular feature. A check in some standards chart just tells you that someone bothered to begin implementing the feature. It doesn't mean that the feature is finished or that it will work when you actually try to build a Web site around it.

In a fit of honesty, I'll even give an example from Safari. Many standards charts have asserted that Safari supports min-width and max-width. How wrong you are. Safari only supports these properties on non-positioned non-replaced elements, but I fooled you, didn't I? I managed to implement just enough to pass the common test cases, because the feature wasn't deeply tested by those of you building your charts. Mozilla's support for this feature is superior to Safari's, but the two browsers have been ending up with the same "score" for min/max-width support.

I would assert that standards charts are meaningless unless they do the following:
(1) Have a minimum of 20 tests per feature that test a variety of settings and (especially for enumerated properties) test the range of values for the property.
(2) Represent support as a percentage, e.g., if the browser passes 10 tests out of 20, it gets a score of 50%.
(3) Make the tests available. It is extremely inconsiderate for chart makers to assert that a browser is buggy at a particular feature without linking to the test in question. In many cases, authors write bad tests, or make invalid assumptions based off the result in a particular browser. Without making the tests available, and specifically explaining in detail why the browser was given a "Partial" or "Buggy", posting the results is unfair.

Web developers deserve accurate results. Only if they're armed with details on each feature will they really know what they can and can't do in a cross-browser setting.

Comments (3) | TrackBack (28)
June 13, 2003
Safari Builds

Regarding Safari builds: the public recognized builds are: 48, 51, 60, 73, and 74. Please do not post to my blog telling me information about other builds, or I'll just dump the comments section. While your feedback is much appreciated and more than welcome if you're using one of the public builds, it is not welcome and is really unwanted if you're using some other build.

Comments (35) | TrackBack (19)
June 09, 2003
Bug Fixes

So... nothing much new to report other than many many bug fixes. Hence the complete lack of updates for a while. :)

Comments (94) | TrackBack (16)
May 21, 2003
Standards Support

Random fun question. If you had to pick a W3C technology to implement next in WebCore, which one would you choose? Justify your answer. ;)

Respond with comments or trackbacks.

(1) XSLT
(2) XForms
(3) SVG
(4) MathML
(5) CSS3
(6) XHTML2
(7) __________ (Other)

Comments (331) | TrackBack (38)
May 20, 2003
Fieldset + Legend

I just finished up fieldset and legend support. By complete coincidence it renders like Mozilla's implementation (which I've actually never even looked at before). Although this code exists in the KHTML trunk, I rewrote it in order to actually get the legend up into the border and to not alter the DOM (the KHTML code was moving the legend in the DOM so that it was the first child).

Comments (28) | TrackBack (7)
May 10, 2003
The Trouble With Widgets II

I received a lot of suggestions for how to solve the Aqua form control margin problem. I thought I'd summarize them in a new blog entry. In all of the solutions I will refer to the extra space required by Aqua form controls as intrinsic margins.

(1) Make the intrinsic margin part of the control itself. In other words, if CSS says to make the form control 100px wide, then the intrinsic margin becomes part of the control. This has the advantage that the widget won't disrupt the surrounding layout of the page, but the obvious disadvantage is that the widget itself won't be the exact width that the Web designer asked for. You also couldn't easily turn off the intrinsic margins without adding an extra CSS property, e.g., safari-intrinsic-margin.

(2) Specify the intrinsic margin using the margin property, but Implement the CSS box-sizing property with a value of margin-box, and then width values could implicitly include margins. With this solution you could override the margins to get rid of them. However with this solution, the form control could still disrupt layout when explicit margins are used in conjunction with widths.

(3) Lay out the form controls without intrinsic margins applied at all. Once everything has been positioned, examine the form controls to see if they are too close to any other content, and if so, apply the minimal intrinsic margin needed to distance them from surrounding content. The problem with this solution is that it's non-trivial to detect your surrounding content efficiently. You could limit the solution to only form controls themselves, but then you could potentially overlap non-form control content like text. This solution can also still result in a disruption of proper layout when the widget applies the margins to distance itself from surrounding content.

(4) This is my own solution, and I think the one I'm going to use. Make the application of the intrinsic margins conditional. If an explicit width is specified for a form control, simply drop the intrinsic left/right margins. Similarly, if an explicit height is specified, drop the top/bottom intrinsic margins. Otherwise, apply the intrinsic margins only if the page did not override with its own margin values.

This approach has one crucial advantage over the previous three options, namely that the widget never disrupts the layout of the page. The disadvantage is that there will be scenarios where the controls might be too close together; however, I don't think such scenarios will be common, especially given that the horizontal and vertical intrinsic margins are applied separately.

Comments (43) | TrackBack (6)
May 09, 2003
The Trouble With Widgets

I've been wrestling with a nasty problem in WebCore. Safari's form controls are native Aqua widgets, which means that they are not designed to butt up against each other. When an input field gets a focus ring around it, you really don't want that focus ring to be drawn on top of other widgets.

The problem is that this isn't how the Web has been designed. Just go look at Google in a browser other than Safari, and you'll see that the text field is so close to the buttons that - were a focus ring to be shown - it would overlap the buttons underneath the text field.

The reason you don't see an overlap in Safari is because of a hack in Safari's built-in CSS file. Input fields, buttons, list boxes, and dropdown lists all have margins applied by default.

The problem with this solution is that it breaks Web sites who use table cell layout with fixed widths and assume that when they set a width on a form control that it will be exactly that width with no extra margins around it. When Safari throws in the extra margins, the site's layout gets messed up.

An example of this is Ben Goodger's blog. There are plenty of others.

Another problem I've created by using margins is that when input fields say they want to have a width of 100%, I actually have to not apply the margins in that case, since objects can then end up spilling out of containing blocks. So Safari conditionally applies the margins on the form controls, which is even nastier.

The good news, though, is that on most sites, having the margins makes them look much better. The question is, how can I get the spacing I need between controls in such a way that I don't break Web sites? Any ideas?

The second problem with Aqua widgets is that they weren't designed to be scalable. Web sites like Orbitz style <select> dropdown lists by setting both an explicit width and a tiny font size using CSS. Safari's dropdown lists can't scale, which means that we honor the width but not the font size, and so you end up being unable to read what's selected in the dropdown list at all.

Not honoring the width of course is not an option, because then page layout is completely hosed. Not honoring the font size also hoses layout because the page frequently expects the widget to be a certain approximate height because of the small font size setting.

It's essentially absolutely critical now that widgets on the Web be fully scalable and support all properties in CSS that affect size, because that's how the widgets on Windows work.

It's kind of amazing to think that, because of Internet Explorer's dominance, the very way widgets have to be designed in order to avoid bad page layout must necessarily match the way widgets are designed on Windows. And people wonder why so many browser engines don't use native widgets...

A third problem with native widgets is the difficulty in supporting other CSS properties like colors and borders, although this is minor compared to the need to support size-affecting properties.

What's a Web browser to do? :)

Comments (67) | TrackBack (6)
Redesign In Progress

Do not attempt to adjust your Web browser. Redesign in progress. :) Pardon the mess.

Comments (53) | TrackBack (12)
May 04, 2003
Safari Response: synapticimpulse.com

Yikes. This bug is my fault. I only handle multiple generated content values if they're all text. Mixing text and images causes little Safari some serious pain. I'm working on a fix now.

Comments (59) | TrackBack (5)
May 01, 2003
Movable Type Woes

Those of you with Movable Type blogs may have noticed that the banners at the tops of your pages misrender in Safari v73. The reason is that the default Movable Type template stylesheet (styles-site.css) is invalid. It contains the following parse error:

letter-spacing: .none;

This is exposing a bug in Safari where rather than simply discarding only the single bogus declaration, Safari discards the whole rule. I have fixed this since v73, but if you want to work around the problem on your blogs, just remove this bad declaration from your CSS file.

It's a shame that the default template used by Movable Type doesn't even validate. Perhaps this has been fixed already in the newest version.

Comments (21) | TrackBack (4)
April 25, 2003
WebCore Update

Spell-checking settings for textareas will now be persistent. I know many people had been requesting this feature, so I'm here to deliver the good news. Direct your thanks to John for implementing it.

Comments (87) | TrackBack (11)
April 22, 2003
Overflow Works!

Ok, I've got it working! It scrolls, tracks events properly, etc. I tested on Squidfingers.com and the high-tech look for Adactio.com. Both render perfectly.

And iht.com articles now display correctly! My guess is that the site was trying to set scrollLeft and scrollTop on the columns in order to position the overflow:hidden content. That didn't work before, but it does now, so the articles display properly!

Woo hoo! If you know of good overflow:auto/scroll/hidden tests (especially DHTML tests), post trackbacks. I'm disabling comments on this entry because I know the invitation to post tests will result in people posting unrelated bug reports, and I don't want to wade through those at the moment.

Comments (0) | TrackBack (18)
April 21, 2003
Got Overflow?

Ok, I have events going to the scrollbars properly. I can move the thumb around and push the arrow buttons, etc., and the scrollbars in the overflow block fully respect z-index etc., and only get events if the layout engine decides they're on top.

Now comes the fun part. I actually have to implement scrolling. Woo woo. Blitting and widget moving. Oh boy. Then I just have to do mouse wheeling, auto-scrolling for drag selection, arrow keys, proper tabbing navigation, proper event coordinate reporting, and the ability to set scrollLeft and scrollTop.

overflow: auto... my arch-enemy.

Comments (27) | TrackBack (5)
April 19, 2003
Overflow Update

I now have scrollbars appearing and disappearing more or less on cue when overflow:auto is specified. There are still a few glitches though with inaccurate measurement.

I have fixed up clientWidth and clientHeight to take into account scrollbars added via overflow, and I have scrollLeft and scrollTop actually returning sensible values (and not the random number generation they do in v73). To address someone's issue in the comments, yes, you will be able to scroll even overflow:hidden blocks by setting scrollLeft and scrollTop and not just auto/scroll blocks.

My next task (after fixing the few layout glitches that remain) will be to get the scrollbars to show the right thumb proportion and to enable/disable properly in overflow:scroll mode.

After that, the real fun begins. Then I have to make the scrollbars actually scroll. This is pretty tricky in our system, since - in order to deal with z-index properly - we always send mouse events into KHTML first, so e.g., the scrollbars don't get the event. I have to send the event into the DOM first and only if it doesn't chew up the event will I let the event go to the scrollbars.

Comments (20) | TrackBack (4)
April 14, 2003
iht.com misrendering

It would be really cool if someone Web-savvy could provide a reduction for iht.com and/or figure out why it misrenders in Safari. Use comments/talkbacks if you figure this out; no email please.

Comments (60) | TrackBack (4)
Oh Boy, Overflow!

Today I began work I've been dreading for six months: the implementation of overflow: auto in KHTML. Feel my pain.

Comments (45) | TrackBack (19)
April 09, 2003
Safari Discussion: 14px vs. 16px

I have received a few complaints about Safari's default font size of 14px (instead of 16 px). I thought I'd kick off a discussion (either through trackbacks or comments) about this issue. I would especially like to hear the arguments in favor of using 16 px instead of 14px.

Comments (234) | TrackBack (8)
April 05, 2003
Bug Fixes

No new WebCore features to report. I've just been busy fixing rendering bugs. I fixed a few problems with floats (extra padding could get added below floats). Also a few line breaking bugs with <nobr> have been addressed.

Most of all though I've been working on tables, mainly reverse engineering (mostly by inspection) the algorithms used by WinIE to size tables and then trying to match it exactly. There have been a lot of table adjustments made, so the results should be more in line with WinIE now. In particular, excite.com is now fixed.

Comments (69) | TrackBack (16)
April 01, 2003
Safari to Drop Table Support

The next release of Safari will be fully embracing Web standards by dropping all support for tables. From now on, any pages that use tables will cause Safari to play a very loud raspberry sound and refuse to display the page.

Auto width tables will actually cause Safari to crash, accompanied by a loud explosion. Safari will then search your hard drive for all files that contain the word "table" and it will replace them with Egyptian hieroglyphics.

For all sites that attempt to nest tables more than four levels deep, Safari will play a loud flushing sound, and it will remove itself from the dock and erase itself from your system in order to protect itself from your bad taste.

I had the opportunity to announce my plans to drop table support to the WASP (We Annoy Safari Programmers), and they applauded the move. "It's high time someone took a stand and stopped supporting these unpredictable monstrosities," said John Feldman, one of the most outspoken members of the WASP. "Who really knows how to build a site using tables anyway?"

Unfortunately word also leaked to the TAG (Table Advocacy Group), and they were outraged. Jim Lerners-Lee had this to say. "I hate all this newfangled crap. XHTML2, XFORMS, CSS3. It's all so complicated. Me, I like tables. Good old-fashioned tables. Especially when used with the blink tag. Safari is obviously taking a dangerous stance by dropping support for tables, since everyone knows they are the backbone of the modern Web."

Despite protests from some individuals, we're not backing down. You'd better get those pages converted. You have been warned.

Comments (50) | TrackBack (27)
March 26, 2003
Font Handling Update

We have successfully switched to selecting the appropriate fonts when rendering to the screen (vs. rendering to a printer). Safari still antialiases everything, but at least the same fonts are being chosen as with other Cocoa apps that use AppKit.

One small step on the road to consistency... :)

Comments (97) | TrackBack (13)
March 25, 2003
WebCore 68

WebCore 68 is available for download here for those who are curious. The most significant changes to the code from v60 to v68 are the new CSS parser and the new inline box model (which also involved a split of RenderFlow into RenderBlock and RenderInline).

Comments (32) | TrackBack (10)
March 21, 2003
Float Handling

Made some serious progress last night and today on Safari's float handling, especially when floats are mixed up with positioned elements. With all of these fixes macromedia.com's pages render correctly. Woo hoo!

Comments (37) | TrackBack (11)
March 19, 2003
An Education in Antialiasing

John Gruber writes about antialiasing again. It's an interesting read.

Comments (31) | TrackBack (6)
The Mind Boggles

I finished the residual style fixes today. All of the code that I wrote to fix up tag misnesting and to reopen tags across paragraphs works like a charm. I deliberately implemented this code to only execute when in quirks mode, so standards mode pages will not get fixed up implicitly if they use bad HTML.

Proud of myself, I then went through my bug list. All of the pages were fixed except for one. Perplexed, I cut out the relevant snippet and pasted it into a local Web page. It worked! I loaded the page online. Nope, didn't work. I brought it up in Mozilla and WinIE, and the page looked just fine.

Confused I downloaded the entire page to disk. Still didn't work in Safari, but it did work in Mozilla and WinIE! I kept reducing the page until finally I noticed that it had an XHTML doctype.

An invalid page, horribly invalid (two body tags, misnested font tags) , was using standards mode in Safari and Mozilla! Even more mind-boggling, Mozilla actually still applies residual style quirks in strict mode. I couldn't believe it!

Maybe I'm taking a hard-line stance here, but I view fixing up tag misnesting as a horrible quirk that should not be implemented in standards mode. If you don't behave strictly when in standards mode, how will people ever write valid HTML?

I hope that Mozilla's behavior is just a bug.

Comments (42) | TrackBack (9)
March 18, 2003
The Residual Style Problem

One of the most critical HTML quirks is a problem that has not yet been solved in KHTML. Consider the following tag sequence found in a real-world screenplay on the Web:

<b><p>Bob:</b> Look out! It's behind you!</p>
<b><p>Carol:</b> What is?</p>
<b><p>Alice:</b> Run!!!</p>
<b><p>Monster:</b> Grrrr.</p>

And so on. This real-world page sends Safari into a nosedive, because it's essentially nesting bold tag after bold tag. Safari allows a paragraph inside a bold tag (as do most browsers in quirks mode), and so it happily builds up a tree thousands of bold tags deep.

Another common example of tag misnesting is the following:

<p><font>I am in a special font, but now the paragraph is closing.</p>

KHTML will currently close up the font tag, and then the following paragraphs won't have the font applied. This is a sensible approach to deal with what is effectively a misnesting of tags, but unfortunately, it's not what Netscape did in the old days, and it's not what WinIE does now.

The problem is that some style (this font) is left over at the close of the paragraph, and this style needs to be reapplied when you open the next paragraph. This residual style can't be lost or forgotten if you want to properly handle tag misnesting.

Another name for this problem is the "Tag Soup" problem, i.e., how do you handle complete garbage HTML. I tend to view the "Residual Style" problem as being a specific subset of the more general "Tag Soup" problem.

Ian Hickson has a a great blog in which he talks about how the various browsers handle this problem.

I am planning on adopting the Mozilla approach to solving this problem, which is to reopen tags that aren't closed properly at the close of a block. I'll also be attempting to identify examples like the first one above, and notice that inline tags got closed in a different scope, i.e., in a deeper block level. This close will be honored, and then I'll immediately close up the inline when you return to the same scope.

It's a shame that so much time has to be spent dealing with invalid HTML, but if you want to work with the real-world Web, you don't have much choice.

Comments (55) | TrackBack (10)
March 16, 2003
Safari Newsflash: PHP-Nuke Cookie Bug Fixed

Maciej fixed the infamous cookie bug last night, so PHP-Nuke sites will now work properly. Quoting Maciej:

I have a fix for the infamous PHP-Nuke login bug. It should be in the next release. Here's the short version: the Netscape cookie spec says when a cookie comes from a URL like "http://www.foo.com/user.php" and no path is explicitly specified, then the path should default to "/user.php". However, it actually needs to default to "/", because that's what all real browsers do. Don't you love the web?

Another bug bites the dust! :)

Comments (30) | TrackBack (6)
March 11, 2003
Safari Response: tantek.com

Tantek blogs about how Safari messes up his CSS presentation slides. The bug actually is with the following:

<style media="print">...
Safari is forgetting to cache the media type it observed on the style attribute. It doesn't have this problem with link elements, just with style elements. Since your print sheet sets all the slides to be visible and hides the footer, that's what causes all the madness.

FWIW, this is issue #5 under "CSS Importing Bugs" on diveintomark's list. It is now fixed.

Comments (10) | TrackBack (4)
Safari Response: daringfireball.net

John Gruber writes about antialiasing in Safari in his blog. The difficulty with antialiasing is that it is ultimately subjective, i.e., people have wildly different opinions regarding what fonts look good antialiased (or not so good).

For example, to me the Geneva example at the end of John's blog entry looks better in Safari than Camino (much better). It's all subjective. None of the antialiased examples look bad to me at least.

That said, what I am investigating is implementing the CSS3 font-smooth property. This will provide author-level control of AA on a site as well as enable users to have control via a user stylesheet, e.g., you could say:

body {
  font-smooth: never;
}
Ultimately what's needed is to put fine-grained control of antialiasing into the user's hands, and adding support for this property will give the user that level of control.

You can also specify cutoff points for AA, e.g., say that only 12px or bigger fonts will get AA, e.g.,

pre, tt {
  font-smooth: 12px;
}
Comments (76) | TrackBack (10)
March 06, 2003
Safari Response: zeldman.com

Zeldman writes about a couple of Safari issues. The first issue is Safari's limited support for the title attribute. We're going to dump our implementation in the status bar and just use a tooltip-style implementation instead (and yes, we'll pick up support for acronym and abbr at the same time). This was sort of an intermediate hack to just get something out there for title attributes on links only. We'll dump it once we do the full TITLE support in a tooltip.

Second, the question about .ico files. The .ico file format actually contains multiple bitmaps, which can have varying color depths and sizes. The engine rendering an .ico has to make a decision regarding which ico to use, and this can get pretty complex, e.g., do you take the 32x32 in 16-bit color, or the 16x16 in 8-bit color? The former results in a scale, but the latter is a lower color depth. A naive decoder will simply use the first .bmp encountered inside the .ico, even if it's an inferior color depth.

Safari just uses the Cocoa NSImage object for displaying .icos, and I suspect that the decoder may be pretty naive about its selection of the appropriate bitmap. In my opinion, color depth should be highest priority. In other words you should try to find a .bmp with matching color depth for your screen first, and then prioritize 16x16 above other sizes only after the best color depth has been determined).

I will look into this issue in Cocoa and see what we're doing right now.

Comments (28) | TrackBack (11)
Tabbed Browsing

I've seen a lot of comments in various Mac forums where people have claimed that "Dave Hyatt said he doesn't like tabbed browsing!" or "Dave Hyatt hates tabbed browsing!" I find these posts perplexing, because I never said any such thing, and of course the opposite is true. I love tabbed browsing. I implemented tabbrowser in the Mozilla trunk. I implemented tabbed browsing in Chimera. I implemented the version used in Phoenix. Given how many times I've implemented it, I'm amazed that people would think that I am not a tabbed browsing devotee.

That said, I wanted to express some of my thoughts about the various UI decisions one has to make when designing a tabbed browsing system.

Target Audience

I think the most important question you have to answer before designing a multi-page system is "Who is my target audience?" In the case of Phoenix the target audience is experts and power users. I do not believe that tabs serve any useful purpose for novice users, because novice users don't ever use multiple views of Web data. They just browse from page to page.

The classic novice user Web setup is to have Windows IE maximized with the sidebar open. That kind of user simply doesn't need tabs. Tabs are total overkill for what that person wants to do with his/her Web browser.

That is why I think ideas like this, although extremely pretty, seem to be targeting an audience that IMO doesn't exist. A power user doesn't want thumbnails, since they wouldn't be easily distinguishable anyway once you opened several tabs, the overflow mechanism for such a system would be clumsy (or would use too much space, scrollbar anyone?), and you lose too much horizontal real estate. Sure, it's got a neat initial "whizzy" factor to it, but it's simply not as usable or as scalable as the classic tab strip model.

Bookmark Groups vs. Folder Options

This is something I've implemented three different ways in Phoenix, Mozilla, and Chimera. In both Chimera and Mozilla the bookmark group is a special entity that you have to make by taking a tab snapshot. I now hate this idea. The implementation is to just have a tagged special folder that when clicked loads all the bookmarks in tabs, a sort of one-click clustered loading. This complicates bookmark management and viewing, since you now have this third kind of entity along with regular folders and bookmarks.

I much prefer the system we came up with for Phoenix, which is borrowed somewhat from Opera. In this system, folder submenus pick up an extra "Open in Tabs" menu item, and you can just load any folder's children in tabs. No special new kind of bookmark group, and no special means required for creating bookmark groups. You just work with folders and can now load a single page of a group by drilling into a folder, or load all the pages in a group.

With the Chimera way, you'd end up having a Blogs group, and then you'd also have to bookmark individual blogs for when you didn't want to load the group. You had needless replication that is avoided by just making the operation available on folders instead.

Replace vs. Append?

When doing clustered loading, we took two approaches. One can be seen in Mozilla, and I personally hate it. The other can be seen in Phoenix and is my favorite choice. Mozilla actually appends the tabs loaded by a bookmark group to the end of the tabbed list. This means that if you click first on a News group and load tabs 1-5 and then click on a Blogs group, you'll end up with new tabs 6-10.

In Phoenix, you replace instead, so the News tabs go away and are replaced by tabs 6-10. The argument for append is basically that you end up with potential data loss in that you may lose access to the previous tabs by closing up some of the ones you replaced, e.g., if the second group has fewer tabs than the first. This is of course a solvable problem, though, and doesn't justify changing the default behavior to append.

Position of Tabs inside the Tab Strip

Chimera centers tabs within the tab strip. Everyone else puts them on the left. The only reason Chimera does this is because I couldn't figure out how to use the normal tab widget to make the tabs be left-aligned. Center-alignment for a dynamic tab system is of course awful, since for every tab you open, all of the tabs move.

It's much better to avoid moving all of the tabs around when a single new tab opens, and left-aligning the tabs inside the tab strip makes for a much less jarring experience.

Where do new tabs open?

A highly debated issue with tabs is "Where should new tabs open?" NetCaptor and the old Chimera (in early versions) use the following model. If you click to open a link in a new tab, then the new page will open just to the right of the current tab. Links will continue to open to the right if you keep opening them, so you may have a setup like this:

1 2 3 4

where 2 is the active tab, and you then open three more links from 2 and end up with:

1 2 7 6 5 3 4

The advantage of this approach is that similar pages stay together. The disadvantage is that the opening of new tabs is more jarring, since you do an insertion in many cases rather than an append.

A disadvantage is that you have to read the pages from right to left in order to preserve the original order. Because of this, when you *close* tabs, this model dictates that you move to the left.

You do have the advantage that when you finish with the child links, you conveniently end up back at the original document as you close up tabs.

The second model, and the one I favor (used in Phoenix, Chimera, and Mozilla now) is to always open new tabs on the far right. Usability testing at AOL showed that this was far and away what users expected to happen, and it lends a smoothness to the tab opening process, since you never move any other tabs.

You also get to read links from left to right instead of right to left, e.g., the previous example would result in:

1 2 3 4 5 6 7

In this model, in order to be able to browse the links you open effectively, when tabs are closed you need to move to the right. Note that when you finish with child links, you don't end up back at the previous page in this case, but in the common case, you do. Note that by far the common case is to simply have:

1

and that you'll open a few links, end up with:

1 2 3 4

and still end up back at 1 once you close up. The ability to easily read left to right, and to not shuffle the tabs around on the tab strip when you open new links more than makes up for the edge case where you may not end up back at the parent tab.

Close Boxes

I actually prefer Galeon's behavior here. Phoenix and Mozilla offer close boxes for the tab strip, but this UI frankly stinks, because the user expectation is that clicking on the X will actually close up the entire tab strip. In effect, the X should map to the "Close Other Tabs" command, but instead it maps to "Close Selected Tab." This is utterly confusing, and at least Chimera avoids the problem by not having a close box at all.

The right way IMO to do this is to have a close box for closing up the tab strip itself in the same place Phoenix and Mozilla have it, but to also have close boxes on the tabs themselves (the way Galeon does it). With this model, it's clear what the different close metaphors are, and you don't end up with user (even power user) confusion.

Background vs. Foreground

Despite the inconsistency with opening links in new windows, I strongly support the default in Phoenix, which is to open links in new tabs in the background by default. This option should be overridable with a modifier key (SHIFT in Phoenix) and also the default should be controllable via a pref. Phoenix, Moz and Chimera all have the same pref and modifier key, but only Phoenix defaults to background loading by default.

It's really interesting just how many different choices have been made by tabbed browsing implementers. Pick the browser that implements the system you like best I guess. :)

Comments (136) | TrackBack (132)
February 27, 2003
Hypothetically Speaking...

... if there were a Safari v62, and it did happen to leak to the public, and someone did happen to run it, and that person did happen to discover a bug with text-decoration, well then I would hypothetically be most grateful, and would in fact fix such a bug with the utmost expedience. In fact, it might even be fixed already, assuming of course there were such a build, and it did in fact have this problem.

Comments (134) | TrackBack (27)
February 22, 2003
Safari Newsflash: WebCore Update

Some more issues I fixed tonight:

  1. Added support for attr to the content property.
  2. Fixed dotted/dashed border drawing to draw the correct type of border (and in the right position).
  3. Implemented the CSS3 :target selector.
Comments (88) | TrackBack (31)
February 21, 2003
Safari Newsflash: CSS Parser Integrated

I have integrated the CSS parser from the KHTML trunk into Safari. It uses the CSS2.1 grammar. Mark will no doubt be disappointed that it closes most of the sheet-content-related hacks. In particular, Safari is no longer vulnerable to the Safari Spacer hack, the Simplified Box Model Hack, the Star HTML bug, or the Inline High Pass filter. It is, however, still vulnerable (interestingly) to Fabrice's Inversion.

Note that some of these hacks really couldn't easily be left in, since using a correct grammar will just naturally close these hacks off. There's no way to easily pollute the grammar to support constructs like the Safari Spacer hack (nor should we).

Comments (23) | TrackBack (135)
February 18, 2003
Safari+NNW

An article about how NetNewsWire and Safari complement one another can be found at O'Reilly here.

I agree, although Safari really needs to be able to reuse windows for URLs sent from applications like NetNewsWire rather than always opening a new window every time.

Comments (39) | TrackBack (7)
February 14, 2003
Safari Response: zeldman.com

Zeldman writes about a mysterious background image problem that plagued his blog in Safari. I wrote up simple test cases and couldn't reproduce the problem. Yo, Zeldman, any chance you could place a reduced test case on your site somewhere so I can debug the problem and fix it?

Comments (16) | TrackBack (7)
Safari Response: bradchoate.com

Brad Choate complains about Safari's handling of backgrounds on inlines in this blog entry. Don't worry, Brad, the new inline box model (post-v60) handles your links perfectly . It even works when the links wrap to multiple lines.

I also got your fancy title tips to work. The transparency effect even works in Safari (how the heck are you accomplishing this?!). In case you want to work around the problem now, you just need to move the hookup of the of the makeNiceTitles event listener (the load handler on the window) to after the body element has been declared (i.e., put it in your body).

If you don't want to pollute the body of your document with a script in order to fix this now, you can wait for a subsequent release of Safari (which will contain the fix).

How did you accomplish that transparency effect without using -moz-opacity? I'm amazed this works in Safari! :)

[Update: Duh, PNGs. I get it.]

Comments (16) | TrackBack (18)
February 08, 2003
A Night Away From the Opera

[Note:: The opinions expressed in this blog entry are purely my own. Take them with the appropriate grain of salt.]

John Gruber has an excellent blog up about Opera's potential withdrawal from the Mac platform.

My earlier blog entry on the subject was a little more concise, consisting of... well, a one word response: "Wah."

Opera is playing the same game that Netscape played in the 4.x days. They call themselves "cross-platform", but what that really means is that they have a solid Windows offering, and then straggling offerings on other platforms.

Did Opera expect some sort of prize just for showing up? Any Mac user could tell you that just showing up is not enough. Nobody wants an afterthought for a browser, or a second-rate knockoff of your shining Windows star.

Being pissed I can understand. Fine, be pissed. But the way their CEO went on the record with news.com and directly insulted KHTML, calling it a "simple little browser," was really unprofessional. It was an insult to all the people who have worked on KHTML over the years (via Apple or KDE, on Safari or Konqueror).

(You'd think the CEO of Opera would be able to distinguish between a browser and a rendering engine, but ok, given his other comments, maybe not.)

Comments (0) | TrackBack (11)
The Sweet Sound of Silence

mpt writes about my little experiment.

The real reason I caved and started using my blog as a GUI feedback zone was to try to stop people from emailing me, and it has worked.

Ever since the experiment went into effect I have only received one piece of email about Safari's GUI. Thank you, readers, for being good citizens and sticking to Trackback and Comments. Besides, this way the whole community can see your GUI feedback and respond, unlike email, which goes only to me.

To the one person who sent me email anyway, I am watching you.

[Update: The entire 800 comment page has been forwarded to the whole Safari team. Thanks again for the feedback.]

Comments (0) | TrackBack (7)
February 07, 2003
Your Thoughts Go Here, Part 2

I just finished slogging through all 750 comments (and 36 trackbacks). Really great feedback. Given the bandwidth limitations, I've had to disable the comment posting.

I will continue to read Trackbacks for more GUI feedback, so if you have a blog of your own, you can give feedback that way by using Trackback on this entry.

Comments (1) | TrackBack (44)
February 06, 2003
Your Thoughts Go Here

Ok, so let's try an experiment. If you want to give me GUI feedback on Safari, post a comment or a trackback in response to this blog entry. I will read the comments and follow all trackbacks for information about what changes/additions you'd most like to see in Safari. If you have a non-GUI request, e.g., some specific CSS flaw or DOM flaw that you feel should be really high priority, you can post here too.

We've disabled comments for now, as it was driving our bandwidth use through the roof. -Admin

Comments (799) | TrackBack (55)
February 04, 2003
A Gentle Reminder

Please do not send me email (or post comments to this blog) asking if/when UI features are going to be implemented.

Also please stop asking me about release dates, when the next release of Safari will be, if there will be nightly builds, what the plans are for builds, etc. etc.

Comments (70) | TrackBack (12)
February 03, 2003
Safari Newsflash: Recent WebCore Work

Some of the things I've been tinkering with in WebCore recently:

  • Added support for display: run-in and display: compact. Not that I completely understand how they're supposed to work, but I do have basic test cases behaving "correctly" (by my interpretation of "correct'").
  • Improved the line breaking code to deal with mixtures of white-space styles on the same line and to be smarter about determining breaks (e.g., not breaking in between spans if there isn't a breakable character).
  • :active now works on all elements and is hierarchical.
  • :hover can now be used in conjunction with descendant rules, so that e.g., Eric Meyer's pure CSS menus work.
  • Lots of bug fixes for positioned elements.

I think I'm finally ready to start in on the inline box model now. This is going to be lots of fun to hack on. :)

Comments (20) | TrackBack (7)
January 31, 2003
Safari Response: zeldman.com

Zeldman writes about a Safari bug in his blog. I investigated the problem and have it fixed.

For those who want to add to a list of known issues, the problem is that Safari collapses margins through bottom borders (oops). This means an example like this:

<div style="border-bottom:1px solid black">
<p>Foo</p>
</div>

will put a bottom margin outside the div! Again, oops. The fix has been made.

Comments (18) | TrackBack (9)
January 28, 2003
Uber-Browser Take #2: Sherfari

I just read Jason Kottke's article suggesting that Sherlock and Safari merge. Yes, I know, the rest of you probably read it two weeks ago, but, hey, do you want me spending all my time reading blogs or should I fix some Safari bugs every now and then? ;) Anyway, I also read John Gruber's rebuttal on Daring Fireball.

After thinking about the RSS issue in previous blogs it seems that applications like Watson and Sherlock provide a similar service for the Web that RSS does. Again, just like NetNewsWire, they present information to you in a more streamlined fashion, and again, you only fall into the browser when your interest is piqued (or in the case of some of the services, when it's time to buy).

Moreover in the case of some of these streamlined services, unlike news/blog feeds, some of these actually do feel "geekier" to me than the corresponding user interface you might have been presented with had you just gone to the Web site directly.

I wouldn't mind discussing how to tightly integrate applications like NetNewsWire and Sherlock with browsers, but at this point I think both should remain separate applications.

Comments (31) | TrackBack (8)
January 27, 2003
More Thoughts on RSS

One really good point several people brought up in response to my previous blog about RSS and Web browsers was that many feeds contain only article excerpts.

In other words RSS feeds seem to break down into two categories: feeds that contain only short excerpts of articles and feeds that contain entire articles. It does seem like integration makes less sense if the majority of feeds fall into the first category.

Then the RSS aggregator becomes useful as a filtering mechanism, with the Web browser being used only to view the articles that you ultimately decide to read. In this mode it seems like HTML display is hardly even a requirement for the aggregator application, since you basically just want to speed-read the excerpts from the RSS file and only go into the full blog if you decide that it's worth following up on.

Of course if RSS is going to contain the full blog entry, or if XHTML ever became popular as a syndication format (thus inspiring people to just use their blog pages as their syndication files as well), then some of the things that I said in my previous blog might hold true.

Comments (27) | TrackBack (13)
January 26, 2003
Rise of the Uber-Browser

Now that I've started using NetNewsWire to read blogs, I find it frustrating to be constantly switching back and forth between NetNewsWire and Safari. This led me to wonder: should RSS capabilities and browsing capabilities be merged into a single "uber-browser" application?

Do news readers like NetNewsWire and Feedreader contain functionality that should be absorbed into browser applications like Safari, Chimera or OmniWeb? Or is the opposite true? Should some minimal browser functionality be incorporated into NetNewsWire?

OmniWeb already contains a very nice bookmark scheduling/updating mechanism. Imagine if you could bookmark an RSS XML file and have a browser transparently present it as a folder in your bookmarks, complete with an unread count and child items that represent blog entries. This mechanism would mesh nicely with bookmark scheduling/updating schemes that exist already in browsers.

Or consider the other direction. NetNewsWire could instead embed a rich HTML control and manage the display of blog entries for you. One idea I had about blog entries in NetNewsWire is the idea of applying user stylesheets to blog posts so that you could format the blog entries according to your own chosen templates. In effect you could pick the "blog theme" to apply to the HTML snippet pulled out of the RSS file.

Another idea along those lines would be allowing authors to somehow specify links to author stylesheets that could be loaded and applied when a snippet is read from HTML embedded inside RSS. Then the author's look could be preserved without having to go back to the original blog Web page.

Yet another irritation is how difficult it is to subscribe to feeds. I'd like to be able to click on an RSS file link in a browser and have it automatically pass that off to my news application. One click should be all it takes to get me subscribed, whether that click happens in a mail app, a Web page, or inside NetNewsWire itself. A protocol handler would work for this. I think of this as being somewhat similar to the "view-source:" protocol supported in many browsers, i.e., you could just say "rss:original-url" or "feed:original-url" and have the appropriate application configured to handle feed subscriptions.

Tabbed browsing has a role here as well. The same person who voraciously devours news feeds is the same kind of person who loves being bombarded with lots of information, and tabs provide one with a means of efficiently handling a lot of information. This makes Chimera's ability to open URLs sent from other applications in tabs very cool, and might help obviate the need for an application like NetNewsWire to build tabs into its own display.

I've heard a lot of people state that RSS and news aggregators are for "geeks" and "blogging enthusiasts," but I simply don't believe that to be true. It should be possible to make an application for managing a large amount of information flow that is accessible to mainstream users. Browsers are trying to make information easier to manage with smarter bookmarking systems and page management capabilities (tabs), and news readers are emerging that (in effect) push new information to you in as it's posted and allow you to switch rapidly between different information sets as well.

There is also an eerie parallel one can draw on the editing side between conventional Web page editors and the need for specialized blog editor applications. Mozilla has MozBlog for this purpose. Should personal blog management become the domain of a specialized application, a sort of uber-editor that can handle HTML editing/publishing but also blog management/publishing, or should it remain as a Web application like Movable Type that you use a browser to access?

Comments (74) | TrackBack (40)
January 21, 2003
Safari Newsflash: More CSS Goodness

Turns out Eric Meyer's Pure CSS Popups demos didn't work quite right after all. Oh, sure, I fixed the :hover effects so that stuff actually started showing up, but oh my was it not laying out correctly.

The bug turned out to be another one of those frighteningly basic ones. I was mightily embarrassed when I found it. Positioned and floating elements inside blocks with inline children were actually being added to the line. So basically all positioning was busted if you put a positioned element inside a block with inline children.

It's all better now though (crosses fingers).

Comments (7) | TrackBack (6)
January 19, 2003
Safari Newsflash: Fun with :hover

I spent all day today reworking :hover in Safari. I fixed the repainting problems that exist in v51 when using :hover with positioned elements. I also now have Safari working flawlessly with Eric Meyer's Pure CSS Menus. I haven't tested them yet, but I'm fairly certain that Safari will also now work with his Pure CSS Popups demos as well.

Comments (43) | TrackBack (8)
January 17, 2003
Safari Newsflash: WebCore

I keep receiving emails from people trying to build applications using WebCore. People are asking me for help or advice on implementing the bridge inside WebCore.

I don't know why people are trying to do this. It should be obvious from looking at the code that WebCore and JavascriptCore are incomplete, and that there are other pieces required in order to build an application around the Safari engine.

If you're trying to embed the Safari layout engine right now, stop it! :) Don't try to build code around these two components.

Update:: Quoted from ADC: "In addition to providing the best web browser for Mac users, one of the goals of Safari is to provide a fast and efficient HTML rendering engine for Mac application developers. Apple is actively
preparing a Safari SDK that will be available later this year."

In other words, all good things come to those who wait, so be patient! :)

Comments (19) | TrackBack (17)
January 15, 2003
Safari Response: news.com

Mike Shaver and Chris Blizzard respond to Paul Festa's anti-Mozilla ranting on news.com. Just goes to show you, boys, you have to be careful what you say in your blogs, since you never know when someone is going to come along and quote you out of context in order to satisfy some obsessive need to bash Mozilla.

Comments (1) |
Safari Newsflash: Alternate Stylesheets

In the "Did you know Safari could do this?" category comes the following tip. Safari supports an extension for examining the preferred stylesheet set and for setting the current active stylesheet set, so if you want to swap between sets of sheets in Safari, you don't have to loop over all of them setting the disabled property. You can get and set the selected set using an extension on DocumentStyle called selectedStylesheetSet. You can examine the preferred set through preferredStylesheetSet.

document.selectedStylesheetSet = 'alternate1';

Comments (3) |
January 14, 2003
Safari Newsflash: XML Lives!

We now have XML limping along in Safari. We are treating application/xhtml+xml, application/xml and text/xml as XML files and sending them into the XML parser (expat). If there's any other MIME type we should be supporting, let us know. Now that we can finally see XML, we have to update it to deal with other fixes we made to the KHTML engine, like making XML processing instructions that load stylesheets delay render object construction (to avoid the flash of unstyled content you'd see otherwise), and to fix our XML component so that it doesn't eat whitespace (so that preformatted text will work in XML).

Comments (9) |
January 13, 2003
Safari Newsflash: New Table Code

I've just finished merging KHTML's new table code (over in the KDE tree) back into Safari. It fixes a lot of bugs with auto layout tables, especially the way extra width is distributed among cells. Yes, Ben, it fixes your blog. Yes, Joe, it fixes yours too. The new code also supports fixed table layout, so the eight of you out there that actually use fixed tables can now rejoice. ;)

Comments (3) |
January 12, 2003
Safari Response: diveintomark.org

Mark writes that he has discovered a way to hide CSS from Safari. He's called it the Safari Spacer Hack. While it's neat that people finally found a bug in the parser that afflicts only Safari, it's just that: a bug, and relying on these bugs in what should be a standards compliant parser is wrong.

Here is a proposal for a way to include Safari-only CSS:

@-konq-only { .... }

Here is a proposal for a way to hide CSS from Safari:

@-konq-end;

The former would be a special block (similar to @-konq-quirks for quirks mode rules in UA sheets) that other browsers would ideally ignore. The implementation of this feature would naturally depend on other browsers ignoring @ directives that they don't understand. Can anyone confirm this?

The second idea, @-konq-end, would be used within a style block or within an external stylesheet to signal the end of the sheet to Safari. It would then ignore anything that followed @konq-end;.

Thoughts?

Comments (3) |
January 11, 2003
Safari Response: memeufacture.com

In response to comments made on Jan 8 on memeufacture.com about the site misrendering. I've narrowed it down to a basic fundamental problem. Whenever text can't fit on a line with a float, KHTML (Safari's version at least, maybe this has been fixed in Konq) shoves the text down past *all* floats. In memeufacture's case there is a horizontal float stripe across the top and another vertical float stripe down the side. What's happening is that rather than just placing the objects right below the horizontal float stripe, the text is being shoved all the way past the vertical stripe as well.

I've filed a bug. This probably affects a fair number of weblog sites (you guys love your floats), so I'll try to address it soon.

Comments (2) |
January 10, 2003
Safari: Before Sending E-mail...

Before sending email, please be aware that I'm not going to comment on future Safari releases (i.e., when they will be, what they will be called, etc.). I am also not going to comment on questions about when particular UI features are going to be implemented, so please - for the love of God - stop asking me if/when UI feature "X" is going to be implemented in Safari.

Comments (4) |
January 09, 2003
Safari Response: diveintomark.org

In Should Safari be intentionally buggy?, Mark brings up the need to have a way to hide CSS from Safari. However all of the suggestions that I see involve ways to include Safari-only CSS, which seems to be the opposite goal. I suppose people want both. I'm certainly open to the idea of a special way of indicating Safari-only CSS, as long as it is in no danger of violating standards and can be done in a reasonable fashion. It seems like finding a way to hide CSS from Safari is more problematic, and it also seems like this would be the more common case.

By the way, Mark, your blog is now fixed in Safari (this is problem #2 on your list). Those who care can bring up khtml/css/html4.css in WebCore and move the -konq-flow-mode properties in the rules for UL, OL, MENU, and DIR into the quirks block at the bottom of the stylesheet.

FWIW, we have also addressed problem #3 and #5-#7 (this was a bug in our image renderer and is not a bug in KHTML), which means #1-#3 and #5-#7 have all been fixed. You keep filing, we'll keep fixing. :)

Comments (1) |
Blogger Pro Blocks Safari

Blogger Pro blocks Safari. I wonder what UA check they do. It must be more sophisticated than simply looking for the word "Gecko" in the user agent string. Blogger, if you're listening, you can treat us just like Mozilla. We will work. What do you think I used to post blogs on the Mac before you started blocking it? :)

Comments (1) |
Safari Response: diveintomark.org Problem #1

I started looking into these CSS issues today. The first issue on Mark's list is that outside list bullets inside an li with text-align: right (either explicitly specified or inherited) are not positioned next to the text content. I think we're off to a good start, because I believe that Safari is not only correct on this test case, but that every other browser is rendering that page incorrectly.

I refer you to paragraph 7 of section 12.6.1 of the CSS2 spec. Essentially since the bullet should be positioned relative to the line box, text-align is not going to have any effect on its position. It will be just outside the line box. To achieve the rendering effect you desire, you should just set list-style-position to inside.

[Update: Boris Zbarsky informed me that the language I referred to is no longer present in CSS2.1. Thus the positioning is now ambiguous, so it's sort of left up to the browsers to decide how to position. I will, however, note that the CSS3 Lists draft does clarify this situation in favor of Safari's interpretation.]

On to issue #2, which is much more obviously a bug. :)

Comments (1) |
Safari Newsflash: versiontracker.com

The issue with VersionTracker's front page has been addressed. The page is actually malformed, and we had to improve our error handling code to deal with the malformation. (A stray div wrapping random table cells inside a table.) Note that this was not a bug in Konqueror but was a regression in Safari only.

Comments (2) |
January 08, 2003
Safari Commentary: The UA String

A number of people have commented on Safari's UA string, which is as follows:

Netscape 5.0 Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en-us) AppleWebKit/48 (like Gecko) Safari/48

The portion of the UA string that seems to be stirring up controversy is the portion that says (like Gecko). The reason it is there is that in order to work with real-world DHTML sites you have essentially two options: you can claim to be MSIE or you can claim to be Gecko. We found that any other choice that we tried led to a significant portion of DHTML malfunctioning. You would not believe (well, maybe you would) how much DHTML exists out there that works only with MSIE or Gecko, and that uses proprietary extensions of each to accomplish the DHTML effects.

Had we released a browser with a UA string that did not superficially match either MSIE or Gecko, users would have downloaded Safari and experienced many malfunctioning Web sites. If anyone thinks that would have been a good idea, please step forward in your blog and explain why. I'm willing to listen.

Our solution was a compromise. We produced a user agent string that is different from Gecko's and easily distinguishable if you choose to sniff for it, but that at this time will pass most UA checks that sniff for Gecko. It may be that enough sites will start sniffing directly for our string that we can drop the "(like Gecko)" from our user agent string, but I'm not optimistic.

We chose to be more like Gecko than like MSIE because we wanted to be lumped into the standards compliant category, because fundamentally we are committed to supporting DOM 1&2, CSS1&2, and enough proprietary MSIE extensions and Gecko extensions (innerHTML, createContextualFragment, offsetWidth/Height, etc.) that we could be placed in a similar category.

That's all from my end. I welcome constructive feedback on this issue.

Comments (2) |
Safari Newsflash: CSS1 Test Suite

As reported on numerous blogs, Safari can't run the CSS1 test suite. This had to do with missing support for <object type="text/html">. We have fixed this issue and are now able to run through the suite. Thanks to the many bloggers who pointed out the problem.

Comments (1) |
Safari Response: flashthefuture.com

Various members of the Flash community have been reporting low frame rates when using Flash in Safari. I'm pleased to report that we have corrected the problem. The issue was not in WebCore, so I can't post a patch, but the issue has been addressed.

Comments (2) |
Safari Newsflash: How to Run Modified Safari Builds

You can check out this article on Slashdot that outlines one approach. I'll just add that if you want to avoid copying the frameworks, you can set DYLD_FRAMEWORK_PATH instead, and then you can leave Safari.app alone. Thanks for saving me some time by writing this article! :)

Comments (2) |
Safari Newsflash: The DPI Issue

Just a little update to let you know that we have Safari working at 96dpi now.

Comments (1) |
Safari Response: meyerweb.com

Eric Meyer writes in his blog:

Meanwhile, I've heard credible rumors that Apple, while it was working on Safari, filed Bugzilla evangelism bugs so that the Standards Evangelists at Netscape (of which I'm one) would get the sites to fix their code to work with Gecko and other standards-compliant browsers. This would then, they apparently hoped, get the sites working in Safari as well. If this turns out to be true, I'm going to be furious; just the idea that it could be true makes me angry. I don't mind helping out Apple. I'm a Macintosh guy and have been for more than a decade now. I do mind being tricked into doing their work for them. Hey, guys, what's wrong with saying, "We're both working on standards-based browsers, so let's work together to get sites to support standards?" You know, being honest? How about that? Anyone think of that?

This is a ludicrous claim and is completely without merit. I challenge you to either retract this statement or back it up with hard facts. As far as I know, I am the only member of the Safari team to have ever filed a bug on the evangelism component in Bugzilla, and the only bugs I can think of that I did file were discovered while using Phoenix or Mozilla. As an example, I pointed out wsj.com's broken "Netscape6" check in the user agent and filed an evangelism bug. I still develop for Phoenix and use it on Windows. Should I avoid filing bugs on Mozilla that if fixed will make Gecko work with more Web sites simply because my email address ends in "apple.com"?

Comments (1) |
Safari Response: scottandrew.com

Scott Andrew writes in his blog:

Word is trickling in about the new Apple browser called Safari, which was apparently announced at Steve Jobs' keynote at MacWorld SF. The rendering engine is built on something called KHTML, which may be the KDE library of the same name. Apparently built for speed, the Apple page boasts of support for XHTML, DOM, JavaScript and Unicode, but provides no details as to the level of support (according to the docs, KHTML itself only supports DOM Level 1 and CSS 1, so I don't hold out much hope) for these technologies.

KHTML supports much of DOM level 2 and CSS2, including the DOM level 2 event model (e.g., mutation events too!) and rich selector syntax (even a little bit of CSS3 selectors). In addition we have added and fixed CSS support in a number of key areas, including partial support for min/max-width, better support for z-index (including support for auto z-indices), and better support for the CSS white-space property.

Comments (1) |
Safari Response: mitzpettel.com

A patch has been posted to address issues with visual Hebrew support. We are examining the patch (especially since it involves Apple-specific #ifdefs), so stay tuned for more details. The patch is here.

I will be providing instructions shortly for how to run a modified Safari with WebCore or JavascriptCore changes, so watch this space.

Comments (1) |