diff options
Diffstat (limited to 'Source/WebCore/Modules/modern-media-controls')
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 Binary files differnew file mode 100644 index 000000000..86061b233 --- /dev/null +++ b/Source/WebCore/Modules/modern-media-controls/images/iOS/airplay-placard@1x.png 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 Binary files differnew file mode 100644 index 000000000..83c72eac3 --- /dev/null +++ b/Source/WebCore/Modules/modern-media-controls/images/iOS/airplay-placard@2x.png 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 Binary files differnew file mode 100644 index 000000000..b8aaef1b9 --- /dev/null +++ b/Source/WebCore/Modules/modern-media-controls/images/iOS/airplay-placard@3x.png 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 Binary files differnew file mode 100644 index 000000000..0b472b915 --- /dev/null +++ b/Source/WebCore/Modules/modern-media-controls/images/iOS/airplay@1x.png 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 Binary files differnew file mode 100644 index 000000000..818c9c200 --- /dev/null +++ b/Source/WebCore/Modules/modern-media-controls/images/iOS/airplay@2x.png 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 Binary files differnew file mode 100644 index 000000000..5a2fd22d6 --- /dev/null +++ b/Source/WebCore/Modules/modern-media-controls/images/iOS/enter-fullscreen@1x.png 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 Binary files differnew file mode 100644 index 000000000..2cb880ac0 --- /dev/null +++ b/Source/WebCore/Modules/modern-media-controls/images/iOS/enter-fullscreen@2x.png 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 Binary files differnew file mode 100644 index 000000000..6db049b38 --- /dev/null +++ b/Source/WebCore/Modules/modern-media-controls/images/iOS/enter-fullscreen@3x.png 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 Binary files differnew file mode 100644 index 000000000..0c1d8b2ef --- /dev/null +++ b/Source/WebCore/Modules/modern-media-controls/images/iOS/interval-skip-back@1x.png 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 Binary files differnew file mode 100644 index 000000000..40d12e427 --- /dev/null +++ b/Source/WebCore/Modules/modern-media-controls/images/iOS/interval-skip-back@2x.png 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 Binary files differnew file mode 100644 index 000000000..c31c36bad --- /dev/null +++ b/Source/WebCore/Modules/modern-media-controls/images/iOS/interval-skip-back@3x.png 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 Binary files differnew file mode 100644 index 000000000..fc4369453 --- /dev/null +++ b/Source/WebCore/Modules/modern-media-controls/images/iOS/invalid-placard@1x.png 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 Binary files differnew file mode 100644 index 000000000..e14d3d478 --- /dev/null +++ b/Source/WebCore/Modules/modern-media-controls/images/iOS/invalid-placard@2x.png 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 Binary files differnew file mode 100644 index 000000000..bb760ad52 --- /dev/null +++ b/Source/WebCore/Modules/modern-media-controls/images/iOS/invalid-placard@3x.png 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 Binary files differnew file mode 100644 index 000000000..bddf4abc6 --- /dev/null +++ b/Source/WebCore/Modules/modern-media-controls/images/iOS/pause@1x.png 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 Binary files differnew file mode 100644 index 000000000..c3226a1c5 --- /dev/null +++ b/Source/WebCore/Modules/modern-media-controls/images/iOS/pause@2x.png 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 Binary files differnew file mode 100644 index 000000000..cf391a6bf --- /dev/null +++ b/Source/WebCore/Modules/modern-media-controls/images/iOS/pause@3x.png 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 Binary files differnew file mode 100644 index 000000000..1c8d80b4d --- /dev/null +++ b/Source/WebCore/Modules/modern-media-controls/images/iOS/pip-in@1x.png 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 Binary files differnew file mode 100644 index 000000000..f7d2b921d --- /dev/null +++ b/Source/WebCore/Modules/modern-media-controls/images/iOS/pip-in@2x.png 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 Binary files differnew file mode 100644 index 000000000..e8b107ad0 --- /dev/null +++ b/Source/WebCore/Modules/modern-media-controls/images/iOS/pip-in@3x.png 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 Binary files differnew file mode 100644 index 000000000..423963a46 --- /dev/null +++ b/Source/WebCore/Modules/modern-media-controls/images/iOS/pip-placard@1x.png 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 Binary files differnew file mode 100644 index 000000000..bbd4cd84b --- /dev/null +++ b/Source/WebCore/Modules/modern-media-controls/images/iOS/pip-placard@2x.png 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 Binary files differnew file mode 100644 index 000000000..412eaba59 --- /dev/null +++ b/Source/WebCore/Modules/modern-media-controls/images/iOS/pip-placard@3x.png 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 Binary files differnew file mode 100644 index 000000000..a49f216b5 --- /dev/null +++ b/Source/WebCore/Modules/modern-media-controls/images/iOS/play@1x.png 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 Binary files differnew file mode 100644 index 000000000..fa77a634f --- /dev/null +++ b/Source/WebCore/Modules/modern-media-controls/images/iOS/play@2x.png 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 Binary files differnew file mode 100644 index 000000000..73e376eae --- /dev/null +++ b/Source/WebCore/Modules/modern-media-controls/images/iOS/play@3x.png 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 Binary files differnew file mode 100644 index 000000000..6c8bd93f4 --- /dev/null +++ b/Source/WebCore/Modules/modern-media-controls/images/iOS/slider-thumb@2x.png 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 Binary files differnew file mode 100644 index 000000000..e7467373e --- /dev/null +++ b/Source/WebCore/Modules/modern-media-controls/images/iOS/start@1x.png 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 Binary files differnew file mode 100644 index 000000000..b1264e8b2 --- /dev/null +++ b/Source/WebCore/Modules/modern-media-controls/images/iOS/start@2x.png 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 Binary files differnew file mode 100644 index 000000000..a520a18b3 --- /dev/null +++ b/Source/WebCore/Modules/modern-media-controls/images/iOS/start@3x.png 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 Binary files differnew file mode 100644 index 000000000..747927b4b --- /dev/null +++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/airplay-fullscreen@1x.png 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 Binary files differnew file mode 100644 index 000000000..9b4f46f9d --- /dev/null +++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/airplay-fullscreen@2x.png 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 Binary files differnew file mode 100644 index 000000000..86061b233 --- /dev/null +++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/airplay-placard@1x.png 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 Binary files differnew file mode 100644 index 000000000..83c72eac3 --- /dev/null +++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/airplay-placard@2x.png 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 Binary files differnew file mode 100644 index 000000000..0b472b915 --- /dev/null +++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/airplay@1x.png 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 Binary files differnew file mode 100644 index 000000000..818c9c200 --- /dev/null +++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/airplay@2x.png 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 Binary files differnew file mode 100644 index 000000000..3549780cb --- /dev/null +++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/enter-fullscreen-compact@1x.png 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 Binary files differnew file mode 100644 index 000000000..e592d7912 --- /dev/null +++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/enter-fullscreen-compact@2x.png 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 Binary files differnew file mode 100644 index 000000000..5a2fd22d6 --- /dev/null +++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/enter-fullscreen@1x.png 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 Binary files differnew file mode 100644 index 000000000..2cb880ac0 --- /dev/null +++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/enter-fullscreen@2x.png 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 Binary files differnew file mode 100644 index 000000000..ecc432210 --- /dev/null +++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/exit-fullscreen@1x.png 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 Binary files differnew file mode 100644 index 000000000..77a206527 --- /dev/null +++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/exit-fullscreen@2x.png 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 Binary files differnew file mode 100644 index 000000000..ee795a8c2 --- /dev/null +++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/forward@1x.png 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 Binary files differnew file mode 100644 index 000000000..691015fb5 --- /dev/null +++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/forward@2x.png 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 Binary files differnew file mode 100644 index 000000000..4ce2060c0 --- /dev/null +++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/interval-skip-back-compact@1x.png 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 Binary files differnew file mode 100644 index 000000000..5bf10d364 --- /dev/null +++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/interval-skip-back-compact@2x.png 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 Binary files differnew file mode 100644 index 000000000..0c1d8b2ef --- /dev/null +++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/interval-skip-back@1x.png 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 Binary files differnew file mode 100644 index 000000000..40d12e427 --- /dev/null +++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/interval-skip-back@2x.png 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 Binary files differnew file mode 100644 index 000000000..fc4369453 --- /dev/null +++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/invalid-placard@1x.png 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 Binary files differnew file mode 100644 index 000000000..e14d3d478 --- /dev/null +++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/invalid-placard@2x.png 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 Binary files differnew file mode 100644 index 000000000..9d0764363 --- /dev/null +++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/media-selection-fullscreen@1x.png 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 Binary files differnew file mode 100644 index 000000000..f50ca3b18 --- /dev/null +++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/media-selection-fullscreen@2x.png 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 Binary files differnew file mode 100644 index 000000000..5df5edb35 --- /dev/null +++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/media-selection@1x.png 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 Binary files differnew file mode 100644 index 000000000..0cfb6b2be --- /dev/null +++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/media-selection@2x.png 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 Binary files differnew file mode 100644 index 000000000..6a489258f --- /dev/null +++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/pause-compact@1x.png 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 Binary files differnew file mode 100644 index 000000000..50ed11f20 --- /dev/null +++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/pause-compact@2x.png 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 Binary files differnew file mode 100644 index 000000000..ee41eb7c9 --- /dev/null +++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/pause-fullscreen@1x.png 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 Binary files differnew file mode 100644 index 000000000..dd49890a8 --- /dev/null +++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/pause-fullscreen@2x.png 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 Binary files differnew file mode 100644 index 000000000..873b93020 --- /dev/null +++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/pause@1x.png 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 Binary files differnew file mode 100644 index 000000000..b93a5e509 --- /dev/null +++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/pause@2x.png 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 Binary files differnew file mode 100644 index 000000000..f7a64bbd1 --- /dev/null +++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/pip-in-fullscreen@1x.png 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 Binary files differnew file mode 100644 index 000000000..fe721f737 --- /dev/null +++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/pip-in-fullscreen@2x.png 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 Binary files differnew file mode 100644 index 000000000..1c8d80b4d --- /dev/null +++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/pip-in@1x.png 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 Binary files differnew file mode 100644 index 000000000..f7d2b921d --- /dev/null +++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/pip-in@2x.png 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 Binary files differnew file mode 100644 index 000000000..423963a46 --- /dev/null +++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/pip-placard@1x.png 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 Binary files differnew file mode 100644 index 000000000..bbd4cd84b --- /dev/null +++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/pip-placard@2x.png 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 Binary files differnew file mode 100644 index 000000000..5c8ad4a15 --- /dev/null +++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/play-compact@1x.png 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 Binary files differnew file mode 100644 index 000000000..b6755cd39 --- /dev/null +++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/play-compact@2x.png 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 Binary files differnew file mode 100644 index 000000000..a49f216b5 --- /dev/null +++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/play-fullscreen@1x.png 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 Binary files differnew file mode 100644 index 000000000..fa77a634f --- /dev/null +++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/play-fullscreen@2x.png 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 Binary files differnew file mode 100644 index 000000000..a49f216b5 --- /dev/null +++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/play@1x.png 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 Binary files differnew file mode 100644 index 000000000..c67d8e3dd --- /dev/null +++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/play@2x.png 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 Binary files differnew file mode 100644 index 000000000..659e984ce --- /dev/null +++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/rewind@1x.png 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 Binary files differnew file mode 100644 index 000000000..876ab734b --- /dev/null +++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/rewind@2x.png 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 Binary files differnew file mode 100644 index 000000000..67171b6ab --- /dev/null +++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/scale-to-fill@1x.png 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 Binary files differnew file mode 100644 index 000000000..b2c1df756 --- /dev/null +++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/scale-to-fill@2x.png 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 Binary files differnew file mode 100644 index 000000000..7034aacfd --- /dev/null +++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/scale-to-fit@1x.png 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 Binary files differnew file mode 100644 index 000000000..324de8b1f --- /dev/null +++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/scale-to-fit@2x.png 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 Binary files differnew file mode 100644 index 000000000..e7467373e --- /dev/null +++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/start@1x.png 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 Binary files differnew file mode 100644 index 000000000..b1264e8b2 --- /dev/null +++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/start@2x.png 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 Binary files differnew file mode 100644 index 000000000..dc65736b2 --- /dev/null +++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/volume-compact@1x.png 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 Binary files differnew file mode 100644 index 000000000..bfe1a8b87 --- /dev/null +++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/volume-compact@2x.png 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 Binary files differnew file mode 100644 index 000000000..1660737ad --- /dev/null +++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/volume-down-fullscreen@1x.png 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 Binary files differnew file mode 100644 index 000000000..5e35ea528 --- /dev/null +++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/volume-down-fullscreen@2x.png 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 Binary files differnew file mode 100644 index 000000000..3ba71892f --- /dev/null +++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/volume-mute@1x.png 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 Binary files differnew file mode 100644 index 000000000..67c2034ab --- /dev/null +++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/volume-mute@2x.png 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 Binary files differnew file mode 100644 index 000000000..4c1cef883 --- /dev/null +++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/volume-up-fullscreen@1x.png 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 Binary files differnew file mode 100644 index 000000000..238cb645d --- /dev/null +++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/volume-up-fullscreen@2x.png 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 Binary files differnew file mode 100644 index 000000000..b45490ae3 --- /dev/null +++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/volume@1x.png 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 Binary files differnew file mode 100644 index 000000000..5bec45c16 --- /dev/null +++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/volume@2x.png 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; + } + +} |