[Modern Media Controls] Media controls use the fullscreen layout after going from...
[WebKit-https.git] / Source / WebCore / Modules / modern-media-controls / media / media-controller.js
1 /*
2  * Copyright (C) 2016 Apple Inc. All Rights Reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 class MediaController
27 {
28
29     constructor(shadowRoot, media, host)
30     {
31         this.shadowRoot = shadowRoot;
32         this.media = media;
33         this.host = host;
34
35         this.container = shadowRoot.appendChild(document.createElement("div"));
36         this.container.className = "media-controls-container";
37
38         if (host)
39             this.container.appendChild(host.textTrackContainer);
40
41         this._updateControlsIfNeeded();
42
43         shadowRoot.addEventListener("resize", this);
44
45         if (media.webkitSupportsPresentationMode)
46             media.addEventListener("webkitpresentationmodechanged", this);
47         else
48             media.addEventListener("webkitfullscreenchange", this);
49     }
50
51     get layoutTraits()
52     {
53         let traits = window.navigator.platform === "MacIntel" ? LayoutTraits.macOS : LayoutTraits.iOS;
54         if (this.media.webkitDisplayingFullscreen)
55             traits = traits | LayoutTraits.Fullscreen;
56         return traits;
57     }
58
59     // Protected
60
61     set pageScaleFactor(pageScaleFactor)
62     {
63         // FIXME: To be implemented.
64     }
65
66     set usesLTRUserInterfaceLayoutDirection(flag)
67     {
68         this.controls.usesLTRUserInterfaceLayoutDirection = flag;
69     }
70
71     handleEvent(event)
72     {
73         if (event.type === "resize" && event.currentTarget === this.shadowRoot)
74             this._updateControlsSize();
75         else if (event.currentTarget === this.media) {
76             this._updateControlsIfNeeded();
77             if (event.type === "webkitpresentationmodechanged")
78                 this._returnMediaLayerToInlineIfNeeded();
79         }
80     }
81
82     // Private
83
84     _updateControlsIfNeeded()
85     {
86         const previousControls = this.controls;
87         const ControlsClass = this._controlsClass();
88         if (previousControls && previousControls.constructor === ControlsClass)
89             return;
90
91         // Before we reset the .controls property, we need to destroy the previous
92         // supporting objects so we don't leak.
93         if (this._supportingObjects) {
94             for (let supportingObject of this._supportingObjects)
95                 supportingObject.destroy();
96         }
97
98         this.controls = new ControlsClass;
99
100         if (this.shadowRoot.host && this.shadowRoot.host.dataset.autoHideDelay)
101             this.controls.controlsBar.autoHideDelay = this.shadowRoot.host.dataset.autoHideDelay;
102
103         if (previousControls) {
104             this.controls.fadeIn();
105             this.container.replaceChild(this.controls.element, previousControls.element);
106             this.controls.usesLTRUserInterfaceLayoutDirection = previousControls.usesLTRUserInterfaceLayoutDirection;
107         } else
108             this.container.appendChild(this.controls.element);        
109
110         this._updateControlsSize();
111
112         this._supportingObjects = [AirplaySupport, ControlsVisibilitySupport, ElapsedTimeSupport, FullscreenSupport, MuteSupport, PiPSupport, PlacardSupport, PlaybackSupport, RemainingTimeSupport, ScrubbingSupport, SeekBackwardSupport, SeekForwardSupport, SkipBackSupport, StartSupport, StatusSupport, TracksSupport, VolumeSupport].map(SupportClass => {
113             return new SupportClass(this);
114         }, this);
115     }
116
117     _updateControlsSize()
118     {
119         this.controls.width = this.media.offsetWidth;
120         this.controls.height = this.media.offsetHeight;
121     }
122
123     _returnMediaLayerToInlineIfNeeded()
124     {
125         if (this.host)
126             window.requestAnimationFrame(() => this.host.setPreparedToReturnVideoLayerToInline(this.media.webkitPresentationMode !== PiPMode));
127     }
128
129     _controlsClass()
130     {
131         const layoutTraits = this.layoutTraits;
132         if (layoutTraits & LayoutTraits.Fullscreen)
133             return MacOSFullscreenMediaControls;
134         return MacOSInlineMediaControls;
135     }
136
137 }