summaryrefslogtreecommitdiff
path: root/Source/WebCore/Modules/modern-media-controls
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/Modules/modern-media-controls')
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/airplay-button.css29
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/airplay-button.js38
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/airplay-placard.js39
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/background-tint.css48
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/background-tint.js34
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/button.css34
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/button.js83
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/buttons-container.css29
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/buttons-container.js78
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/controls-bar.css35
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/controls-bar.js248
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/forward-button.js38
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/fullscreen-button.js39
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/icon-button.css33
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/icon-button.js134
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/icon-service.js102
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/invalid-placard.js37
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/ios-inline-media-controls.css132
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/ios-inline-media-controls.js109
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/layout-item.js53
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/layout-node.js307
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/macos-compact-inline-media-controls.css71
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/macos-fullscreen-media-controls.css119
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/macos-fullscreen-media-controls.js173
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/macos-inline-media-controls.css118
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/macos-inline-media-controls.js202
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/macos-media-controls.css51
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/macos-media-controls.js81
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/media-controls.css77
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/media-controls.js148
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/mute-button.js53
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/pip-button.js38
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/pip-placard.js38
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/placard.css70
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/placard.js48
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/play-pause-button.js53
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/rewind-button.js38
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/scheduler.js66
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/scrubber.js156
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/seek-button.js73
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/skip-back-button.js38
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/slider.css47
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/slider.js152
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/start-button.css58
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/start-button.js42
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/status-label.css37
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/status-label.js85
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/text-tracks.css82
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/time-control.js93
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/time-label.css33
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/time-label.js80
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/tracks-button.js38
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/tracks-panel.css105
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/tracks-panel.js305
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/volume-down-button.js38
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/volume-slider.css30
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/volume-slider.js120
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/volume-up-button.js38
-rw-r--r--Source/WebCore/Modules/modern-media-controls/gesture-recognizers/gesture-recognizer.js379
-rw-r--r--Source/WebCore/Modules/modern-media-controls/gesture-recognizers/pinch.js205
-rw-r--r--Source/WebCore/Modules/modern-media-controls/gesture-recognizers/tap.js109
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/iOS/airplay-placard@1x.pngbin0 -> 2945 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/iOS/airplay-placard@2x.pngbin0 -> 3210 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/iOS/airplay-placard@3x.pngbin0 -> 3553 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/iOS/airplay@1x.pngbin0 -> 330 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/iOS/airplay@2x.pngbin0 -> 362 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/iOS/enter-fullscreen@1x.pngbin0 -> 426 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/iOS/enter-fullscreen@2x.pngbin0 -> 653 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/iOS/enter-fullscreen@3x.pngbin0 -> 958 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/iOS/interval-skip-back@1x.pngbin0 -> 931 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/iOS/interval-skip-back@2x.pngbin0 -> 1705 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/iOS/interval-skip-back@3x.pngbin0 -> 2250 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/iOS/invalid-placard@1x.pngbin0 -> 1007 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/iOS/invalid-placard@2x.pngbin0 -> 1831 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/iOS/invalid-placard@3x.pngbin0 -> 2403 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/iOS/pause@1x.pngbin0 -> 1277 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/iOS/pause@2x.pngbin0 -> 1375 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/iOS/pause@3x.pngbin0 -> 1459 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/iOS/pip-in@1x.pngbin0 -> 351 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/iOS/pip-in@2x.pngbin0 -> 399 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/iOS/pip-in@3x.pngbin0 -> 487 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/iOS/pip-placard@1x.pngbin0 -> 2243 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/iOS/pip-placard@2x.pngbin0 -> 4106 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/iOS/pip-placard@3x.pngbin0 -> 6281 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/iOS/play@1x.pngbin0 -> 546 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/iOS/play@2x.pngbin0 -> 778 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/iOS/play@3x.pngbin0 -> 1013 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/iOS/slider-thumb@2x.pngbin0 -> 1475 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/iOS/start@1x.pngbin0 -> 592 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/iOS/start@2x.pngbin0 -> 1135 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/iOS/start@3x.pngbin0 -> 1545 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/airplay-fullscreen@1x.pngbin0 -> 294 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/airplay-fullscreen@2x.pngbin0 -> 431 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/airplay-placard@1x.pngbin0 -> 2945 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/airplay-placard@2x.pngbin0 -> 3210 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/airplay@1x.pngbin0 -> 330 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/airplay@2x.pngbin0 -> 362 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/enter-fullscreen-compact@1x.pngbin0 -> 263 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/enter-fullscreen-compact@2x.pngbin0 -> 560 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/enter-fullscreen@1x.pngbin0 -> 426 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/enter-fullscreen@2x.pngbin0 -> 653 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/exit-fullscreen@1x.pngbin0 -> 363 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/exit-fullscreen@2x.pngbin0 -> 506 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/forward@1x.pngbin0 -> 569 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/forward@2x.pngbin0 -> 969 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/interval-skip-back-compact@1x.pngbin0 -> 535 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/interval-skip-back-compact@2x.pngbin0 -> 1304 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/interval-skip-back@1x.pngbin0 -> 931 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/interval-skip-back@2x.pngbin0 -> 1705 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/invalid-placard@1x.pngbin0 -> 1007 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/invalid-placard@2x.pngbin0 -> 1831 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/media-selection-fullscreen@1x.pngbin0 -> 333 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/media-selection-fullscreen@2x.pngbin0 -> 484 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/media-selection@1x.pngbin0 -> 351 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/media-selection@2x.pngbin0 -> 581 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/pause-compact@1x.pngbin0 -> 1233 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/pause-compact@2x.pngbin0 -> 1307 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/pause-fullscreen@1x.pngbin0 -> 1277 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/pause-fullscreen@2x.pngbin0 -> 1375 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/pause@1x.pngbin0 -> 1275 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/pause@2x.pngbin0 -> 1373 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/pip-in-fullscreen@1x.pngbin0 -> 233 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/pip-in-fullscreen@2x.pngbin0 -> 387 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/pip-in@1x.pngbin0 -> 351 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/pip-in@2x.pngbin0 -> 399 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/pip-placard@1x.pngbin0 -> 2243 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/pip-placard@2x.pngbin0 -> 4106 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/play-compact@1x.pngbin0 -> 314 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/play-compact@2x.pngbin0 -> 545 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/play-fullscreen@1x.pngbin0 -> 546 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/play-fullscreen@2x.pngbin0 -> 778 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/play@1x.pngbin0 -> 546 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/play@2x.pngbin0 -> 778 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/rewind@1x.pngbin0 -> 595 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/rewind@2x.pngbin0 -> 881 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/scale-to-fill@1x.pngbin0 -> 362 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/scale-to-fill@2x.pngbin0 -> 342 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/scale-to-fit@1x.pngbin0 -> 205 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/scale-to-fit@2x.pngbin0 -> 206 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/start@1x.pngbin0 -> 592 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/start@2x.pngbin0 -> 1135 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/volume-compact@1x.pngbin0 -> 479 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/volume-compact@2x.pngbin0 -> 1093 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/volume-down-fullscreen@1x.pngbin0 -> 318 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/volume-down-fullscreen@2x.pngbin0 -> 623 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/volume-mute@1x.pngbin0 -> 936 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/volume-mute@2x.pngbin0 -> 1545 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/volume-up-fullscreen@1x.pngbin0 -> 577 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/volume-up-fullscreen@2x.pngbin0 -> 1250 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/volume@1x.pngbin0 -> 877 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/volume@2x.pngbin0 -> 2476 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/js-files63
-rw-r--r--Source/WebCore/Modules/modern-media-controls/main.js47
-rw-r--r--Source/WebCore/Modules/modern-media-controls/media/airplay-support.js60
-rw-r--r--Source/WebCore/Modules/modern-media-controls/media/controls-visibility-support.js77
-rw-r--r--Source/WebCore/Modules/modern-media-controls/media/fullscreen-support.js76
-rw-r--r--Source/WebCore/Modules/modern-media-controls/media/media-controller-support.js101
-rw-r--r--Source/WebCore/Modules/modern-media-controls/media/media-controller.js203
-rw-r--r--Source/WebCore/Modules/modern-media-controls/media/mute-support.js52
-rw-r--r--Source/WebCore/Modules/modern-media-controls/media/pip-support.js64
-rw-r--r--Source/WebCore/Modules/modern-media-controls/media/placard-support.js64
-rw-r--r--Source/WebCore/Modules/modern-media-controls/media/playback-support.js51
-rw-r--r--Source/WebCore/Modules/modern-media-controls/media/scrubbing-support.js79
-rw-r--r--Source/WebCore/Modules/modern-media-controls/media/seek-backward-support.js39
-rw-r--r--Source/WebCore/Modules/modern-media-controls/media/seek-forward-support.js39
-rw-r--r--Source/WebCore/Modules/modern-media-controls/media/seek-support.js77
-rw-r--r--Source/WebCore/Modules/modern-media-controls/media/skip-back-support.js54
-rw-r--r--Source/WebCore/Modules/modern-media-controls/media/start-support.js93
-rw-r--r--Source/WebCore/Modules/modern-media-controls/media/status-support.js59
-rw-r--r--Source/WebCore/Modules/modern-media-controls/media/time-labels-support.js51
-rw-r--r--Source/WebCore/Modules/modern-media-controls/media/tracks-support.js150
-rw-r--r--Source/WebCore/Modules/modern-media-controls/media/volume-down-support.js41
-rw-r--r--Source/WebCore/Modules/modern-media-controls/media/volume-support.js57
-rw-r--r--Source/WebCore/Modules/modern-media-controls/media/volume-up-support.js41
174 files changed, 7104 insertions, 0 deletions
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/airplay-button.css b/Source/WebCore/Modules/modern-media-controls/controls/airplay-button.css
new file mode 100644
index 000000000..3856c8db6
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/airplay-button.css
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+button.airplay.on {
+ background-color: -apple-wireless-playback-target-active !important;
+ mix-blend-mode: normal !important;
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/airplay-button.js b/Source/WebCore/Modules/modern-media-controls/controls/airplay-button.js
new file mode 100644
index 000000000..3b6c9ed30
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/airplay-button.js
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+class AirplayButton extends IconButton
+{
+
+ constructor(layoutDelegate)
+ {
+ super({
+ cssClassName: "airplay",
+ iconName: Icons.Airplay,
+ layoutDelegate
+ });
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/airplay-placard.js b/Source/WebCore/Modules/modern-media-controls/controls/airplay-placard.js
new file mode 100644
index 000000000..f167f48e7
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/airplay-placard.js
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+class AirplayPlacard extends Placard
+{
+
+ constructor(layoutDelegate)
+ {
+ super({
+ iconName: Icons.AirplayPlacard,
+ title: UIString("AirPlay"),
+ description: UIString("This video is playing on your Apple TV"),
+ layoutDelegate
+ });
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/background-tint.css b/Source/WebCore/Modules/modern-media-controls/controls/background-tint.css
new file mode 100644
index 000000000..7de98049f
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/background-tint.css
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2017 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+.background-tint {
+ pointer-events: none;
+}
+
+.background-tint,
+.background-tint > div {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+}
+
+.background-tint > .blur {
+ /* FIXME: we want to use the real System Dark treatment here, see <rdar://problem/19993961> */
+ background-color: rgba(30, 30, 30, 0.45);
+ -webkit-backdrop-filter: saturate(180%) blur(20px);
+}
+
+.background-tint > .tint {
+ background-color: rgb(41, 41, 41);
+ mix-blend-mode: lighten;
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/background-tint.js b/Source/WebCore/Modules/modern-media-controls/controls/background-tint.js
new file mode 100644
index 000000000..29df8eeb7
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/background-tint.js
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2017 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+class BackgroundTint extends LayoutNode
+{
+
+ constructor()
+ {
+ super(`<div class="background-tint"><div class="blur"></div><div class="tint"></div></div>`);
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/button.css b/Source/WebCore/Modules/modern-media-controls/controls/button.css
new file mode 100644
index 000000000..bf16ceff2
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/button.css
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+button {
+ position: absolute;
+ top: 0;
+ left: 0;
+ border: 0;
+ -webkit-appearance: none;
+ -webkit-user-select: none;
+ -webkit-tap-highlight-color: transparent;
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/button.js b/Source/WebCore/Modules/modern-media-controls/controls/button.js
new file mode 100644
index 000000000..6df091699
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/button.js
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+class Button extends LayoutItem
+{
+
+ constructor(layoutDelegate)
+ {
+ super({
+ element: "<button />",
+ layoutDelegate
+ });
+
+ this._enabled = true;
+
+ if (GestureRecognizer.SupportsTouches)
+ this._tapGestureRecognizer = new TapGestureRecognizer(this.element, this);
+ else
+ this.element.addEventListener("click", this);
+ }
+
+ // Public
+
+ get enabled()
+ {
+ return this._enabled;
+ }
+
+ set enabled(flag)
+ {
+ if (this._enabled === flag)
+ return;
+
+ this._enabled = flag;
+ if (this.layoutDelegate && typeof this.layoutDelegate.layout === "function")
+ this.layoutDelegate.layout();
+ }
+
+ // Protected
+
+ handleEvent(event)
+ {
+ if (event.type === "click" && event.currentTarget === this.element)
+ this._notifyDelegateOfActivation();
+ }
+
+ gestureRecognizerStateDidChange(recognizer)
+ {
+ if (this._tapGestureRecognizer === recognizer && recognizer.state === GestureRecognizer.States.Recognized)
+ this._notifyDelegateOfActivation();
+ }
+
+ // Private
+
+ _notifyDelegateOfActivation()
+ {
+ if (this._enabled && this.uiDelegate && typeof this.uiDelegate.buttonWasPressed === "function")
+ this.uiDelegate.buttonWasPressed(this);
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/buttons-container.css b/Source/WebCore/Modules/modern-media-controls/controls/buttons-container.css
new file mode 100644
index 000000000..23469b8f3
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/buttons-container.css
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+.buttons-container {
+ position: absolute;
+ height: 100%;
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/buttons-container.js b/Source/WebCore/Modules/modern-media-controls/controls/buttons-container.js
new file mode 100644
index 000000000..0af2602ef
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/buttons-container.js
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+class ButtonsContainer extends LayoutNode
+{
+
+ constructor({ buttons = [], leftMargin = 24, rightMargin = 24, buttonMargin = 24, cssClassName = "" } = {})
+ {
+ super(`<div class="buttons-container ${cssClassName}"></div>`);
+
+ this.buttons = buttons;
+ this.leftMargin = leftMargin;
+ this.rightMargin = rightMargin;
+ this.buttonMargin = buttonMargin;
+ }
+
+ // Public
+
+ get buttons()
+ {
+ return this._buttons;
+ }
+
+ set buttons(buttons)
+ {
+ if (!Array.isArray(buttons))
+ return;
+
+ this._buttons = buttons;
+ this.needsLayout = true;
+ }
+
+ layout()
+ {
+ super.layout();
+
+ const children = [];
+ let x = this.leftMargin;
+
+ this._buttons.forEach(button => {
+ if (!button.enabled || button.dropped)
+ return;
+ button.x = x;
+ x += button.width + this.buttonMargin;
+ children.push(button);
+ });
+
+ if (children.length)
+ this.width = x - this.buttonMargin + this.rightMargin;
+ else
+ this.width = this.buttonMargin + this.rightMargin;
+
+ this.children = children;
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/controls-bar.css b/Source/WebCore/Modules/modern-media-controls/controls/controls-bar.css
new file mode 100644
index 000000000..e72aea9cc
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/controls-bar.css
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+.controls-bar {
+ position: absolute;
+ transition: opacity 0.1s linear;
+}
+
+.controls-bar.faded {
+ opacity: 0;
+ pointer-events: none;
+ transition-duration: 0.25s;
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/controls-bar.js b/Source/WebCore/Modules/modern-media-controls/controls/controls-bar.js
new file mode 100644
index 000000000..25146b833
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/controls-bar.js
@@ -0,0 +1,248 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+class ControlsBar extends LayoutNode
+{
+
+ constructor(mediaControls)
+ {
+ super(`<div class="controls-bar"></div>`);
+
+ this._translation = new DOMPoint;
+ this._mediaControls = mediaControls;
+
+ if (GestureRecognizer.SupportsTouches)
+ this._tapGestureRecognizer = new TapGestureRecognizer(this._mediaControls.element, this);
+
+ this.autoHideDelay = ControlsBar.DefaultAutoHideDelay;
+
+ this.fadesWhileIdle = false;
+ this.userInteractionEnabled = true;
+ }
+
+ // Public
+
+ get translation()
+ {
+ return new DOMPoint(this._translation.x, this._translation.y);
+ }
+
+ set translation(point)
+ {
+ if (this._translation.x === point.x && this._translation.y === point.y)
+ return;
+
+ this._translation = new DOMPoint(point.x, point.y);
+ this.markDirtyProperty("translation");
+ }
+
+ get userInteractionEnabled()
+ {
+ return this._userInteractionEnabled;
+ }
+
+ set userInteractionEnabled(flag)
+ {
+ if (this._userInteractionEnabled === flag)
+ return;
+
+ this._userInteractionEnabled = flag;
+ this.markDirtyProperty("userInteractionEnabled");
+ }
+
+ get fadesWhileIdle()
+ {
+ return this._fadesWhileIdle;
+ }
+
+ set fadesWhileIdle(flag)
+ {
+ if (this._fadesWhileIdle == flag)
+ return;
+
+ this._fadesWhileIdle = flag;
+
+ if (GestureRecognizer.SupportsTouches)
+ this._tapGestureRecognizer.enabled = flag;
+ else {
+ if (flag) {
+ this._mediaControls.element.addEventListener("mousemove", this);
+ this._mediaControls.element.addEventListener("mouseleave", this);
+ this.element.addEventListener("mouseenter", this);
+ this.element.addEventListener("mouseleave", this);
+ } else {
+ this._mediaControls.element.removeEventListener("mousemove", this);
+ this._mediaControls.element.removeEventListener("mouseleave", this);
+ this.element.removeEventListener("mouseenter", this);
+ this.element.removeEventListener("mouseleave", this);
+ }
+ }
+
+ if (flag && !this.faded)
+ this._resetAutoHideTimer(false);
+ else if (!flag)
+ this.faded = false;
+ }
+
+ get visible()
+ {
+ return super.visible;
+ }
+
+ set visible(flag)
+ {
+ if (this.visible === flag)
+ return;
+
+ // If we just got made visible again, let's fade the controls in.
+ if (flag && !this.visible)
+ this.faded = false;
+ else if (!flag)
+ this._cancelNonEnforcedAutoHideTimer();
+
+ super.visible = flag;
+
+ this._mediaControls.controlsBarVisibilityDidChange(this);
+ }
+
+ get faded()
+ {
+ return !!this._faded;
+ }
+
+ set faded(flag)
+ {
+ if (this._faded === flag)
+ return;
+
+ this._faded = flag;
+ if (!flag)
+ this._resetAutoHideTimer(true);
+ else
+ delete this._enforceAutoHideTimer;
+
+ this.markDirtyProperty("faded");
+ }
+
+ // Protected
+
+ handleEvent(event)
+ {
+ if (event.currentTarget === this._mediaControls.element) {
+ if (event.type === "mousemove") {
+ this.faded = false;
+ this._resetAutoHideTimer(true);
+ } else if (event.type === "mouseleave" && this._fadesWhileIdle && !this._enforceAutoHideTimer)
+ this.faded = true;
+ } else if (event.currentTarget === this.element) {
+ if (event.type === "mouseenter") {
+ this._disableAutoHiding = true;
+ this._cancelNonEnforcedAutoHideTimer();
+ } else if (event.type === "mouseleave") {
+ delete this._disableAutoHiding;
+ this._resetAutoHideTimer(true);
+ } else if (event.type === "focus")
+ this.faded = false;
+ }
+ }
+
+ gestureRecognizerStateDidChange(recognizer)
+ {
+ if (this._tapGestureRecognizer !== recognizer || recognizer.state !== GestureRecognizer.States.Recognized)
+ return;
+
+ if (this.faded)
+ this.faded = false;
+ else {
+ let ancestor = this.element.parentNode;
+ while (ancestor && !(ancestor instanceof ShadowRoot))
+ ancestor = ancestor.parentNode;
+
+ const shadowRoot = ancestor;
+ if (!shadowRoot)
+ return;
+
+ const tapLocation = recognizer.locationInClient();
+ const tappedElement = shadowRoot.elementFromPoint(tapLocation.x, tapLocation.y);
+ if (!this.element.contains(tappedElement))
+ this.faded = true;
+ }
+ }
+
+ commitProperty(propertyName)
+ {
+ if (propertyName === "translation")
+ this.element.style.transform = `translate(${this._translation.x}px, ${this._translation.y}px)`;
+ else if (propertyName === "faded")
+ this.element.classList.toggle("faded", this.faded);
+ else if (propertyName === "userInteractionEnabled")
+ this.element.style.pointerEvents = this._userInteractionEnabled ? "all" : "none";
+ else
+ super.commitProperty(propertyName);
+ }
+
+ // Private
+
+ _cancelNonEnforcedAutoHideTimer()
+ {
+ if (!this._enforceAutoHideTimer)
+ this._cancelAutoHideTimer();
+
+ }
+
+ _cancelAutoHideTimer()
+ {
+ window.clearTimeout(this._autoHideTimer);
+ delete this._autoHideTimer;
+ }
+
+ _resetAutoHideTimer(cancelable)
+ {
+ if (cancelable && this._enforceAutoHideTimer)
+ return;
+
+ this._cancelAutoHideTimer();
+
+ if (cancelable)
+ delete this._enforceAutoHideTimer;
+ else
+ this._enforceAutoHideTimer = true;
+
+ this._autoHideTimer = window.setTimeout(this._autoHideTimerFired.bind(this), this.autoHideDelay);
+ }
+
+ _autoHideTimerFired()
+ {
+ delete this._enforceAutoHideTimer;
+ if (this._disableAutoHiding)
+ return;
+
+ this._cancelAutoHideTimer();
+ this.faded = this._fadesWhileIdle;
+ }
+
+}
+
+ControlsBar.DefaultAutoHideDelay = 4000;
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/forward-button.js b/Source/WebCore/Modules/modern-media-controls/controls/forward-button.js
new file mode 100644
index 000000000..5a21221d2
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/forward-button.js
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+class ForwardButton extends SeekButton
+{
+
+ constructor(layoutDelegate)
+ {
+ super({
+ cssClassName: "forward",
+ iconName: Icons.Forward,
+ layoutDelegate
+ });
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/fullscreen-button.js b/Source/WebCore/Modules/modern-media-controls/controls/fullscreen-button.js
new file mode 100644
index 000000000..9c7bfd792
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/fullscreen-button.js
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+class FullscreenButton extends IconButton
+{
+
+ constructor(layoutDelegate)
+ {
+ super({
+ cssClassName: "fullscreen",
+ layoutDelegate
+ });
+
+ this.iconName = this.layoutTraits & LayoutTraits.Fullscreen ? Icons.ExitFullscreen : Icons.EnterFullscreen;
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/icon-button.css b/Source/WebCore/Modules/modern-media-controls/controls/icon-button.css
new file mode 100644
index 000000000..465f5d367
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/icon-button.css
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+button.icon {
+ -webkit-mask-repeat: no-repeat;
+}
+
+button.icon:active,
+button.icon.on {
+ background-color: white !important;
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/icon-button.js b/Source/WebCore/Modules/modern-media-controls/controls/icon-button.js
new file mode 100644
index 000000000..53e8c3b73
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/icon-button.js
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+class IconButton extends Button
+{
+
+ constructor({ layoutDelegate = null, cssClassName = "", iconName = "" } = {})
+ {
+ super(layoutDelegate);
+
+ this.element.classList.add("icon");
+ if (!!cssClassName)
+ this.element.classList.add(cssClassName);
+
+ this._image = null;
+ this._iconName = "";
+ this._iconLayoutTraits = LayoutTraits.Unknown;
+
+ if (!!iconName)
+ this.iconName = iconName;
+ }
+
+ // Public
+
+ get iconName()
+ {
+ return this._iconName;
+ }
+
+ set iconName(iconName)
+ {
+ if (this._iconName === iconName)
+ return;
+
+ this._loadImage(iconName);
+ }
+
+ get on()
+ {
+ return this.element.classList.contains("on");
+ }
+
+ set on(flag) {
+ this.element.classList.toggle("on", flag);
+ }
+
+ layoutTraitsDidChange()
+ {
+ if (this._iconLayoutTraits !== this.layoutTraits)
+ this._loadImage(this._iconName);
+ }
+
+ // Protected
+
+ handleEvent(event)
+ {
+ if (event.type === "load" && event.target === this._image)
+ this._imageDidLoad();
+ else
+ super.handleEvent(event);
+ }
+
+ layout()
+ {
+ super.layout();
+
+ this.element.style.webkitMaskImage = `url(${this._image.src})`;
+ this.element.style.webkitMaskSize = `${this.width}px ${this.height}px`;
+ }
+
+ // Private
+
+ _loadImage(iconName)
+ {
+ if (this._image)
+ this._image.removeEventListener("load", this);
+
+ this._iconLayoutTraits = this.layoutTraits;
+ this._image = iconService.imageForIconNameAndLayoutTraits(iconName, this._iconLayoutTraits);
+
+ this._iconName = iconName;
+
+ if (this._image.complete)
+ this._updateImage();
+ else
+ this._image.addEventListener("load", this);
+ }
+
+ _imageDidLoad()
+ {
+ this._image.removeEventListener("load", this);
+ this._updateImage();
+ }
+
+ _updateImage()
+ {
+ this.needsLayout = true;
+
+ const width = this._image.width / window.devicePixelRatio;
+ const height = this._image.height / window.devicePixelRatio;
+
+ if (this.width === width && this.height === height)
+ return;
+
+ this.width = width;
+ this.height = height;
+
+ if (this.layoutDelegate)
+ this.layoutDelegate.needsLayout = true;
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/icon-service.js b/Source/WebCore/Modules/modern-media-controls/controls/icon-service.js
new file mode 100644
index 000000000..417e9f51d
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/icon-service.js
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+const Icons = {
+ Airplay : "airplay",
+ AirplayPlacard : "airplay-placard",
+ EnterFullscreen : "enter-fullscreen",
+ EnterPiP : "pip-in",
+ ExitFullscreen : "exit-fullscreen",
+ Forward : "forward",
+ InvalidPlacard : "invalid-placard",
+ Pause : "pause",
+ PiPPlacard : "pip-placard",
+ Play : "play",
+ Rewind : "rewind",
+ ScaleToFill : "scale-to-fill",
+ ScaleToFit : "scale-to-fit",
+ SkipBack : "interval-skip-back",
+ Start : "start",
+ Tracks : "media-selection",
+ Volume : "volume",
+ VolumeDown : "volume-down",
+ VolumeMuted : "volume-mute",
+ VolumeUp : "volume-up"
+};
+
+const IconsWithFullscreenVariants = [Icons.Airplay, Icons.Tracks, Icons.Pause, Icons.EnterPiP, Icons.Play, Icons.VolumeDown, Icons.VolumeUp];
+const IconsWithCompactVariants = [Icons.Play, Icons.Pause, Icons.SkipBack, Icons.Volume, Icons.VolumeMuted, Icons.EnterFullscreen];
+
+const iconService = new class IconService {
+
+ constructor()
+ {
+ this.images = {};
+ }
+
+ // Public
+
+ imageForIconNameAndLayoutTraits(iconName, layoutTraits)
+ {
+ const [fileName, platform] = this._fileNameAndPlatformForIconNameAndLayoutTraits(iconName, layoutTraits);
+ const path = `${platform}/${fileName}.png`;
+
+ let image = this.images[path];
+ if (image)
+ return image;
+
+ image = this.images[path] = new Image;
+
+ if (this.mediaControlsHost)
+ image.src = "data:image/png;base64," + this.mediaControlsHost.base64StringForIconAndPlatform(fileName, platform);
+ else
+ image.src = `${this.directoryPath}/${path}`;
+
+ return image;
+ }
+
+ // Private
+
+ _fileNameAndPlatformForIconNameAndLayoutTraits(iconName, layoutTraits)
+ {
+ let platform;
+ if (layoutTraits & LayoutTraits.macOS)
+ platform = "macOS";
+ else if (layoutTraits & LayoutTraits.iOS)
+ platform = "iOS";
+ else
+ throw "Could not identify icon's platform from layout traits.";
+
+ if (layoutTraits & LayoutTraits.Fullscreen && IconsWithFullscreenVariants.includes(iconName))
+ iconName += "-fullscreen";
+ else if (layoutTraits & LayoutTraits.Compact && IconsWithCompactVariants.includes(iconName))
+ iconName += "-compact";
+
+ const fileName = `${iconName}@${window.devicePixelRatio}x`;
+
+ return [fileName, platform];
+ }
+
+};
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/invalid-placard.js b/Source/WebCore/Modules/modern-media-controls/controls/invalid-placard.js
new file mode 100644
index 000000000..18d3af29b
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/invalid-placard.js
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+class InvalidPlacard extends Placard
+{
+
+ constructor(layoutDelegate)
+ {
+ super({
+ iconName: Icons.InvalidPlacard,
+ layoutDelegate
+ });
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/ios-inline-media-controls.css b/Source/WebCore/Modules/modern-media-controls/controls/ios-inline-media-controls.css
new file mode 100644
index 000000000..ba683a9d3
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/ios-inline-media-controls.css
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* Controls bar */
+
+.media-controls.ios.inline > .controls-bar {
+ left: 0;
+ right: 0;
+ bottom: 0;
+ height: 50px;
+}
+
+.media-controls.ios.inline > .controls-bar:before {
+ content: "";
+ position: absolute;
+ left: 0;
+ top: 0;
+ width: 100%;
+ height: 100%;
+ -webkit-appearance: media-controls-light-bar-background;
+ will-change: transform;
+}
+
+:host(audio) .media-controls.ios.inline > .controls-bar:before {
+ -webkit-appearance: none;
+ background-color: rgb(212, 212, 212);
+}
+
+.media-controls.ios.inline .time-control {
+ position: absolute;
+}
+
+/* Buttons */
+
+.media-controls.ios.inline button {
+ background-color: black;
+}
+
+.media-controls.ios.inline button:active {
+ background-color: white;
+}
+
+.media-controls.ios.inline > .controls-bar button {
+ height: 100% !important;
+}
+
+/* Make right container flush to the right */
+
+.media-controls.ios.inline .buttons-container.right {
+ right: 0;
+}
+
+/* Buttons placement */
+
+.media-controls.ios.inline button.play-pause {
+ -webkit-mask-position-y: 12px;
+}
+
+.media-controls.ios.inline button.skip-back {
+ -webkit-mask-position-y: 10px;
+}
+
+.media-controls.ios.inline button.airplay {
+ -webkit-mask-position-y: 13px;
+}
+
+.media-controls.ios.inline button.pip {
+ -webkit-mask-position-y: 13px;
+}
+
+.media-controls.ios.inline button.fullscreen {
+ -webkit-mask-position-y: 13px;
+}
+
+/* Time labels */
+
+.media-controls.ios.inline .time-label {
+ top: 14.5px;
+ color: black;
+}
+
+/* Scrubber */
+
+.media-controls.ios.inline .scrubber.slider {
+ top: 13px;
+}
+
+.media-controls.ios.inline .scrubber.slider > div {
+ position: absolute;
+ top: 10px;
+ left: 2px;
+ right: 2px;
+ height: 3px;
+ border-radius: 1.5px;
+ background-color: rgba(0, 0, 0, 0.55);
+ mix-blend-mode: plus-darker;
+}
+
+.media-controls.ios.inline .scrubber.slider > input::-webkit-slider-thumb {
+ width: 15px;
+ height: 50px;
+ background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAiCAYAAABIiGl0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAxNpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMDE0IDc5LjE1Njc5NywgMjAxNC8wOC8yMC0wOTo1MzowMiAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iUGl4ZWxtYXRvciAyLjIiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6NTkwQ0UwODdBQTcxMTFFNEE5QTZGQTVGMjFBNkUxN0UiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6NTkwQ0UwODhBQTcxMTFFNEE5QTZGQTVGMjFBNkUxN0UiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo1OTBDRTA4NUFBNzExMUU0QTlBNkZBNUYyMUE2RTE3RSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo1OTBDRTA4NkFBNzExMUU0QTlBNkZBNUYyMUE2RTE3RSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/Pofoz4wAAAJGSURBVHjaxJfNTsJAEMfbLmoNaKIhIE18A+LReOXmi/gQnnwSLz6Fnjh4Id4kHElI5EDwpIkgImWdf/Nfs5ISCrR1kkmg7c5vZz/mw3WSy66oL7ojqqiQkPotOhGdaq1XGnMTvC+KlprN5kW9Xr8sFotnhUKhCsUHs9lsCB2NRu1Op3PfaDRa8vhDdCSqnQ0E3lX7/f6NGH7RCQXfYgzG0sZacthut6/G4/Gj3lAwFjZgKyn0qNfrXYdh+Ka3FNiALdhc6Sk+1CkL4Us997E0aXga5zmX3Y87vdVt9jTJnvPA/blJJZxEnbHwtJds8Mk6V2ZTAQMsAD1EJAQHpdSpk7GAARaYAPuISE5OQpYfeYwwmBeYrMhjZeJuHkKWAtj7B7DnJchQaQsylgvwHGktL6qwXsH0TE7NETw091gjiecFFtYzmNFSo3LICyysBzBxsI5xoWUJWllHL8lSfTnViFyTyGO4PhgMbrP2lgxNpnPAdBXkkBYDsg48lqbRDLrd7t18Pn9P21PYhG3zl8yoXsY+1zCjDEufgIxjMqPCHMm5wpdBBsVeQK2QpUzZs8dKsGY+SrG8DSxvj8j6DdOKHUPZhkO3KOiDBWiZDGW3MC4f7FPVYvKQyuE8YQvzFJMUcJg+qfit3YVKc4clqB8H3zAThWzmJmzstGN1fI511LVJXYzl7hZQ00F+2dA4sH3P7Am4a0zAeDklEODZYueoVgz+DS4rWk5tTdj2cmqt4tr9sccJFqyG3N4CGxrSu3AZ0MiPAAMAZrLkuVVmRJsAAAAASUVORK5CYII=");
+ background-repeat: no-repeat;
+ background-size: 15px 17px;
+ background-position: 0px 18px;
+ background-color: transparent;
+ box-shadow: none;
+ border: none;
+ -webkit-appearance: none !important;
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/ios-inline-media-controls.js b/Source/WebCore/Modules/modern-media-controls/controls/ios-inline-media-controls.js
new file mode 100644
index 000000000..963e22b01
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/ios-inline-media-controls.js
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+class IOSInlineMediaControls extends MediaControls
+{
+
+ constructor(options = {})
+ {
+ options.layoutTraits = LayoutTraits.iOS;
+
+ super(options);
+
+ this.element.classList.add("ios");
+ this.element.classList.add("inline");
+
+ this._leftContainer = new ButtonsContainer({
+ buttons: [this.playPauseButton, this.skipBackButton],
+ cssClassName: "left"
+ });
+
+ this._rightContainer = new ButtonsContainer({
+ buttons: [this.airplayButton, this.pipButton, this.fullscreenButton],
+ cssClassName: "right"
+ });
+
+ this.controlsBar.children = [this._leftContainer, this._rightContainer];
+
+ this._pinchGestureRecognizer = new PinchGestureRecognizer(this.element, this);
+ }
+
+ // Protected
+
+ gestureRecognizerStateDidChange(recognizer)
+ {
+ if (this._pinchGestureRecognizer !== recognizer)
+ return;
+
+ if (recognizer.state !== GestureRecognizer.States.Ended && recognizer.state !== GestureRecognizer.States.Changed)
+ return;
+
+ if (recognizer.scale > IOSInlineMediaControls.MinimumScaleToEnterFullscreen && this.delegate && typeof this.delegate.iOSInlineMediaControlsRecognizedPinchInGesture === "function")
+ this.delegate.iOSInlineMediaControlsRecognizedPinchInGesture();
+ }
+
+ // Public
+
+ layout()
+ {
+ super.layout();
+
+ // Reset dropped buttons.
+ for (let button of this._rightContainer.buttons)
+ delete button.dropped;
+
+ this._leftContainer.layout();
+ this._rightContainer.layout();
+
+ this.timeControl.width = this.width - this._leftContainer.width - this._rightContainer.width;
+
+ if (this.timeControl.isSufficientlyWide) {
+ this.controlsBar.insertBefore(this.timeControl, this._rightContainer);
+ this.timeControl.x = this._leftContainer.width;
+ } else {
+ this.timeControl.remove();
+ // Since we don't have enough space to display the scrubber, we may also not have
+ // enough space to display all buttons in the left and right containers, so gradually drop them.
+ for (let control of [this.airplayButton, this.pipButton, this.skipBackButton, this.fullscreenButton]) {
+ // Nothing left to do if the combined container widths is shorter than the available width.
+ if (this._leftContainer.width + this._rightContainer.width < this.width)
+ break;
+
+ // If the control was already not participating in layout, we can skip it.
+ if (!control.visible)
+ continue;
+
+ // This control must now be dropped.
+ control.dropped = true;
+
+ this._leftContainer.layout();
+ this._rightContainer.layout();
+ }
+ }
+ }
+
+}
+
+IOSInlineMediaControls.MinimumScaleToEnterFullscreen = 1.5;
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/layout-item.js b/Source/WebCore/Modules/modern-media-controls/controls/layout-item.js
new file mode 100644
index 000000000..a8d188a32
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/layout-item.js
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+const LayoutTraits = {
+ Unknown : 0,
+ macOS : 1 << 0,
+ iOS : 1 << 1,
+ Fullscreen : 1 << 2,
+ Compact : 1 << 3,
+ ReducedPadding : 1 << 4,
+ TightPadding : 1 << 5
+};
+
+class LayoutItem extends LayoutNode
+{
+
+ constructor({ element = null, layoutDelegate = null } = {})
+ {
+ super(element);
+
+ this.layoutDelegate = layoutDelegate;
+ }
+
+ // Public
+
+ get layoutTraits()
+ {
+ return (this.layoutDelegate && this.layoutDelegate.layoutTraits) || LayoutTraits.Unknown;
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/layout-node.js b/Source/WebCore/Modules/modern-media-controls/controls/layout-node.js
new file mode 100644
index 000000000..2a87f27d6
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/layout-node.js
@@ -0,0 +1,307 @@
+
+const dirtyNodes = new Set;
+const nodesRequiringChildrenUpdate = new Set;
+
+class LayoutNode
+{
+
+ constructor(stringOrElement)
+ {
+
+ if (!stringOrElement)
+ this.element = document.createElement("div");
+ else if (stringOrElement instanceof Element)
+ this.element = stringOrElement;
+ else if (typeof stringOrElement === "string" || stringOrElement instanceof String)
+ this.element = elementFromString(stringOrElement);
+
+ this._parent = null;
+ this._children = [];
+
+ this._x = 0;
+ this._y = 0;
+ this._width = 0;
+ this._height = 0;
+ this._visible = true;
+
+ this._needsLayout = false;
+ this._dirtyProperties = new Set;
+
+ this._pendingDOMManipulation = LayoutNode.DOMManipulation.None;
+ }
+
+ get x()
+ {
+ return this._x;
+ }
+
+ set x(x)
+ {
+ if (x === this._x)
+ return;
+
+ this._x = x;
+ this.markDirtyProperty("x");
+ }
+
+ get y()
+ {
+ return this._y;
+ }
+
+ set y(y)
+ {
+ if (y === this._y)
+ return;
+
+ this._y = y;
+ this.markDirtyProperty("y");
+ }
+
+ get width()
+ {
+ return this._width;
+ }
+
+ set width(width)
+ {
+ if (width === this._width)
+ return;
+
+ this._width = width;
+ this.markDirtyProperty("width");
+ }
+
+ get height()
+ {
+ return this._height;
+ }
+
+ set height(height)
+ {
+ if (height === this._height)
+ return;
+
+ this._height = height;
+ this.markDirtyProperty("height");
+ }
+
+ get visible()
+ {
+ return this._visible;
+ }
+
+ set visible(flag)
+ {
+ if (flag === this._visible)
+ return;
+
+ this._visible = flag;
+ this.markDirtyProperty("visible");
+ }
+
+ get needsLayout()
+ {
+ return this._needsLayout || this._pendingDOMManipulation !== LayoutNode.DOMManipulation.None || this._dirtyProperties.size > 0;
+ }
+
+ set needsLayout(flag)
+ {
+ if (this.needsLayout === flag)
+ return;
+
+ this._needsLayout = flag;
+ this._updateDirtyState();
+ }
+
+ get parent()
+ {
+ return this._parent;
+ }
+
+ get children()
+ {
+ return this._children;
+ }
+
+ set children(children)
+ {
+ while (this._children.length)
+ this.removeChild(this._children[0]);
+
+ for (let child of children)
+ this.addChild(child);
+ }
+
+ parentOfType(type)
+ {
+ let node = this;
+ while (node = node._parent) {
+ if (node instanceof type)
+ return node;
+ }
+ return null;
+ }
+
+ addChild(child, index)
+ {
+ child.remove();
+
+ if (index === undefined || index < 0 || index > this._children.length)
+ index = this._children.length;
+
+ this._children.splice(index, 0, child);
+ child._parent = this;
+
+ child._markNodeManipulation(LayoutNode.DOMManipulation.Addition);
+
+ return child;
+ }
+
+ insertBefore(newSibling, referenceSibling)
+ {
+ return this.addChild(newSibling, this._children.indexOf(referenceSibling));
+ }
+
+ insertAfter(newSibling, referenceSibling)
+ {
+ const index = this._children.indexOf(referenceSibling);
+ return this.addChild(newSibling, index + 1);
+ }
+
+ removeChild(child)
+ {
+ if (child._parent !== this)
+ return;
+
+ const index = this._children.indexOf(child);
+ if (index === -1)
+ return;
+
+ this._children.splice(index, 1);
+ child._parent = null;
+
+ child._markNodeManipulation(LayoutNode.DOMManipulation.Removal);
+
+ return child;
+ }
+
+ remove()
+ {
+ if (this._parent instanceof LayoutNode)
+ return this._parent.removeChild(this);
+ }
+
+ markDirtyProperty(propertyName)
+ {
+ const hadProperty = this._dirtyProperties.has(propertyName);
+ this._dirtyProperties.add(propertyName);
+
+ if (!hadProperty)
+ this._updateDirtyState();
+ }
+
+ commitProperty(propertyName)
+ {
+ const style = this.element.style;
+
+ switch (propertyName) {
+ case "x":
+ style.left = `${this._x}px`;
+ break;
+ case "y":
+ style.top = `${this._y}px`;
+ break;
+ case "width":
+ style.width = `${this._width}px`;
+ break;
+ case "height":
+ style.height = `${this._height}px`;
+ break;
+ case "visible":
+ style.display = this._visible ? "inherit" : "none";
+ break;
+ }
+ }
+
+ layout()
+ {
+ if (this._pendingDOMManipulation === LayoutNode.DOMManipulation.Removal) {
+ const parent = this.element.parentNode;
+ if (parent)
+ parent.removeChild(this.element);
+ }
+
+ for (let propertyName of this._dirtyProperties)
+ this.commitProperty(propertyName);
+
+ this._dirtyProperties.clear();
+
+ if (this._pendingDOMManipulation === LayoutNode.DOMManipulation.Addition)
+ nodesRequiringChildrenUpdate.add(this.parent);
+ }
+
+ // Private
+
+ _markNodeManipulation(manipulation)
+ {
+ this._pendingDOMManipulation = manipulation;
+ this._updateDirtyState();
+ }
+
+ _updateDirtyState()
+ {
+ if (this.needsLayout) {
+ dirtyNodes.add(this);
+ scheduler.scheduleLayout(performScheduledLayout);
+ } else {
+ dirtyNodes.delete(this);
+ if (dirtyNodes.size === 0)
+ scheduler.unscheduleLayout(performScheduledLayout);
+ }
+ }
+
+ _updateChildren()
+ {
+ let nextChildElement = null;
+ const element = this.element;
+ for (let i = this.children.length - 1; i >= 0; --i) {
+ let child = this.children[i];
+ let childElement = child.element;
+
+ if (child._pendingDOMManipulation === LayoutNode.DOMManipulation.Addition) {
+ element.insertBefore(childElement, nextChildElement);
+ child._pendingDOMManipulation = LayoutNode.DOMManipulation.None;
+ }
+
+ nextChildElement = childElement;
+ }
+ }
+
+}
+
+LayoutNode.DOMManipulation = {
+ None: 0,
+ Removal: 1,
+ Addition: 2
+};
+
+function performScheduledLayout()
+{
+ const previousDirtyNodes = Array.from(dirtyNodes);
+ dirtyNodes.clear();
+ previousDirtyNodes.forEach(node => {
+ node._needsLayout = false;
+ node.layout();
+ });
+
+ nodesRequiringChildrenUpdate.forEach(node => node._updateChildren());
+ nodesRequiringChildrenUpdate.clear();
+}
+
+function elementFromString(elementString)
+{
+ const element = document.createElement("div");
+ element.innerHTML = elementString;
+ return element.firstElementChild;
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/macos-compact-inline-media-controls.css b/Source/WebCore/Modules/modern-media-controls/controls/macos-compact-inline-media-controls.css
new file mode 100644
index 000000000..456d78957
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/macos-compact-inline-media-controls.css
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* Controls bar */
+
+.media-controls.mac.inline.compact > .controls-bar {
+ height: 25px;
+}
+
+/* Controls placement */
+
+.media-controls.mac.inline.compact button.play-pause {
+ -webkit-mask-position-y: 6px;
+}
+
+.media-controls.mac.inline.compact button.skip-back {
+ -webkit-mask-position-y: 6px;
+}
+
+.media-controls.mac.inline.compact .scrubber.slider {
+ top: 12px;
+}
+
+.media-controls.mac.inline.compact button.mute {
+ -webkit-mask-position-y: 6px;
+}
+
+.media-controls.mac.inline.compact button.fullscreen {
+ -webkit-mask-position-y: 6.5px;
+}
+
+/* Labels */
+
+.media-controls.mac.inline.compact .time-label,
+.media-controls.mac.inline.compact .status-label {
+ top: 5px;
+ font-size: 12px;
+}
+
+/* Volume slider */
+
+.media-controls.mac.inline.compact .volume-slider-container {
+ height: 21px;
+ transform: rotate(-90deg) translate(5px, -33px);
+}
+
+.media-controls.mac.inline.compact .volume.slider {
+ top: 5px;
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/macos-fullscreen-media-controls.css b/Source/WebCore/Modules/modern-media-controls/controls/macos-fullscreen-media-controls.css
new file mode 100644
index 000000000..2d7c9766b
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/macos-fullscreen-media-controls.css
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* Controls bar */
+
+.media-controls.mac.fullscreen {
+ --controls-bar-width: 468px;
+ --tracks-panel-right-margin: 40px;
+
+ width: 100% !important;
+ height: 100% !important;
+}
+
+.media-controls.mac.fullscreen > .controls-bar {
+ left: calc((100% - var(--controls-bar-width)) / 2);
+ bottom: 25px;
+ width: var(--controls-bar-width);
+ height: 75px;
+ overflow: hidden;
+}
+
+.media-controls.mac.fullscreen > .controls-bar > .background-tint > div {
+ border-radius: 8px;
+}
+
+/* Volume slider */
+
+.media-controls.mac.fullscreen .volume.slider {
+ left: 31px;
+ top: 19px;
+}
+
+.media-controls.mac.fullscreen:not(.uses-ltr-user-interface-layout-direction) .volume.slider {
+ transform: scaleX(-1);
+}
+
+.media-controls.mac.fullscreen button.volume-down {
+ -webkit-mask-position-y: 18px;
+}
+
+.media-controls.mac.fullscreen button.volume-up {
+ -webkit-mask-position-y: 17px;
+}
+
+/* Button containers */
+
+.media-controls.mac.fullscreen .buttons-container {
+ height: 44px;
+}
+
+.media-controls.mac.fullscreen .buttons-container.center {
+ left: 50%;
+ top: 0;
+ transform: translateX(-50%);
+}
+
+/* Buttons placement for center container */
+
+.media-controls.mac.fullscreen button.rewind {
+ -webkit-mask-position-y: 17px;
+}
+
+.media-controls.mac.fullscreen button.play-pause {
+ -webkit-mask-position-y: 12px;
+}
+
+.media-controls.mac.fullscreen button.forward {
+ -webkit-mask-position-y: 17px;
+}
+
+/* Make right container flush to the right */
+
+.media-controls.mac.fullscreen .buttons-container.right {
+ right: 0;
+}
+
+/* Buttons placement for right container */
+
+.media-controls.mac.fullscreen .buttons-container.right button {
+ -webkit-mask-position-y: 18px;
+}
+
+/* Scrubber */
+
+.media-controls.mac.fullscreen .time-control {
+ position: absolute;
+ left: 10px;
+ top: 48px;
+}
+
+.media-controls.mac.fullscreen .time-label {
+ font-size: 12px;
+}
+
+.media-controls.mac.fullscreen .scrubber {
+ top: -3px;
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/macos-fullscreen-media-controls.js b/Source/WebCore/Modules/modern-media-controls/controls/macos-fullscreen-media-controls.js
new file mode 100644
index 000000000..2bebd171c
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/macos-fullscreen-media-controls.js
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+const ButtonMarginForThreeButtonsOrLess = 24;
+const ButtonMarginForFourButtons = 16;
+const ButtonMarginForFiveButtons = 12;
+const FullscreenTimeControlWidth = 457;
+
+class MacOSFullscreenMediaControls extends MacOSMediaControls
+{
+
+ constructor(options = {})
+ {
+ options.layoutTraits = LayoutTraits.macOS | LayoutTraits.Fullscreen;
+
+ super(options);
+
+ this.element.classList.add("fullscreen");
+
+ // Set up fullscreen-specific buttons.
+ this.volumeDownButton = new VolumeDownButton(this);
+ this.volumeUpButton = new VolumeUpButton(this);
+ this.rewindButton = new RewindButton(this);
+ this.forwardButton = new ForwardButton(this);
+ this.fullscreenButton.isFullscreen = true;
+
+ this.volumeSlider.width = 60;
+
+ this._leftContainer = new ButtonsContainer({
+ buttons: [this.volumeDownButton, this.volumeSlider, this.volumeUpButton],
+ cssClassName: "left",
+ leftMargin: 12,
+ rightMargin: 0,
+ buttonMargin: 6
+ });
+
+ this._centerContainer = new ButtonsContainer({
+ buttons: [this.rewindButton, this.playPauseButton, this.forwardButton],
+ cssClassName: "center",
+ leftMargin: 27,
+ rightMargin: 27,
+ buttonMargin: 27
+ });
+
+ this._rightContainer = new ButtonsContainer({
+ buttons: [this.airplayButton, this.pipButton, this.tracksButton, this.fullscreenButton],
+ cssClassName: "right",
+ leftMargin: 12,
+ rightMargin: 12
+ });
+
+ this.controlsBar.children = [new BackgroundTint, this._leftContainer, this._centerContainer, this._rightContainer, this.timeControl];
+
+ this.element.addEventListener("mousedown", this);
+ }
+
+ // Public
+
+ showTracksPanel()
+ {
+ super.showTracksPanel();
+
+ const tracksButtonBounds = this.tracksButton.element.getBoundingClientRect();
+ this.tracksPanel.rightX = window.innerWidth - tracksButtonBounds.right;
+ this.tracksPanel.bottomY = window.innerHeight - tracksButtonBounds.top + 1;
+ }
+
+ // Protected
+
+ handleEvent(event)
+ {
+ switch (event.type) {
+ case "mousedown":
+ this._handleMousedown(event);
+ break;
+ case "mousemove":
+ this._handleMousemove(event);
+ break;
+ case "mouseup":
+ this._handleMouseup(event);
+ break;
+ }
+ }
+
+ layout()
+ {
+ super.layout();
+
+ const numberOfEnabledButtons = this._rightContainer.buttons.filter(button => button.enabled).length;
+
+ let buttonMargin = ButtonMarginForFiveButtons;
+ if (numberOfEnabledButtons === 4)
+ buttonMargin = ButtonMarginForFourButtons;
+ else if (numberOfEnabledButtons <= 3)
+ buttonMargin = ButtonMarginForThreeButtonsOrLess;
+
+ this._rightContainer.buttonMargin = buttonMargin;
+
+ this._centerContainer.layout();
+ this._rightContainer.layout();
+
+ this.timeControl.width = FullscreenTimeControlWidth;
+ }
+
+ // Private
+
+ _handleMousedown(event)
+ {
+ super.handleEvent(event);
+
+ if (event.target !== this.controlsBar.element)
+ return;
+
+ event.preventDefault();
+
+ this._lastDragPoint = this._pointForEvent(event);
+
+ this.element.addEventListener("mousemove", this, true);
+ this.element.addEventListener("mouseup", this, true);
+ }
+
+ _handleMousemove(event)
+ {
+ event.preventDefault();
+
+ const currentDragPoint = this._pointForEvent(event);
+
+ this.controlsBar.translation = new DOMPoint(
+ this.controlsBar.translation.x + currentDragPoint.x - this._lastDragPoint.x,
+ this.controlsBar.translation.y + currentDragPoint.y - this._lastDragPoint.y
+ );
+
+ this._lastDragPoint = currentDragPoint;
+ }
+
+ _handleMouseup(event)
+ {
+ event.preventDefault();
+
+ delete this._lastDragPoint;
+
+ this.element.removeEventListener("mousemove", this, true);
+ this.element.removeEventListener("mouseup", this, true);
+ }
+
+ _pointForEvent(event)
+ {
+ return new DOMPoint(event.clientX, event.clientY);
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/macos-inline-media-controls.css b/Source/WebCore/Modules/modern-media-controls/controls/macos-inline-media-controls.css
new file mode 100644
index 000000000..1c5892dc9
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/macos-inline-media-controls.css
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* Controls bar */
+
+.media-controls.mac.inline > .controls-bar {
+ left: 0;
+ bottom: 0;
+ width: 100%;
+ height: 50px;
+}
+
+.media-controls.mac.inline > .controls-bar > * {
+ position: absolute;
+}
+
+/* Start button blur */
+
+.media-controls.mac.inline > button.start > div {
+ -webkit-backdrop-filter: saturate(180%) blur(20px);
+}
+
+/* Controls placement */
+
+.media-controls.mac.inline button.play-pause {
+ -webkit-mask-position-y: 12px;
+}
+
+.media-controls.mac.inline button.skip-back {
+ -webkit-mask-position-y: 10px;
+}
+
+.media-controls.mac.inline .scrubber.slider {
+ top: 13px;
+}
+
+.media-controls.mac.inline button.mute {
+ -webkit-mask-position-y: 10px;
+}
+
+.media-controls.mac.inline button.airplay {
+ -webkit-mask-position-y: 13px;
+}
+
+.media-controls.mac.inline button.pip {
+ -webkit-mask-position-y: 13px;
+}
+
+.media-controls.mac.inline button.tracks {
+ -webkit-mask-position-y: 15px;
+}
+
+.media-controls.mac.inline button.fullscreen {
+ -webkit-mask-position-y: 13px;
+}
+
+/* Labels */
+
+.media-controls.mac.inline .time-label,
+.media-controls.mac.inline .status-label {
+ top: 14.5px;
+}
+
+/* Volume slider */
+
+.media-controls.mac.inline .volume-slider-container {
+ position: absolute;
+
+ bottom: 50px;
+ width: 81px;
+ height: 31px;
+ transform: rotate(-90deg) translate(25px, -25px);
+}
+
+.media-controls.mac.inline .volume-slider-container > .background-tint {
+ top: 0;
+ left: 1px;
+ right: 0;
+ bottom: 0px;
+ width: auto;
+}
+
+.media-controls.mac.inline .volume-slider-container > .background-tint > div {
+ border-radius: 0 4px 4px 0;
+}
+
+.media-controls.mac.inline .volume.slider {
+ left: 11px;
+ top: 10px;
+}
+
+/* Tracks Panel */
+
+.media-controls.mac.inline .tracks-panel {
+ bottom: 51px;
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/macos-inline-media-controls.js b/Source/WebCore/Modules/modern-media-controls/controls/macos-inline-media-controls.js
new file mode 100644
index 000000000..29e69705a
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/macos-inline-media-controls.js
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+class MacOSInlineMediaControls extends MacOSMediaControls
+{
+
+ constructor(options)
+ {
+ super(options);
+
+ this.element.classList.add("inline");
+
+ this.leftContainer = new ButtonsContainer({
+ buttons: [this.playPauseButton, this.skipBackButton],
+ cssClassName: "left"
+ });
+
+ this.rightContainer = new ButtonsContainer({
+ cssClassName: "right"
+ });
+
+ this._matchLayoutTraits();
+
+ this._backgroundTint = new BackgroundTint;
+
+ this._volumeSliderContainer = new LayoutNode(`<div class="volume-slider-container"></div>`);
+ this._volumeSliderContainer.children = [new BackgroundTint, this.volumeSlider];
+ this._volumeSliderContainer.visible = false;
+ this.volumeSlider.width = 60;
+
+ // Wire up events to display the volume slider.
+ this.muteButton.element.addEventListener("mouseenter", this);
+ this.muteButton.element.addEventListener("mouseleave", this);
+ this._volumeSliderContainer.element.addEventListener("mouseleave", this);
+
+ this.controlsBar.children = [this._backgroundTint, this.leftContainer, this.rightContainer, this._volumeSliderContainer];
+ }
+
+ // Public
+
+ get layoutTraits()
+ {
+ return this._layoutTraits;
+ }
+
+ set layoutTraits(layoutTraits)
+ {
+ if (this._layoutTraits === layoutTraits)
+ return;
+
+ this._layoutTraits = layoutTraits;
+ this._matchLayoutTraits();
+ }
+
+ layout()
+ {
+ super.layout();
+
+ if (!this.controlsBar.visible)
+ return;
+
+ // Reset dropped buttons.
+ this.rightContainer.buttons.concat(this.leftContainer.buttons).forEach(button => delete button.dropped);
+
+ this.leftContainer.layout();
+ this.rightContainer.layout();
+
+ const middleContainer = this.statusLabel.enabled ? this.statusLabel : this.timeControl;
+ this.controlsBar.children = [this._backgroundTint, this.leftContainer, middleContainer, this.rightContainer, this._volumeSliderContainer];
+
+ if (middleContainer === this.timeControl)
+ this.timeControl.width = this.width - this.leftContainer.width - this.rightContainer.width;
+
+ if (middleContainer === this.timeControl && this.timeControl.isSufficientlyWide)
+ this.timeControl.x = this.leftContainer.width;
+ else {
+ this.timeControl.remove();
+
+ let droppedControls = false;
+
+ // Since we don't have enough space to display the scrubber, we may also not have
+ // enough space to display all buttons in the left and right containers, so gradually drop them.
+ for (let button of [this.airplayButton, this.pipButton, this.tracksButton, this.muteButton, this.skipBackButton, this.fullscreenButton]) {
+ // Nothing left to do if the combined container widths is shorter than the available width.
+ if (this.leftContainer.width + this.rightContainer.width < this.width)
+ break;
+
+ droppedControls = true;
+
+ // If the button was already not participating in layout, we can skip it.
+ if (!button.visible)
+ continue;
+
+ // This button must now be dropped.
+ button.dropped = true;
+
+ this.leftContainer.layout();
+ this.rightContainer.layout();
+ }
+
+ // We didn't need to drop controls and we have status text to show.
+ if (!droppedControls && middleContainer === this.statusLabel) {
+ this.statusLabel.x = this.leftContainer.width;
+ this.statusLabel.width = this.width - this.leftContainer.width - this.rightContainer.width;
+ }
+ }
+
+ this.rightContainer.x = this.width - this.rightContainer.width;
+ this._volumeSliderContainer.x = this.rightContainer.x + this.muteButton.x;
+ }
+
+ showTracksPanel()
+ {
+ super.showTracksPanel();
+ this.tracksPanel.rightX = this.rightContainer.width - this.tracksButton.x - this.tracksButton.width;
+ }
+
+ // Protected
+
+ handleEvent(event)
+ {
+ super.handleEvent(event);
+ this._volumeSliderContainer.visible = event.type === "mouseenter" || event.relatedTarget === this._volumeSliderContainer.element;
+ }
+
+ controlsBarVisibilityDidChange(controlsBar)
+ {
+ if (controlsBar.visible)
+ this.layout();
+ }
+
+ // Private
+
+ _matchLayoutTraits()
+ {
+ if (!this.leftContainer || !this.rightContainer)
+ return;
+
+ const layoutTraits = this.layoutTraits;
+ if (layoutTraits & LayoutTraits.Compact) {
+ this.leftContainer.leftMargin = 8;
+ this.leftContainer.rightMargin = 12;
+ this.leftContainer.buttonMargin = 12;
+ this.rightContainer.leftMargin = 12;
+ this.rightContainer.rightMargin = 8;
+ this.rightContainer.buttonMargin = 12;
+ } else if (layoutTraits & LayoutTraits.TightPadding) {
+ this.leftContainer.leftMargin = 12;
+ this.leftContainer.rightMargin = 12;
+ this.leftContainer.buttonMargin = 12;
+ this.rightContainer.leftMargin = 12;
+ this.rightContainer.rightMargin = 12;
+ this.rightContainer.buttonMargin = 12;
+ } else if (layoutTraits & LayoutTraits.ReducedPadding) {
+ this.leftContainer.leftMargin = 12;
+ this.leftContainer.rightMargin = 16;
+ this.leftContainer.buttonMargin = 16;
+ this.rightContainer.leftMargin = 0;
+ this.rightContainer.rightMargin = 12;
+ this.rightContainer.buttonMargin = 16;
+ } else {
+ this.leftContainer.leftMargin = 24;
+ this.leftContainer.rightMargin = 24;
+ this.leftContainer.buttonMargin = 24;
+ this.rightContainer.leftMargin = 24;
+ this.rightContainer.rightMargin = 24;
+ this.rightContainer.buttonMargin = 24;
+ }
+
+ if (layoutTraits & LayoutTraits.Compact)
+ this.rightContainer.buttons = [this.muteButton, this.fullscreenButton];
+ else
+ this.rightContainer.buttons = [this.muteButton, this.airplayButton, this.pipButton, this.tracksButton, this.fullscreenButton];
+
+ this.leftContainer.buttons.forEach(button => button.layoutTraitsDidChange());
+ this.rightContainer.buttons.forEach(button => button.layoutTraitsDidChange());
+
+ this.element.classList.toggle("compact", layoutTraits & LayoutTraits.Compact);
+ }
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/macos-media-controls.css b/Source/WebCore/Modules/modern-media-controls/controls/macos-media-controls.css
new file mode 100644
index 000000000..bb16d9f9d
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/macos-media-controls.css
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2017 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* Buttons in controls bar */
+
+.media-controls.mac button:active {
+ background-color: white;
+}
+
+.media-controls.mac > .controls-bar button {
+ height: 100% !important;
+ background-color: rgba(255, 255, 255, 0.45);
+ mix-blend-mode: plus-lighter;
+}
+
+.media-controls.mac > .controls-bar .time-label {
+ color: rgba(255, 255, 255, 0.45);
+ mix-blend-mode: plus-lighter;
+}
+
+.media-controls.mac > .controls-bar .slider > canvas {
+ mix-blend-mode: plus-lighter;
+}
+
+.media-controls.mac > .controls-bar .slider > input::-webkit-slider-thumb {
+ width: 3px !important;
+ height: 100% !important;
+ -webkit-appearance: none !important;
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/macos-media-controls.js b/Source/WebCore/Modules/modern-media-controls/controls/macos-media-controls.js
new file mode 100644
index 000000000..85cb875a6
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/macos-media-controls.js
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+class MacOSMediaControls extends MediaControls
+{
+
+ constructor({ width, height, layoutTraits = LayoutTraits.macOS } = {})
+ {
+ super({ width, height, layoutTraits });
+
+ this.element.classList.add("mac");
+
+ this.muteButton = new MuteButton(this);
+ this.tracksButton = new TracksButton(this);
+ this.tracksPanel = new TracksPanel;
+ this.volumeSlider = new VolumeSlider;
+
+ this.element.addEventListener("mousedown", this);
+ this.element.addEventListener("click", this);
+ }
+
+ // Public
+
+ showTracksPanel()
+ {
+ this.tracksButton.on = true;
+ this.tracksButton.element.blur();
+ this.controlsBar.userInteractionEnabled = false;
+ this.tracksPanel.presentInParent(this);
+ }
+
+ hideTracksPanel()
+ {
+ this.tracksButton.on = false;
+ this.tracksButton.element.focus();
+ this.controlsBar.userInteractionEnabled = true;
+ this.tracksPanel.hide();
+ }
+
+ // Protected
+
+ handleEvent(event)
+ {
+ if (event.currentTarget !== this.element)
+ return;
+
+ // Only notify that the background was clicked when the "mousedown" event
+ // was also received, which wouldn't happen if the "mousedown" event caused
+ // the tracks panel to be hidden.
+ if (event.type === "mousedown")
+ this._receivedMousedown = true;
+ else if (event.type === "click") {
+ if (this._receivedMousedown && event.target === this.element && this.delegate && typeof this.delegate.macOSControlsBackgroundWasClicked === "function")
+ this.delegate.macOSControlsBackgroundWasClicked();
+ delete this._receivedMousedown
+ }
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/media-controls.css b/Source/WebCore/Modules/modern-media-controls/controls/media-controls.css
new file mode 100644
index 000000000..d5d02595c
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/media-controls.css
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+:host(audio) {
+ width: 200px;
+ height: 50px;
+}
+
+/* We need to use relative positioning due to webkit.org/b/163603 */
+.media-controls-container {
+ position: relative;
+}
+
+.media-controls-container,
+.media-controls-container > * {
+ left: 0;
+ top: 0;
+ width: 100%;
+}
+
+.media-controls-container,
+.media-controls-container * {
+ -webkit-text-zoom: reset;
+ -webkit-text-size-adjust: auto;
+}
+
+.media-controls-container > * {
+ position: absolute;
+}
+
+.media-controls {
+ height: 100%;
+ font-family: -apple-system;
+ -webkit-user-select: none;
+ white-space: nowrap;
+ cursor: default;
+}
+
+:host(:-webkit-animating-full-screen-transition) .media-controls {
+ display: none;
+}
+
+.media-controls > .controls-bar {
+ position: absolute;
+}
+
+.media-controls.fade-in {
+ animation-name: fade-in;
+ animation-duration: 350ms;
+}
+
+@keyframes fade-in {
+ from { opacity: 0 }
+ to { opacity: 1 }
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/media-controls.js b/Source/WebCore/Modules/modern-media-controls/controls/media-controls.js
new file mode 100644
index 000000000..30687c5ea
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/media-controls.js
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+class MediaControls extends LayoutNode
+{
+
+ constructor({ width = 300, height = 150, layoutTraits = LayoutTraits.Unknown } = {})
+ {
+ super(`<div class="media-controls"></div>`);
+
+ this._scaleFactor = 1;
+
+ this.width = width;
+ this.height = height;
+ this.layoutTraits = layoutTraits;
+
+ this.startButton = new StartButton(this);
+
+ this.playPauseButton = new PlayPauseButton(this);
+ this.skipBackButton = new SkipBackButton(this);
+ this.airplayButton = new AirplayButton(this);
+ this.pipButton = new PiPButton(this);
+ this.fullscreenButton = new FullscreenButton(this);
+
+ this.statusLabel = new StatusLabel(this);
+ this.timeControl = new TimeControl(this);
+
+ this.controlsBar = new ControlsBar(this);
+
+ this.airplayPlacard = new AirplayPlacard(this);
+ this.invalidPlacard = new InvalidPlacard(this);
+ this.pipPlacard = new PiPPlacard(this);
+
+ this.showsStartButton = false;
+ }
+
+ // Public
+
+ get showsStartButton()
+ {
+ return !!this._showsStartButton;
+ }
+
+ set showsStartButton(flag)
+ {
+ if (this._showsStartButton === flag)
+ return;
+
+ this._showsStartButton = flag;
+ this._invalidateChildren();
+ }
+
+ get usesLTRUserInterfaceLayoutDirection()
+ {
+ return this.element.classList.contains("uses-ltr-user-interface-layout-direction");
+ }
+
+ set usesLTRUserInterfaceLayoutDirection(flag)
+ {
+ this.element.classList.toggle("uses-ltr-user-interface-layout-direction", flag);
+ }
+
+ get scaleFactor()
+ {
+ return this._scaleFactor;
+ }
+
+ set scaleFactor(scaleFactor)
+ {
+ if (this._scaleFactor === scaleFactor)
+ return;
+
+ this._scaleFactor = scaleFactor;
+ this.markDirtyProperty("scaleFactor");
+ }
+
+ get showsPlacard()
+ {
+ return this.children[0] instanceof Placard;
+ }
+
+ showPlacard(placard)
+ {
+ const children = [placard];
+ if (placard === this.airplayPlacard)
+ children.push(this.controlsBar);
+
+ this.children = children;
+ }
+
+ hidePlacard()
+ {
+ if (this.showsPlacard)
+ this.children[0].remove();
+ this._invalidateChildren();
+ }
+
+ fadeIn()
+ {
+ this.element.classList.add("fade-in");
+ }
+
+ // Protected
+
+ commitProperty(propertyName)
+ {
+ if (propertyName === "scaleFactor")
+ this.element.style.zoom = 1 / this._scaleFactor;
+ else
+ super.commitProperty(propertyName);
+ }
+
+ controlsBarVisibilityDidChange(controlsBar)
+ {
+ // Implemented by subclasses as needed.
+ }
+
+ // Private
+
+ _invalidateChildren()
+ {
+ if (!this.showsPlacard)
+ this.children = [this._showsStartButton ? this.startButton : this.controlsBar];
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/mute-button.js b/Source/WebCore/Modules/modern-media-controls/controls/mute-button.js
new file mode 100644
index 000000000..a879a4ff1
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/mute-button.js
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+class MuteButton extends IconButton
+{
+
+ constructor(layoutDelegate)
+ {
+ super({
+ cssClassName: "mute",
+ iconName: Icons.Volume,
+ layoutDelegate
+ });
+ }
+
+ // Public
+
+ get muted()
+ {
+ return this.iconName === Icons.VolumeMuted;
+ }
+
+ set muted(flag)
+ {
+ if (this.muted === flag)
+ return;
+
+ this.iconName = flag ? Icons.VolumeMuted : Icons.Volume;
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/pip-button.js b/Source/WebCore/Modules/modern-media-controls/controls/pip-button.js
new file mode 100644
index 000000000..fe0780921
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/pip-button.js
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+class PiPButton extends IconButton
+{
+
+ constructor(layoutDelegate)
+ {
+ super({
+ cssClassName: "pip",
+ iconName: Icons.EnterPiP,
+ layoutDelegate
+ });
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/pip-placard.js b/Source/WebCore/Modules/modern-media-controls/controls/pip-placard.js
new file mode 100644
index 000000000..1ee8bb82a
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/pip-placard.js
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+class PiPPlacard extends Placard
+{
+
+ constructor(layoutDelegate)
+ {
+ super({
+ iconName: Icons.PiPPlacard,
+ description: UIString("This video is playing in Picture in Picture"),
+ layoutDelegate
+ });
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/placard.css b/Source/WebCore/Modules/modern-media-controls/controls/placard.css
new file mode 100644
index 000000000..a8415a69c
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/placard.css
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+.placard {
+ position: absolute;
+ left: 0;
+ top: 0;
+ width: 100%;
+ height: 100%;
+
+ background-color: black;
+ color: rgb(164, 164, 164);
+}
+
+.placard .container {
+ position: absolute;
+ left: 50%;
+ top: 50%;
+ max-width: 402px;
+
+ transform: translate(-50%, -50%);
+}
+
+.placard .icon {
+ position: relative;
+ left: 50%;
+ -webkit-mask-repeat: no-repeat;
+ background-color: rgb(164, 164, 164) !important;
+
+ margin-bottom: 10px;
+
+ transform: translateX(-50%);
+
+ pointer-events: none;
+}
+
+.placard .title,
+.placard .description {
+ text-align: center;
+}
+
+.placard .title {
+ font-size: 20px;
+}
+
+.placard .description {
+ font-size: 13px;
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/placard.js b/Source/WebCore/Modules/modern-media-controls/controls/placard.js
new file mode 100644
index 000000000..73ad5e10f
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/placard.js
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+class Placard extends LayoutItem
+{
+
+ constructor({ iconName = null, title = "", description = "", layoutDelegate = null } = {})
+ {
+ super({
+ element: `<div class="placard"></div>`,
+ layoutDelegate
+ });
+
+ const container = this.addChild(new LayoutNode(`<div class="container"></div>`));
+
+ if (iconName)
+ container.addChild(new IconButton(this)).iconName = iconName;
+
+ if (!!title)
+ container.addChild(new LayoutNode(`<div class="title">${title}</div>`));
+
+ if (!!description)
+ container.addChild(new LayoutNode(`<div class="description">${description}</div>`));
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/play-pause-button.js b/Source/WebCore/Modules/modern-media-controls/controls/play-pause-button.js
new file mode 100644
index 000000000..4b9314939
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/play-pause-button.js
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+class PlayPauseButton extends IconButton
+{
+
+ constructor(layoutDelegate)
+ {
+ super({
+ cssClassName: "play-pause",
+ iconName: Icons.Play,
+ layoutDelegate
+ });
+ }
+
+ // Public
+
+ get playing()
+ {
+ return this.iconName === Icons.Pause;
+ }
+
+ set playing(flag)
+ {
+ if (this.playing === flag)
+ return;
+
+ this.iconName = flag ? Icons.Pause : Icons.Play;
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/rewind-button.js b/Source/WebCore/Modules/modern-media-controls/controls/rewind-button.js
new file mode 100644
index 000000000..9f2bab5f3
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/rewind-button.js
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+class RewindButton extends SeekButton
+{
+
+ constructor(layoutDelegate)
+ {
+ super({
+ cssClassName: "rewind",
+ iconName: Icons.Rewind,
+ layoutDelegate
+ });
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/scheduler.js b/Source/WebCore/Modules/modern-media-controls/controls/scheduler.js
new file mode 100644
index 000000000..f85beb037
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/scheduler.js
@@ -0,0 +1,66 @@
+
+const scheduler = new class
+{
+
+ constructor()
+ {
+ this._frameID = -1;
+ this._layoutCallbacks = new Set;
+ }
+
+ // Public
+
+ get hasScheduledLayoutCallbacks()
+ {
+ return this._frameID !== -1 || this._layoutCallbacks.size > 0;
+ }
+
+ scheduleLayout(callback)
+ {
+ if (typeof callback !== "function")
+ return;
+
+ this._layoutCallbacks.add(callback);
+ this._requestFrameIfNeeded();
+ }
+
+ unscheduleLayout(callback)
+ {
+ if (typeof callback !== "function")
+ return;
+
+ this._layoutCallbacks.delete(callback);
+ }
+
+ // Private
+
+ _requestFrameIfNeeded()
+ {
+ if (this._frameID === -1 && this._layoutCallbacks.size > 0)
+ this._frameID = window.requestAnimationFrame(this._frameDidFire.bind(this));
+ }
+
+ _frameDidFire()
+ {
+ if (typeof scheduler.frameWillFire === "function")
+ scheduler.frameWillFire();
+
+ this._layout();
+ this._frameID = -1;
+ this._requestFrameIfNeeded();
+
+ if (typeof scheduler.frameDidFire === "function")
+ scheduler.frameDidFire();
+ }
+
+ _layout()
+ {
+ // Layouts are not re-entrant.
+ const layoutCallbacks = this._layoutCallbacks;
+ this._layoutCallbacks = new Set;
+
+ for (let callback of layoutCallbacks)
+ callback();
+ }
+
+};
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/scrubber.js b/Source/WebCore/Modules/modern-media-controls/controls/scrubber.js
new file mode 100644
index 000000000..499399636
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/scrubber.js
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+class Scrubber extends Slider
+{
+
+ constructor(layoutDelegate)
+ {
+ super({
+ cssClassName: "scrubber",
+ layoutDelegate
+ });
+
+ this.height = 23;
+
+ // Add the element used to draw the track on iOS.
+ if (this.layoutTraits & LayoutTraits.iOS)
+ this.addChild(new LayoutNode(`<div></div>`), 0);
+
+ this._buffered = 0;
+ }
+
+ // Public
+
+ get buffered()
+ {
+ return this._buffered;
+ }
+
+ set buffered(buffered)
+ {
+ if (this._buffered === buffered)
+ return;
+
+ this._buffered = buffered;
+ this.needsLayout = true;
+ }
+
+ // Protected
+
+ draw(ctx)
+ {
+ const width = this.width;
+ const height = this.height;
+
+ if (!this.width || !this.height)
+ return;
+
+ const dpr = window.devicePixelRatio;
+
+ ctx.save();
+ ctx.scale(dpr, dpr);
+ ctx.clearRect(0, 0, width, height);
+
+ const layoutTraits = this.layoutTraits;
+ if (layoutTraits & LayoutTraits.macOS)
+ this._drawMacOS(ctx, width, height);
+ else if (layoutTraits & LayoutTraits.iOS)
+ this._drawiOS(ctx, width, height);
+
+ ctx.restore();
+ }
+
+ _drawMacOS(ctx, width, height)
+ {
+ const trackHeight = 3;
+ const trackY = (height - trackHeight) / 2;
+ const scrubberWidth = 4;
+ const scrubberHeight = height;
+ const borderSize = 2;
+ const scrubberPosition = Math.max(0, Math.min(width - scrubberWidth, Math.round(width * this.value)));
+
+ // Draw background.
+ ctx.save();
+ ctx.beginPath();
+ addRoundedRect(ctx, 0, trackY, width, trackHeight, trackHeight / 2);
+ ctx.closePath();
+ ctx.clip();
+ ctx.fillStyle = "rgb(20, 20, 20)";
+ ctx.fillRect(0, 0, width, height);
+ ctx.restore();
+
+ // Buffered.
+ ctx.save();
+ ctx.beginPath();
+ addRoundedRect(ctx, 0, trackY, width, trackHeight, trackHeight / 2);
+ ctx.closePath();
+ ctx.clip();
+ ctx.fillStyle = "rgb(39, 39, 39)";
+ ctx.fillRect(0, 0, width * this._buffered, height);
+ ctx.restore();
+
+ // Draw played section.
+ ctx.save();
+ ctx.beginPath();
+ addRoundedRect(ctx, 0, trackY, width, trackHeight, trackHeight / 2);
+ ctx.closePath();
+ ctx.clip();
+ ctx.fillStyle = "rgb(84, 84, 84)";
+ ctx.fillRect(0, 0, width * this.value, height);
+ ctx.restore();
+
+ // Draw the scrubber.
+ ctx.save();
+ ctx.clearRect(scrubberPosition - 1, 0, scrubberWidth + borderSize, height, 0);
+ ctx.beginPath();
+ addRoundedRect(ctx, scrubberPosition, 0, scrubberWidth, scrubberHeight, 1);
+ ctx.closePath();
+ ctx.clip();
+ ctx.fillStyle = "rgb(138, 138, 138)";
+ ctx.fillRect(0, 0, width, height);
+ ctx.restore();
+ }
+
+ _drawiOS(ctx, width, height)
+ {
+ const trackHeight = 3;
+ const trackY = (height - trackHeight) / 2;
+ const scrubberWidth = 15;
+ const leftPadding = 2;
+ const rightPadding = 2;
+ const trackWidth = this.width - leftPadding - rightPadding - scrubberWidth;
+
+ // Draw played section.
+ ctx.save();
+ ctx.beginPath();
+ addRoundedRect(ctx, leftPadding, trackY, scrubberWidth / 2 + trackWidth * this.value, trackHeight, trackHeight / 2);
+ ctx.closePath();
+ ctx.fillStyle = "white";
+ ctx.fill();
+ ctx.restore();
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/seek-button.js b/Source/WebCore/Modules/modern-media-controls/controls/seek-button.js
new file mode 100644
index 000000000..c72c1f165
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/seek-button.js
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+class SeekButton extends IconButton
+{
+
+ constructor(options)
+ {
+ super(options);
+
+ this.element.addEventListener("mousedown", this);
+ }
+
+ // Protected
+
+ handleEvent(event)
+ {
+ if (event.type === "mousedown" && event.currentTarget === this.element)
+ this._didStartPressing();
+ else if (event.type === "mouseup")
+ this._didStopPressing();
+ else
+ super.handleEvent(event);
+ }
+
+ // Private
+
+ _didStartPressing()
+ {
+ const mediaControls = this.parentOfType(MediaControls);
+ if (!mediaControls)
+ return;
+
+ this._mouseupTarget = mediaControls.element;
+ this._mouseupTarget.addEventListener("mouseup", this, true);
+ this._notifyDelegateOfPressingState(true);
+ }
+
+ _didStopPressing()
+ {
+ this._mouseupTarget.removeEventListener("mouseup", this, true);
+ this._notifyDelegateOfPressingState(false);
+ }
+
+ _notifyDelegateOfPressingState(isPressed)
+ {
+ if (this._enabled && this.uiDelegate && typeof this.uiDelegate.buttonPressedStateDidChange === "function")
+ this.uiDelegate.buttonPressedStateDidChange(this, isPressed);
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/skip-back-button.js b/Source/WebCore/Modules/modern-media-controls/controls/skip-back-button.js
new file mode 100644
index 000000000..c5c2d0c2a
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/skip-back-button.js
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+class SkipBackButton extends IconButton
+{
+
+ constructor(layoutDelegate)
+ {
+ super({
+ cssClassName: "skip-back",
+ iconName: Icons.SkipBack,
+ layoutDelegate
+ });
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/slider.css b/Source/WebCore/Modules/modern-media-controls/controls/slider.css
new file mode 100644
index 000000000..cb5ab15b7
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/slider.css
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+.slider {
+ position: absolute;
+}
+
+.slider > canvas,
+.slider > input {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+}
+
+.slider > canvas {
+ pointer-events: none;
+}
+
+.slider > input {
+ margin: 0;
+ background-color: transparent;
+ -webkit-appearance: none !important;
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/slider.js b/Source/WebCore/Modules/modern-media-controls/controls/slider.js
new file mode 100644
index 000000000..c32d662b8
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/slider.js
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+class Slider extends LayoutItem
+{
+
+ constructor({ layoutDelegate = null, cssClassName = "" } = {})
+ {
+ super({
+ element: `<div class="slider ${cssClassName}"></div>`,
+ layoutDelegate
+ });
+
+ this._canvas = new LayoutNode(`<canvas></canvas>`);
+
+ this._input = new LayoutNode(`<input type="range" min="0" max="1" step="0.001" />`);
+ this._input.element.addEventListener("change", this);
+ this._input.element.addEventListener("input", this);
+
+ this.value = 0;
+
+ this.children = [this._canvas, this._input];
+ }
+
+ // Public
+
+ get value()
+ {
+ if (this._value !== undefined)
+ return this._value;
+ return parseFloat(this._input.element.value);
+ }
+
+ set value(value)
+ {
+ if (this._valueIsChanging)
+ return;
+
+ this._value = value;
+ this.markDirtyProperty("value");
+ this.needsLayout = true;
+ }
+
+ get width()
+ {
+ return super.width;
+ }
+
+ set width(width)
+ {
+ super.width = width;
+ this.needsLayout = true;
+ }
+
+ // Protected
+
+ handleEvent(event)
+ {
+ switch (event.type) {
+ case "input":
+ this._handleInputEvent();
+ break;
+ case "change":
+ this._handleChangeEvent();
+ break;
+ }
+ }
+
+ commitProperty(propertyName)
+ {
+ switch (propertyName) {
+ case "value":
+ this._input.element.value = this._value;
+ delete this._value;
+ break;
+ case "width":
+ this._canvas.element.width = this.width * window.devicePixelRatio;
+ case "height":
+ this._canvas.element.height = this.height * window.devicePixelRatio;
+ default :
+ super.commitProperty(propertyName);
+ break;
+ }
+ }
+
+ layout()
+ {
+ super.layout();
+ this.draw(this._canvas.element.getContext("2d"));
+ }
+
+ draw(ctx)
+ {
+ // Implemented by subclasses.
+ }
+
+ // Private
+
+ _handleInputEvent()
+ {
+ if (!this._valueIsChanging && this.uiDelegate && typeof this.uiDelegate.controlValueWillStartChanging === "function")
+ this.uiDelegate.controlValueWillStartChanging(this);
+ this._valueIsChanging = true;
+ if (this.uiDelegate && typeof this.uiDelegate.controlValueDidChange === "function")
+ this.uiDelegate.controlValueDidChange(this);
+
+ this.needsLayout = true;
+ }
+
+ _handleChangeEvent()
+ {
+ delete this._valueIsChanging;
+ if (this.uiDelegate && typeof this.uiDelegate.controlValueDidStopChanging === "function")
+ this.uiDelegate.controlValueDidStopChanging(this);
+
+ this.needsLayout = true;
+ }
+
+}
+
+function addRoundedRect(ctx, x, y, width, height, radius) {
+ ctx.moveTo(x + radius, y);
+ ctx.arcTo(x + width, y, x + width, y + radius, radius);
+ ctx.lineTo(x + width, y + height - radius);
+ ctx.arcTo(x + width, y + height, x + width - radius, y + height, radius);
+ ctx.lineTo(x + radius, y + height);
+ ctx.arcTo(x, y + height, x, y + height - radius, radius);
+ ctx.lineTo(x, y + radius);
+ ctx.arcTo(x, y, x + radius, y, radius);
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/start-button.css b/Source/WebCore/Modules/modern-media-controls/controls/start-button.css
new file mode 100644
index 000000000..82e172dc4
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/start-button.css
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+button.start {
+ position: absolute;
+ left: 50%;
+ top: 50%;
+ width: 70px;
+ height: 70px;
+
+ background-color: transparent !important;
+
+ transform: translate3d(-50%, -50%, 0);
+}
+
+button.start > * {
+ position: absolute;
+ left: 0;
+ top: 0;
+ width: 100%;
+ height: 100%;
+}
+
+button.start > div {
+ -webkit-appearance: media-controls-light-bar-background;
+ -webkit-clip-path: circle(50%);
+ will-change: transform;
+}
+
+button.start > img {
+ opacity: 0.6;
+}
+
+button.start:active > img {
+ opacity: 1;
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/start-button.js b/Source/WebCore/Modules/modern-media-controls/controls/start-button.js
new file mode 100644
index 000000000..9860c7e79
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/start-button.js
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+class StartButton extends Button
+{
+
+ constructor(layoutDelegate)
+ {
+ super(layoutDelegate);
+
+ this.element.classList.add("start");
+
+ const background = this.element.appendChild(document.createElement("div"));
+ background.className = "background";
+
+ const image = this.element.appendChild(new Image);
+ image.src = iconService.imageForIconNameAndLayoutTraits(Icons.Start, this.layoutTraits).src;
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/status-label.css b/Source/WebCore/Modules/modern-media-controls/controls/status-label.css
new file mode 100644
index 000000000..a05b80bc7
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/status-label.css
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+.status-label {
+ position: absolute;
+
+ text-align: left;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ overflow: hidden;
+
+ font-size: 14px;
+
+ color: rgba(255, 255, 255, 0.572);
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/status-label.js b/Source/WebCore/Modules/modern-media-controls/controls/status-label.js
new file mode 100644
index 000000000..b39cc79dc
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/status-label.js
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+class StatusLabel extends LayoutItem
+{
+
+ constructor(layoutDelegate)
+ {
+ super({
+ element: `<div class="status-label"></div>`,
+ layoutDelegate
+ });
+
+ this._text = "";
+ this._enabled = false;
+ }
+
+ // Public
+
+ get text()
+ {
+ return this._text;
+ }
+
+ set text(text)
+ {
+ if (text === this._text)
+ return;
+
+ this._text = text;
+ this.markDirtyProperty("text");
+
+ if (this.layoutDelegate)
+ this.layoutDelegate.needsLayout = true;
+ }
+
+ get enabled()
+ {
+ return this._enabled;
+ }
+
+ set enabled(enabled)
+ {
+ if (enabled === this._enabled)
+ return;
+
+ this._enabled = enabled;
+
+ if (this.layoutDelegate)
+ this.layoutDelegate.needsLayout = true;
+ }
+
+ // Protected
+
+ commitProperty(propertyName)
+ {
+ if (propertyName === "text")
+ this.element.textContent = this._text;
+ else
+ super.commitProperty(propertyName);
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/text-tracks.css b/Source/WebCore/Modules/modern-media-controls/controls/text-tracks.css
new file mode 100644
index 000000000..ab65e22c2
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/text-tracks.css
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+video::-webkit-media-text-track-container {
+ bottom: 50px;
+ overflow: hidden;
+ padding-bottom: 5px;
+ z-index: 0;
+
+ text-align: center;
+ color: rgba(255, 255, 255, 1);
+
+ letter-spacing: normal;
+ word-spacing: normal;
+ text-transform: none;
+ text-indent: 0;
+ text-decoration: none;
+ pointer-events: none;
+ -webkit-user-select: none;
+
+ -webkit-flex: 1 1 auto;
+
+ -webkit-line-box-contain: block inline-box replaced;
+}
+
+video::cue {
+ background-color: rgba(0, 0, 0, 0.8);
+}
+
+video::-webkit-media-text-track-display {
+ position: absolute;
+ overflow: hidden;
+ white-space: pre-wrap;
+ -webkit-box-sizing: border-box;
+ font: 22px sans-serif;
+}
+
+video::-webkit-media-text-track-display-backdrop {
+ display: inline-block;
+}
+
+video::cue(:future) {
+ color: gray;
+}
+
+video::-webkit-media-text-track-container b {
+ font-weight: bold;
+}
+
+video::-webkit-media-text-track-container u {
+ text-decoration: underline;
+}
+
+video::-webkit-media-text-track-container i {
+ font-style: italic;
+}
+
+video::-webkit-media-text-track-container .hidden {
+ display: none;
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/time-control.js b/Source/WebCore/Modules/modern-media-controls/controls/time-control.js
new file mode 100644
index 000000000..8a78ccd16
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/time-control.js
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+const MinimumScrubberWidth = 168;
+const ElapsedTimeLabelLeftMargin = -2;
+const ElapsedTimeLabelWidth = 40;
+const RemainingTimeLabelWidth = 49;
+const AdditionalTimeLabelWidthOverAnHour = 22;
+const ScrubberMargin = 5;
+
+class TimeControl extends LayoutItem
+{
+
+ constructor(layoutDelegate)
+ {
+ super({
+ element: `<div class="time-control"></div>`,
+ layoutDelegate
+ });
+
+ this.elapsedTimeLabel = new TimeLabel;
+ this.scrubber = new Scrubber(layoutDelegate);
+ this.remainingTimeLabel = new TimeLabel;
+
+ this.children = [this.elapsedTimeLabel, this.scrubber, this.remainingTimeLabel];
+
+ this._labelsMayDisplayTimesOverAnHour = false;
+ }
+
+ // Public
+
+ get labelsMayDisplayTimesOverAnHour()
+ {
+ return this._labelsMayDisplayTimesOverAnHour;
+ }
+
+ set labelsMayDisplayTimesOverAnHour(flag)
+ {
+ if (this._labelsMayDisplayTimesOverAnHour === flag)
+ return;
+
+ this._labelsMayDisplayTimesOverAnHour = flag;
+ this.layout();
+ }
+
+ get width()
+ {
+ return super.width;
+ }
+
+ set width(width)
+ {
+ super.width = width;
+
+ const extraWidth = this._labelsMayDisplayTimesOverAnHour ? AdditionalTimeLabelWidthOverAnHour : 0;
+ const elapsedTimeLabelWidth = ElapsedTimeLabelWidth + extraWidth;
+ const remainingTimeLabelWidth = RemainingTimeLabelWidth + extraWidth;
+
+ this.elapsedTimeLabel.x = ElapsedTimeLabelLeftMargin;
+ this.elapsedTimeLabel.width = elapsedTimeLabelWidth;
+ this.scrubber.x = this.elapsedTimeLabel.x + elapsedTimeLabelWidth + ScrubberMargin;
+ this.scrubber.width = this._width - elapsedTimeLabelWidth - ScrubberMargin - remainingTimeLabelWidth;
+ this.remainingTimeLabel.x = this.scrubber.x + this.scrubber.width + ScrubberMargin;
+ }
+
+ get isSufficientlyWide()
+ {
+ return this.scrubber.width >= MinimumScrubberWidth;
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/time-label.css b/Source/WebCore/Modules/modern-media-controls/controls/time-label.css
new file mode 100644
index 000000000..76d2e91ee
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/time-label.css
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+.time-label {
+ position: absolute;
+
+ font-family: -apple-system-monospaced-numbers;
+ font-size: 14px;
+
+ text-align: right;
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/time-label.js b/Source/WebCore/Modules/modern-media-controls/controls/time-label.js
new file mode 100644
index 000000000..cd0e753ab
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/time-label.js
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+class TimeLabel extends LayoutNode
+{
+
+ constructor()
+ {
+ super(`<div class="time-label"></div>`);
+
+ this.value = 0;
+ }
+
+ // Public
+
+ get value()
+ {
+ return this._value;
+ }
+
+ set value(value)
+ {
+ if (value === this._value)
+ return;
+
+ this._value = value;
+ this.markDirtyProperty("value");
+ }
+
+ // Protected
+
+ commitProperty(propertyName)
+ {
+ if (propertyName === "value")
+ this.element.textContent = this._formattedTime();
+ else
+ super.commitProperty(propertyName);
+ }
+
+ // Private
+
+ _formattedTime()
+ {
+ const time = this._value || 0;
+ const absTime = Math.abs(time);
+ const intSeconds = Math.floor(absTime % 60).toFixed(0);
+ const intMinutes = Math.floor((absTime / 60) % 60).toFixed(0);
+ const intHours = Math.floor(absTime / (60 * 60)).toFixed(0);
+
+ const timeStrings = [intMinutes, intSeconds];
+ if (intHours > 0)
+ timeStrings.unshift(intHours);
+
+ const sign = time < 0 ? "-" : "";
+ return sign + timeStrings.map(x => `00${x}`.slice(-2)).join(":");
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/tracks-button.js b/Source/WebCore/Modules/modern-media-controls/controls/tracks-button.js
new file mode 100644
index 000000000..5505f2c0b
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/tracks-button.js
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+class TracksButton extends IconButton
+{
+
+ constructor(layoutDelegate)
+ {
+ super({
+ cssClassName: "tracks",
+ iconName: Icons.Tracks,
+ layoutDelegate
+ });
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/tracks-panel.css b/Source/WebCore/Modules/modern-media-controls/controls/tracks-panel.css
new file mode 100644
index 000000000..b0717483f
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/tracks-panel.css
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+.tracks-panel {
+ position: absolute;
+ display: inline-block;
+}
+
+.tracks-panel > .background-tint > div {
+ border-radius: 7px;
+}
+
+.tracks-panel * {
+ font-size: 14px;
+ font-weight: 500;
+}
+
+.tracks-panel.fade-out {
+ transition-property: opacity;
+ transition-duration: 265ms;
+ opacity: 0;
+}
+
+.tracks-panel > section {
+ border-top: 2px solid rgb(104, 104, 104);
+ will-change: transform;
+}
+
+.tracks-panel > section:first-of-type {
+ border-top: 0;
+}
+
+.tracks-panel > section > h3 {
+ color: rgb(150, 150, 150);
+ padding: 5px 20px 1px 21px;
+ margin: 0;
+}
+
+.tracks-panel > section > ul {
+ list-style-type: none;
+ margin-top: 0;
+ padding: 0;
+}
+
+.tracks-panel > section > ul > li {
+ position: relative;
+ padding: 1px 20px 1px 33px;
+ color: white;
+}
+
+.tracks-panel > section > ul > li:focus {
+ background-color: rgba(26, 68, 243, 0.6);
+ -webkit-backdrop-filter: saturate(180%) blur(20px);
+ outline: none;
+}
+
+.tracks-panel > section > ul > li.selected:before {
+ position: absolute;
+ top: 3px;
+ left: 12px;
+ width: 12px;
+ display: inline-block;
+ content: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 300 300"><polygon fill="white" points="252.301,4.477 126.667,194.104 43.358,108.3 6.868,161.408 132.515,290.814 297.732,49.926"/></svg>');
+}
+
+.tracks-panel > section > ul > li.animated {
+ animation-name: tracks-panel-item-selection;
+ animation-duration: 150ms;
+ animation-timing-function: step-end;
+ animation-fill-mode: forwards;
+}
+
+@keyframes tracks-panel-item-selection {
+ 0%, 55.55% {
+ background-color: rgba(26, 68, 243, 0.6);
+ -webkit-backdrop-filter: saturate(180%) blur(20px);
+ }
+
+ 22.22% {
+ background: none;
+ -webkit-backdrop-filter: none;
+ }
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/tracks-panel.js b/Source/WebCore/Modules/modern-media-controls/controls/tracks-panel.js
new file mode 100644
index 000000000..4a69bb6e2
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/tracks-panel.js
@@ -0,0 +1,305 @@
+
+class TracksPanel extends LayoutNode
+{
+
+ constructor()
+ {
+ super(`<div class="tracks-panel"></div>`);
+ this._backgroundTint = new BackgroundTint;
+ this._rightX = 0;
+ this._bottomY = 0;
+ }
+
+ // Public
+
+ get presented()
+ {
+ return !!this.parent;
+ }
+
+ presentInParent(node)
+ {
+ if (this.parent === node)
+ return;
+
+ this.children = this._childrenFromDataSource();
+
+ node.addChild(this);
+
+ this.element.removeEventListener("transitionend", this);
+ this.element.classList.remove("fade-out");
+
+ this._mousedownTarget().addEventListener("mousedown", this, true);
+ window.addEventListener("keydown", this, true);
+
+ this._focusedTrackNode = null;
+ }
+
+ hide()
+ {
+ if (!this.presented)
+ return;
+
+ this._mousedownTarget().removeEventListener("mousedown", this, true);
+ window.removeEventListener("keydown", this, true);
+
+ this.element.addEventListener("transitionend", this);
+
+ // Ensure a transition will indeed happen by starting it only on the next frame.
+ window.requestAnimationFrame(() => { this.element.classList.add("fade-out"); });
+ }
+
+ get bottomY()
+ {
+ return this._bottomY;
+ }
+
+ set bottomY(bottomY)
+ {
+ if (this._bottomY === bottomY)
+ return;
+
+ this._bottomY = bottomY;
+ this.markDirtyProperty("bottomY");
+ }
+
+ get rightX()
+ {
+ return this._rightX;
+ }
+
+ set rightX(x)
+ {
+ if (this._rightX === x)
+ return;
+
+ this._rightX = x;
+ this.markDirtyProperty("rightX");
+ }
+
+ // Protected
+
+ trackNodeSelectionAnimationDidEnd(trackNode)
+ {
+ if (this.uiDelegate && typeof this.uiDelegate.tracksPanelSelectionDidChange === "function")
+ this.uiDelegate.tracksPanelSelectionDidChange(trackNode.index, trackNode.sectionIndex);
+ }
+
+ mouseMovedOverTrackNode(trackNode)
+ {
+ this._focusTrackNode(trackNode);
+ }
+
+ mouseExitedTrackNode(trackNode)
+ {
+ this._focusedTrackNode.element.blur();
+ delete this._focusedTrackNode;
+ }
+
+ commitProperty(propertyName)
+ {
+ if (propertyName === "rightX")
+ this.element.style.right = `${this._rightX}px`;
+ else if (propertyName === "bottomY")
+ this.element.style.bottom = `${this._bottomY}px`;
+ else
+ super.commitProperty(propertyName);
+ }
+
+ handleEvent(event)
+ {
+ switch (event.type) {
+ case "mousedown":
+ this._handleMousedown(event);
+ break;
+ case "keydown":
+ this._handleKeydown(event);
+ break;
+ case "transitionend":
+ this.remove();
+ break;
+ }
+ }
+
+ // Private
+
+ _mousedownTarget()
+ {
+ const mediaControls = this.parentOfType(MacOSFullscreenMediaControls);
+ if (mediaControls)
+ return mediaControls.element;
+ return window;
+ }
+
+ _childrenFromDataSource()
+ {
+ const children = [this._backgroundTint];
+
+ this._trackNodes = [];
+
+ const dataSource = this.dataSource;
+ if (!dataSource)
+ return children;
+
+ const numberOfSections = dataSource.tracksPanelNumberOfSections();
+ if (numberOfSections === 0)
+ return children;
+
+ for (let sectionIndex = 0; sectionIndex < numberOfSections; ++sectionIndex) {
+ let sectionNode = new LayoutNode(`<section></section>`);
+ sectionNode.addChild(new LayoutNode(`<h3>${dataSource.tracksPanelTitleForSection(sectionIndex)}</h3>`));
+
+ let tracksListNode = sectionNode.addChild(new LayoutNode(`<ul></ul>`));
+ let numberOfTracks = dataSource.tracksPanelNumberOfTracksInSection(sectionIndex);
+ for (let trackIndex = 0; trackIndex < numberOfTracks; ++trackIndex) {
+ let trackTitle = dataSource.tracksPanelTitleForTrackInSection(trackIndex, sectionIndex);
+ let trackSelected = dataSource.tracksPanelIsTrackInSectionSelected(trackIndex, sectionIndex);
+ let trackNode = tracksListNode.addChild(new TrackNode(trackIndex, sectionIndex, trackTitle, trackSelected, this));
+ this._trackNodes.push(trackNode);
+ }
+ children.push(sectionNode);
+ }
+
+ return children;
+ }
+
+ _handleMousedown(event)
+ {
+ if (this.element.contains(event.target))
+ return;
+
+ this._dismiss();
+
+ event.preventDefault();
+ event.stopPropagation();
+ }
+
+ _handleKeydown(event)
+ {
+ switch (event.key) {
+ case "Home":
+ case "PageUp":
+ this._focusFirstTrackNode();
+ break;
+ case "End":
+ case "PageDown":
+ this._focusLastTrackNode();
+ break;
+ case "ArrowDown":
+ if (event.altKey || event.metaKey)
+ this._focusLastTrackNode();
+ else
+ this._focusNextTrackNode();
+ break;
+ case "ArrowUp":
+ if (event.altKey || event.metaKey)
+ this._focusFirstTrackNode();
+ else
+ this._focusPreviousTrackNode();
+ break;
+ case " ":
+ case "Enter":
+ if (this._focusedTrackNode)
+ this._focusedTrackNode.activate();
+ break;
+ case "Escape":
+ this._dismiss();
+ break;
+ }
+ }
+
+ _dismiss()
+ {
+ if (this.parent && typeof this.parent.hideTracksPanel === "function")
+ this.parent.hideTracksPanel();
+ }
+
+ _focusTrackNode(trackNode)
+ {
+ if (!trackNode || trackNode === this._focusedTrackNode)
+ return;
+
+ trackNode.element.focus();
+ this._focusedTrackNode = trackNode;
+ }
+
+ _focusPreviousTrackNode()
+ {
+ const previousIndex = this._focusedTrackNode ? this._trackNodes.indexOf(this._focusedTrackNode) - 1 : this._trackNodes.length - 1;
+ this._focusTrackNode(this._trackNodes[previousIndex]);
+ }
+
+ _focusNextTrackNode()
+ {
+ this._focusTrackNode(this._trackNodes[this._trackNodes.indexOf(this._focusedTrackNode) + 1]);
+ }
+
+ _focusFirstTrackNode()
+ {
+ this._focusTrackNode(this._trackNodes[0]);
+ }
+
+ _focusLastTrackNode()
+ {
+ this._focusTrackNode(this._trackNodes[this._trackNodes.length - 1]);
+ }
+
+}
+
+class TrackNode extends LayoutNode
+{
+
+ constructor(index, sectionIndex, title, selected, panel)
+ {
+ super(`<li tabindex="0">${title}</li>`);
+
+ this.index = index;
+ this.sectionIndex = sectionIndex;
+ this._panel = panel;
+ this._selected = selected;
+
+ if (selected)
+ this.element.classList.add("selected");
+
+ this.element.addEventListener("mousemove", this);
+ this.element.addEventListener("mouseleave", this);
+ this.element.addEventListener("click", this);
+ }
+
+ // Public
+
+ activate()
+ {
+ this.element.addEventListener("animationend", this);
+ this.element.classList.add("animated");
+ }
+
+ // Protected
+
+ handleEvent(event)
+ {
+ switch (event.type) {
+ case "mousemove":
+ this._panel.mouseMovedOverTrackNode(this);
+ break;
+ case "mouseleave":
+ this._panel.mouseExitedTrackNode(this);
+ break;
+ case "click":
+ this.activate();
+ break;
+ case "animationend":
+ this._animationDidEnd();
+ break;
+ }
+ }
+
+ // Private
+
+ _animationDidEnd()
+ {
+ this.element.removeEventListener("animationend", this);
+ this._panel.trackNodeSelectionAnimationDidEnd(this);
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/volume-down-button.js b/Source/WebCore/Modules/modern-media-controls/controls/volume-down-button.js
new file mode 100644
index 000000000..b5c9d1daf
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/volume-down-button.js
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2017 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+class VolumeDownButton extends IconButton
+{
+
+ constructor(layoutDelegate)
+ {
+ super({
+ cssClassName: "volume-down",
+ iconName: Icons.VolumeDown,
+ layoutDelegate
+ });
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/volume-slider.css b/Source/WebCore/Modules/modern-media-controls/controls/volume-slider.css
new file mode 100644
index 000000000..42b904bd6
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/volume-slider.css
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+.volume.slider > input::-webkit-slider-thumb {
+ width: 11px;
+ height: 11px;
+ border-radius: 5.5px;
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/volume-slider.js b/Source/WebCore/Modules/modern-media-controls/controls/volume-slider.js
new file mode 100644
index 000000000..993eb16f1
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/volume-slider.js
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+class VolumeSlider extends Slider
+{
+
+ constructor(layoutDelegate)
+ {
+ super({
+ cssClassName: "volume",
+ layoutDelegate
+ });
+
+ this.height = 11;
+ this.enabled = true;
+
+ this._active = false;
+ this.element.addEventListener("mousedown", this);
+ }
+
+ // Protected
+
+ handleEvent(event)
+ {
+ super.handleEvent(event);
+
+ if (event instanceof MouseEvent) {
+ this._active = event.type === "mousedown";
+ if (event.type === "mousedown")
+ window.addEventListener("mouseup", this, true);
+ else
+ window.removeEventListener("mouseup", this, true);
+ }
+ }
+
+ draw(ctx)
+ {
+ const width = this.width;
+ const height = this.height;
+
+ if (!this.width || !this.height)
+ return;
+
+ const dpr = window.devicePixelRatio;
+
+ ctx.save();
+ ctx.scale(dpr, dpr);
+ ctx.clearRect(0, 0, width, height);
+
+ const trackHeight = 3;
+ const knobRadius = 5.5;
+ const knobDiameter = 2 * knobRadius;
+ const borderSize = 2;
+ const knobX = Math.round(this.value * (width - knobDiameter - borderSize));
+
+ // Draw portion of volume under slider thumb.
+ ctx.save();
+ ctx.beginPath();
+ addRoundedRect(ctx, 0, 4, knobX + 2, trackHeight, trackHeight / 2);
+ ctx.closePath();
+ ctx.clip();
+ ctx.fillStyle = "rgb(84, 84, 84)";
+ ctx.fillRect(0, 0, width, height);
+ ctx.restore();
+
+ // Draw portion of volume above slider thumb.
+ ctx.save();
+ ctx.beginPath();
+ addRoundedRect(ctx, knobX, 4, width - knobX, trackHeight, trackHeight / 2);
+ ctx.closePath();
+ ctx.clip();
+ ctx.fillStyle = "rgb(39, 39, 39)";
+ ctx.fillRect(0, 0, width, height);
+ ctx.restore();
+
+ // Clear a hole in the slider for the scrubber.
+ ctx.save();
+ ctx.beginPath();
+ addRoundedRect(ctx, knobX, 0, knobDiameter + borderSize, height, (knobDiameter + borderSize) / 2);
+ ctx.closePath();
+ ctx.clip();
+ ctx.clearRect(0, 0, width, height);
+ ctx.restore();
+
+ // Draw scrubber.
+ ctx.save();
+ ctx.beginPath();
+ addRoundedRect(ctx, knobX + 1, 0, knobDiameter, knobDiameter, knobRadius);
+ ctx.closePath();
+ ctx.clip();
+ ctx.fillStyle = this._active ? "white" : "rgb(138, 138, 138)";
+ ctx.fillRect(0, 0, width, height);
+ ctx.restore();
+
+ ctx.restore();
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/volume-up-button.js b/Source/WebCore/Modules/modern-media-controls/controls/volume-up-button.js
new file mode 100644
index 000000000..8d9432436
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/volume-up-button.js
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2017 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+class VolumeUpButton extends IconButton
+{
+
+ constructor(layoutDelegate)
+ {
+ super({
+ cssClassName: "volume-up",
+ iconName: Icons.VolumeUp,
+ layoutDelegate
+ });
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/gesture-recognizers/gesture-recognizer.js b/Source/WebCore/Modules/modern-media-controls/gesture-recognizers/gesture-recognizer.js
new file mode 100644
index 000000000..c1ec8572a
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/gesture-recognizers/gesture-recognizer.js
@@ -0,0 +1,379 @@
+
+class GestureRecognizer
+{
+
+ constructor(target = null, delegate = null)
+ {
+ this._targetTouches = [];
+
+ this.modifierKeys = {
+ alt : false,
+ ctrl : false,
+ meta : false,
+ shift : false
+ };
+
+ this._state = GestureRecognizer.States.Possible;
+ this._enabled = true;
+
+ this.target = target;
+ this.delegate = delegate;
+ }
+
+ // Public
+
+ get state()
+ {
+ return this._state;
+ }
+
+ set state(state)
+ {
+ if (this._state === state && state !== GestureRecognizer.States.Changed)
+ return;
+
+ this._state = state;
+ if (this.delegate && typeof this.delegate.gestureRecognizerStateDidChange === "function")
+ this.delegate.gestureRecognizerStateDidChange(this);
+ }
+
+ get target()
+ {
+ return this._target;
+ }
+
+ set target(target)
+ {
+ if (!target || this._target === target)
+ return;
+
+ this._target = target;
+ this._initRecognizer();
+ }
+
+ get numberOfTouches()
+ {
+ return this._targetTouches.length;
+ }
+
+ get enabled()
+ {
+ return this._enabled;
+ }
+
+ set enabled(enabled)
+ {
+ if (this._enabled === enabled)
+ return;
+
+ this._enabled = enabled;
+
+ if (!enabled) {
+ if (this.numberOfTouches === 0) {
+ this._removeTrackingListeners();
+ this.reset();
+ } else
+ this.enterCancelledState();
+ }
+
+ this._updateBaseListeners();
+ }
+
+ reset()
+ {
+ // Implemented by subclasses.
+ }
+
+ locationInElement(element)
+ {
+ const p = new DOMPoint;
+ const touches = this._targetTouches;
+ const count = touches.length;
+ for (let i = 0; i < count; ++i) {
+ const touch = touches[i];
+ p.x += touch.pageX;
+ p.y += touch.pageY;
+ }
+ p.x /= count;
+ p.y /= count;
+
+ if (!element)
+ return p;
+
+ // FIXME: are WebKitPoint and DOMPoint interchangeable?
+ const wkPoint = window.webkitConvertPointFromPageToNode(element, new WebKitPoint(p.x, p.y));
+ return new DOMPoint(wkPoint.x, wkPoint.y);
+ }
+
+ locationInClient()
+ {
+ const p = new DOMPoint;
+ const touches = this._targetTouches;
+ const count = touches.length;
+ for (let i = 0; i < count; ++i) {
+ const touch = touches[i];
+ p.x += touch.clientX;
+ p.y += touch.clientY;
+ }
+ p.x /= count;
+ p.y /= count;
+
+ return p;
+ }
+
+ locationOfTouchInElement(touchIndex, element)
+ {
+ const touch = this._targetTouches[touchIndex];
+ if (!touch)
+ return new DOMPoint;
+
+ const touchLocation = new DOMPoint(touch.pageX, touch.pageY);
+ if (!element)
+ return touchLocation;
+
+ // FIXME: are WebKitPoint and DOMPoint interchangeable?
+ const wkPoint = window.webkitConvertPointFromPageToNode(element, new WebKitPoint(touchLocation.x, touchLocation.y));
+ return new DOMPoint(wkPoint.x, wkPoint.y);
+ }
+
+ touchesBegan(event)
+ {
+ if (event.currentTarget !== this._target)
+ return;
+
+ window.addEventListener(GestureRecognizer.Events.TouchMove, this, true);
+ window.addEventListener(GestureRecognizer.Events.TouchEnd, this, true);
+ window.addEventListener(GestureRecognizer.Events.TouchCancel, this, true);
+ this.enterPossibleState();
+ }
+
+ touchesMoved(event)
+ {
+ // Implemented by subclasses.
+ }
+
+ touchesEnded(event)
+ {
+ // Implemented by subclasses.
+ }
+
+ touchesCancelled(event)
+ {
+ // Implemented by subclasses.
+ }
+
+ gestureBegan(event)
+ {
+ if (event.currentTarget !== this._target)
+ return;
+
+ window.addEventListener(GestureRecognizer.Events.GestureChange, this, true);
+ window.addEventListener(GestureRecognizer.Events.GestureEnd, this, true);
+ this.enterPossibleState();
+ }
+
+ gestureChanged(event)
+ {
+ // Implemented by subclasses.
+ }
+
+ gestureEnded(event)
+ {
+ // Implemented by subclasses.
+ }
+
+ enterPossibleState()
+ {
+ this.state = GestureRecognizer.States.Possible;
+ }
+
+ enterBeganState()
+ {
+ if (this.delegate && typeof this.delegate.gestureRecognizerShouldBegin === "function" && !this.delegate.gestureRecognizerShouldBegin(this)) {
+ this.enterFailedState();
+ return;
+ }
+ this.state = GestureRecognizer.States.Began;
+ }
+
+ enterEndedState()
+ {
+ this.state = GestureRecognizer.States.Ended;
+ this._removeTrackingListeners();
+ this.reset();
+ }
+
+ enterCancelledState()
+ {
+ this.state = GestureRecognizer.States.Cancelled;
+ this._removeTrackingListeners();
+ this.reset();
+ }
+
+ enterFailedState()
+ {
+ this.state = GestureRecognizer.States.Failed;
+ this._removeTrackingListeners();
+ this.reset();
+ }
+
+ enterChangedState()
+ {
+ this.state = GestureRecognizer.States.Changed;
+ }
+
+ enterRecognizedState()
+ {
+ this.state = GestureRecognizer.States.Recognized;
+ }
+
+ // Protected
+
+ handleEvent(event)
+ {
+ this._updateTargetTouches(event);
+ this._updateKeyboardModifiers(event);
+
+ switch (event.type) {
+ case GestureRecognizer.Events.TouchStart:
+ this.touchesBegan(event);
+ break;
+ case GestureRecognizer.Events.TouchMove:
+ this.touchesMoved(event);
+ break;
+ case GestureRecognizer.Events.TouchEnd:
+ this.touchesEnded(event);
+ break;
+ case GestureRecognizer.Events.TouchCancel:
+ this.touchesCancelled(event);
+ break;
+ case GestureRecognizer.Events.GestureStart:
+ this.gestureBegan(event);
+ break;
+ case GestureRecognizer.Events.GestureChange:
+ this.gestureChanged(event);
+ break;
+ case GestureRecognizer.Events.GestureEnd:
+ this.gestureEnded(event);
+ break;
+ }
+ }
+
+ // Private
+
+ _initRecognizer()
+ {
+ this.reset();
+ this.state = GestureRecognizer.States.Possible;
+
+ this._updateBaseListeners();
+ }
+
+ _updateBaseListeners()
+ {
+ if (!this._target)
+ return;
+
+ if (this._enabled) {
+ this._target.addEventListener(GestureRecognizer.Events.TouchStart, this);
+ if (GestureRecognizer.SupportsGestures)
+ this._target.addEventListener(GestureRecognizer.Events.GestureStart, this);
+ } else {
+ this._target.removeEventListener(GestureRecognizer.Events.TouchStart, this);
+ if (GestureRecognizer.SupportsGestures)
+ this._target.removeEventListener(GestureRecognizer.Events.GestureStart, this);
+ }
+ }
+
+ _removeTrackingListeners()
+ {
+ window.removeEventListener(GestureRecognizer.Events.TouchMove, this, true);
+ window.removeEventListener(GestureRecognizer.Events.TouchEnd, this, true);
+ window.removeEventListener(GestureRecognizer.Events.GestureChange, this, true);
+ window.removeEventListener(GestureRecognizer.Events.GestureEnd, this, true);
+ }
+
+ _updateTargetTouches(event)
+ {
+ if (!GestureRecognizer.SupportsTouches) {
+ if (event.type === GestureRecognizer.Events.TouchEnd)
+ this._targetTouches = [];
+ else
+ this._targetTouches = [event];
+ return;
+ }
+
+ if (!(event instanceof TouchEvent))
+ return;
+
+ // With a touchstart event, event.targetTouches is accurate so
+ // we simply add all of those.
+ if (event.type === GestureRecognizer.Events.TouchStart) {
+ this._targetTouches = [];
+ let touches = event.targetTouches;
+ for (let i = 0, count = touches.length; i < count; ++i)
+ this._targetTouches.push(touches[i]);
+ return;
+ }
+
+ // With a touchmove event, the target is window so event.targetTouches is
+ // inaccurate so we add all touches that we knew about previously.
+ if (event.type === GestureRecognizer.Events.TouchMove) {
+ let targetIdentifiers = this._targetTouches.map(function(touch) {
+ return touch.identifier;
+ });
+
+ this._targetTouches = [];
+ let touches = event.touches;
+ for (let i = 0, count = touches.length; i < count; ++i) {
+ let touch = touches[i];
+ if (targetIdentifiers.indexOf(touch.identifier) !== -1)
+ this._targetTouches.push(touch);
+ }
+ return;
+ }
+
+ // With a touchend or touchcancel event, we only keep the existing touches
+ // that are also found in event.touches.
+ let allTouches = event.touches;
+ let existingIdentifiers = [];
+ for (let i = 0, count = allTouches.length; i < count; ++i)
+ existingIdentifiers.push(allTouches[i].identifier);
+
+ this._targetTouches = this._targetTouches.filter(function(touch) {
+ return existingIdentifiers.indexOf(touch.identifier) !== -1;
+ });
+ }
+
+ _updateKeyboardModifiers(event)
+ {
+ this.modifierKeys.alt = event.altKey;
+ this.modifierKeys.ctrl = event.ctrlKey;
+ this.modifierKeys.meta = event.metaKey;
+ this.modifierKeys.shift = event.shiftKey;
+ }
+
+}
+
+GestureRecognizer.SupportsTouches = "createTouch" in document;
+GestureRecognizer.SupportsGestures = !!window.GestureEvent;
+
+GestureRecognizer.States = {
+ Possible : "possible",
+ Began : "began",
+ Changed : "changed",
+ Ended : "ended",
+ Cancelled : "cancelled",
+ Failed : "failed",
+ Recognized : "ended"
+};
+
+GestureRecognizer.Events = {
+ TouchStart : GestureRecognizer.SupportsTouches ? "touchstart" : "mousedown",
+ TouchMove : GestureRecognizer.SupportsTouches ? "touchmove" : "mousemove",
+ TouchEnd : GestureRecognizer.SupportsTouches ? "touchend" : "mouseup",
+ TouchCancel : "touchcancel",
+ GestureStart : "gesturestart",
+ GestureChange : "gesturechange",
+ GestureEnd : "gestureend"
+};
diff --git a/Source/WebCore/Modules/modern-media-controls/gesture-recognizers/pinch.js b/Source/WebCore/Modules/modern-media-controls/gesture-recognizers/pinch.js
new file mode 100644
index 000000000..12bf0713a
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/gesture-recognizers/pinch.js
@@ -0,0 +1,205 @@
+
+const MAXIMUM_TIME_FOR_RECORDING_GESTURES = 100;
+const MAXIMUM_DECELERATION_TIME = 500;
+
+class PinchGestureRecognizer extends GestureRecognizer
+{
+
+ constructor(target, delegate)
+ {
+ super(target, delegate);
+
+ this.scaleThreshold = 0;
+ this._scaledMinimumAmount = false;
+ }
+
+ // Public
+
+ get velocity()
+ {
+ const lastGesture = this._gestures[this._gestures.length - 1];
+ if (!lastGesture)
+ return this._velocity;
+
+ const elapsedTime = Date.now() - (lastGesture.timeStamp + MAXIMUM_TIME_FOR_RECORDING_GESTURES);
+ if (elapsedTime <= 0)
+ return this._velocity;
+
+ const f = Math.max((MAXIMUM_DECELERATION_TIME - elapsedTime) / MAXIMUM_DECELERATION_TIME, 0);
+ return this._velocity * f;
+ }
+
+ // Protected
+
+ touchesBegan(event)
+ {
+ if (event.currentTarget !== this.target)
+ return;
+
+ // Additional setup for when the the platform doesn't natively
+ // provide us with gesture events.
+ if (!GestureRecognizer.SupportsGestures) {
+ // A pinch gesture can only be performed with 2 fingers, anything more
+ // and we failed our gesture.
+ if (this.numberOfTouches > 2) {
+ this.enterFailedState();
+ return;
+ }
+
+ // We can only start tracking touches with 2 fingers.
+ if (this.numberOfTouches !== 2)
+ return;
+
+ this._startDistance = this._distance();
+
+ // We manually add a start value so that we always have 2 entries in the
+ // _gestures array so that we don't have to check for the existence of 2
+ // entries when computing velocity.
+ this._recordGesture(1);
+
+ this._scaledMinimumAmount = false;
+ this._updateStateWithEvent(event);
+ } else if (this.numberOfTouches !== 2) {
+ // When we support gesture events, we only care about the case where we're
+ // using two fingers.
+ return;
+ }
+
+ super.touchesBegan(event);
+ }
+
+ touchesMoved(event)
+ {
+ // This method only needs to be overriden in the case where the platform
+ // doesn't natively provide us with gesture events.
+ if (GestureRecognizer.SupportsGestures)
+ return;
+
+ if (this.numberOfTouches !== 2)
+ return;
+
+ this._updateStateWithEvent(event);
+ }
+
+ touchesEnded(event)
+ {
+ // This method only needs to be overriden in the case where the platform
+ // doesn't natively provide us with gesture events.
+ if (GestureRecognizer.SupportsGestures)
+ return;
+
+ // If we don't have the required number of touches or have not event
+ // obtained 2 fingers, then there's nothing for us to do.
+ if (this.numberOfTouches >= 2 || !this._startDistance)
+ return;
+
+ if (this._scaledMinimumAmount)
+ this.enterEndedState();
+ else
+ this.enterFailedState();
+ }
+
+ gestureBegan(event)
+ {
+ super.gestureBegan(event);
+
+ // We manually add a start value so that we always have 2 entries in the
+ // _gestures array so that we don't have to check for the existence of 2
+ // entries when computing velocity.
+ this._recordGesture(event.scale);
+
+ this._scaledMinimumAmount = false;
+ this._updateStateWithEvent(event);
+
+ event.preventDefault();
+ }
+
+ gestureChanged(event)
+ {
+ event.preventDefault();
+
+ this._updateStateWithEvent(event);
+ }
+
+ gestureEnded(event)
+ {
+ if (this._scaledMinimumAmount)
+ this.enterEndedState();
+ else
+ this.enterFailedState();
+ }
+
+ reset()
+ {
+ this.scale = 1;
+ this._velocity = 0;
+ this._gestures = [];
+ delete this._startDistance;
+ }
+
+ // Private
+
+ _recordGesture(scale)
+ {
+ const currentTime = Date.now();
+ const count = this._gestures.push({
+ scale: scale,
+ timeStamp: currentTime
+ });
+
+ // We want to keep at least two gestures at all times.
+ if (count <= 2)
+ return;
+
+ const scaleDirection = this._gestures[count - 1].scale >= this._gestures[count - 2].scale;
+ let i = count - 3;
+ for (; i >= 0; --i) {
+ let gesture = this._gestures[i];
+ if (currentTime - gesture.timeStamp > MAXIMUM_TIME_FOR_RECORDING_GESTURES ||
+ this._gestures[i + 1].scale >= gesture.scale !== scaleDirection)
+ break;
+ }
+
+ if (i > 0)
+ this._gestures = this._gestures.slice(i + 1);
+ }
+
+ _updateStateWithEvent(event)
+ {
+ const scaleSinceStart = GestureRecognizer.SupportsGestures ? event.scale : this._distance() / this._startDistance;
+
+ if (!this._scaledMinimumAmount) {
+ if (Math.abs(1 - scaleSinceStart) >= this.scaleThreshold) {
+ this._scaledMinimumAmount = true;
+ this.scale = 1;
+ this.enterBeganState();
+ }
+ return;
+ }
+
+ this._recordGesture(scaleSinceStart);
+
+ const oldestGesture = this._gestures[0];
+ const ds = scaleSinceStart - oldestGesture.scale;
+ const dt = Date.now() - oldestGesture.timeStamp;
+ this._velocity = (dt === 0) ? 0 : ds / dt * 1000;
+
+ this.scale *= scaleSinceStart / this._gestures[this._gestures.length - 2].scale;
+
+ this.enterChangedState();
+ }
+
+ _distance()
+ {
+ console.assert(this.numberOfTouches === 2);
+
+ const firstTouch = this._targetTouches[0];
+ const firstTouchPoint = new DOMPoint(firstTouch.pageX, firstTouch.pageY);
+
+ const secondTouch = this._targetTouches[1];
+ const secondTouchPoint = new DOMPoint(secondTouch.pageX, secondTouch.pageY);
+
+ return Math.sqrt(Math.pow(firstTouchPoint.x - secondTouchPoint.x, 2) + Math.pow(firstTouchPoint.y - secondTouchPoint.y, 2));
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/gesture-recognizers/tap.js b/Source/WebCore/Modules/modern-media-controls/gesture-recognizers/tap.js
new file mode 100644
index 000000000..6f2dbf885
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/gesture-recognizers/tap.js
@@ -0,0 +1,109 @@
+
+const MOVE_TOLERANCE = GestureRecognizer.SupportsTouches ? 40 : 0;
+const WAITING_FOR_NEXT_TAP_TO_START_TIMEOUT = 350;
+const WAITING_FOR_TAP_COMPLETION_TIMEOUT = 750;
+
+class TapGestureRecognizer extends GestureRecognizer
+{
+
+ constructor(target, delegate)
+ {
+ super(target, delegate);
+
+ this.numberOfTapsRequired = 1;
+ this.numberOfTouchesRequired = 1;
+ this.allowsRightMouseButton = false;
+ }
+
+ // Protected
+
+ touchesBegan(event)
+ {
+ if (event.currentTarget !== this.target)
+ return;
+
+ if (event.button === 2 && !this.allowsRightMouseButton)
+ return;
+
+ super.touchesBegan(event);
+
+ if (this.numberOfTouches !== this.numberOfTouchesRequired) {
+ this.enterFailedState();
+ return;
+ }
+
+ this._startPoint = super.locationInElement();
+ this._startClientPoint = super.locationInClient();
+
+ this._rewindTimer(WAITING_FOR_TAP_COMPLETION_TIMEOUT);
+ }
+
+ touchesMoved(event)
+ {
+ event.preventDefault();
+
+ const touchLocation = super.locationInElement();
+ const distance = Math.sqrt(Math.pow(this._startPoint.x - touchLocation.x, 2) + Math.pow(this._startPoint.y - touchLocation.y, 2));
+ if (distance > MOVE_TOLERANCE)
+ this.enterFailedState();
+ }
+
+ touchesEnded(event)
+ {
+ this._taps++;
+
+ if (this._taps === this.numberOfTapsRequired) {
+ // We call prevent default here to override the potential double-tap-to-zoom
+ // behavior of the browser.
+ event.preventDefault();
+
+ this.enterRecognizedState();
+ this.reset();
+ }
+
+ this._rewindTimer(WAITING_FOR_NEXT_TAP_TO_START_TIMEOUT);
+ }
+
+ reset()
+ {
+ this._taps = 0;
+ this._clearTimer();
+ }
+
+ locationInElement(element)
+ {
+ const p = this._startPoint || new DOMPoint;
+
+ if (!element)
+ return p;
+
+ // FIXME: are WebKitPoint and DOMPoint interchangeable?
+ const wkPoint = window.webkitConvertPointFromPageToNode(element, new WebKitPoint(p.x, p.y));
+ return new DOMPoint(wkPoint.x, wkPoint.y);
+ }
+
+ locationInClient()
+ {
+ return this._startClientPoint || new DOMPoint;
+ }
+
+ // Private
+
+ _clearTimer()
+ {
+ window.clearTimeout(this._timerId);
+ delete this._timerId;
+ }
+
+ _rewindTimer(timeout)
+ {
+ this._clearTimer();
+ this._timerId = window.setTimeout(this._timerFired.bind(this), timeout);
+ }
+
+ _timerFired()
+ {
+ this.enterFailedState();
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/images/iOS/airplay-placard@1x.png b/Source/WebCore/Modules/modern-media-controls/images/iOS/airplay-placard@1x.png
new file mode 100644
index 000000000..86061b233
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/iOS/airplay-placard@1x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/iOS/airplay-placard@2x.png b/Source/WebCore/Modules/modern-media-controls/images/iOS/airplay-placard@2x.png
new file mode 100644
index 000000000..83c72eac3
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/iOS/airplay-placard@2x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/iOS/airplay-placard@3x.png b/Source/WebCore/Modules/modern-media-controls/images/iOS/airplay-placard@3x.png
new file mode 100644
index 000000000..b8aaef1b9
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/iOS/airplay-placard@3x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/iOS/airplay@1x.png b/Source/WebCore/Modules/modern-media-controls/images/iOS/airplay@1x.png
new file mode 100644
index 000000000..0b472b915
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/iOS/airplay@1x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/iOS/airplay@2x.png b/Source/WebCore/Modules/modern-media-controls/images/iOS/airplay@2x.png
new file mode 100644
index 000000000..818c9c200
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/iOS/airplay@2x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/iOS/enter-fullscreen@1x.png b/Source/WebCore/Modules/modern-media-controls/images/iOS/enter-fullscreen@1x.png
new file mode 100644
index 000000000..5a2fd22d6
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/iOS/enter-fullscreen@1x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/iOS/enter-fullscreen@2x.png b/Source/WebCore/Modules/modern-media-controls/images/iOS/enter-fullscreen@2x.png
new file mode 100644
index 000000000..2cb880ac0
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/iOS/enter-fullscreen@2x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/iOS/enter-fullscreen@3x.png b/Source/WebCore/Modules/modern-media-controls/images/iOS/enter-fullscreen@3x.png
new file mode 100644
index 000000000..6db049b38
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/iOS/enter-fullscreen@3x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/iOS/interval-skip-back@1x.png b/Source/WebCore/Modules/modern-media-controls/images/iOS/interval-skip-back@1x.png
new file mode 100644
index 000000000..0c1d8b2ef
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/iOS/interval-skip-back@1x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/iOS/interval-skip-back@2x.png b/Source/WebCore/Modules/modern-media-controls/images/iOS/interval-skip-back@2x.png
new file mode 100644
index 000000000..40d12e427
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/iOS/interval-skip-back@2x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/iOS/interval-skip-back@3x.png b/Source/WebCore/Modules/modern-media-controls/images/iOS/interval-skip-back@3x.png
new file mode 100644
index 000000000..c31c36bad
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/iOS/interval-skip-back@3x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/iOS/invalid-placard@1x.png b/Source/WebCore/Modules/modern-media-controls/images/iOS/invalid-placard@1x.png
new file mode 100644
index 000000000..fc4369453
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/iOS/invalid-placard@1x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/iOS/invalid-placard@2x.png b/Source/WebCore/Modules/modern-media-controls/images/iOS/invalid-placard@2x.png
new file mode 100644
index 000000000..e14d3d478
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/iOS/invalid-placard@2x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/iOS/invalid-placard@3x.png b/Source/WebCore/Modules/modern-media-controls/images/iOS/invalid-placard@3x.png
new file mode 100644
index 000000000..bb760ad52
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/iOS/invalid-placard@3x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/iOS/pause@1x.png b/Source/WebCore/Modules/modern-media-controls/images/iOS/pause@1x.png
new file mode 100644
index 000000000..bddf4abc6
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/iOS/pause@1x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/iOS/pause@2x.png b/Source/WebCore/Modules/modern-media-controls/images/iOS/pause@2x.png
new file mode 100644
index 000000000..c3226a1c5
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/iOS/pause@2x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/iOS/pause@3x.png b/Source/WebCore/Modules/modern-media-controls/images/iOS/pause@3x.png
new file mode 100644
index 000000000..cf391a6bf
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/iOS/pause@3x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/iOS/pip-in@1x.png b/Source/WebCore/Modules/modern-media-controls/images/iOS/pip-in@1x.png
new file mode 100644
index 000000000..1c8d80b4d
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/iOS/pip-in@1x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/iOS/pip-in@2x.png b/Source/WebCore/Modules/modern-media-controls/images/iOS/pip-in@2x.png
new file mode 100644
index 000000000..f7d2b921d
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/iOS/pip-in@2x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/iOS/pip-in@3x.png b/Source/WebCore/Modules/modern-media-controls/images/iOS/pip-in@3x.png
new file mode 100644
index 000000000..e8b107ad0
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/iOS/pip-in@3x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/iOS/pip-placard@1x.png b/Source/WebCore/Modules/modern-media-controls/images/iOS/pip-placard@1x.png
new file mode 100644
index 000000000..423963a46
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/iOS/pip-placard@1x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/iOS/pip-placard@2x.png b/Source/WebCore/Modules/modern-media-controls/images/iOS/pip-placard@2x.png
new file mode 100644
index 000000000..bbd4cd84b
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/iOS/pip-placard@2x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/iOS/pip-placard@3x.png b/Source/WebCore/Modules/modern-media-controls/images/iOS/pip-placard@3x.png
new file mode 100644
index 000000000..412eaba59
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/iOS/pip-placard@3x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/iOS/play@1x.png b/Source/WebCore/Modules/modern-media-controls/images/iOS/play@1x.png
new file mode 100644
index 000000000..a49f216b5
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/iOS/play@1x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/iOS/play@2x.png b/Source/WebCore/Modules/modern-media-controls/images/iOS/play@2x.png
new file mode 100644
index 000000000..fa77a634f
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/iOS/play@2x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/iOS/play@3x.png b/Source/WebCore/Modules/modern-media-controls/images/iOS/play@3x.png
new file mode 100644
index 000000000..73e376eae
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/iOS/play@3x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/iOS/slider-thumb@2x.png b/Source/WebCore/Modules/modern-media-controls/images/iOS/slider-thumb@2x.png
new file mode 100644
index 000000000..6c8bd93f4
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/iOS/slider-thumb@2x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/iOS/start@1x.png b/Source/WebCore/Modules/modern-media-controls/images/iOS/start@1x.png
new file mode 100644
index 000000000..e7467373e
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/iOS/start@1x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/iOS/start@2x.png b/Source/WebCore/Modules/modern-media-controls/images/iOS/start@2x.png
new file mode 100644
index 000000000..b1264e8b2
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/iOS/start@2x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/iOS/start@3x.png b/Source/WebCore/Modules/modern-media-controls/images/iOS/start@3x.png
new file mode 100644
index 000000000..a520a18b3
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/iOS/start@3x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/airplay-fullscreen@1x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/airplay-fullscreen@1x.png
new file mode 100644
index 000000000..747927b4b
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/airplay-fullscreen@1x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/airplay-fullscreen@2x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/airplay-fullscreen@2x.png
new file mode 100644
index 000000000..9b4f46f9d
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/airplay-fullscreen@2x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/airplay-placard@1x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/airplay-placard@1x.png
new file mode 100644
index 000000000..86061b233
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/airplay-placard@1x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/airplay-placard@2x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/airplay-placard@2x.png
new file mode 100644
index 000000000..83c72eac3
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/airplay-placard@2x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/airplay@1x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/airplay@1x.png
new file mode 100644
index 000000000..0b472b915
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/airplay@1x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/airplay@2x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/airplay@2x.png
new file mode 100644
index 000000000..818c9c200
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/airplay@2x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/enter-fullscreen-compact@1x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/enter-fullscreen-compact@1x.png
new file mode 100644
index 000000000..3549780cb
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/enter-fullscreen-compact@1x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/enter-fullscreen-compact@2x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/enter-fullscreen-compact@2x.png
new file mode 100644
index 000000000..e592d7912
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/enter-fullscreen-compact@2x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/enter-fullscreen@1x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/enter-fullscreen@1x.png
new file mode 100644
index 000000000..5a2fd22d6
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/enter-fullscreen@1x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/enter-fullscreen@2x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/enter-fullscreen@2x.png
new file mode 100644
index 000000000..2cb880ac0
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/enter-fullscreen@2x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/exit-fullscreen@1x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/exit-fullscreen@1x.png
new file mode 100644
index 000000000..ecc432210
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/exit-fullscreen@1x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/exit-fullscreen@2x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/exit-fullscreen@2x.png
new file mode 100644
index 000000000..77a206527
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/exit-fullscreen@2x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/forward@1x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/forward@1x.png
new file mode 100644
index 000000000..ee795a8c2
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/forward@1x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/forward@2x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/forward@2x.png
new file mode 100644
index 000000000..691015fb5
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/forward@2x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/interval-skip-back-compact@1x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/interval-skip-back-compact@1x.png
new file mode 100644
index 000000000..4ce2060c0
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/interval-skip-back-compact@1x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/interval-skip-back-compact@2x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/interval-skip-back-compact@2x.png
new file mode 100644
index 000000000..5bf10d364
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/interval-skip-back-compact@2x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/interval-skip-back@1x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/interval-skip-back@1x.png
new file mode 100644
index 000000000..0c1d8b2ef
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/interval-skip-back@1x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/interval-skip-back@2x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/interval-skip-back@2x.png
new file mode 100644
index 000000000..40d12e427
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/interval-skip-back@2x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/invalid-placard@1x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/invalid-placard@1x.png
new file mode 100644
index 000000000..fc4369453
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/invalid-placard@1x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/invalid-placard@2x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/invalid-placard@2x.png
new file mode 100644
index 000000000..e14d3d478
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/invalid-placard@2x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/media-selection-fullscreen@1x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/media-selection-fullscreen@1x.png
new file mode 100644
index 000000000..9d0764363
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/media-selection-fullscreen@1x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/media-selection-fullscreen@2x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/media-selection-fullscreen@2x.png
new file mode 100644
index 000000000..f50ca3b18
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/media-selection-fullscreen@2x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/media-selection@1x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/media-selection@1x.png
new file mode 100644
index 000000000..5df5edb35
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/media-selection@1x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/media-selection@2x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/media-selection@2x.png
new file mode 100644
index 000000000..0cfb6b2be
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/media-selection@2x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/pause-compact@1x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/pause-compact@1x.png
new file mode 100644
index 000000000..6a489258f
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/pause-compact@1x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/pause-compact@2x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/pause-compact@2x.png
new file mode 100644
index 000000000..50ed11f20
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/pause-compact@2x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/pause-fullscreen@1x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/pause-fullscreen@1x.png
new file mode 100644
index 000000000..ee41eb7c9
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/pause-fullscreen@1x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/pause-fullscreen@2x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/pause-fullscreen@2x.png
new file mode 100644
index 000000000..dd49890a8
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/pause-fullscreen@2x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/pause@1x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/pause@1x.png
new file mode 100644
index 000000000..873b93020
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/pause@1x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/pause@2x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/pause@2x.png
new file mode 100644
index 000000000..b93a5e509
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/pause@2x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/pip-in-fullscreen@1x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/pip-in-fullscreen@1x.png
new file mode 100644
index 000000000..f7a64bbd1
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/pip-in-fullscreen@1x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/pip-in-fullscreen@2x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/pip-in-fullscreen@2x.png
new file mode 100644
index 000000000..fe721f737
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/pip-in-fullscreen@2x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/pip-in@1x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/pip-in@1x.png
new file mode 100644
index 000000000..1c8d80b4d
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/pip-in@1x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/pip-in@2x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/pip-in@2x.png
new file mode 100644
index 000000000..f7d2b921d
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/pip-in@2x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/pip-placard@1x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/pip-placard@1x.png
new file mode 100644
index 000000000..423963a46
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/pip-placard@1x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/pip-placard@2x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/pip-placard@2x.png
new file mode 100644
index 000000000..bbd4cd84b
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/pip-placard@2x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/play-compact@1x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/play-compact@1x.png
new file mode 100644
index 000000000..5c8ad4a15
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/play-compact@1x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/play-compact@2x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/play-compact@2x.png
new file mode 100644
index 000000000..b6755cd39
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/play-compact@2x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/play-fullscreen@1x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/play-fullscreen@1x.png
new file mode 100644
index 000000000..a49f216b5
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/play-fullscreen@1x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/play-fullscreen@2x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/play-fullscreen@2x.png
new file mode 100644
index 000000000..fa77a634f
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/play-fullscreen@2x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/play@1x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/play@1x.png
new file mode 100644
index 000000000..a49f216b5
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/play@1x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/play@2x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/play@2x.png
new file mode 100644
index 000000000..c67d8e3dd
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/play@2x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/rewind@1x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/rewind@1x.png
new file mode 100644
index 000000000..659e984ce
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/rewind@1x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/rewind@2x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/rewind@2x.png
new file mode 100644
index 000000000..876ab734b
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/rewind@2x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/scale-to-fill@1x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/scale-to-fill@1x.png
new file mode 100644
index 000000000..67171b6ab
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/scale-to-fill@1x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/scale-to-fill@2x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/scale-to-fill@2x.png
new file mode 100644
index 000000000..b2c1df756
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/scale-to-fill@2x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/scale-to-fit@1x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/scale-to-fit@1x.png
new file mode 100644
index 000000000..7034aacfd
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/scale-to-fit@1x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/scale-to-fit@2x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/scale-to-fit@2x.png
new file mode 100644
index 000000000..324de8b1f
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/scale-to-fit@2x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/start@1x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/start@1x.png
new file mode 100644
index 000000000..e7467373e
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/start@1x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/start@2x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/start@2x.png
new file mode 100644
index 000000000..b1264e8b2
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/start@2x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/volume-compact@1x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/volume-compact@1x.png
new file mode 100644
index 000000000..dc65736b2
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/volume-compact@1x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/volume-compact@2x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/volume-compact@2x.png
new file mode 100644
index 000000000..bfe1a8b87
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/volume-compact@2x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/volume-down-fullscreen@1x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/volume-down-fullscreen@1x.png
new file mode 100644
index 000000000..1660737ad
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/volume-down-fullscreen@1x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/volume-down-fullscreen@2x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/volume-down-fullscreen@2x.png
new file mode 100644
index 000000000..5e35ea528
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/volume-down-fullscreen@2x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/volume-mute@1x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/volume-mute@1x.png
new file mode 100644
index 000000000..3ba71892f
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/volume-mute@1x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/volume-mute@2x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/volume-mute@2x.png
new file mode 100644
index 000000000..67c2034ab
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/volume-mute@2x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/volume-up-fullscreen@1x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/volume-up-fullscreen@1x.png
new file mode 100644
index 000000000..4c1cef883
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/volume-up-fullscreen@1x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/volume-up-fullscreen@2x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/volume-up-fullscreen@2x.png
new file mode 100644
index 000000000..238cb645d
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/volume-up-fullscreen@2x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/volume@1x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/volume@1x.png
new file mode 100644
index 000000000..b45490ae3
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/volume@1x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/volume@2x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/volume@2x.png
new file mode 100644
index 000000000..5bec45c16
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/volume@2x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/js-files b/Source/WebCore/Modules/modern-media-controls/js-files
new file mode 100644
index 000000000..b5ca1eea6
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/js-files
@@ -0,0 +1,63 @@
+gesture-recognizers/gesture-recognizer.js
+gesture-recognizers/tap.js
+gesture-recognizers/pinch.js
+controls/scheduler.js
+controls/layout-node.js
+controls/layout-item.js
+controls/icon-service.js
+controls/background-tint.js
+controls/time-control.js
+controls/time-label.js
+controls/slider.js
+controls/volume-slider.js
+controls/scrubber.js
+controls/button.js
+controls/start-button.js
+controls/icon-button.js
+controls/play-pause-button.js
+controls/skip-back-button.js
+controls/mute-button.js
+controls/airplay-button.js
+controls/pip-button.js
+controls/tracks-button.js
+controls/fullscreen-button.js
+controls/seek-button.js
+controls/rewind-button.js
+controls/forward-button.js
+controls/volume-down-button.js
+controls/volume-up-button.js
+controls/buttons-container.js
+controls/status-label.js
+controls/controls-bar.js
+controls/tracks-panel.js
+controls/media-controls.js
+controls/ios-inline-media-controls.js
+controls/macos-media-controls.js
+controls/macos-inline-media-controls.js
+controls/macos-fullscreen-media-controls.js
+controls/placard.js
+controls/airplay-placard.js
+controls/invalid-placard.js
+controls/pip-placard.js
+media/media-controller-support.js
+media/airplay-support.js
+media/controls-visibility-support.js
+media/fullscreen-support.js
+media/mute-support.js
+media/pip-support.js
+media/placard-support.js
+media/playback-support.js
+media/scrubbing-support.js
+media/seek-support.js
+media/seek-backward-support.js
+media/seek-forward-support.js
+media/skip-back-support.js
+media/start-support.js
+media/status-support.js
+media/time-labels-support.js
+media/tracks-support.js
+media/volume-down-support.js
+media/volume-support.js
+media/volume-up-support.js
+media/media-controller.js
+main.js
diff --git a/Source/WebCore/Modules/modern-media-controls/main.js b/Source/WebCore/Modules/modern-media-controls/main.js
new file mode 100644
index 000000000..237335b7a
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/main.js
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// This is called from HTMLMediaElement::ensureMediaControlsInjectedScript().
+function createControls(shadowRoot, media, host)
+{
+ if (host) {
+ iconService.mediaControlsHost = host;
+ shadowRoot.appendChild(document.createElement("style")).textContent = host.shadowRootCSSText;
+ }
+
+ return new MediaController(shadowRoot, media, host);
+}
+
+function UIString(string)
+{
+ if (!("UIStrings" in window))
+ return string;
+
+ if (string in UIStrings)
+ return UIStrings[string];
+
+ console.error(`Localization for "${string}" not found.`);
+ return "LOCALIZED STRING NOT FOUND";
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/media/airplay-support.js b/Source/WebCore/Modules/modern-media-controls/media/airplay-support.js
new file mode 100644
index 000000000..e3d725c05
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/media/airplay-support.js
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+class AirplaySupport extends MediaControllerSupport
+{
+
+ // Protected
+
+ get control()
+ {
+ return this.mediaController.controls.airplayButton;
+ }
+
+ get mediaEvents()
+ {
+ return ["webkitplaybacktargetavailabilitychanged", "webkitcurrentplaybacktargetiswirelesschanged"];
+ }
+
+ buttonWasPressed(control)
+ {
+ this.mediaController.media.webkitShowPlaybackTargetPicker();
+ }
+
+ handleEvent(event)
+ {
+ if (event.type === "webkitplaybacktargetavailabilitychanged")
+ this._routesAvailable = event.availability === "available";
+
+ super.handleEvent(event);
+ }
+
+ syncControl()
+ {
+ this.control.enabled = !!this._routesAvailable;
+ this.control.on = this.mediaController.media.webkitCurrentPlaybackTargetIsWireless;
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/media/controls-visibility-support.js b/Source/WebCore/Modules/modern-media-controls/media/controls-visibility-support.js
new file mode 100644
index 000000000..498fa544d
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/media/controls-visibility-support.js
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+class ControlsVisibilitySupport extends MediaControllerSupport
+{
+
+ constructor(mediaController)
+ {
+ super(mediaController);
+
+ this._controlsAttributeObserver = new MutationObserver(this._updateControls.bind(this));
+ this._controlsAttributeObserver.observe(mediaController.media, { attributes: true, attributeFilter: ["controls"] });
+
+ this._updateControls();
+ }
+
+ // Protected
+
+ destroy()
+ {
+ this._controlsAttributeObserver.disconnect();
+ }
+
+ get mediaEvents()
+ {
+ return ["loadedmetadata", "play", "pause"];
+ }
+
+ get tracksToMonitor()
+ {
+ return [this.mediaController.media.videoTracks];
+ }
+
+ handleEvent()
+ {
+ this._updateControls();
+ }
+
+ // Private
+
+ _updateControls()
+ {
+ const media = this.mediaController.media;
+ const isVideo = media instanceof HTMLVideoElement && media.videoTracks.length > 0;
+ let shouldShowControls = media.controls && !!media.currentSrc;
+ if (isVideo)
+ shouldShowControls = shouldShowControls && media.readyState > HTMLMediaElement.HAVE_NOTHING;
+
+ const controls = this.mediaController.controls;
+ controls.startButton.visible = shouldShowControls;
+ controls.controlsBar.visible = shouldShowControls;
+ controls.controlsBar.fadesWhileIdle = isVideo ? !media.paused : false;
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/media/fullscreen-support.js b/Source/WebCore/Modules/modern-media-controls/media/fullscreen-support.js
new file mode 100644
index 000000000..168da00b6
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/media/fullscreen-support.js
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+class FullscreenSupport extends MediaControllerSupport
+{
+
+ constructor(mediaController)
+ {
+ super(mediaController);
+
+ if (mediaController.controls instanceof IOSInlineMediaControls)
+ mediaController.controls.delegate = this;
+ }
+
+ // Protected
+
+ get control()
+ {
+ return this.mediaController.controls.fullscreenButton;
+ }
+
+ get mediaEvents()
+ {
+ return ["loadedmetadata", "error"];
+ }
+
+ get tracksToMonitor()
+ {
+ return [this.mediaController.media.videoTracks];
+ }
+
+ buttonWasPressed(control)
+ {
+ const media = this.mediaController.media;
+ if (media.webkitDisplayingFullscreen)
+ media.webkitExitFullscreen();
+ else
+ media.webkitEnterFullscreen();
+ }
+
+ iOSInlineMediaControlsRecognizedPinchInGesture()
+ {
+ this.mediaController.media.webkitEnterFullscreen();
+ }
+
+ syncControl()
+ {
+ const control = this.control;
+ const media = this.mediaController.media;
+ control.enabled = media.webkitSupportsFullscreen && media.videoTracks.length > 0;
+ control.isFullScreen = media.webkitDisplayingFullscreen;
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/media/media-controller-support.js b/Source/WebCore/Modules/modern-media-controls/media/media-controller-support.js
new file mode 100644
index 000000000..55c42b175
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/media/media-controller-support.js
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+class MediaControllerSupport
+{
+
+ constructor(mediaController)
+ {
+ this.mediaController = mediaController;
+
+ for (let eventType of this.mediaEvents)
+ mediaController.media.addEventListener(eventType, this);
+
+ for (let tracks of this.tracksToMonitor) {
+ for (let eventType of ["change", "addtrack", "removetrack"])
+ tracks.addEventListener(eventType, this);
+ }
+
+ if (!this.control)
+ return;
+
+ this.syncControl();
+
+ this.control.uiDelegate = this;
+ }
+
+ // Public
+
+ destroy()
+ {
+ const media = this.mediaController.media;
+ for (let eventType of this.mediaEvents)
+ media.removeEventListener(eventType, this);
+
+ for (let tracks of this.tracksToMonitor) {
+ for (let eventType of ["change", "addtrack", "removetrack"])
+ tracks.removeEventListener(eventType, this);
+ }
+
+ if (this.control)
+ this.control.uiDelegate = null;
+ }
+
+ // Protected
+
+ get control()
+ {
+ // Implemented by subclasses.
+ }
+
+ get mediaEvents()
+ {
+ // Implemented by subclasses.
+ return [];
+ }
+
+ get tracksToMonitor()
+ {
+ // Implemented by subclasses.
+ return [];
+ }
+
+ buttonWasPressed(control)
+ {
+ // Implemented by subclasses.
+ }
+
+ handleEvent(event)
+ {
+ // Implemented by subclasses.
+ if (this.control)
+ this.syncControl();
+ }
+
+ syncControl()
+ {
+ // Implemented by subclasses.
+ }
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/media/media-controller.js b/Source/WebCore/Modules/modern-media-controls/media/media-controller.js
new file mode 100644
index 000000000..710517482
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/media/media-controller.js
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+const CompactModeMaxWidth = 241;
+const ReducedPaddingMaxWidth = 300;
+const AudioTightPaddingMaxWidth = 400;
+
+class MediaController
+{
+
+ constructor(shadowRoot, media, host)
+ {
+ this.shadowRoot = shadowRoot;
+ this.media = media;
+ this.host = host;
+
+ this.container = shadowRoot.appendChild(document.createElement("div"));
+ this.container.className = "media-controls-container";
+ this.container.addEventListener("click", this, true);
+
+ if (host) {
+ host.controlsDependOnPageScaleFactor = this.layoutTraits & LayoutTraits.iOS;
+ this.container.appendChild(host.textTrackContainer);
+ }
+
+ this._updateControlsIfNeeded();
+
+ shadowRoot.addEventListener("resize", this);
+
+ media.videoTracks.addEventListener("addtrack", this);
+ media.videoTracks.addEventListener("removetrack", this);
+
+ if (media.webkitSupportsPresentationMode)
+ media.addEventListener("webkitpresentationmodechanged", this);
+ else
+ media.addEventListener("webkitfullscreenchange", this);
+ }
+
+ // Public
+
+ get layoutTraits()
+ {
+ let traits = window.navigator.platform === "MacIntel" ? LayoutTraits.macOS : LayoutTraits.iOS;
+ if (this.media.webkitSupportsPresentationMode) {
+ if (this.media.webkitPresentationMode === "fullscreen")
+ return traits | LayoutTraits.Fullscreen;
+ } else if (this.media.webkitDisplayingFullscreen)
+ return traits | LayoutTraits.Fullscreen;
+
+ const controlsWidth = this._controlsWidth();
+ if (controlsWidth <= CompactModeMaxWidth)
+ return traits | LayoutTraits.Compact;
+
+ const isAudio = this.media instanceof HTMLAudioElement || this.media.videoTracks.length === 0;
+ if (isAudio && controlsWidth <= AudioTightPaddingMaxWidth)
+ return traits | LayoutTraits.TightPadding;
+
+ if (!isAudio && controlsWidth <= ReducedPaddingMaxWidth)
+ return traits | LayoutTraits.ReducedPadding;
+
+ return traits;
+ }
+
+ togglePlayback()
+ {
+ if (this.media.paused)
+ this.media.play();
+ else
+ this.media.pause();
+ }
+
+ // Protected
+
+ set pageScaleFactor(pageScaleFactor)
+ {
+ this.controls.scaleFactor = pageScaleFactor;
+ this._updateControlsSize();
+ }
+
+ set usesLTRUserInterfaceLayoutDirection(flag)
+ {
+ this.controls.usesLTRUserInterfaceLayoutDirection = flag;
+ }
+
+ macOSControlsBackgroundWasClicked()
+ {
+ // Toggle playback when clicking on the video but not on any controls on macOS.
+ if (this.media.controls)
+ this.togglePlayback();
+ }
+
+ handleEvent(event)
+ {
+ if (event instanceof TrackEvent && event.currentTarget === this.media.videoTracks)
+ this._updateControlsIfNeeded();
+ else if (event.type === "resize" && event.currentTarget === this.shadowRoot)
+ this._updateControlsIfNeeded();
+ else if (event.type === "click" && event.currentTarget === this.container)
+ this._containerWasClicked(event);
+ else if (event.currentTarget === this.media) {
+ this._updateControlsIfNeeded();
+ if (event.type === "webkitpresentationmodechanged")
+ this._returnMediaLayerToInlineIfNeeded();
+ }
+ }
+
+ // Private
+
+ _containerWasClicked(event)
+ {
+ // We need to call preventDefault() here since, in the case of Media Documents,
+ // playback may be toggled when clicking on the video.
+ event.preventDefault();
+ }
+
+ _updateControlsIfNeeded()
+ {
+ const layoutTraits = this.layoutTraits;
+ const previousControls = this.controls;
+ const ControlsClass = this._controlsClassForLayoutTraits(layoutTraits);
+ if (previousControls && previousControls.constructor === ControlsClass) {
+ this.controls.layoutTraits = layoutTraits;
+ this._updateControlsSize();
+ return;
+ }
+
+ // Before we reset the .controls property, we need to destroy the previous
+ // supporting objects so we don't leak.
+ if (this._supportingObjects) {
+ for (let supportingObject of this._supportingObjects)
+ supportingObject.destroy();
+ }
+
+ this.controls = new ControlsClass;
+ this.controls.delegate = this;
+
+ if (this.shadowRoot.host && this.shadowRoot.host.dataset.autoHideDelay)
+ this.controls.controlsBar.autoHideDelay = this.shadowRoot.host.dataset.autoHideDelay;
+
+ if (previousControls) {
+ this.controls.fadeIn();
+ this.container.replaceChild(this.controls.element, previousControls.element);
+ this.controls.usesLTRUserInterfaceLayoutDirection = previousControls.usesLTRUserInterfaceLayoutDirection;
+ } else
+ this.container.appendChild(this.controls.element);
+
+ this.controls.layoutTraits = layoutTraits;
+ this._updateControlsSize();
+
+ this._supportingObjects = [AirplaySupport, ControlsVisibilitySupport, FullscreenSupport, MuteSupport, PiPSupport, PlacardSupport, PlaybackSupport, ScrubbingSupport, SeekBackwardSupport, SeekForwardSupport, SkipBackSupport, StartSupport, StatusSupport, TimeLabelsSupport, TracksSupport, VolumeSupport, VolumeDownSupport, VolumeUpSupport].map(SupportClass => {
+ return new SupportClass(this);
+ }, this);
+ }
+
+ _updateControlsSize()
+ {
+ this.controls.width = this._controlsWidth();
+ this.controls.height = Math.round(this.media.offsetHeight * this.controls.scaleFactor);
+ }
+
+ _controlsWidth()
+ {
+ return Math.round(this.media.offsetWidth * (this.controls ? this.controls.scaleFactor : 1));
+ }
+
+ _returnMediaLayerToInlineIfNeeded()
+ {
+ if (this.host)
+ window.requestAnimationFrame(() => this.host.setPreparedToReturnVideoLayerToInline(this.media.webkitPresentationMode !== PiPMode));
+ }
+
+ _controlsClassForLayoutTraits(layoutTraits)
+ {
+ if (layoutTraits & LayoutTraits.iOS)
+ return IOSInlineMediaControls;
+ if (layoutTraits & LayoutTraits.Fullscreen)
+ return MacOSFullscreenMediaControls;
+ return MacOSInlineMediaControls;
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/media/mute-support.js b/Source/WebCore/Modules/modern-media-controls/media/mute-support.js
new file mode 100644
index 000000000..d733e423b
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/media/mute-support.js
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+class MuteSupport extends MediaControllerSupport
+{
+
+ // Protected
+
+ get control()
+ {
+ return this.mediaController.controls.muteButton;
+ }
+
+ get mediaEvents()
+ {
+ return ["volumechange"];
+ }
+
+ buttonWasPressed(control)
+ {
+ const media = this.mediaController.media;
+ media.muted = !media.muted;
+ }
+
+ syncControl()
+ {
+ this.control.muted = this.mediaController.media.muted;
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/media/pip-support.js b/Source/WebCore/Modules/modern-media-controls/media/pip-support.js
new file mode 100644
index 000000000..2a73a3182
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/media/pip-support.js
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+const PiPMode = "picture-in-picture";
+const InlineMode = "inline";
+
+class PiPSupport extends MediaControllerSupport
+{
+
+ // Protected
+
+ get control()
+ {
+ return this.mediaController.controls.pipButton;
+ }
+
+ get mediaEvents()
+ {
+ return ["loadedmetadata", "error", "webkitpresentationmodechanged", "webkitcurrentplaybacktargetiswirelesschanged"];
+ }
+
+ get tracksToMonitor()
+ {
+ return [this.mediaController.media.videoTracks];
+ }
+
+ buttonWasPressed(control)
+ {
+ const media = this.mediaController.media;
+ media.webkitSetPresentationMode(media.webkitPresentationMode === PiPMode ? InlineMode : PiPMode);
+ }
+
+ syncControl()
+ {
+ const media = this.mediaController.media;
+ if (media.webkitSupportsPresentationMode)
+ this.control.enabled = media instanceof HTMLVideoElement && media.webkitSupportsPresentationMode(PiPMode) && !media.webkitCurrentPlaybackTargetIsWireless;
+ else
+ this.control.enabled = false;
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/media/placard-support.js b/Source/WebCore/Modules/modern-media-controls/media/placard-support.js
new file mode 100644
index 000000000..5ef401c26
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/media/placard-support.js
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+class PlacardSupport extends MediaControllerSupport
+{
+
+ constructor(mediaController)
+ {
+ super(mediaController);
+ this._updatePlacard();
+ }
+
+ // Protected
+
+ get mediaEvents()
+ {
+ return ["error", "webkitpresentationmodechanged", "webkitcurrentplaybacktargetiswirelesschanged"];
+ }
+
+ handleEvent(event)
+ {
+ this._updatePlacard();
+ }
+
+ // Private
+
+ _updatePlacard()
+ {
+ const controls = this.mediaController.controls;
+
+ const media = this.mediaController.media;
+ if (media.webkitPresentationMode === "picture-in-picture")
+ controls.showPlacard(controls.pipPlacard);
+ else if (media.webkitCurrentPlaybackTargetIsWireless)
+ controls.showPlacard(controls.airplayPlacard);
+ else if (media instanceof HTMLVideoElement && media.error !== null && media.played.length === 0)
+ controls.showPlacard(controls.invalidPlacard);
+ else
+ controls.hidePlacard();
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/media/playback-support.js b/Source/WebCore/Modules/modern-media-controls/media/playback-support.js
new file mode 100644
index 000000000..9e27e43e5
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/media/playback-support.js
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+class PlaybackSupport extends MediaControllerSupport
+{
+
+ // Protected
+
+ get control()
+ {
+ return this.mediaController.controls.playPauseButton;
+ }
+
+ get mediaEvents()
+ {
+ return ["play", "pause"];
+ }
+
+ buttonWasPressed(control)
+ {
+ this.mediaController.togglePlayback();
+ }
+
+ syncControl()
+ {
+ this.control.playing = !this.mediaController.media.paused;
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/media/scrubbing-support.js b/Source/WebCore/Modules/modern-media-controls/media/scrubbing-support.js
new file mode 100644
index 000000000..3d9bcd68d
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/media/scrubbing-support.js
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+class ScrubbingSupport extends MediaControllerSupport
+{
+
+ // Protected
+
+ get control()
+ {
+ return this.mediaController.controls.timeControl.scrubber;
+ }
+
+ get mediaEvents()
+ {
+ return ["timeupdate", "progress"];
+ }
+
+ controlValueWillStartChanging(control)
+ {
+ const media = this.mediaController.media;
+ const isPaused = media.paused;
+ if (!isPaused)
+ media.pause();
+
+ this._wasPausedWhenScrubbingStarted = isPaused;
+ }
+
+ controlValueDidChange(control)
+ {
+ const media = this.mediaController.media;
+ media.fastSeek(control.value * media.duration);
+ }
+
+ controlValueDidStopChanging(control)
+ {
+ if (!this._wasPausedWhenScrubbingStarted)
+ this.mediaController.media.play();
+
+ delete this._wasPausedWhenScrubbingStarted;
+ }
+
+ syncControl()
+ {
+ const media = this.mediaController.media;
+ if (isNaN(media.duration))
+ return;
+
+ let buffered = 0;
+ for (let i = 0, count = media.buffered.length; i < count; ++i)
+ buffered = Math.max(media.buffered.end(i), buffered);
+
+ this.control.buffered = buffered / media.duration;
+ this.control.value = media.currentTime / media.duration;
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/media/seek-backward-support.js b/Source/WebCore/Modules/modern-media-controls/media/seek-backward-support.js
new file mode 100644
index 000000000..cc00201f8
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/media/seek-backward-support.js
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+class SeekBackwardSupport extends SeekSupport
+{
+
+ get control()
+ {
+ return this.mediaController.controls.rewindButton;
+ }
+
+ get multiplier()
+ {
+ return -1;
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/media/seek-forward-support.js b/Source/WebCore/Modules/modern-media-controls/media/seek-forward-support.js
new file mode 100644
index 000000000..6c98df24f
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/media/seek-forward-support.js
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+class SeekForwardSupport extends SeekSupport
+{
+
+ get control()
+ {
+ return this.mediaController.controls.forwardButton;
+ }
+
+ get multiplier()
+ {
+ return 1;
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/media/seek-support.js b/Source/WebCore/Modules/modern-media-controls/media/seek-support.js
new file mode 100644
index 000000000..596f2bc80
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/media/seek-support.js
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+class SeekSupport extends MediaControllerSupport
+{
+
+ // Protected
+
+ get multiplier()
+ {
+ // Implemented by subclasses.
+ }
+
+ buttonPressedStateDidChange(control, isPressed)
+ {
+ if (isPressed)
+ this._startSeeking();
+ else
+ this._stopSeeking();
+ }
+
+ // Private
+
+ _startSeeking()
+ {
+ const media = this.mediaController.media;
+ const isPaused = media.paused;
+ if (isPaused)
+ media.play();
+
+ this._wasPausedWhenSeekingStarted = isPaused;
+ this._interval = window.setInterval(this._seek.bind(this), SeekSupport.SeekDelay);
+ this._seek();
+ }
+
+ _stopSeeking()
+ {
+ const media = this.mediaController.media;
+ media.playbackRate = media.defaultPlaybackRate;
+ if (this._wasPausedWhenSeekingStarted)
+ media.pause();
+ if (this._interval)
+ window.clearInterval(this._interval);
+ }
+
+ _seek()
+ {
+ const media = this.mediaController.media;
+ media.playbackRate = Math.min(SeekSupport.MaximumSeekRate, Math.abs(media.playbackRate * 2)) * this.multiplier;
+ }
+
+}
+
+SeekSupport.MaximumSeekRate = 8;
+SeekSupport.SeekDelay = 1500;
diff --git a/Source/WebCore/Modules/modern-media-controls/media/skip-back-support.js b/Source/WebCore/Modules/modern-media-controls/media/skip-back-support.js
new file mode 100644
index 000000000..ea07c703a
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/media/skip-back-support.js
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+const SkipBackSeconds = 30;
+
+class SkipBackSupport extends MediaControllerSupport
+{
+
+ // Protected
+
+ get control()
+ {
+ return this.mediaController.controls.skipBackButton;
+ }
+
+ get mediaEvents()
+ {
+ return ["durationchange"];
+ }
+
+ buttonWasPressed(control)
+ {
+ const media = this.mediaController.media;
+ media.currentTime = Math.max(media.currentTime - SkipBackSeconds, media.seekable.start(0));
+ }
+
+ syncControl()
+ {
+ this.control.enabled = this.mediaController.media.duration !== Number.POSITIVE_INFINITY;
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/media/start-support.js b/Source/WebCore/Modules/modern-media-controls/media/start-support.js
new file mode 100644
index 000000000..bd9d7a312
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/media/start-support.js
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+class StartSupport extends MediaControllerSupport
+{
+
+ // Protected
+
+ get control()
+ {
+ return this.mediaController.controls.startButton;
+ }
+
+ get mediaEvents()
+ {
+ return ["loadedmetadata", "play", "error", "webkitfullscreenchange"];
+ }
+
+ buttonWasPressed(control)
+ {
+ this.mediaController.media.play();
+ }
+
+ handleEvent(event)
+ {
+ if (event.type === "play")
+ this._hasPlayed = true;
+
+ super.handleEvent(event);
+ }
+
+ syncControl()
+ {
+ this.mediaController.controls.showsStartButton = this._shouldShowStartButton();
+ }
+
+ // Private
+
+ _shouldShowStartButton()
+ {
+ const media = this.mediaController.media;
+
+ if (this._hasPlayed || media.played.length)
+ return false;
+
+ if (!media.paused)
+ return false;
+
+ if (media.autoplay)
+ return false;
+
+ if (media instanceof HTMLAudioElement)
+ return false;
+
+ if (media.webkitDisplayingFullscreen)
+ return false;
+
+ if (!media.currentSrc)
+ return false;
+
+ if (media.error)
+ return false;
+
+ const host = this.mediaController.host;
+ if (!media.controls && host && host.allowsInlineMediaPlayback)
+ return false;
+
+ return true;
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/media/status-support.js b/Source/WebCore/Modules/modern-media-controls/media/status-support.js
new file mode 100644
index 000000000..206c73032
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/media/status-support.js
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+class StatusSupport extends MediaControllerSupport
+{
+
+ // Protected
+
+ get control()
+ {
+ return this.mediaController.controls.statusLabel;
+ }
+
+ get mediaEvents()
+ {
+ return ["durationchange", "loadstart", "error", "abort", "suspend", "stalled", "waiting", "emptied", "loadedmetadata", "loadeddata", "canplay", "canplaythrough"];
+ }
+
+ syncControl()
+ {
+ const media = this.mediaController.media;
+ const isLiveBroadcast = media.duration === Number.POSITIVE_INFINITY;
+ const isPlayable = media.readyState > HTMLMediaElement.HAVE_METADATA && !media.error;
+
+ if (!!media.error)
+ this.control.text = UIString("Error");
+ else if (isLiveBroadcast && media.readyState >= HTMLMediaElement.HAVE_CURRENT_DATA)
+ this.control.text = UIString("Live Broadcast");
+ else if (!isPlayable && media.networkState === HTMLMediaElement.NETWORK_LOADING)
+ this.control.text = UIString("Loading");
+ else
+ this.control.text = "";
+
+ this.control.enabled = isLiveBroadcast || !isPlayable;
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/media/time-labels-support.js b/Source/WebCore/Modules/modern-media-controls/media/time-labels-support.js
new file mode 100644
index 000000000..e9b72d227
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/media/time-labels-support.js
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+class TimeLabelsSupport extends MediaControllerSupport
+{
+
+ // Protected
+
+ get control()
+ {
+ return this.mediaController.controls.timeControl;
+ }
+
+ get mediaEvents()
+ {
+ return ["timeupdate", "durationchange"];
+ }
+
+ syncControl()
+ {
+ const media = this.mediaController.media;
+ const shouldShowZeroDurations = isNaN(media.duration) || media.duration === Number.POSITIVE_INFINITY;
+
+ this.control.elapsedTimeLabel.value = shouldShowZeroDurations ? 0 : media.currentTime;
+ this.control.remainingTimeLabel.value = shouldShowZeroDurations ? 0 : (media.currentTime - media.duration);
+ this.control.labelsMayDisplayTimesOverAnHour = !shouldShowZeroDurations && media.duration >= (60 * 60);
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/media/tracks-support.js b/Source/WebCore/Modules/modern-media-controls/media/tracks-support.js
new file mode 100644
index 000000000..d0bdf4b99
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/media/tracks-support.js
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+class TracksSupport extends MediaControllerSupport
+{
+
+ constructor(mediaController)
+ {
+ super(mediaController);
+
+ if (!this.control)
+ return;
+
+ this.mediaController.controls.tracksPanel.dataSource = this;
+ this.mediaController.controls.tracksPanel.uiDelegate = this;
+ }
+
+ // Protected
+
+ get control()
+ {
+ return this.mediaController.controls.tracksButton;
+ }
+
+ get mediaEvents()
+ {
+ return ["loadedmetadata"];
+ }
+
+ get tracksToMonitor()
+ {
+ return [this.mediaController.media.audioTracks, this.mediaController.media.textTracks];
+ }
+
+ buttonWasPressed(control)
+ {
+ this.mediaController.controls.showTracksPanel();
+ }
+
+ tracksPanelNumberOfSections()
+ {
+ let numberOfSections = 0;
+ if (this._canPickAudioTracks())
+ numberOfSections++;
+ if (this._canPickTextTracks())
+ numberOfSections++;
+ return numberOfSections;
+ }
+
+ tracksPanelTitleForSection(sectionIndex)
+ {
+ if (sectionIndex == 0 && this._canPickAudioTracks())
+ return UIString("Audio");
+ return UIString("Subtitles");
+ }
+
+ tracksPanelNumberOfTracksInSection(sectionIndex)
+ {
+ if (sectionIndex == 0 && this._canPickAudioTracks())
+ return this._audioTracks().length;
+ return this._textTracks().length;
+ }
+
+ tracksPanelTitleForTrackInSection(trackIndex, sectionIndex)
+ {
+ let track;
+ if (sectionIndex == 0 && this._canPickAudioTracks())
+ track = this._audioTracks()[trackIndex];
+ else
+ track = this._textTracks()[trackIndex];
+
+ if (this.mediaController.host)
+ return this.mediaController.host.displayNameForTrack(track);
+ return track.label;
+ }
+
+ tracksPanelIsTrackInSectionSelected(trackIndex, sectionIndex)
+ {
+ if (sectionIndex == 0 && this._canPickAudioTracks())
+ return this._audioTracks()[trackIndex].enabled;
+ return this._textTracks()[trackIndex].mode !== "disabled";
+ }
+
+ tracksPanelSelectionDidChange(trackIndex, sectionIndex)
+ {
+ if (sectionIndex == 0 && this._canPickAudioTracks())
+ this._audioTracks().forEach((audioTrack, index) => audioTrack.enabled = index === trackIndex);
+ else
+ this._textTracks().forEach((textTrack, index) => textTrack.mode = index === trackIndex ? "showing" : "disabled");
+
+ this.mediaController.controls.hideTracksPanel();
+ }
+
+ syncControl()
+ {
+ this.control.enabled = this._canPickAudioTracks() || this._canPickTextTracks();
+ }
+
+ // Private
+
+ _textTracks()
+ {
+ return this._sortedTrackList(this.mediaController.media.textTracks);
+ }
+
+ _audioTracks()
+ {
+ return this._sortedTrackList(this.mediaController.media.audioTracks);
+ }
+
+ _canPickAudioTracks()
+ {
+ const audioTracks = this._audioTracks();
+ return audioTracks && audioTracks.length > 1;
+ }
+
+ _canPickTextTracks()
+ {
+ const textTracks = this._textTracks();
+ return textTracks && textTracks.length > 0;
+ }
+
+ _sortedTrackList(tracks)
+ {
+ return Array.from(this.mediaController.host ? this.mediaController.host.sortedTrackListForMenu(tracks) : tracks);
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/media/volume-down-support.js b/Source/WebCore/Modules/modern-media-controls/media/volume-down-support.js
new file mode 100644
index 000000000..94c20c5ad
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/media/volume-down-support.js
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2017 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+class VolumeDownSupport extends MediaControllerSupport
+{
+
+ // Protected
+
+ get control()
+ {
+ return this.mediaController.controls.volumeDownButton;
+ }
+
+ buttonWasPressed(control)
+ {
+ this.mediaController.media.volume = 0;
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/media/volume-support.js b/Source/WebCore/Modules/modern-media-controls/media/volume-support.js
new file mode 100644
index 000000000..4a41be650
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/media/volume-support.js
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+class VolumeSupport extends MediaControllerSupport
+{
+
+ // Protected
+
+ get control()
+ {
+ return this.mediaController.controls.volumeSlider;
+ }
+
+ get mediaEvents()
+ {
+ return ["volumechange"];
+ }
+
+ controlValueWillStartChanging(control)
+ {
+ this.mediaController.media.muted = false;
+ }
+
+ controlValueDidChange(control)
+ {
+ this.mediaController.media.volume = control.value;
+ }
+
+ syncControl()
+ {
+ const media = this.mediaController.media;
+ this.control.value = media.muted ? 0 : media.volume;
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/media/volume-up-support.js b/Source/WebCore/Modules/modern-media-controls/media/volume-up-support.js
new file mode 100644
index 000000000..5edfdc575
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/media/volume-up-support.js
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2017 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+class VolumeUpSupport extends MediaControllerSupport
+{
+
+ // Protected
+
+ get control()
+ {
+ return this.mediaController.controls.volumeUpButton;
+ }
+
+ buttonWasPressed(control)
+ {
+ this.mediaController.media.volume = 1;
+ }
+
+}