Essentials • 1:07:49
Safari on Mac OS X and Windows includes a complete set of developer tools for quickly debugging HTML, CSS and JavaScript. Learn how to use these powerful tools to improve performance, tweak layout to perfection, and quickly diagnose JavaScript problems on even the most complicated web sites.
Speakers: Timothy Hatcher, Adam Roben
Unlisted on Apple Developer site
Downloads from Apple
Transcript
This transcript was generated using Whisper, it has known transcription errors. We are working on an improved version.
Nice crowd. I'm Tim Hatcher. I'm an engineer on the Safari and WebKit team. And I'm going to be presenting with Adam Roben, who's going to come on stage later during the presentation. So you're here to hear about debugging websites using Safari's Integrated Developer Tools. And how many of you have used debugging tools that are available in other browsers? So we have a lot of web developers, it seems. How many of you are familiar with Safari's current developer tools? Great, that's good to see. And one last question. How many of you have downloaded a WebKit nightly build? Great. Keep doing that.
So when you're making a website, you're going to use a lot of these different web development tools, from a text editor that does basic syntax highlighting, all the way to a full-featured IDE like Coda or Dashcode. And some of you have probably even written some of these apps that I have up here. But there's always something that's missing when you're using one of these apps.
And that's the browser. There's always a need to use the browser. You're always going to need to see exactly what the user is going to look at your site, exactly how it's going to look. So hopefully that's Safari for every one of you. And hopefully you're targeting Safari, developing apps for Safari, and users tell us they love using Safari. They love Safari for its great user experience and great feel.
And we've heard in the past that developers are using other browsers because they like the tools. Well, we've changed that. We've drastically increased our tools that we have available in Safari. So hopefully the same browser that your users are using, you can be using to develop your websites with.
[Transcript missing]
Once you turn this on, you get a menu that looks something like this. And the menu's changed a little bit in Safari 4 Developer Preview. And there's a couple new items that we're going to talk about later in the presentation. But first I'm going to talk about this first group of items.
This group is about browser interoperability. And what's browser interoperability? Well, if you're a web developer, you already know. Your page has to work in other browsers. You're developing your website to web standards and you want to test between all the browsers to make sure these standards work exactly how you think in all the browsers.
So the first item does exactly what it's titled. It opens the page with whatever browser you select from this menu. So it'll open in Firefox or Opera and tell that browser to open to that URL that you're currently viewing. So it's great to jump back and forth between Safari and the other browsers to test your page.
[Transcript missing]
So you can type whatever you want here. And Safari will send this user agent string to the server and post it to your scripts available until you close the window or change the user agent back to the default or some other item.
So now let's talk about the Snippet Editor. I'm skipping over the other items that we're going to talk about later in the presentation. So the snippet editor I love using. And I hope you guys really take advantage of the snippet editor and its power to let you quickly and easily test WebKit and use your HTML snippets and ideas that you might brainstorm in the shower in the morning or something. So you have this idea of this CSS style and you want to try it out real quick.
Well, I hate creating test.html or test.html42 all over my hard drive and all over my desktop and just to test out a short little thing that Dave Hyatt just blogged about on the WebKit blog. So I can fire up the nightly build, open up the snippet editor, type in some HTML and see it update live right in the snippet editor as I'm typing. So you can quickly and easily change different... Try different tags, try anything you want, anything that you can render as a web page, you can type in here and see exactly as you want it in the preview pane.
So that's the snippet editor. And I'm skipping over the other two items, but let's talk about the disable items. This is the bulk of the menu. There's a lot of things you can disable in a web browser. And some of your users are gonna have some of these items disabled. Maybe JavaScript, some of the other things we don't expose to the user, but are available to you to disable to test different code paths and different scenarios that other users from different browsers might actually experience.
And they're good for testing other things. So what are they good for testing? Well, the caches, you can disable all of the browser caches. Browsers have many caches. There's the back-forward cache, there's the disk cache, there's the memory cache, there's the application cache, there's all sorts of caches.
While testing networking loading, you don't want to ever get the cache. You always want to get your resources over the network. So disabling the cache is good for that. And other situations where you're updating your site live, you're uploading new versions every two seconds, and you want to make sure you're always getting that fresh version. I hate when it's 3 a.m.
and I'm working on a site and I upload a resource and I reload the browser and things don't work and it turns out the browser is still using the last version and I've spent an hour trying to figure it out. So that's why you want to disable the caches. And we've heard a lot of feedback of people want to do this, so this was added for you. Amen.
Images, Styles, and JavaScript. You can already disable JavaScript from the security preferences, I believe, in Safari. And we want to give you, the developer, quick access to that so you can quickly disable JavaScript, test your site, make sure it's going to work for those users that are paranoid and want to keep JavaScript off for some reason. I don't know how they get around the web nowadays with JavaScript disabled, but it's always good to test, right? And there's always browsers that don't support JavaScript, so you want to test those cases.
Then there's testing semantic markup. That's the popular trend now, is separating presentation from style. Or, not presentation from style, that's the same thing, right? Separating content from style. So, you can easily disable the style sheet. And when you choose this item, it turns off all the author style sheets and all of the user style sheets. So, you'll see exactly what your HTML markup will look like, say, in like a text browser like links or something like that.
Then there's the runaway JavaScript timer. And you might not know what the runaway JavaScript timer is. You might not have experienced it before. Hopefully you haven't. And that means websites are doing the right thing and not hogging the user experience with long-running scripts. But sometimes you need to debug or test out a script that runs for many seconds.
And
[Transcript missing]
We would always just let the JavaScript do whatever they want. And users would get the beach ball or things like that. So in Safari, we've added a dialogue that comes up for the user that lets the user interrupt the script in case the site has gone off the deep end. And the user needs a way to exit the script.
Otherwise, they're going to have to force quit Safari. And we never want that, right? But there's cases where you, the developer, need to test your scripts, figure out why this script has gone off the deep end. And you want to see if it actually ever ends or if it's an infinite loop. So you can just disable this timer, and you'll never see the dialogue as a developer. So that's the disable items of the develop menu. Now I'm going to invite Adam on stage, and he's going to talk to you about the Web Inspector.
Thanks, Tim. So I am Adam Roben. I'm another engineer on the Safari and WebKit team, and I'm going to be talking to you today, and Tim is also going to be talking to you for the rest of the session about the Web Inspector. This is one of our main web development tools in Safari. It was first introduced in Safari 3.0, although it was hidden inside a hidden menu that you couldn't get to without running some script on the command line.
In Safari 3.1, it was promoted to the Develop menu, which Tim already showed you how to turn on. And now, in the Safari 4 developer preview, we've really enhanced our existing features and added a lot of great new features based on the feedback we've gotten from all of you. So we'd really like you all to check it out, and I'm going to give you a little overview of it.
But first of all, how do we get to the Inspector? Well, as Tim said, you can get to it from the Develop menu. Also, when you have the Develop menu enabled, anytime you right-click or control-click on a web, you'll get this extra context menu item called Inspect Element, which will bring up the Inspector and focus it on whatever element you clicked in the page. And once you do one of these things, you're going to see the brand-new, redesigned Web Inspector that's included in the Safari 4 developer preview. And it looks like this.
[Transcript missing]
So I'm going to give you an overview of the three main parts of the Web Inspector. First, at the top of the window, you can see that we have a toolbar that lets you access the five different panels that break the Inspector up. Each of these panels is designed to let you focus on a particular debugging task. Down at the bottom of the window is an interactive JavaScript console where you can enter JavaScript and have it evaluated live at any time.
And the third main part of the Web Inspector is not in the window at all. It's an API for your website to use to provide you with detailed debugging information and functionality. So first, let's go through the panels. And the first one is called Elements. And this is what it looks like.
Now, this may look pretty familiar to you if you've used the Web Inspector before. The main feature of it is a DOM tree on the left side. On the right is style information for the element that's selected in the DOM tree. And down at the bottom of the window is a breadcrumb that shows you where in the tree the selected element is located. The next panel is called Resources, and it looks like this.
And now this may also look familiar to you if you ever used the network view of previous versions of the Web Inspector. You see here on the right, we have a graph showing you the loading profile of your web page. Up at the top is a summary graph that also shows you information about the data loaded. And on the left side is a list of all the resources that your page loaded. If you click on any one of these, you'll see either the textual source for the resource, if it was something like a document or a JavaScript file or a style sheet.
And if you click on an image resource or a font resource, you'll see a preview right in the resources panel. Now the next panel is something entirely new to the Safari 4 Developer Preview. It's called Scripts, and this is what it looks like. This is the home to our new integrated JavaScript debugger, which Tim is going to tell you a lot more about later.
The panel after scripts is also new in the Safari for Developer Preview. It's called Profiles, and here it is. Now, this exposes some new functionality that we've built right into WebKit's JavaScript engine that lets you profile your JavaScript source code and see the fastest functions, the slowest functions, and figure out where your page is spending time when it's executing script.
The final panel you may have seen a little bit of if you were here in the previous session about offline data, and that's the Databases panel. This was also in Safari 3.1. Now this lets you get at the HTML5 client-side database storage that WebKit supports. And this is a way for you to store structured data persistently on the user's computer. It's a new feature of the upcoming HTML5 standard, and it's fully supported in WebKit.
On the left, we have a list of all of the databases that your page opened, and each of the tables that each of those databases contains. When you have a database selected, as we currently do in this screenshot, you are shown a query view where you can enter SQL queries and have them evaluated live against the database that you've selected. And if you select one of the tables, you'll see a database browser, a table browser that will show you all of the data in the database, all the columns and all the rows and all the data in the cells.
So those are the inspector's panels. The next thing I want to talk to you about is the interactive JavaScript console. Now the console is a very useful thing, so we want to enable you to get to it at any time. So no matter where you are in the inspector, at any moment, you can click on the show console button down in the status bar, or you can press the escape key to bring up the console.
And up it will come, and right away you can start entering JavaScript expressions and evaluating them right in the inspector as if they were being evaluated in the page. Now two new features to the console in the Safari for Developer Preview are tab completion and auto completion, which makes it really easy to type in long JavaScript expressions.
So the final part of the inspector that I mentioned before is the API for your sites to use. And this is exposed through the window.console object. Now the window.console object has been available in Safari for quite some time, but we've recently made some big improvements and added a lot of new functionality to it to make it even more useful for your site.
The console object has methods for debugging logging and assertions and for starting and stopping profiles. And all of this information appears right in the inspector where you can see it while debugging the rest of your website. And I should also note that we've taken care to make sure that all of our console API is completely compatible with Firebug. So if your site is already coded to use Firebug, it'll work right away in the inspector without you having to change a thing. Thank you.
So those are the three main parts of the Inspector. Now, at this point, you might be thinking to yourself, you know, this Inspector thing sounds like it's pretty useful and pretty cool, you know, but I have a WebKit application that has a lot of web content in it, and I want to be able to use the Inspector to debug that content.
Or maybe you're thinking that you're a web developer and you try to always stay on top of the latest standards and technologies in the web world, and these things move pretty fast, and you don't want to have to wait for a new version of Safari to be released just so you can try out the new features in the Inspector to support these new technologies.
Or maybe if you're my favorite kind of developer, and maybe you're at the WebKit party if you're like this, you were thinking, boy, I have some really great ideas for how to make this inspector so much better than before, and I want to implement them myself. Well, if you're thinking any of these things, you're in luck, because the inspector is not a Safari feature. It's actually a WebKit feature. It's included in the WebKit framework, and it's part of the WebKit open source project.
This means that on any day, at any time, you can download the very latest version of WebKit using a WebKit nightly build from nightly.webkit.org and try out the latest version of the inspector with all of its newest features. You can also file bugs on the inspector about new features that you would like or things that aren't working quite the way you would expect at our public Bugzilla database at bugs.webkit.org. And finally, you can check out and build the code yourself from our public subversion repository at svn.webkit.org.
And if you're a web developer, you can download the inspector from the webkit.org. And get started right away making changes to the inspector and improvements. And the inspector is written almost entirely using web technologies. That means it's written using HTML, CSS, and JavaScript. The entire user interface is this way. And so it's really easy to get started if you're a web developer making changes to the inspector.
So that's pretty much it for the inspector. Now you have an overview of what's there. And now we're going to move into the first of the more practical sessions of this talk. So first we're going to talk about finding and fixing content and layout errors. So what do we mean when we're talking about content? Well, really there are two types of content that we're concerned with.
There is your page's style and appearance and your page's markup. First, we're going to talk about the style and appearance. But, you know, we've shown you a lot of slides and you're probably a little anxious to see how this whole inspector thing works, so we're going to go straight to a demo.
So here we are. We've just installed our developer DVD, and I'm going to open up Safari. And turn on the web inspector. So as Tim said, you just go to Safari's preferences and in the advanced pane of the preferences, there's a checkbox, show develop menu. So I'm going to check that and you'll see up in the menu bar, the develop menu appears right next to the window menu. And now I have access to all of those features that Tim was talking about, the user agent menu, the open page width, and the ways to get to the inspector itself.
So, if you were here in the offline data session, you probably saw a pretty cool web calendar demo, and we're going to use that a little more in this session. So let's open that up. So here it is. We've entered a bunch of events here, and all of our data is being loaded off of a SQL database, off of the local disk, and it's really pretty useful.
But you might notice that we have some scroll bars here that seem to be kind of obscuring our content and making it a little hard to see what's going on. So I'm going to choose to inspect right here by right-clicking and choosing Inspect Element to see what the inspector can tell me about the style in this area that might be causing these scroll bars to appear.
So if I choose Inspect Element, the inspector comes up and it puts me right in the Elements panel and selects the element that I was clicking on in the page. Now you can see over on the right here in the Styles pane, the first item is called the Computed Style.
This is the style that the element is being rendered with on the page. And if you scroll down a little farther, you can see all of the CSS rules that we declared in our style sheets and that the browser applies automatically and how these were built up to obtain that computed style.
So I'm going to collapse the computed style for a moment and look mostly at the style sheet that we've created here. Now I can see that we have an overflow rule. Now overflow is a property that controls what happens when content extends outside of the element containing it. It can turn on and off scroll bars and make them appear only when needed.
And so this seems probably like the place we need to look to figure out what's going on with these scroll bars. Now here we have auto set, which means that when the content is too big, scroll bars should appear. And when it's not too big, they shouldn't. Now I'm going to try just removing the overflow property entirely, just to see if Safari's defaults give me the appearance I want.
You can edit any CSS in the inspector in the styles pane just by double clicking on it. And this brings up an edit field. You can select any text and delete it. And I'm going to try just deleting all of the text in the rule and hitting enter to commit that. So when I hit enter, you can see that in the page, the styles updated live.
Now the scroll bars did go away, but I think now we have a problem that's a little bit different, which is that our data is now extending into the following days on the calendar. So this doesn't seem quite what we want. So maybe we should try a few other properties, values for the overflow property. Now it looks like here we're extending a lot in the vertical direction.
So I'm going to try modifying overflow Y first, which controls just that direction. And if you want to add a new property, you can just double click on any existing property and start typing right now, overflow Y. And then you can see that the data is now extending into the following days on the calendar. So I'm going to try to do that. So I'm going to type overflow Y hidden.
Now I think that this will fix some of our issues. And sure enough, it did. Now we're not extending in the Y direction anymore, but we still do seem to have a horizontal scroll bar on some days. So to get rid of that, we're going to do the same thing for overflow X. So I'll double click and type in the property I want. And then I'm going to type in the property I want.
And when I hit enter, the styles update, and now it looks like our problem has been fixed. So you can see how the inspector makes it really easy to update styles live in the page and experiment around with different looks. Now, of course, if we want this data to go live to our users, we'll have to make the same edits in the source file and upload it to our server. The edits that we make here are just modifying the live representation of the page in the browser.
So can we go back to slides, please? So that's how to find and fix layout errors. And now let's talk about the other part of content errors that I mentioned before, which are markup errors. Now there are two main kinds of markup errors. The first kind are syntax errors. This is when you have content that is just not valid HTML syntax at all. One example of this would be misnested tags. If you have tags that are opening and closing in the wrong order, then this is one kind of syntax error that you'll encounter.
Another kind of syntax error is to have extra closing tags. In general, you're supposed to have one opening tag for each closing tag. And so if these opening tags and closing tags don't match up in number, then you have some extra tags that you'll need to get rid of.
Markup Error #2: Non-conforming content This is content that is syntactically valid, but doesn't conform to the HTML standard. To even recognize one of these kinds of errors, you have to have your head really deep in the HTML standard and know all of the different rules in it. One example of an error like this is when you have a tag that's placed in an area that HTML says is not allowed. For example, here we have a form tag that's a child of a table tag.
And while this is syntactically valid HTML, the HTML specification says it's not allowed. Now to find these errors in the past, either you had to just know what they were, like I said, or you had to use some external tools such as an HTML validator. And these other tools are very useful, but they can't tell you what the browser will actually do when it finds it. And it's an external tool that you have to use in addition to everything else that you're already using to debug your website.
So a new feature in the WebKit engine that's built right in is the ability to find and identify and explain these errors to you and tell you what WebKit does in response to them. And this is all exposed through the inspector. So let's see how we might use this.
So here we have a page that we're inspecting. We opened up the inspector and loaded the page, and we're looking at the DOM tree. Now you can notice that down in the bottom right, we have this little error count. If you're a user of Xcode, this will look very familiar to you. It shows you compiler errors and warnings in the same place. So here the inspector is telling us that we have three errors in our page. And if we click on that error count, the console will appear with a listing of all of those errors.
Now next to each error is a hyperlink that takes you right to the source file and source line where the error occurred. So if you click on one of those, we'll move over to the resources panel. And the resource where the error occurred will be selected, so you can see its source. And the line where the error occurred will be highlighted, so you can find it right away.
Now you can see that we've also inserted these red error bubbles to show you the errors right in line and all the context that surrounds them. So this makes it very easy to see where the errors are occurring and to get a handle on them. and what's going on.
So let's look at the very first error here. Often these kinds of content errors, the markup errors, will cascade and cause many other errors farther down the line. So it's good to focus on the first ones first. So the first error we have here says, XML self-closing tag syntax used on the anchor element. This tag will not be closed. So you may not be able to see this in the screenshot here. So here's a larger version of the markup that is being talked about.
Here we have an anchor element, and on the right edge of it, you can see that we have a forward slash just preceding the closing right angle bracket. Now this syntax is valid in XML, but is not valid in HTML. And so WebKit is telling you that since this is HTML content, this tag actually won't be closed at all, and the tag will remain open.
So it turns out that this error is actually causing all of the errors that we're seeing on this page. And so the other errors here are saying that an anchor element is misnested or not properly closed. Which is the case here. It's cloning the anchor element in order to preserve the styles applied by it.
So this is something that web browsers have to do when they encounter misnested or not properly closed content, just to make your page look the way you might expect it to from the markup. And you can see the effect of this here in the DOM tree. It turns out that this anchor element has actually been cloned into two other places in the DOM. And in some cases, it even has child elements, child nodes.
Now these are real nodes. They're not just something that we're showing you in the inspector. They're actually in the DOM. And this will even affect scripts that are running on your page. For example, if we use the inspector's console to find out how many anchor elements are on the page, it'll tell us that there are four, even though in our original source code, we only had two. So these two cloned elements really do exist and they can trip up your page.
So how can we fix this? Well, WebKit has already told us that we're using an XML self-closing tag syntax, and so all we have to do is get rid of that syntax. So here again is the self-closing syntax, and to turn this into syntactically valid HTML, all we have to do is remove the forward slash and place a closing tag after it.
And so if we make this change in our source and upload it to the server again and reload the page, then you can see that down in the bottom right, we don't have any errors anymore. The source code doesn't have any error bubbles in it, and if we go over to the elements panel and look at the DOM tree, we don't have any cloned anchor elements anymore. The DOM looks just like we would expect from the source of the page.
So the inspector makes it really, really easy to find out where these errors are on your page and how to fix them. So now that we've talked about content and layout errors, I'm going to give it back over to Tim, who's going to tell you how to find and fix JavaScript bugs in your page using the inspector.
Thanks, Adam. So a lot of you said you were familiar with the Nightly Builds or have downloaded one in the past. And you might have seen a debugger that we've had in the past called Drosera. And you've probably used it and filed a bunch of bugs about it, I hope, and shared your experiences with us. And we've heard a lot of feedback about that experimental app that we've been shipping with the Nightly Builds for a while.
And we've taken all that feedback that you've given us and designed a brand new JavaScript debugger that's now integrated directly in the Web Inspector. So it's exactly what all of you have been asking for. So let's talk about this brand new debugger. Well, it's fast. It's a lot faster than the previous counterpart, the previous thing that I was talking about, Drosera. And it's great. It lets you break, pause your program any time. And here's what it looks like.
This is our debugging tool now shipping in Safari Developer Preview 4. Well, not really. It does ship with Safari Developer Preview 4, but you've already used it, you've seen it before. The alert function lets you inspect variables, right? But this is really what you're here for. It's the brand new debugger built right into the Web Inspector.
So let's talk about this debugger a little bit. It's great. It's better than Alert. It's better than Console.log. Console.log is great too for certain situations, but it's not great for debugging. No one likes instrumenting code just to debug some problem that they're running into. And everyone knows JavaScript is a finicky language and it has all these different quirks that it has that is very different than many other languages. So it's great for no code instrumentation debugging, this brand new debugger that we have.
And it works with any production website. So maybe you can't even instrument your code. You can still debug your website using this new debugger. And it provides a great amount of detail that you can't get with any other alert or console.log. So let's show you how to get started using the debugger. And one of the items I skipped over earlier is the Start Debugging item now available in the Develop menu. This is new in the Safari Developer Preview. And it lets you jump right into the Web Inspector, goes to the Scripts panel, and starts debugging right away.
But if you don't choose this menu item and you just open the Web Inspector and go to the Scripts panel, you'll see something like this. And since our JavaScript engine is the fastest out there, we want to keep that way for all the end users. Even people that have the Web Inspector turned on. But maybe they're not actively debugging JavaScript.
We don't want to have to slow down the JavaScript. And when I say slow down the JavaScript just for debugging, it's still fast. Debugging with this brand new Web Inspector is as fast as using JavaScript as we shipped it in Safari 3. So when I talk about slow, Safari 3 was still fast in JavaScript. So our debugger is great even when you're attached.
Still, Safari 4 is lightning speed compared to Safari 3's JavaScript. So we give you an option to start debugging, and this adds those extra hooks that the Web Inspector needs to debug your JavaScript. So just clicking the Start Debugging button will reload your page and enter those debugging hooks. So let's go right into a demo.
So using this calendar app, I was talking to Ada and Brady, and they shared with me some experiences while developing this that they needed a debugger for. And let's talk about that. So I have a version of the calendar here, and it's actually broken. So I'm now on the broken calendar.
Well, it doesn't look broken visually. All the events are showing up. But let's take a look at the Web Inspector. So under the development menu, we can bring up the error console. The error console will just open the Web Inspector and animate in the console. Well, the page looks fine. We didn't have any errors. And we still don't have any errors. So, let's figure out what's going on and where my error went.
That's correct. So my colleagues are telling me I need to do a search, and that's where the broken code is. So let's bring up the error console. We don't have any errors except for this resource being interpreted as text HTML. That's fine. We can just ignore that. So let's go ahead and search. Let's search for Safari. Oh, there's our error.
So now we have an error count and one warning. And right here in the console, you can see our JavaScript exception. And just like the markup errors that Adam was talking about, we give you a link right to the source. You can click on that, and it takes you directly to that line of code in the resources panel and highlights the line and gives you the inline error bubble. So thank you. Great.
Glad you like that. So I really don't know what's going on, what's undefined here. The error is a little vague and the nature of JavaScript, it can be really anything. So let's jump right into the debugger. Let's go to the scripts panel. And start debugging. So you notice the page reloads, and now we're presented with all the scripts that were loaded in that process, in that page.
and they're available from the pop-up menu here. So this is the Scripts Panel. I'll go over the various pieces of the UI here in a second. And one other item down here is the Pause on Exceptions button. And you can turn this on or off. And whenever an exception is encountered, the debugger will break and pause and show you where that exception is happening right when it happens. So let's go ahead and do the search again.
And there we are. We're broken in the debugger. And now the UI has really come to life. Before you're paused, it's kind of empty and boring. And once you're paused on a statement, you can see the sidebar has filled in with a lot of information, such as the call stack. And since this is a callback function from a database query, we only have one function in our call stack.
And below that, we have all of the scope variables. And since this function was defined inside of another function, it created a closure. And those familiar with JavaScript will know what a closure is. It lets you get access to the parent functions, local variables that were in scope at that time when the function was declared.
So you can see we separate the different scopes. So you have your local scope and then you have your closure scope, and that can keep chaining. You can have multiple closures. And then we have your global scope, which you can toggle this open and we keep it closed by default. But you can see all of the properties on the window object. And we're inspecting this live. These are the variables and the values of all the JavaScript objects that are here. And you can toggle these down and see what's in these arrays and deeply nested object graphs.
So we're here and I remember it was complaining about some undefined variable. So I have a hunch that it might be this function that we're trying to call inside this callback. The function, the callback is only doing one thing, so it has to be something like this, right? So we can evaluate stuff while we're paused.
And sure enough, this function is undefined and you can't call an undefined function, right? What could this be? Well, maybe the this object is undefined. Well, this is a special variable and it's never undefined. It's always defined to something, I believe. And the this object, the this variable is a magic function. It changes and morphs around and masquerades as different things.
And it depends on how the function was called in JavaScript. And in this case, it was a callback. So you really have no control as a developer of what the this object is when your callback is called. So you need, if you're going to access external data or another object, you need to have some other way to access that instead of relying on the this variable.
So, and you can also see what the this variable value is over here, and I evaluated it in the console. And at this point in time, it's the function, it's the current function. And that's not what we need. You can see that this variable, this object, doesn't have the function we're looking for on it. So let's go into Xcode and see what we can do. and open up our broken JavaScript. And let's go down to the line of code.
Here's where we need to look. So here's the line of code that we're using the this property, the this variable, I mean. So since we have a closure here with this callback, we can define another variable that we can access later. So we can just say variable self equals this. And self is not a keyword in JavaScript, unlike Objective-C. So a lot of developers have worked around issues just like this.
This, right? Where they need some other name for a variable that they're going to access inside of one of these callback functions or inside of a closure like this. So we can now use self in replace of this. And that self variable is going to be in scope because we're creating a closure by using this callback like this. So let's go ahead and save this.
This, this, this, this. What this am I talking about, right? So we've reloaded the page, and we're still in the debugger. After I've reloaded the page, the debugger is still attached, and it keeps track of you. It updates the resources as new scripts are loaded, as evals are evaluated, and they would show up in the menu. And there's the same three files I loaded earlier. And break on exceptions is still turned on.
So if an exception would happen right now, it would break right in the debugger. So let's go ahead and do the search again. Safari. Oh. Well, I don't have any data in my database. So something has happened there. There we go. We still got the undefined. Let's see where that's happening.
Well, that's curious. So let's reload again. and try our search. There we go. It was just a caching issue. I should have turned off caches in the development menu, right? Well, it looks like we fixed search, but the debugger can be used for a lot more than just fixing code as an exception happens.
You might have never written any of this code and you've been tasked to fix it, right? That happens all the time in the development world, right? Someone else drops this big pile of code on your lap and you need to figure out how it works in two days and ship it for a demo, right? I love this app and Ada did a great job in writing this demo for the previous session, but I had no clue how it worked. I'd never seen this code before. So I was very curious of how the days are populated up here on this chart.
So one feature of the debugger is You can just click the pause button and here's all the function flow controls that we have available for stepping and pausing and continuing. Very similar if you have used XCodes inline debugger that's available. So we can go ahead and hit pause.
Well, it didn't pause, right? JavaScript's not always running. So until the next JavaScript execution, we're waiting to have something triggered so we can pause. So the next time we click one of these buttons or an event fires or a timer fires, then the debugger will inter-pause the pause state and show you the line of code where it's at. Until then, it's just waiting for a user event.
So let's go ahead and click the next arrow. And that triggered an on click. As you can see in the call stack here, And we're now paused in the debugger and this on click has some script. And you can set breakpoints by clicking anywhere in the gutter here. And very similar to any other debugging interface you've probably used in the past.
Well, let's go ahead and step in. Step in. Let's keep going and see if we can find something interesting. And you can see the execution line update as I step, and it changes files just like you would expect. And the scope variables will change, though there's not much interesting here. You saw a month title change.
Let's get into some interesting code that we might figure out how the day grids initialize. So here, I just stepped into the function initDayGrid. So this sounds like what I was looking for, right? I need to figure out how this day grid is populated. Maybe there's a bug I need to fix, or I'm just curious about it.
So I don't want to step too much, so let's go ahead and set a break point and we'll go ahead and continue. We've just stepped over all that code and continued right to that point when the execution happened on that line of code. So now we have a bunch of undefined variables still, nothing really interesting here. Let's go ahead and step over.
Now we're going to get into this day grid. Oh, wait, something happened in the background. Our calendar is no longer there. I guess some line of code earlier that I stepped over must have removed the elements in that grid. So it looks like this for loop is going to populate the new days.
Let's keep stepping and it looks like we're about ready to add the new day. And sure enough, there it is. So as you're incrementally stepping through your code, Safari is still there and updating the rendering as new DOM elements are added and resized and things. So you can really see exactly what's going on in between various big functions like this init day grid where all of a sudden the whole day grid is filled in. but you don't know what happens in between.
So let's go ahead and set another breakpoint right here. So this gets hit for every day grid. And if you notice in the background, it's going to be populating the day grid as I keep clicking continue. I keep hitting this breakpoint. And you can see the local variables are updating as I step through.
And everything that you're going to expect from a debugger is right here available at your fingertips. So... What if I want to experiment a little? Debuggers are great with that, right? You can inspect, but you can also change. So let's go ahead and type i equals 20. So now we've changed the local variable i, and you'll notice it changed over here too. So before it was like 3 or something like that. Now it's 20.
What's that going to mean? What's that going to do to our code? So let's go ahead and disable this breakpoint so the code just runs full speed until we hit another breakpoint or until we pause it again. So we've now initialized our whole day grid, but it looks like my actions have messed up the code a little bit.
We have hanging days and an extra week we shouldn't. So it really lets you see the power and flexibility of the new debugger that lets you experiment and be more playful with your code. And test out different things that you might not try otherwise without a debugger. So let's go back to slides.
So that's the brand new debugger. I hope every one of you goes and downloads the new Safari 4 developer preview if you haven't already and try out the new Web Inspector, including the debugger I just talked about. And like Adam said, we're constantly improving these tools. So any feedback you have on these tools, you should go to the open source project, file bugs, enhancement requests, things you might like to see in these tools.
Or if you're a handy web developer and know JavaScript and HTML, which who doesn't, right? And if you're a handy web developer and know JavaScript and HTML, which who doesn't, right? The Web Inspector is all written in JavaScript and HTML. That debugger is written in JavaScript and HTML. We debugged the debugger as we were implementing it. As it got further along, we used it to debug itself, which is... I don't even know what that means.
So feel free to jump in if you have any feature ideas or just want to implement something. We're always available on IRC to talk about things that you might want to implement or feature ideas. So feel free. So now I'm going to hand it back over to Adam, and he is going to talk about improving page performance.
[Transcript missing]
So next let's talk about high latency servers. Web pages today are being served from many different servers. Many of them are outside of your own control. For example, if you are embedding data from Google Maps or something like that. Now latency refers to the amount of time spent between when the browser first requests a resource from the server and when data is first received from that server. And we've made this very easy to find in the Safari 4 Developer Preview and the Web Inspector. So let's take a look at how to see it.
Now here we are in the Resources panel again, and we've selected the time graph up in the upper left. Now anytime you're looking at the time graph, you'll notice that the bar next to each resource is divided into three parts. This is a new feature in the Safari 4 Developer Preview. We have a beginning of the bar, which is when the resource was first requested from the web server. The middle of the bar, where we change from light to dark, is when the first byte of data was received from the server.
And the right edge of the bar is when the last byte of data was received from the server. So for example, in this long green bar in the middle, we have a very long light area, which means the amount of time from when we first requested the data to when we first started receiving the data is very large.
And that's the latency. The light part of the bar is the latency. Now you can scroll through all of your resources and try to eyeball the one with the largest latency, but we've actually made it even easier for you. Down in the status bar, you'll notice that we have a menu that lets you change the sorting order.
Right now we're sorting by the start time, but if we change it to sort by latency, suddenly we have our resources ordered in terms of the amount of latency that transpired while requesting that resource. So the resources and servers with the largest latency will float right to the top.
Now you can see here that our resources with the two highest latencies are actually both coming from an external server. Now since the server is outside of our control, we can't go change any settings on it, but perhaps we could do that. We can do something to ensure that these resources don't block the loading of the rest of the page, so that the user can start interacting even before they've been loaded.
So now let's talk about having a large number of resources on your page. Each request that the web browser makes to the page has a certain amount of overhead, the HTTP headers being sent with the request and the TCP packets, and there's also latency for each resource. So anytime you can reduce the number of resources on your page, you stand a good chance of improving your page's loading performance. And one particularly easy way to reduce the number of resources on your page without actually reducing the amount of data it loads is to combine small resources into larger ones. So let's take a look in the Resources panel at how we can find these small resources.
So here we are in the Resources panel again, and we've chosen the size graph, and we're sorting by size. And now if we scroll all the way down to the bottom, we'll see all of our smallest resources. Now you can see here that, for example, this page is loading about five or six very small CSS files, and we could easily combine all of these files into one and have a single resource instead of the six different ones, and similarly, a single request instead of six requests. So this is one good way to improve your page's loading performance.
And finally, let's talk about serially loaded resources. Now, normally when the browser is loading your web page, it will request many resources in parallel. But there are certain situations where this isn't possible. For example, if you have a resource that's being loaded by some script, the resource can't load until the script has finished loading and started executing.
So let's see how we might identify this kind of a situation in the Web Inspector. Here we are in the Resources panel once more, and we've selected the time graph, and we've chosen a sort by start time. Now here you can see that we've got a lot of resources that are loading in parallel. We've got many CSS files loading at once, many images loading at once, and this is really great.
This is what you want to see when your web page is loading. It means that the browser is parallelizing things as much as it can. But if we scroll down to the bottom of this graph, you'll actually notice that our very last resource, this image down here, is waiting to, it's not even being requested until almost everything else is finished loading.
And this gives us an idea that there must be something that's holding up the request of this resource. Now if we look up above, you'll notice that there's a script resource that seems to vaguely line up with this one. And since both of these resources are being served from the same server, we can make an educated guess that the script is actually causing the image.
To load, and that's why the image isn't able to load in parallel with everything else. Now, once again, these are the scripts and images coming from an external server, so we can't make any direct changes. But if this were one of our scripts, we could perhaps make the image be statically loaded inside our web page so that it would load in parallel, just like any other image. Or at the very least, we could have the script start the load of the image at the very beginning of its execution so that the image would be ready as soon as possible.
So those are some examples of how to identify different kinds of page loading performance problems in the inspector and some suggestions for maybe how to fix them. This is stuff that's normally very hard to figure out if you're using a standard network tool like TCP flow that shows you things at a very low level. The inspector kind of breaks it down for you at the level that you can understand. So now let's talk about the other side of performance, which is JavaScript performance. And for that, I'm going to give you a demo.
So here we are back in the calendar demo that Tim showed you. Now I'm going to make sure that all of our breakpoints here are cleared out so that they're not getting in our way. So let's see, we seem to be missing our events once more, so we'll get those back.
Oh, I see, we're in July where apparently I'm not doing anything. It's a big vacation. So back to June where we have to work, but we're here at WWDC and it's really great. So as Tim showed you, one of the features of this calendar is a search field. And when you search for events, you get a listing of the events on the side that match the text that you searched for, and also each of the events gets highlighted.
Now, back in previous months when I was even more busy, I actually noticed that when searching, when I have a lot of events, things can get a little bit slow. And so I'd like to try to figure out what's going on here and what's causing my page to be slow when searching. And to figure this out, I'm going to use the inspector's new JavaScript profiler.
So in the inspector, we have this profiles panel that I mentioned to you earlier, and it will show you a profile. Now here, we've instrumented our code using the console.profile and console.profileEnd functions to instrument a section of our script's execution. And we've recorded a profile, and we're seeing the profile on the right. Now let me show you where these instrumented calls are so that you can see what's going on here.
We're here in the process query results function, and we've inserted two calls. At the beginning of the function, we're calling console.profile, and we're passing it a title for the profile, which you can see shows up right in the inspector. That's the name of the profile we've passed in. And then at the end of the function, we're calling console.profile end, which finishes recording the profile. We're passing in the same title here so the profiler knows which profile to stop.
You can have multiple profiles running at once, and this way the right one will be terminated. So in the inspector, if you've ever used any of OS X's standard performance tools like Shark or Instruments, this should look very familiar to you. We have a number of columns here showing you all of the information that you need.
On the right, we have the function column, which shows you the names of all the functions called by the script while it was being profiled. And actually, if you expand any of these functions, you'll see all of the functions that it called, including functions that are built into the JavaScript engine, functions that I didn't even define in my script file, such as char at or index of.
Just to the left of that, you'll see the number of times each function was called. And then we have the two time columns. The total time column tells you how much time was spent in each function and all of the functions that it called. And the self column shows you the amount of time spent just in that function without any of the functions it called. So just in the source lines of that particular function.
Also, down here in the status bar, we have a percent button. When clicked, this will change the time from being relative percentages to being absolute times. So you can really get a feel for how much absolute time is being spent in each function. But for our purposes, we're going to be looking mostly at percentages.
So now since we're sorting by the total time, we can see the slowest functions right up at the very top of our profile here. And the very slowest one is highlight events in calendar, highlight event in calendar. Now you can see that the self time is 36% and the total time is 57%. So this means that almost all of the time spent within this function is actually spent in the function itself and not any of the sub functions it's calling. But let's see what those sub functions are. We've got three of them here. Get elements by tag name. Test.
And the regular expression constructor. Now you can see that we're calling each of these functions many, many times. So you can get an idea that this function is doing a lot of work in calling these functions over and over to try to highlight this event in the calendar.
Now to get a sense for the source code itself, we've given you a hyperlink next to each function that's defined in one of your JavaScript files that will take you right to the file in the resources panel. So here it is. We've clicked on the link and it's taken us right to highlight event in calendar. And we can get a feel just for what's going on. Now let's look at this. This is an Xcode where the text is a bit bigger and you can see it better.
And here is highlight event in calendar. Now this looks like some pretty standard DOM walking code. We've got looking for all of the unordered list elements on our page and we're looping over all of them and we're trying to find ones that have the contents class name. And then for each of those, we're looping over all of the list elements that it contains and we're checking with a regular expression that those list items have a class that matches the name of the calendar that this event is in.
And once we've discovered that that's true, we go on and check that the event that the list item contains the title that matches the events title. And we're looking for events that are not already highlighted and then finally adding the highlighted class to them to give them this nice yellow appearance. So this is pretty standard code that you might have in one of your web apps, but we'd like to kind of speed it up.
Now, if you were here at the advanced Ajax talk earlier this day, you might have heard about a new function that's on the document that's called query selector. All. This lets you use CSS selectors to select elements in your document and lets the browser really do the heavy lifting of this kind of DOM traversal for you.
So I'm going to try to rewrite this function using query selector all and see if we can speed things up from our 57% of total time that we have currently. So query selector all is, as I said, it's a method on the document and it returns a node list that I'm going to call items because it's going to store our list items. So we're going to call document dot query selector all.
And its argument is a CSS selector. So we want to just duplicate what our DOM walking code was doing here. So we're looking for unordered lists that have the contents class and we're looking for their children, which are list items that have a class name that matches the calendar that this event was in. So here we're dynamically adding in the name of the calendar as the class name of the list item. And we're also looking for list items that are not already containing the highlighted class. So we're going to search for one. that aren't highlighted.
And so this is our query. I hope that this will give us the list items that we're looking for. Now, as I said, the browser is really doing all the work for us here. So we don't have to look for all of the unordered lists, and we don't have to check that they have the contents class, and we don't have to look for their list items, and we don't have to look that they match the right class.
Now, our selector here doesn't say anything about the contents of the list item, so I think we're going to have to keep this code. But it is checking that things are not already highlighted, so we don't have to check the class name. And then we'll add the class like we wanted. Now, we need to loop over these items. As I said, this is a node list that we're getting back. So we're just going to loop over all of our items one by one.
And we'll store each one in a local item variable since that's what the rest of the code is already using. And let me re-indent this so it's a little bit easier to see and delete our extra brace. And so here's the new version of our function. So let's save this and see what this does to our profile time. Now remember, highlight event in calendar was taking 57% of the total time when processing these query results. So let's reload the page to try this again and load up the new script. And we'll pull up the search field. And we'll type in Safari just like before.
And you can see in the background the profile got recorded automatically by those console.profile and console.profile end calls. And if we click on it, we can see that highlight event in calendar is no longer the top function. It's actually down here in spot number three. It's been reduced to 22% of total time. So we've really made a big impact here on the performance of this function. And the profiler really helped us to see how to do it. So let's go back to slides. Thank you.
So, you've now seen how to improve your page's performance, both with its network loading performance and its JavaScript performance. And you've gotten a big overview of all of our developer tools and particularly the Web Inspector. We hope that you've seen that we've made some big improvements in these tools and that they've made it really easy for you to debug your website right in the browser without going anywhere else. We hope you're really going to like them. So we hope that you walk out of here today by going home and turning on the Develop menu from the Advanced Preferences dialog, easily debugging your site using the Web Inspector and the other developer tools, and profiting.
If you need any more information, please check out the WebKit Open Source Project and the nightly WebKit builds and contact our technologies evangelists, Vicki Murley and Mark Malone. And now we'd like to open it up to QA. We've also got some great labs. There's an offline data solutions lab and a compatibility lab coming up right after this session downstairs, so please join us there.