Transcript
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
[ Silence ]
>> Simon Fraser: Good morning.
This session is about
Power and Performance,
Optimizing your Websites
for Great Battery Life
and Responsive Scrolling.
I'm Simon Fraser
and I'm Engineer
on the Safari and WebKit Team.
So as you've heard about all
week, one of our primary goals
with OS X Mavericks was to
get great power efficiency
but still maintain and even
improve responsiveness.
So through all the levels of the
operating system we've optimized
for fast performance
and long battery life.
And we know that battery life
is really important to you
because we all use
MacBook's every day
and you really want
that all day battery.
And we also know that you spend
a lot of time browsing the web
so you want a web browser
that's super power efficient and
yet still feels fast
and responsive.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
But that's not an easy
thing for a web browser.
Web browsers have to
load complex pages.
Those pages contain scripts
that have to be executed.
They load plugins and so on.
So this is quite a challenge
but this is a challenge
that we've met head on in Safari
7 so we have a great new set
of features in Safari 7
that give us much
better power efficiency.
But we also need your help.
In many ways we can only be as
power efficient as the web pages
that we load and display so
we need your help to make sure
that your pages are
power efficient
and that they also play well
with Safari's new
power saving features.
So I'll be talking about
that later on in the session.
So there are three main
sections in this talk today.
First of all I'll talk about
the new power saving features
in Safari.
Secondly I'll talk about how
you can make power efficient web
pages, some new web API you
can use, the tools you use
to detect whether your page
is using too much power
and also some common mistakes
that we've seen pages make
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
that cause them to use too much
power than the author intended.
And finally I'll talk
about Safari's new response
to scrolling which will give
your users a buttery smooth
scrolling experience on your web
pages and how you can make sure
that your pages get
that best scrolling.
So let me start by talking
about these new power
saving features in Safari.
And there are two features that
I want to talk about today.
The first one is called
App Nap for Safari tabs
and the second one is
called Power Saver.
So if you were in any
of the energy related
sessions yesterday you'll know
that there's a new feature in
OS X Mavericks called App Nap.
Now the purpose of App Nap
is to focus system recourses
on the task that's most
important to the user.
So what we've done is
we've applied App Nap
to tabs in Safari.
And this means we can
Nap background tabs
and that saves us
a lot of power.
So how does this work?
Well you may already know
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
that in Safari we actually use
a separate process for loading
and rendering the web page
and we call this the
Web Content Process.
New in Safari 7 now, each tab
has its own web content process
and that means that the pages
in different tabs are mostly
isolated from each other.
But it also means that
we can now apply App Nap
to the processes for
each tab independently
and of course now we can do
that, we can Nap background tabs
and this allows Safari to
focus all its resources
on the tab you're
working with right there
which makes it really
responsive.
So when can we do
this napping of tabs?
Well in general we'll
Nap any tab
that the user can't
currently see
so that means background tabs,
tabs that minimize windows
on another space or with the
screen servers on covering
up Safari and in
general any window
which is what we call
occluded which just means
that it's covered
up by something else
and the user can't see it.
And there's one other
case where we can Nap tabs
and that's what we
call idle windows.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So the system can now tell
us whether a window has been
updated recently.
And if it hasn't then
we can apply App Nap
to that web process which means
that if a web page is loaded,
maybe it's in the background,
and you can kind of see part
of the window but
it's not actually,
the page is not really
doing anything.
That means that page is
also eligible for napping.
So what is the impact on your
web page of being in a tab
that has been App Napped?
Well if you were in any
of the energy related
sessions you'll know that one
of the worst things for power
efficiency is the firing
of timers.
Every time a timer fires
the system has to wake up,
the CPU has to ramp up
from a low power mode
to a how power mode and
all this is very wasteful.
So one of the best ways
that we can save power is
to rate limit the JavaScript
timers and those are setTimeout
and setInterval in JavaScript.
If you're using
requestAnimationFrame,
which I'll talk about a bit
later on in the session,
that will also get rate limited.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
But when we were working on
this we found a couple cases
where tabs in the background
are actually doing useful work
or things that you
want to continue.
For example there are
some audio players
that you know periodically
load data dynamically
as they're running and that's
something you want to continue
to work because you
want that audio
to keep playing audio
in the background.
So we actually detect that and
we don't Nap tabs in that case.
And if tabs are also dynamically
loaded content we won't
Nap them.
So what should you
do to make sure
that your web pages play nicely
with App Nap for Safari Tabs?
Well in general, nothing.
We hope this will just
work very transparently.
But you should be aware
that your timers
could be rate-limited.
So that's App Nap
for Safari Tabs.
We think this is
going to be great
for people we call tab
holders, the kinds of people
who have lots of tabs
open and lots of windows.
Those kinds of people should
really investigate Safari
Reading Lists because that's a
much better way of keeping track
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
of all of those URL's.
But if you do have lots
of windows and lots
of tabs open now, Safari is able
to Nap all those background tabs
which means that it's
really focusing resources
on the tab you're
currently working with
and that makes you feel
much more responsive.
So the second new
feature I want to talk
about today is called
Safari Power Saver.
One of the things we found
when we were investigating
power usage in Safari,
and this didn't really
surprise us,
is that plugins use an
enormous amount of power.
And this is a problem we
didn't have to solve in iOS.
Now in OS X we could have
simply stopped running plugins
but the problem is
that would result
in a broken user experience.
Many people visit a page and not
really understand why it didn't
work the way it used to
because the plugins not running.
So we had to fix this in a way
that was a bit smarter
than that.
And we came up with something
that we call Power Saver.
This gave us, the impact of
turning on Power Saver we got an
up to 35% reduction in
CPU power consumption
so it's really great
energy saving.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So you know why do
we need this thing?
Well this is typical of the
web pages you visit every day,
like a typical news
site or a blog site.
And one of the things you
may not really pay attention
to is all the marginal content,
all the peripheral content
which you don't really care
about, often it's adds.
But what you may not
realize is how much
of this content is actually
running through plugins.
So without Power Saver these
plugins are running all the time
and that is, of course, causing
the CPU to be busy all the time.
The CPU is always active.
It gets warm.
The fans come on.
And of course that's all
really bad for battery life.
Now that we can pause these
plugins the CPU usage is much
lower and it has a really
positive impact on battery life.
So I said before,
we do this in a way
that most users won't notice.
It's unobtrusive.
So we call it Smart
Power Saving.
And generally the way we do this
is through three techniques.
First of all when you visit
a page in order to see
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
like an internet
video or something,
some plugin is up
front and center.
We'll just continue to run
that because you probably went
to that page to see
that thing anyway.
So that we call a primary
plugin and we'll just run.
Secondly, plugins are often used
for things like playing audio
and those plugins might
be like really small
or positioned somewhere
else on the page
and we'll just let
those continue to run.
And finally if the user
interacts with a plugin
on the page we'll remember
the fact that they clicked
that plugin on the page and next
time they visit we'll just start
that plugin automatically.
So what's Power Saver
doing under the hood?
Well when a page loads we
let the plugins run but just
for a short time
and we let them run
until they've given us a
useful snatch of their contents
and then we actually
replace the plugin
in the page with that snapshot.
And then if the user clicks
the snapshot we'll go ahead
and recreate the plugin, we'll
pass that click event through
and then the plugin
we'll run as normal.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
And in many cases the
user won't even notice.
When we do pause plugins, if the
user happens to hover over one
of those plugins, Safari
will put up this banner
that just indicates that it's
done something to that plugin
but the user can just go ahead
and click anywhere on the plugin
and the plugin will
start running.
So what should you do to make
sure that your pages play nicely
with Safari Power Saver?
Well of course the most obvious
thing is to use fewer plugins.
If you can use one of the
great HTML 5 technologies
like the audio and video
elements or Canvas or Web Audio
or SPG or any of those for
rich media presentations,
those are always going
to be more power
efficient then a plugin
because we've optimized
those really heavily.
We have seen a few cases
where pages that communicate
with a plugin through
script have had problems.
And generally in these cases
the page is not prepared
to handle the fact that there
may be more than one instance
of a plugin for a given
object or embed element
over the lifetime of the page.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So the page needs to
be prepared for plugins
to be destroyed and recreated.
And generally what this means is
that the plugin should
request state for the page
when it starts up, rather
than the page pushing state
down to the plugin and
expecting that to persist.
So that's Safari Power Saver.
And it gives us a really great
energy saving and we hope
that most of users will not
even notice that it's there.
So I talked about these two
new features in Safari 7
that combined give us
really great power savings.
But we also need your help.
We need you to make sure
that your pages are really power
efficient and we need to do this
because we can't just have
Safari do power saving things
that break the way pages work.
So I'll tell you about how
to make power efficient
web pages now.
Firstly we've got a couple
new pieces of web API
which you can use to make
sure your pages save power.
Secondly I'll talk about
the tools you can use
to detect whether your
pages are using more power
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
than they should.
And finally I'll talk about some
common mistakes we have seen
that really is a pitfall
that some pages fall
into that cause them to
use a lot more power.
So first the new web API, now
there are two of these I'd
like to talk about, the first
is called Page Visibility
and the second is the
requestAnimationFrame API.
So let's talk about
Page Visibility.
This is a W3 specification
that allows a page to detect
when it's hidden
and get notified
when the visibility changes.
Now I've already talked about
hidden pages in the context
of App Nap for Safari Tabs.
The reason we have or the way
you'd use Page Visibility is
to make sure that your
page can take its own steps
to conserve power when
it's in the background.
And by in the background I mean
the same things that I talked
about in the context of
App Nap for Safari Tabs.
Any pages that are minimized
or hidden or occluded.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So there are some things that
the browser does to hidden pages
that are kind of like
App Nap for Safari Tabs.
The browser will also
rate-limit JavaScript timers
and we're not the only
browser that does this.
Other browsers do this too.
But there are some things
that we know are related
to visual updates like
requestAnimationFrame
and CSS Transition Animations.
And we know with hidden pages
none of this work has to happen
so we pause both of these.
So how do you use this API?
Well there are two pieces
to it, there's request
in the current state and
there's getting notified
about state changes.
So to query the current
state there are a couple
of new attributes on
the document object.
There's a simple
Boolean hidden property,
which obviously is
just true or false,
and then there's a property
that returns a string
and this property is
called visibility state.
And it returns the strings
hidden, visible or prerender.
Now hidden and visible
are pretty obvious
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
but let me explain
prerender for a second.
Now in Safari when you're
typing the location field,
the browser's actually going
to preload the top head,
the most likely thing that
you're going to load so that
when you actually hit the return
key you've already mostly loaded
that page and we can
show it right away
and this feels really fast.
But this means that the browser
is actually loading a page
in a way that's not
currently visible to the user.
And the way we indicate
this to the content is
to have document.visibilityState
return prerender.
And you might want to make
use of this in your pages
to do things like not counting
add impressions in that case.
So the second piece
of this API is a way
to listen for state changes.
And the way you do that is by
adding an event listener just
as you would for user event.
So you add an event
listener on the document.
The events called the
visibility change.
And then in your event
handler you simply check one
of these properties
like document.hidden
to know whether your
page is hidden or not.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So that's page visibility new
in Safari 6.1 and 7 and in iOS7.
So before I talk about
the next new API I want
to give a little
bit of background
on how people do animations
in JavaScript currently.
And pretty much the
only way traditionally
to do this would be to use
one of the timing function,
setTimeout or setInterval.
But let's have a
couple of problems.
The biggest problem is you
really don't know how often
to file those timers in order
to get smooth animation.
You don't really know what
the display frequency is.
You don't know what else
the browser is doing.
And often we see content
that calls set time
out with a 0 time out.
And that's actually really bad.
So under the hood 0 is actually
clamped to 4 milliseconds.
But most of our displays
update at 60 hertz
so that's a 16.7
millisecond interval.
But if you're generating
frames every 4 milliseconds,
it means that 3 out of every
4 frames is just never shown
to the user so that's
a real waste of power.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Now the second problem
with using timers is the
browser can't turn them off,
it can't optimize them away
because it really doesn't
know what you're doing inside
that JavaScript callback.
So requestAnimationFrame
is designed
to solve these two problems.
So it's a Web API for animation
and it consists of two methods
on the window object
requestAnimationFrame
and then there's a function
that you can use just
to cancel an early request.
And its advantages though
of using timers are twofold.
First, the browser handles
the display-refresh problem.
It will call your request
for animation during callback
at just the right time so
you can get work done to show
up at the next display cycle.
And secondly because we know
that requestAnimationFrame
is being used
for visual updates we can
pause it in background tabs.
And requestAnimationFrame
is described
in the W3C Animation
Timing Spec.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So before requestAnimationFrame
you probably had an animation
loop that looked
something like this.
You have a do animation
function.
It probably did some date math
to figure out where it was
in the animation timeline and
then it would update the display
and then call set timeout
to get called again.
And you know the
author here has guessed
that 60 milliseconds
is a good interval.
So requestAnimationFrame
is pretty much a drop
in replacement for that.
But it actually helps
you a bit more.
Your call back gets passed a
time value which is the time
in milliseconds since the page
started to load so instead
of doing date math
you can just use that.
But the other thing to note is
that requestAnimationFrame
is just a one shot call back,
just like setTimeout.
So if you want to get
called for another frame
of animation then you just call
requestAnimationFrame again
inside your callback.
So doing this will enable your
callback called basically just
before every display refreshed
so you can do really smooth
animation in JavaScript.
So requestAnimationFrame
you may be familiar
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
with as WebKit
requestAnimationFrame
in earlier releases but now
it's just requestAnimationFrame
and it's in Safari
6.1 and 7 and iOS7.
So what should you do with
these new JavaScript API's
to make sure that your
pages use less power?
Well the most important thing is
to use the page visibility API
to stop doing work in pages
that the users can't see.
For example, you
might have a ticker
on the page that's fetching
data from the server
to update stock prices
or something like that
and there's no point in doing
that work in a background tab
so you can just use page
visibility to turn off that.
And that also means that
your server load is reduced
so that's great on both sides.
If you do need to use JavaScript
for animation then you should
use requestAnimationFrame
instead of just doing
setTimeout or setInterval.
One slight wrinkle we've
seen here is you may remember
that I said that CSS Transitions
and Animations are paused
when a tab is either in the
background or for example
when the user switches bases
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
and the Safari window
ends up being now visible.
And we've seen this trip
up a couple of pages
where the page was
relying on the fact
that it would receive
transition end events
or animation end events in order
to progress its business logic.
So there's a general rule here
for well behaving pages which is
to separate out the business
logic from the visual updates.
So in hidden pages you can let
the business logic continue
but will be able to save power
by not running those
visual updates.
So at this point I'd like to
invite my colleague Tim Horton
onto the stage to show we can
take a power inefficient page
and fix it using
page visibility.
[Applause]
>> Tim Horton: So
I'm Tim Horton.
I also work on Safari
WebKit with Simon.
And so if any of you
guys were in What's New
in WebKit yesterday you met
Enrika [phonetic] and you know
that she really likes to cook.
[laughter] So what you
don't know, or may not know,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
is that she also has a giant
pizza oven in her backyard
and it takes a while for
this oven to heat up.
So she's installed a
thermometer into it,
an internet connected
thermometer into it,
and we've written a web app so
she can, from inside her house,
monitor the temperature.
So I'm going to show
that to you now.
So here it is.
You can see the pizza
oven is heating up.
This is a graph of
temperature over time.
This is temperature on a 500
millisecond JavaScript timer.
The green ticks at the top
display every time we fetch the
temperature from
the thermometer.
So let's look at the
source code for a second.
It's just using setInterval,
very basic.
Just straight forward JavaScript
timing, nothing fancy.
So let's see what happens
right now when we switch tabs.
So right now in the
background the other tab,
the timers are rate
limited, as Simon mentioned,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
but we're still doing
updates occasionally
and you can see the green
ticks coming at a lower rate
but we're still getting data.
So we're still not being a
great power citizen here, right,
we're still doing
updates in the background.
And we can do better.
We can use 0 power
in the background
by adopting the page visibility
guide that was just mentioned.
So let's replace this
function, this setup function
that just starts the
timer explicitly with one
that instead respects
page visibility.
We listen to the page
visibility page event
and when it changes we
start and stop our timer,
very simple, not much code.
Let's refresh and now
let's do a spaces swipe.
My friends just got back
from Bali so I was checking
out the Island to see
where they had been.
Now let's swipe back.
And you can see here while
the page was hidden we did no
updates at all, no green ticks.
You'll also notice while it
was hidden the graph kind
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
of looks broken.
That's a big oopsy on our part.
So maybe we should fix this.
So luckily the thermometer that
Enrika chose stores five minutes
of data back in time so we can
ask for all of the temperatures
since the last time we updated,
when we come back from hiddency.
So let's just make
that simple change just
to make our graph look more
correct, and this is just, yeah.
And now let's see
what it looks like.
So now in the background
we're doing no work at all
but when we come
back we've filled
in the graph and
it looks perfect.
Now just pretend you remove
the grey and the green
and you just got the graph.
It looks perfect.
And the background
uses no power.
So now you've seen a
simple example of how
to use the page API to
make a very effective power
and bandwidth efficient website.
Back to you, Simon.
[Applause]
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
>> Simon Fraser: Thank you, Tim.
So I talked about the new
web API available to you
so that your pages can use
less power in the background.
Now let me talk about
how to figure
out if your pages are
using too much power.
And there are two
levels of tools here
that I want to talk about.
There are tools provided
by the system
and then there's
some tools in Safari.
So first briefly I'll talk
about the system tools.
OS X Mavericks now has an
energy tab and activity monitor.
And so this is the kind of thing
that users would go to to find
out which applications have
recently been using a lot
of energy.
So it's actually really bad for
Safari to shop in this list.
You don't want your site to
be the site that causes Safari
to be using all the power here.
Similarly in the battery menu we
have a listing of applications
that have been using power
and again it's really bad
if Safari is here so make sure
your pages don't cause that.
Of course as web developers
you're probably more interested
in the tools that are
closer to your web content.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
There were a couple of
great sessions yesterday
on Safari Web Inspector and I
encourage you if you didn't go
to those to go back
and watch the videos.
So briefly, to use
the web inspector
for investigating power issues
you would enable the develop
menu and then access
the web inspector
through Safari's develop menu.
And then the tab that's
most interesting in terms
of power usage is
the timelines tab.
So you can switch to that tab
and then you would start
recording a timeline
by pressing the button.
And then interact with
your page in a way
that you think is interesting,
maybe you can scroll the page
or reload it and see what
happens at loading time
or interact within other ways.
So what this timeline panel
shows you is all the activity
that the page has been doing,
loading resources, the layout
and rendering work that
the engine is doing,
all the JavaScript is running,
the events that are firing.
Now you may think that
the amount of power used
by the page is going to
be roughly proportional
to the amount of
JavaScript that's running
but that's actually
not usually the case.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Usually the most common
cause of badly behaving pages
that we see are interactions
between the JavaScript
and layout and rendering so
I'll talk about some of those
in the context of common
mistakes we've seen
that web pages make.
And there are two basic areas
here I'd like to talk about.
First of all pages that cause
too much layout to happen
and secondly pages that
just paint too much.
So what is layout?
Well layout is the process
by which the browser takes
the [inaudible], applies style
from the CSS and then computes
the positions and the sizes
of all the boxes and lays
them out on the page.
And that's quite an
expensive process.
And we've optimized this a lot.
And we've also optimized
when the browser does layout.
We want to layout as
infrequently as possible
so that you know we're
not doing excess work
and generally we can layout
just before we paint stuff
on the page.
But there is a problem
here and that's
that there are various
properties and functions exposed
to JavaScript that force
us to eagerly do layout.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
And there's quite
a few of these.
But there's a general theme
which is they're all
geometry related.
They're all properties that
are getting things like sizes,
widths and heights and positions
and scroll information
and stuff like that.
But when JavaScript accesses
any of these properties
or calls these functions,
the browser has to,
if layout is currently what
we call stale, the browser has
to go ahead and do a layout
so it can give you the
correct answer to the question
that you're asking and
that can be expensive.
Now when pages do this
we tend to see a patent
like this in the inspector.
There'll be a repeated cycle of
invalidate styles, recalc style,
invalidate layout and layout.
And generally the
kind of JavaScript
that causes this problem is
some JavaScript that has a loop
and this is very common.
And the loop does
two things generally.
There'll be a call to one of
these geometry related functions
like offsetHeight and that's the
point at which the browser has
to make sure the
layouts up to date.
Now the first line through
the loop you're probably fine.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
But then later on in the loop
it's common for the JavaScript
to invalidate style
by changing something
like you know
food.style.something=whatever.
So that invalidation
means that next time
through the loop the
offsetHeight is going to have
to recompute all the
layout information
and that's what gets expensive.
So this is not good.
Now generally the way you fix
this is to break this work
into two separate loops.
First you should fetch all
the geometry information
and then you can batch all of
the style changing later on
and that setup should
just result in one layout,
which will be much
more efficient.
Now the second issue we
see fairly often is pages
that paint too much and there's
one very common cause of this
which is people leaving
animated images
in their background style.
It's quite common for pages
to have an animated image
in the background style and
then load stuff on top of it
and they assume because the
animated image is covered
up the browser is going to
be able to stop painting it.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Well in many cases we can.
We've done a lot of
ultimizations to detect
when animated images are covered
up so we don't have
to animate them.
But we can't detect
all the cases.
So the best advice I have
for you is to make sure
that you don't leave
these animating images
in your style once you
finish loading a content.
This is especially true on pages
that have the infinite
scrolling model.
We see pages where there's
an animated spin and right
at the bottom it's
normally off the screen
and even this can cause
painting to happen for reasons
that I'll mention later on.
But of course the
real way to tell
if you're painting too much is
to look in the web inspector.
And if you see in the layout
and rendering timeline a lot
of painting like this, it
means you've probably got one
of those animated images sitting
on your page so you need to go
and find it and remove it.
So I've talked about a
couple of common mistakes
but there's a more
general theme here which is
to use the right
tool for the job.
If you're doing animation,
CSS transitions
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
and animations are always going
to be more efficient
then running animations
in JavaScript.
And if you need to do
animations in JavaScript,
requestAnimationFrame is
much better than using timers
because we can pause it
and it gets the right
display update frequency.
There are many other cases
where you might be running
JavaScript right now
but you could be using something
that's built into the browser.
If you're doing responsive
layouts that change depending
on window size, for example,
you should be using CSS
Media Queries instead
of running JavaScript.
And there are a number of new
layout models in WebKit now
which Beth talked about
yesterday in the session
on What's New in
Safari and WebKit.
For example, CSS Flexible Boxes.
So you can use these to get
interesting layouts that change
as the page changes size
rather than running JavaScript.
And there's a bunch of other
features like this too.
Beth yesterday also
talked about CSS calc.
So this is another great
way where you can ditch
in JavaScript, use a feature
that's built into the browser
and automatically get better
power efficiency that way.
So I would like to take a moment
to talk about Safari Extensions.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
If you are a Safari Extension
Developer it's extremely
important that your
scripts don't fall into any
of these common pitfalls and
you make sure your scripts
are super-efficient.
And this is because your
extension can inject scripts
into every page the user visits.
There's something else that's
important for extensions
which is that extensions use the
page visibility API to make sure
that they're only doing work in
pages that the users can see.
There's also another reason
that extensions should
use page visibility.
You remember I talked about
the prerender state earlier
and how that's involved in the
browser preloading the top head?
Extensions need to be very
careful to not show UI
to the user for pages
that the user can't see.
So extensions need to check
document.hidden and only show UI
if the page is actually visible.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So I talked about two new
power saving features in Safari
and I've talked about how you
can make power efficient web
content and how to detect
where your content is less power
efficient then it should be.
But you might think
with all this talk
about saving power
they're actually going
to have worse performance,
maybe we need
to you know sit power
more carefully
and we won't be as good.
But in many ways,
or in some cases,
you can actually get
better performance
by using a different algorithm
or actually using
a new architecture
that makes better use of the
hardware and we've done this
with responsive scrolling
in Safari so I'd
like to talk about that now.
Now people love to scroll.
Now we all have iOS devices
where the scrolling
is buttery smooth
and now we have track pads on
our desks and on our notebooks.
You feel that really direct
connection with the web content
and you want the web content
to be really responsive
and scroll right away.
So it was very important for us
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
that Safari had extremely
responsive scrolling.
But to explain how this works
I need to step back a moment
and explain how scrolling
worked in the old days.
So it used to be that when
you scrolled the browser would
simply copy a chunk of
bits up in the window
and then it would paint the
exposed strip at the bottom.
So every scroll would be
copy paint, copy paint
and that all happened
synchronously
on the main thread.
But that also meant that if
the web page was busy doing
something like loading
or running JavaScript,
it means the web page
could block scrolling
and that would give a less
than great user experience.
So we knew we had to
get to a model in Safari
where we could scroll
without doing painting.
So I'll explain how
that works now.
Instead of painting the page
into the window we
actually paint the page
into a series of tiles.
And these tiles are actually
core animation layers
under the hood.
But it now means that we can
do scrolling not by painting
but simply by moving
those tiles around.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So the implementation is
actually just setting the
position of the layer
that contains the tiles
and that means scrolling is
just changing a layer position.
And that also means that
we're leveraging the GPU
because the window server is
compositing this layer tree
and that makes great
use of the hardware
so this is really efficient.
It does mean that we have to
keep some extra tiles around so
that we always have content
that is ready to scroll
into the window and that
ties back to that problem
with animated images
I talked about before
where an image might be off the
page but still causing painting
and that's because it might
be on one of these tiles
that we're keeping up to date
so we can scroll within anytime.
So that explains how we
can scroll without painting
but how do we scroll
in such a way
that the web page
can't interfere,
that can't block the scrolling?
And to explain that, let me
get a bit nerdy for a minute
and explain our Threading
model in Safari.
So of course there are two
processes involved that I talked
about the web content
process earlier.
But the user events initially
go to this Safari application.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
But Safari then sends
those events directly
to the web content process
and they're received
in the web content
process by a thread
that we call the event
thread that is always ready
to receive those events.
It's never blocked
by anything else.
And then the event thread
simply bounces those events
to what we call the
scrolling thread
and the scrolling thread is the
one that's changing the layer
position there.
And all this can happen even
if the main thread is busy
doing layout and painting.
So this means that we
can do scrolling even
when layouts happening or the
page is busy doing other stuff.
So this is what gives us our
really responsive scrolling.
Now this is all fine when the
page is simply you know block
of pixels that moves up and down
but there are various features
in CSS that makes scrolling
a little more complicated
than that.
So let me just quickly
explain some of those.
The first one is fixed position,
which I'm sure you're all
familiar with as web developers,
the position fix property.
And of course it's very
common, for example,
his social link aside,
that the author has styled
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
with position fixed and when
the page scrolls of course
that social link stays
in the same place.
Standard position
fixed behavior.
But the browser has to
understand this now and be able
to analyze the page and break
the page up into a series
of layers so it knows how
to move all those layers
around when you scroll and
keep the right behavior.
There's a second sort of special
scrolling behavior in CSS
and that's fixed backgrounds,
backgrounds that have the
background attachment fixed
style in them and these
are also quite common.
For example the author of this
page may have put a background
image on the page by starting
the body in this case.
And then they said they wanted
background detachment fixed.
And that means that when the
page scrolls the background
image actually stays in the
same place in the view port
so it's sort of the background
equivalent of position fixed
so it looks like this.
But again, this is a scrolling
behavior the browser now
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
understands and we do
that by breaking the page
into a series of layers.
And then we know how
to move those layers
around at the right time
when the user scrolls.
So it's this functionality
that allows us
to get really responsive
scrolling
on many more pages in Safari 7.
Now I need to say a little bit
more about fixed backgrounds.
When we looked at pages
that were using fixed
backgrounds we found
that by far the most
common case was for pages
to use fixed backgrounds
to style the page
background, normally the body.
So that's the case
that we optimized for.
And you can style the
page background either
by putting the fixed background
style on the HTML element
or the body element in your
CSS but you really want
to do both and not one of those.
But actually CSS 2.1 has
something to say here.
If we go and read CSS 2.1
in detail it does recommend
that you style the page
background by putting that style
on the body not on the HTML so
we recommend that you do that.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Now there's something else
when we're looking at scrolling
that we saw that was really
common and that is pages
that use scroll events to do
stuff, to move things around,
when to use the scrolling
the page.
But there's a problem
with scroll events
in that they're asynchronous.
Many browsers have
discovered that if you try
to run JavaScript at the same
time the user is scrolling,
that really impacts the users
experience in scrolling,
it really can make
scrolling less than stellar.
So a lot of browsers now send
scroll events asynchronously
to the page.
But there's one behavior that
we saw that was really common
where people were still
using these scroll events
which have this asynchronous
problem
to get what we call
sticky behavior
and I'll explain what that is.
Here are a couple of examples.
This is the Apple Store.
And if you watch that box
on the right hand side,
when the page scrolls that box
moves with the page for a bit
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
and then it sticks
to the top edge.
And as the page scrolls
back down it drops back
into its old location.
Here's another example
from Yelp.
If you take a look at the map on
the right hand side you'll see
that when the page scrolls
the map moves up with the page
but it sticks to the top.
And at a certain point the
content underneath the map is
pushing it up back
to the viewport.
You may also have noticed a
little jump at the beginning
where the map disappeared
under the edge slightly
before it came back down
and that's caused by the fact
that scroll events
are asynchronous
so the page only hears about
the scroll a little time
after it's happened.
So by using scroll events to get
this behavior you're never going
to get frame accurate
really smooth scrolling.
So we knew in Safari that we
had to make a declarative way
where authors could get this
behavior in CSS without having
to run any JavaScript.
And that's what we
call sticky position.
So this is a new value for the
position property called sticky
and because it's still in draw
form it has the WebKit prefix.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
And to be a little more
technical, sticky specifies
that an element takes
up space in the flow
but its position is
constrained by the viewport
or some scrollable ancestor
and the containing block.
What does that mean?
Well let me show
you a few pictures.
So here's a page with a
sticky element, the red one,
which is simply positioned
or simply inflow
after the green box.
So at this point the
element is behaving just
like position relative would.
So when you scroll the page
the sticky element just scrolls
up with the page.
But now the sticky element has
hit the edge of the viewport
so it starts behaving more like
position fixed at this point.
And so as the page scrolls
it stays in the same place.
But if you scroll a bit more the
sticky element hits the bottom
edge of its containing
block and so if you continue
to scroll the bottom edge
of the containing block
is pushing the sticky item
out of the page.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
And so this is exactly
the behavior we saw
with the Yelp map.
So how do you get this behavior
now with this new property?
Well it's actually
just two lines of CSS.
You specify that you want the
sticky property for position
and then you have to
tell us which edge
of the viewport the sticky
thing is going to stick
to so top right from
the left and you have
to tell us how far away from
that edge you want to stick.
So in this case we're saying
we want to stick 10 pixels
from the top and that's all
we need to get that behavior.
So we've been pursuing position
sticky in the CSS working group
and they really loved it.
They were really enthusiastic.
And we hope to see it
in a future version
of the CSS positioning module.
And it's present in Safari
6.1 and 7 and on iOS 7 Mac.
So what should you
do to make sure
that your pages are getting the
best possible scrolling behavior
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
in Safari?
If you use fixed backgrounds
you should only use them
for the page background and
preferably on the body element.
Now I talked about scroll events
which is the most common scroll
event handler that we see
but there's one other scroll
event that people register
for which unfortunately
does force us to drop
out of our best scrolling mode
and that's the mousewheel event.
Now mousewheel doesn't have
this asynchronous nature.
People expect that
mousewheel events are delivered
synchronously with scrolling so
if we see a mouse event header
on the page we have to drop out
of our fastest scrolling mode
and get you know good scrolling
but not great scrolling.
And of course as I said, if
you're using scroll events
and if you're using them just
to get sticky behavior
then you should start using
position sticky.
Finally this is another
case where we optimize
for the most common case
that we saw which is
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
that almost all pages scroll by
just scrolling the main document
but there are a few pages
that scroll by putting a bunch
of content into overflow
scroll or iframes or frames.
And we didn't ultimize
for those yet.
So you should make sure that you
simply use main document scroll
then and that's how your users
will have the best scrolling
experiences on your pages.
So at this point I'd like to
invite Tim back on the stage
to show us how we can take a
page that doesn't scroll as well
as it should and to
scroll really well.
[Applause]
>> Tim Horton: Alright so
my friends who just got back
from Bali sent me this website
full of their photos and noted
to me that scrolling
didn't really seem as smooth
as they expected it to be
and they knew that I worked
on WebKit so they wanted to
see if there was anything
that they could do
to make it better.
So here's that site.
And let's just scroll
it for a minute
and see what it looks like.
So you might be able to
see up here on the screen
that scrolling is
kind of chunky.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
It's not really super smooth
and I can really, really feel it
with my fingers down
on the track pad.
So let's see if we can figure
out why this is happening.
Let's go to the develop menu
and show the web inspector.
And let's record a layout
remembering timeline while we're
scrolling and just see
what it looks like.
So as we scroll along here
we're painting constantly
and that's not good.
That consumes power and it
obviously doesn't perform well.
So let's go back and look at the
page and see if we can figure
out why this is happening.
You may remember, and Simon
mentions to me quite often,
that fixed backgrounds
can often drop us out of,
or can in some cases drop
us out of fast scrolling.
So you notice that this page
back here has a fixed background
so let's look at the code and
see if that's what's happening.
[Noise]
So as I look through
the code I notice
in fact they do have a fixed
background on an element
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
that is not a root element
and not the body
[inaudible] tells them to.
So let's just move
that up in the code,
up to the body, and
see what happens.
Woe, scrolling is
perfectly smooth now.
Let's go back into
the web inspector
and see what it looks like now.
We'll record another
timeline and take a look.
And you'll notice now
as we scroll there's really
not much painting happening.
That's excellent.
So let's look at the
JavaScript timeline,
which we notice has lots
of stuff going on in it
for some reason and it looks
like this page is using
scroll events for some reason.
You can see all of
these scroll events.
Let's go back and look
at the page again and see
if we can maybe determine why it
would be doing that since waking
up for JavaScript isn't
great for power either.
So you'll notice here at the
top of the page, scroll down,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
see the map and post section
there, it's sticking to the top.
You can see that, oops.
So let's look at the page
and see what they might
be doing with JavaScript.
And you see here they
do actually have a bunch
of JavaScript that's just being
used to perform that sticky
and that's not great
because just
as I mentioned we now
have position sticky
so we can actually just delete
this JavaScript, get rid of all
of the JavaScript on the page.
Let's see what the page looks
like now just for kicks.
So you see now the
sidebar does not stick.
It's alright because
again we have sticky.
We can go and with two lines we
can apply to that same element,
we can apply sticky positioning
and tell it to stick to the top
and now let's refresh and scroll
and we get the same
behavior we had before,
right there very nice.
And let's look one more time
back in the develop menu
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
and show the web inspector
and record another
timeline while scrolling
and you'll see there's
nothing, no painting,
no JavaScript events, nothing.
This is fantastic for power.
It's fantastic for performance.
And so I hope this has shown
you at least some small things
that you can do to maybe make
your website scroll better
and use less power.
Thank you.
[Applause]
>> Simon Fraser: Thank you, Tim.
Once you've feel the scrolling
performance in Safari 7
in OS X Mavericks we think
you'll really love it.
And you should really make sure
that your pages get the
best possible scrolling
because your uses will notice
and they'll love your pages.
So let me wrap up.
So I talked about the two new
power saving features in Safari,
App Nap for Safari
Tabs and Power Saver,
that give us these
great power savings
and make Safari super
power efficient.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
And we've also talked
about responses scrolling,
which is not only a more
efficient way of scrolling
but really improves our
scrolling performance
in a way that you can feel.
But we also talked about
how web content also needs
to be ultimized.
So we want you to go back
and look at your web content
and take this advice to heart
so you should reduce the amount
of work you do in
background pages
by using the page
visibility API and also
to use requestAnimationFrame for
animations if you really need
to use JavaScript
for animations.
You should make sure that
you don't fall into any
of those common pitfalls that
we talk about in terms of laying
out too much and painting too
much and use the web inspector
to make sure that,
as Tim showed,
when you scroll your page
there's really no work.
The browser's just taking care
of everything under the hood.
And finally, as we said,
responsive scrolling is
really great, you'll love it.
And we think your
users will love it
when they see it on your pages.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So if you need more information
you contact John Geleynse
or a look at the
documentation on Safari.center.
If you're an extension
developer you should go
and read the updates
to the Safari extensions
development guide
because there are some important
points about changes in Safari 7
that you should go
and read about.
And finally, of course,
the Apple Developer forums are a
great place to go and get help.
Now there were a lot of
related sessions yesterday
and I encourage you to
go and watch the videos
if you haven't seen
those already.
There were two on
the web inspector.
There was one on New
Features in WebKit Safari
and then there were a couple
of more OS level sessions
about battery life
and actually coming
up next I think is
the App Nap talk
which will give you the
sort of system level view
of the App Nap feature
that I talked about.
Thank you very much.
[Applause]
[Silence]