-
Notifications
You must be signed in to change notification settings - Fork 3
/
index.html
753 lines (750 loc) · 28.9 KB
/
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
<!DOCTYPE html>
<html>
<head>
<meta name="generator" content=
"HTML Tidy for HTML5 (experimental) for Mac OS X https://github.com/w3c/tidy-html5/tree/c63cc39">
<title>
Wake Lock: Use cases
</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<script src='https://www.w3.org/Tools/respec/respec-w3c-common' class=
'remove'>
</script>
<script class='remove'>
// (this is to make tidy happy)
var respecConfig = {
specStatus: "ED",
shortName: "wake-lock-use-cases",
//publishDate: "2014-04-03",
//previousPublishDate: "2014-02-20",
//previousMaturity: "",
edDraftURI: "https://w3c-webmob.github.io/wake-lock-use-cases/",
editors: [{
name: "Marcos Caceres",
company: "Mozilla"
}, {
name: "Natasha Rooney",
company: "GSMA"
}, {
name: "Dominique Hazael-Massieux",
company: "W3C"
}],
wg: 'Web and Mobile Interest Group',
wgURI: 'http://www.w3.org/Mobile/IG/',
wgPublicList: 'public-web-mobile',
charterDisclosureURI: 'http://www.w3.org/2013/07/webmobile-ig-charter.html',
wgPatentURI: '',
otherLinks: [{
key: 'Repository',
data: [{
value: 'We are on Github.',
href: 'https://github.com/w3c-webmob/wake-lock-use-cases'
}, {
value: 'File a bug.',
href: 'https://github.com/w3c-webmob/wake-lock-use-cases/issues/'
}, {
value: 'Commit history.',
href: 'https://github.com/w3c-webmob/wake-lock-use-cases/commits/'
}]
}]
};
</script>
<style>
/*(this is to make tidy happy)*/
figure{
margin: 0;
text-align:center;
width: 100%;
}
figure > img {
border: thin solid black;
}
img.stretchy{
width: 100%;
height: auto;
max-width: -moz-min-content;
max-width: -webkit-min-content;
max-width: -ms-min-content;
max-width: min-content;
}
</style>
</head>
<body>
<section id="abstract">
<p>This document illustrates the use cases a mechanism to control the power-saving state of a device would enable on the Web platform.</p>
</section>
<section id="sotd">
<p>
This is a work in progress. You can contribute to this specification by
contributing additional use cases through our <a href=
"https://github.com/w3c-webmob/wake-lock-use-cases">GitHub
repository</a>.
</p>
</section>
<section>
<h2>Introduction</h2>
<p>
The web platform currently lacks a means to prevent a device from
entering a power-saving state (i.e., some means that prevents an aspect
of the system from "going to sleep"). As this document tries to
demonstrate, there are strong use cases where applications need to
temporarily prevent some aspect of the device from entering a
power-saving state. As this functionality is common on other platforms,
this document tries to make the case that this functionality should
also be made available to the web platform.
</p>
<p>
To understand the use cases that motivate an application from
preventing a device from entering a power-saving state we examined a
set of applications running on iOS, Android, and Firefox OS. By looking
at these native applications, and the conditions under which they
prevent a device from entering a power-saving state, we've come up with
a set of <a>requirements</a>. We hope that the web community can
address these requirements by creating a specification that affords
similar functionality to the web platform.
</p>
</section>
<section>
<h2>
Why we need wake locks on the Web
</h2>
<p>
For tasks such as watching a video full-screen user agents have, for a
long time now, prevented the screen from going to sleep automatically.
Naturally users would be annoyed if they were watching a movie and
constantly needed to move a mouse or poke at the screen of a device to
stop the screen from going black.
</p>
<section>
<h3>
Keeping the screen awake
</h3>
<p>
As computing devices have become increasingly mobile, users find
themselves relying on mobile devices to assist them with everyday
tasks where it is not always practical or desirable to touch the
device to keep it "awake". These can be simple things, like using the
phone as a flashlight, to complex tasks such as cooking or allowing
the phone to navigate us to some destination while driving. In
addition, touch enabled devices have become commodified enough that
they can be used as interactive signage. Some of these use cases are
illustrated in the figure below.
</p>
<figure>
<img src="images/contexts.jpg" alt="" class="stretchy" width="800"
height="154">
<figcaption>
Examples of tasks and contexts in which it is necessary to prevent
the screen from going to sleep: from right to left, navigation from
one location to another, reading an e-book, preparing a meal by
following a recipe, and using a touch-enabled device as signage. In
this final case, the device displays if a particular meeting room
is booked - and allows users to manage booking a room by
interacting with the screen, which is always on.
</figcaption>
</figure>
</section>
<section>
<h3>
Keeping the system awake
</h3>
<p>
On the flip-side, keeping the screen on is not the only kind of "wake
lock" applications require. There is also use cases that there needs
to be a kind of lock that also keeps the system awake for a short
amount of time to allow arbitrary tasks to complete. It is well-known
that, under normal conditions, the display of a device consumes a
high percentage of a device's battery. Only keeping the screen on
when absolutely necessary can greatly assist in prolonging the
battery life of a device throughout a day.
</p>
<p>
As mobile devices are generally not as powerful as desktop class
machines, and because of the uncertain network conditions under which
these devices operate, some tasks can take a long time to complete -
longer than users are willing to wait before putting their device
back into their pocket, or sometimes longer than the default timeout
most devices have to put the screen to sleep (e.g., 1 minute or there
abouts). Consider importing and processing a set of contacts over a
slow network connection, which can sometimes take minutes. It would
be unrealistic to expect the user to hold their phone in their hand
while they wait for a synchronization task to complete: users should
be able to switch off the screen of a device and trust that an
application will do its best to complete a certain task in the
background.
</p>
<p>
In other words the user shutting off the screen doesn't mean that an
application must stop running and have the device enter a low-power
state. An application may need to intervene and finish what it's
doing or it risks data-loss. In another concrete example a user may
compose an email with a large photo attachment. When they press send
they just as quickly press the power button to turn off the screen
and put the phone back in their pocket. In such a situation, as
happens with iOS's mail app, the application continues to run by
sending the email before putting the phone in a low-power state. Note
that this system level lock includes keeping the WiFi and cellular
radio from also shutting down or entering a low power state while
some task is being performed.
</p>
</section>
<section>
<h3>
Potential for abuse
</h3>
<p>
As with any device API on the Web, there is the risk that this API
could be intentionally or unintentionally abused by developers.
</p>
<p>
Abuse cases include, but are not limited to:
</p>
<ul>
<li>Requesting a wake lock on the system and/or the screen and then
not releasing it. For the screen wake lock, if the user does not
themselves power off the screen, this would cause the user's battery
to be drained more quickly than normal. Likewise with the system
lock.
</li>
<li>Requesting a system lock, and then exploiting it to perform
computationally expensive operations without the user's consent while
the screen is off (e.g., mining crypto-currencies and sending results
to a server).
</li>
<li>Combining a system lock with an API like Geolocation could allow
an application to track a user while the screen is off: as the system
is not actually powered-down, it could continue sending information
to a server without the user's knowledge.
</li>
</ul>
<p>
Ultimately, it will be up to user agents to mitigate these sorts of
abuse cases. However, the design of the API may also assist in
mitigating certain kinds of abuses.
</p>
</section>
<section>
<h3>
Current workarounds
</h3>
<p>
To overcome the lack of support for wake locks in the platform, there
is <a href=
"https://code.google.com/p/chromium/issues/detail?id=386255">hearsay
evidence</a> that <q>…Developers are resorting to hacks like playing
hidden videos to prevent the screen from sleeping.</q>
</p>
</section>
</section>
<section>
<h2>
Methodology and limitations
</h2>
<p>
We used <a href=
"http://en.wikipedia.org/wiki/Accidental_sampling">convenience
sampling</a> to select the applications in this document. That is, we
selected applications that we knew prevented the screen from going to
sleep when the application was being used to perform some particular
action. We also reached out to people on twitter for suggestions as to
which applications we should look at. Suggestions were captured as
"<a href=
"https://github.com/w3c-webmob/screen-wake-use-cases/issues">issues</a>"
in our GitHub repository.
</p>As stated on Wikipedia's entry about <a href=
"http://en.wikipedia.org/wiki/Accidental_sampling">convenience
sampling</a>: "[one] cannot scientifically make generalizations about the
total population from this sample because it would not be representative
enough." Regardless, we view the range of applications we collected as
exemplifying common use cases for this kind of functionality. As such, we
still created requirements from what we observed across applications and
other platforms. We encourage the community to contribute more, or
contradictory/abuse-cases, examples to the Web and Mobile IG. You can do
this via a pull request to the <a href=
"https://github.com/w3c-webmob/screen-wake-use-cases">screen-wake
repository on GitHub</a>.
<section>
<h3>
How we looked at the apps
</h3>
<p>
For each application, we asked:
</p>
<ul>
<li>Why does the application need a wake lock?
</li>
<li>How does the user benefit?
</li>
<li>When is the wake lock applied and released?
</li>
</ul>
</section>
</section>
<section>
<h2>
Support on various platforms
</h2>
<p>
Below is a list of APIs from native platforms that developers can make
use of to prevent a device from going to sleep. The list shows that
this functionality is commonly available to developers without
significant security restrictions. It is important to note that some
platforms differentiate between putting the system to sleep and putting
the display to sleep. Requesting that the system (or "CPU") not sleep
while allowing the display to sleep allows long running tasks to
complete.
</p>
<dl>
<dt>
iOS:
</dt>
<dd>
<a href=
"https://developer.apple.com/library/ios/documentation/uikit/reference/UIApplication_Class/Reference/Reference.html#//apple_ref/occ/instp/UIApplication/idleTimerDisabled">
UIApplication.idleTimerDisabled</a>.
</dd>
<dt>
Android:
</dt>
<dd>
<a href=
"https://developer.android.com/training/scheduling/wakelock.html">Wake
locks</a>: supports both CPU and display.
</dd>
<dt>
Windows 8
</dt>
<dd>
<a href=
"http://msdn.microsoft.com/en-us/library/windowsphone/develop/ff941090%28v=vs.105%29.aspx">
Idle detection for Windows Phone 8</a>
</dd>
<dt>
FirefoxOS:
</dt>
<dd>
<a href=
"https://developer.mozilla.org/en-US/docs/Web/API/Navigator.requestWakeLock">
navigator.requestWakeLock()</a> - Supports "cpu", "display", and
"wifi".
</dd>
<dt>
ChromeApps:
</dt>
<dd>
<a href=
"https://developer.chrome.com/extensions/power.html">chrome.power.requestKeepAwake()</a>
- supporting both "system" and "display".
</dd>
<dt>
Tizen:
</dt>
<dd>
<a href=
"https://developer.tizen.org/dev-guide/2.2.0/org.tizen.web.device.apireference/tizen/power.html#requestidp102368">
tizen.power.request()</a>. Note that Tizen supports both "screen" and
"cpu".
</dd>
</dl>
</section>
<section>
<h2>
Brazil 2014
</h2>
<figure>
<img src="images/brazil2014.jpg" width="188" height="324" alt="" class=
"stretchy">
<figcaption>
The "Brazil 2014" iOS app allows users to keep up with news related
to the 2014 World Cup.
</figcaption>
</figure>
<dl>
<dt>
Why does the application need a wake lock?
</dt>
<dd>
The application allows users to follow football matches in real-time:
receiving updates as the match is taking place. Allowing users to
follow the action in real-time seems to be the primary motivator.
</dd>
<dt>
How does the user benefit?
</dt>
<dd>
During a match, the user can simply place their device on a surface
for convenient viewing - or they can simply hold the device in their
hand. Without requiring any user interaction light-weight textual and
graphical information is streamed to the device and automatically
displayed on screen. This means that the user does not need to
interact directly with the device to receive updates of what is
happening during a match. Note that this application does not stream
live video.
</dd>
<dt>
When is the wake lock applied and released?
</dt>
<dd>
The wake lock is only applied when the user opens the "action" screen
of the application and if, and only if, a match is actually taking
place. The wake lock is released as soon as match is finished.
</dd>
</dl>
</section>
<section>
<h2>
Google Play Books
</h2>
<figure>
<img class="stretchy" src="images/books.jpg" width="382" height="238"
alt="">
<figcaption>
Books on Google Play is an Android application for managing and
reading a collection of e-books.
</figcaption>
</figure>
<dl>
<dt>
Why does the application need a wake lock?
</dt>
<dd>
The Screen stays 'awake' allowing users to continue reading and
e-book without having to continuously poke at the screen to keep the
device awake.
</dd>
<dt>
How does the user benefit?
</dt>
<dd>
Allows a user the read at their own pace without needing to
constantly poke the screen to keep it awake. It also means that users
can keep their hands away from the screen until they need to turn the
page.
</dd>
<dt>
When is the wake lock applied and released?
</dt>
<dd>
The lock is applied only once the user opens a book. It is released
once the user returns to the book selection menu. Additionally, if
the user doesn't turn the page of a book after about 5 minutes, the
application automatically releases the wake lock.
</dd>
</dl>
<p>
Applications that exhibit similar behavior:
</p>
<ul>
<li>
<a href="http://fbreader.org/">FBReader</a> on Android is
configurable. It default is to only prevent sleep when battery level
is greater than 50%.
</li>
</ul>
</section>
<section>
<h2>
Nigela's quick collections
</h2>
<figure>
<img class="stretchy" src="images/nigela.jpg" width="205" height="292"
alt="">
<figcaption>
Nigela's quick collections is a cooking application that contains a
collection of recipes and a set of steps for how to prepare each
dish.
</figcaption>
</figure>
<dl>
<dt>
Why does the application need a wake lock?
</dt>
<dd>
Allowing users to prepare dishes while simultaneously interacting
with the recipes of the application (recipes are spread across
multiple screens and require multiple steps to be followed in order).
A user may find it annoying if the device went to sleep while they
were trying to follow a recipe (as this involves a lot of
multitasking on the side of the user, their hands might not be
available, or their hands might be dirty or wet).
</dd>
<dt>
How does the user benefit?
</dt>
<dd>
As the user's hands could be dirty or wet, the app keeps the screen
on and allows the user to navigate the application using voice
commands instead.
</dd>
<dt>
When is the wake lock applied and released?
</dt>
<dd>
The wake lock is only engaged when the user explicitly opens up a
recipe. When the user leaves a recipe and returns to, say, the main
screen, the wake lock is released.
</dd>
</dl>
<p>
Other applications that exhibit similar behavior:
</p>
<ul>
<li>
<a href=
"https://play.google.com/store/apps/details?id=com.mufumbo.android.recipe.search">
Allthecooks Recipes</a>.
</li>
</ul>
</section>
<section>
<h2>
Google Maps
</h2>
<figure>
<img src="images/maps.jpg" alt="" class="stretchy" height="336" width=
"192">
<figcaption>
Google maps on iOS is a popular maps application that supports turn
by turn navigation.
</figcaption>
</figure>
<dl>
<dt>
Why does the application need a wake lock?
</dt>
<dd>
Safety. Keeping the screen on allows the user to periodically glance
at their phone to get oriented while navigating to their destination.
On iOS, even if the user turns off the screen, the application
continues to run and notifies the when they need to make a turn.
</dd>
<dt>
How does the user benefit?
</dt>
<dd>
When in navigation mode, the screen will stay 'awake' so users do not
need to physically interact with the device when navigating.
</dd>
<dt>
When is the wake lock applied and released?
</dt>
<dd>
The screen lock is only applied when the user begins navigating. The
screen lock is released when the user stops navigating or reaches
their destination.
</dd>
</dl>
<p>
Other applications that exhibit similar behavior:
</p>
<ul>
<li>iOS Maps.
</li>
</ul>
</section>
<section>
<h2>
Xee Photo Viewer
</h2>
<figure>
<img src="images/photoviewer.jpg" alt="" class="stretchy" width="401"
height="228">
<figcaption>
Xee is an application for viewing images. It provides a "slideshow"
mode, which automatically presents a sequence of images in full
screen.
</figcaption>
</figure>
<dl>
<dt>
Why does the application need a wake lock?
</dt>
<dd>
The wake lock makes sure that the screen does not switch off in the
middle of the slideshow.
</dd>
<dt>
How does the user benefit?
</dt>
<dd>
Becasue the screen stays 'awake', users do not need to physically
interact with the device to ensure the slideshow continues running.
This leaves the user free to interact with others whom may also be
viewing the images in the same physical space.
</dd>
<dt>
When is the wake lock applied and released?
</dt>
<dd>
The screen lock is only applied when the user starts the slideshow.
The wake lock is released when the slideshow reaches the last photo
in a set or it gets interrupted by the user.
</dd>
</dl>
<p>
Other applications that exhibit similar behavior:
</p>
<ul>
<li>FastStone Image Viewer for Windows
</li>
<li>iPhoto
</li>
</ul>
</section>
<section>
<h2>
Firefox OS Contacts
</h2>
<figure>
<img src="images/contacts.jpg" alt="" width="192" height="288" class=
"stretchy">
<figcaption>
Firefox OS's contacts application allows a user to manage contacts,
as well as import friends from Facebook as contacts.
</figcaption>
</figure>
<dl>
<dt>
Why does the application need a wake lock?
</dt>
<dd>
To be able to import contacts and performing long-lived tasks without
requiring the user to keep the screen on.
</dd>
<dt>
How does the user benefit?
</dt>
<dd>
The user is assured that importing contacts will complete without
needing to keep the screen on. This conserves battery.
</dd>
<dt>
When is the wake lock applied and released?
</dt>
<dd>
When the import task is complete.
</dd>
</dl>
</section>
<section>
<h2>
Other applications
</h2>
<p>
A range of other applications benefit, or could benefit, from a wake
lock. For instance:
</p>
<ul>
<li>
<a href="http://g.co/cardboard">Google Cardboard</a> needs to stay
awake since there's no direct user interaction with the screen. It
would benefit from having wake-lock functionality. Mozilla's <a href=
"http://blog.bitops.com/blog/2014/06/26/first-steps-for-vr-on-the-web/">
VR efforts</a>, which rely on the Oculus Rift, would also benefit
from having this API.
</li>
<li>The "AliveECG - Heart Monitor" for Android is an application that
monitors single-channel electrocardiogram (ECG) rhythm strips. The
screen that shows the beats per minute prevents the screen from
locking. This allows patients and medical professionals to monitor the
ECG input in real-time without needing to worry that the device will go
to sleep.
</li>
<li>
<a href=
"https://play.google.com/store/apps/details?id=com.lander.hello&hl=en">
Magic The Gathering Counter</a> stays 'awake' for users to count
lives without having the screen dim while playing.
</li>
<li>iOS's compass application uses a wake lock to prevent the screen
from going to sleep. This allows users to orient themselves without
needing to worry that the screen will power off unexpectedly.
</li>
</ul>
</section>
<section>
<h2>
Requirements
</h2>
<p>
Any specification seeking to address these <dfn>requirements</dfn>:
</p>
<ol>
<li>MUST support requesting a wake lock on at least the
<strong>system</strong> and on the <strong>display</strong>. The
display being the screen or primary output modality of the device. The
system meaning that the device can put the screen to sleep, but MUST
continue running a task until an application releases the lock on the
system. Keeping the screen awake MUST (obviously) also keep the system
awake.
</li>
<li>MUST be extensible, allowing for new types of locks to be specified
in the future.
</li>
<li>MUST support a way for user agents to support wake locks only under
specific security conditions (e.g., only in full screen).
</li>
<li>MUST support a means for scripts to be notified if a request for a
lock fails to be granted. And if a lock request is not granted, the UA
SHOULD provide the developer and script with appropriate information
detailing why the request was not granted.
</li>
<li>MUST allow developers to release a previously requested wake lock.
</li>
<li>MAY allow scripts to check if an origin already holds a particular
kind of lock.
</li>
<li>MUST make the user agent ultimately responsible for managing the
wake locks. The user agent MAY automatically release the lock for
security reasons, and MUST release all locks when an application is
closed or navigated to another origin. It MAY also release the lock
after some amount of inactivity.
</li>
<li>MUST NOT be applied solely at the application level (e.g., just
once when the application starts). Instead, developers MUST be able to
request wake locks at arbitrary moments in time to help the user
complete a task.
</li>
<li>MUST be possible for the end user to override the wake lock on the
screen (e.g., by pressing the power button on the device, which puts
the screen to sleep). Applications SHOULD be notified when such a thing
happens, so they can request a system lock if they need to complete
some task.
</li>
<li>MAY allow a means to hint to the user agent how long a lock is to
be held (e.g., "keep the screen on for at least 5 mins").
</li>
<li>MUST allow the end user to have ultimate control over which
documents have control over a wake locks (e.g., not allowing a page to
get get any kind of wake lock without user opt-in).
</li>
</ol>
</section>
<section class="appendix">
<h2>
Observations
</h2>
<p>
The following are a set of related observation we made while we were
preparing this document.
</p>
<p>
In iOS, an orientation change can serve as a trigger to prevent the
screen from locking. However, this only works on apps that support
changes in orientation. This is not the case in Android, where changing
the orientation of the device has no effect - even if the application
supports multiple orientations.
</p>
</section>
<section class="appendix">
<h2>
Acknowledgments
</h2>
<p>
Huge thanks to Ehsan Akhgari, Seth Ladd, Ian Hickson, Boris Smus, Damon
Douglas, Ilya Bogdanovich, Tab Atkins, and Domenic Denicola.
</p>
</section>
</body>
</html>