d0cfb5c55fc87f101dda406899a918040e791dab
[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             media.addEventListener("webkitpresentationmodechanged", this);
40             this.container.appendChild(host.textTrackContainer);
41         }
42
43         this._updateControlsIfNeeded();
44
45         shadowRoot.addEventListener("resize", this);
46
47         media.addEventListener("webkitfullscreenchange", this);
48     }
49
50     get layoutTraits()
51     {
52         let traits = window.navigator.platform === "MacIntel" ? LayoutTraits.macOS : LayoutTraits.iOS;
53         if (this.media.webkitDisplayingFullscreen)
54             traits = traits | LayoutTraits.Fullscreen;
55         return traits;
56     }
57
58     // Protected
59
60     set pageScaleFactor(pageScaleFactor)
61     {
62         // FIXME: To be implemented.
63     }
64
65     set usesLTRUserInterfaceLayoutDirection(flag)
66     {
67         this.controls.usesLTRUserInterfaceLayoutDirection = flag;
68     }
69
70     handleEvent(event)
71     {
72         if (event.type === "resize" && event.currentTarget === this.shadowRoot)
73             this._updateControlsSize();
74         else if (event.currentTarget !== this.media)
75             return;
76         else if (event.type === "webkitfullscreenchange")
77             this._updateControlsIfNeeded();
78         else if (event.type === "webkitpresentationmodechanged")
79             this._returnMediaLayerToInlineIfNeeded();
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         window.requestAnimationFrame(() => this.host.setPreparedToReturnVideoLayerToInline(this.media.webkitPresentationMode !== PiPMode));
126     }
127
128     _controlsClass()
129     {
130         const layoutTraits = this.layoutTraits;
131         if (layoutTraits & LayoutTraits.Fullscreen)
132             return MacOSFullscreenMediaControls;
133         return MacOSInlineMediaControls;
134     }
135
136 }