[iOS] remove "enter optimized fullscreen" gesture
[WebKit-https.git] / Source / WebCore / Modules / mediacontrols / mediaControlsiOS.js
1 function createControls(root, video, host)
2 {
3     return new ControllerIOS(root, video, host);
4 };
5
6 function ControllerIOS(root, video, host)
7 {
8     this.doingSetup = true;
9     this.hasWirelessPlaybackTargets = false;
10     this._pageScaleFactor = 1;
11     this.isListeningForPlaybackTargetAvailabilityEvent = false;
12     Controller.call(this, root, video, host);
13
14     this.updateWirelessTargetAvailable();
15     this.updateWirelessPlaybackStatus();
16     this.setNeedsTimelineMetricsUpdate();
17
18     host.controlsDependOnPageScaleFactor = true;
19     this.doingSetup = false;
20 };
21
22 /* Enums */
23 ControllerIOS.StartPlaybackControls = 2;
24
25 /* Globals */
26 ControllerIOS.gWirelessImage = 'data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 245"><g fill="#1060FE"><path d="M193.6,6.3v121.6H6.4V6.3H193.6 M199.1,0.7H0.9v132.7h198.2V0.7L199.1,0.7z"/><path d="M43.5,139.3c15.8,8,35.3,12.7,56.5,12.7s40.7-4.7,56.5-12.7H43.5z"/></g><g text-anchor="middle" font-family="Helvetica Neue"><text x="100" y="204" fill="white" font-size="24">##DEVICE_TYPE##</text><text x="100" y="234" fill="#5C5C5C" font-size="21">##DEVICE_NAME##</text></g></svg>';
27 ControllerIOS.gSimulateWirelessPlaybackTarget = false; // Used for testing when there are no wireless targets.
28
29 ControllerIOS.prototype = {
30     addVideoListeners: function() {
31         Controller.prototype.addVideoListeners.call(this);
32
33         this.listenFor(this.video, 'webkitbeginfullscreen', this.handleFullscreenChange);
34         this.listenFor(this.video, 'webkitendfullscreen', this.handleFullscreenChange);
35         this.listenFor(this.video, 'webkitcurrentplaybacktargetiswirelesschanged', this.handleWirelessPlaybackChange);
36     },
37
38     removeVideoListeners: function() {
39         Controller.prototype.removeVideoListeners.call(this);
40
41         this.stopListeningFor(this.video, 'webkitbeginfullscreen', this.handleFullscreenChange);
42         this.stopListeningFor(this.video, 'webkitendfullscreen', this.handleFullscreenChange);
43         this.stopListeningFor(this.video, 'webkitcurrentplaybacktargetiswirelesschanged', this.handleWirelessPlaybackChange);
44
45         this.setShouldListenForPlaybackTargetAvailabilityEvent(false);
46     },
47
48     createBase: function() {
49         Controller.prototype.createBase.call(this);
50
51         var startPlaybackButton = this.controls.startPlaybackButton = document.createElement('button');
52         startPlaybackButton.setAttribute('pseudo', '-webkit-media-controls-start-playback-button');
53         startPlaybackButton.setAttribute('aria-label', this.UIString('Start Playback'));
54
55         this.listenFor(this.base, 'gesturestart', this.handleBaseGestureStart);
56         this.listenFor(this.base, 'gesturechange', this.handleBaseGestureChange);
57         this.listenFor(this.base, 'gestureend', this.handleBaseGestureEnd);
58         this.listenFor(this.base, 'touchstart', this.handleWrapperTouchStart);
59         this.stopListeningFor(this.base, 'mousemove', this.handleWrapperMouseMove);
60         this.stopListeningFor(this.base, 'mouseout', this.handleWrapperMouseOut);
61
62         this.listenFor(document, 'visibilitychange', this.handleVisibilityChange);
63     },
64
65     shouldHaveStartPlaybackButton: function() {
66         var allowsInline = this.host.mediaPlaybackAllowsInline;
67
68         if (this.isPlaying)
69             return false;
70
71         if (this.isAudio() && allowsInline)
72             return false;
73
74         if (this.isFullScreen())
75             return false;
76
77         if (!this.video.currentSrc && this.video.error)
78             return false;
79
80         if (!this.video.controls && allowsInline)
81             return false;
82
83         if (this.video.currentSrc && this.video.error)
84             return true;
85
86         if (!this.doingSetup && !this.host.userGestureRequired && allowsInline)
87             return false;
88
89         return true;
90     },
91
92     shouldHaveControls: function() {
93         if (this.shouldHaveStartPlaybackButton())
94             return false;
95
96         return Controller.prototype.shouldHaveControls.call(this);
97     },
98
99     shouldHaveAnyUI: function() {
100         return this.shouldHaveStartPlaybackButton() || Controller.prototype.shouldHaveAnyUI.call(this) || this.currentPlaybackTargetIsWireless();
101     },
102
103     currentPlaybackTargetIsWireless: function() {
104         return ControllerIOS.gSimulateWirelessPlaybackTarget || (('webkitCurrentPlaybackTargetIsWireless' in this.video) && this.video.webkitCurrentPlaybackTargetIsWireless);
105     },
106
107     updateWirelessPlaybackStatus: function() {
108         if (this.currentPlaybackTargetIsWireless()) {
109             var backgroundImageSVG = "url('" + ControllerIOS.gWirelessImage + "')";
110
111             var deviceName = "";
112             var deviceType = "";
113             var type = this.host.externalDeviceType;
114             if (type == "airplay") {
115                 deviceType = this.UIString('##WIRELESS_PLAYBACK_DEVICE_TYPE##');
116                 deviceName = this.UIString('##WIRELESS_PLAYBACK_DEVICE_NAME##', '##DEVICE_NAME##', this.host.externalDeviceDisplayName || "Apple TV");
117             } else if (type == "tvout") {
118                 deviceType = this.UIString('##TVOUT_DEVICE_TYPE##');
119                 deviceName = this.UIString('##TVOUT_DEVICE_NAME##');
120             }
121
122             backgroundImageSVG = backgroundImageSVG.replace('##DEVICE_TYPE##', deviceType);
123             backgroundImageSVG = backgroundImageSVG.replace('##DEVICE_NAME##', deviceName);
124
125             this.controls.inlinePlaybackPlaceholder.style.backgroundImage = backgroundImageSVG;
126             this.controls.inlinePlaybackPlaceholder.setAttribute('aria-label', deviceType + ", " + deviceName);
127
128             this.controls.inlinePlaybackPlaceholder.classList.remove(this.ClassNames.hidden);
129             this.controls.wirelessTargetPicker.classList.add(this.ClassNames.active);
130         } else {
131             this.controls.inlinePlaybackPlaceholder.classList.add(this.ClassNames.hidden);
132             this.controls.wirelessTargetPicker.classList.remove(this.ClassNames.active);
133         }
134     },
135
136     updateWirelessTargetAvailable: function() {
137         if (ControllerIOS.gSimulateWirelessPlaybackTarget || this.hasWirelessPlaybackTargets)
138             this.controls.wirelessTargetPicker.classList.remove(this.ClassNames.hidden);
139         else
140             this.controls.wirelessTargetPicker.classList.add(this.ClassNames.hidden);
141     },
142
143     createControls: function() {
144         Controller.prototype.createControls.call(this);
145
146         var panelCompositedParent = this.controls.panelCompositedParent = document.createElement('div');
147         panelCompositedParent.setAttribute('pseudo', '-webkit-media-controls-panel-composited-parent');
148
149         var inlinePlaybackPlaceholder = this.controls.inlinePlaybackPlaceholder = document.createElement('div');
150         inlinePlaybackPlaceholder.setAttribute('pseudo', '-webkit-media-controls-inline-playback-placeholder');
151         inlinePlaybackPlaceholder.classList.add(this.ClassNames.hidden);
152         inlinePlaybackPlaceholder.setAttribute('aria-label', this.UIString('Display Optimized Full Screen'));
153
154         var wirelessTargetPicker = this.controls.wirelessTargetPicker = document.createElement('button');
155         wirelessTargetPicker.setAttribute('pseudo', '-webkit-media-controls-wireless-playback-picker-button');
156         wirelessTargetPicker.setAttribute('aria-label', this.UIString('Choose Wireless Display'));
157         this.listenFor(wirelessTargetPicker, 'touchstart', this.handleWirelessPickerButtonTouchStart);
158         this.listenFor(wirelessTargetPicker, 'touchend', this.handleWirelessPickerButtonTouchEnd);
159         this.listenFor(wirelessTargetPicker, 'touchcancel', this.handleWirelessPickerButtonTouchCancel);
160
161         if (!ControllerIOS.gSimulateWirelessPlaybackTarget)
162             wirelessTargetPicker.classList.add(this.ClassNames.hidden);
163
164         this.listenFor(this.controls.startPlaybackButton, 'touchstart', this.handleStartPlaybackButtonTouchStart);
165         this.listenFor(this.controls.startPlaybackButton, 'touchend', this.handleStartPlaybackButtonTouchEnd);
166         this.listenFor(this.controls.startPlaybackButton, 'touchcancel', this.handleStartPlaybackButtonTouchCancel);
167
168         this.listenFor(this.controls.panel, 'touchstart', this.handlePanelTouchStart);
169         this.listenFor(this.controls.panel, 'touchend', this.handlePanelTouchEnd);
170         this.listenFor(this.controls.panel, 'touchcancel', this.handlePanelTouchCancel);
171         this.listenFor(this.controls.playButton, 'touchstart', this.handlePlayButtonTouchStart);
172         this.listenFor(this.controls.playButton, 'touchend', this.handlePlayButtonTouchEnd);
173         this.listenFor(this.controls.playButton, 'touchcancel', this.handlePlayButtonTouchCancel);
174         this.listenFor(this.controls.fullscreenButton, 'touchstart', this.handleFullscreenTouchStart);
175         this.listenFor(this.controls.fullscreenButton, 'touchend', this.handleFullscreenTouchEnd);
176         this.listenFor(this.controls.fullscreenButton, 'touchcancel', this.handleFullscreenTouchCancel);
177         this.listenFor(this.controls.optimizedFullscreenButton, 'touchstart', this.handleOptimizedFullscreenTouchStart);
178         this.listenFor(this.controls.optimizedFullscreenButton, 'touchend', this.handleOptimizedFullscreenTouchEnd);
179         this.listenFor(this.controls.optimizedFullscreenButton, 'touchcancel', this.handleOptimizedFullscreenTouchCancel);
180         this.stopListeningFor(this.controls.playButton, 'click', this.handlePlayButtonClicked);
181     },
182
183     setControlsType: function(type) {
184         if (type === this.controlsType)
185             return;
186         Controller.prototype.setControlsType.call(this, type);
187
188         if (type === ControllerIOS.StartPlaybackControls)
189             this.addStartPlaybackControls();
190         else
191             this.removeStartPlaybackControls();
192
193         this.updateShouldListenForPlaybackTargetAvailabilityEvent();
194     },
195
196     addStartPlaybackControls: function() {
197         this.base.appendChild(this.controls.startPlaybackButton);
198     },
199
200     removeStartPlaybackControls: function() {
201         if (this.controls.startPlaybackButton.parentNode)
202             this.controls.startPlaybackButton.parentNode.removeChild(this.controls.startPlaybackButton);
203     },
204
205     configureInlineControls: function() {
206         this.controls.panel.appendChild(this.controls.playButton);
207         this.controls.panel.appendChild(this.controls.statusDisplay);
208         this.controls.panel.appendChild(this.controls.timelineBox);
209         this.controls.panel.appendChild(this.controls.wirelessTargetPicker);
210         if (!this.isLive) {
211             this.controls.timelineBox.appendChild(this.controls.currentTime);
212             this.controls.timelineBox.appendChild(this.controls.timeline);
213             this.controls.timelineBox.appendChild(this.controls.remainingTime);
214         }
215         if (!this.isAudio()) {
216             this.controls.panel.appendChild(this.controls.fullscreenButton);
217             if (this.host.optimizedFullscreenSupported)
218                 this.controls.panel.appendChild(this.controls.optimizedFullscreenButton);
219         }
220     },
221
222     configureFullScreenControls: function() {
223         // Do nothing
224     },
225
226     hideControls: function() {
227         Controller.prototype.hideControls.call(this);
228         this.updateShouldListenForPlaybackTargetAvailabilityEvent();
229     },
230
231     showControls: function() {
232         Controller.prototype.showControls.call(this);
233         this.updateShouldListenForPlaybackTargetAvailabilityEvent();
234     },
235
236     addControls: function() {
237         this.base.appendChild(this.controls.inlinePlaybackPlaceholder);
238         this.base.appendChild(this.controls.panelCompositedParent);
239         this.controls.panelCompositedParent.appendChild(this.controls.panel);
240         this.setNeedsTimelineMetricsUpdate();
241     },
242
243     updateControls: function() {
244         if (this.shouldHaveStartPlaybackButton())
245             this.setControlsType(ControllerIOS.StartPlaybackControls);
246         else if (this.isFullScreen() && this.host.fullscreenMode !== "optimized")
247             this.setControlsType(Controller.FullScreenControls);
248         else
249             this.setControlsType(Controller.InlineControls);
250
251         this.setNeedsTimelineMetricsUpdate();
252     },
253
254     updateTime: function() {
255         Controller.prototype.updateTime.call(this);
256         this.updateProgress();
257     },
258
259     progressFillStyle: function() {
260         return 'rgba(0, 0, 0, 0.5)';
261     },
262
263     updateProgress: function(forceUpdate) {
264         Controller.prototype.updateProgress.call(this, forceUpdate);
265
266         if (!forceUpdate && this.controlsAreHidden())
267             return;
268
269         var width = this.timelineWidth;
270         var height = this.timelineHeight;
271
272         // Magic number, matching the value for ::-webkit-media-controls-timeline::-webkit-slider-thumb
273         // in mediaControlsiOS.css. Since we cannot ask the thumb for its offsetWidth as it's in its own
274         // shadow dom, just hard-code the value.
275         var thumbWidth = 16;
276         var endX = thumbWidth / 2 + (width - thumbWidth) * this.video.currentTime / this.video.duration;
277
278         var context = document.getCSSCanvasContext('2d', 'timeline-' + this.timelineID, width, height);
279         context.fillStyle = 'white';
280         context.fillRect(0, 0, endX, height);
281     },
282
283     formatTime: function(time) {
284         if (isNaN(time))
285             time = 0;
286         var absTime = Math.abs(time);
287         var intSeconds = Math.floor(absTime % 60).toFixed(0);
288         var intMinutes = Math.floor((absTime / 60) % 60).toFixed(0);
289         var intHours = Math.floor(absTime / (60 * 60)).toFixed(0);
290         var sign = time < 0 ? '-' : String();
291
292         if (intHours > 0)
293             return sign + intHours + ':' + String('00' + intMinutes).slice(-2) + ":" + String('00' + intSeconds).slice(-2);
294
295         return sign + String('00' + intMinutes).slice(intMinutes >= 10 ? -2 : -1) + ":" + String('00' + intSeconds).slice(-2);
296     },
297
298     handleTimelineChange: function(event) {
299         Controller.prototype.handleTimelineChange.call(this);
300         this.updateProgress();
301     },
302
303     handlePlayButtonTouchStart: function() {
304         this.controls.playButton.classList.add('active');
305     },
306
307     handlePlayButtonTouchEnd: function(event) {
308         this.controls.playButton.classList.remove('active');
309
310         if (this.canPlay())
311             this.video.play();
312         else
313             this.video.pause();
314
315         return true;
316     },
317
318     handlePlayButtonTouchCancel: function(event) {
319         this.controls.playButton.classList.remove('active');
320         return true;
321     },
322
323     handleBaseGestureStart: function(event) {
324         this.gestureStartTime = new Date();
325         // If this gesture started with two fingers inside the video, then
326         // don't treat it as a potential zoom, unless we're still waiting
327         // to play.
328         if (this.mostRecentNumberOfTargettedTouches == 2 && this.controlsType != ControllerIOS.StartPlaybackControls)
329             event.preventDefault();
330     },
331
332     handleBaseGestureChange: function(event) {
333         if (!this.video.controls || this.isAudio() || this.isFullScreen() || this.gestureStartTime === undefined || this.controlsType == ControllerIOS.StartPlaybackControls)
334             return;
335         
336         var scaleDetectionThreshold = 0.2;
337         if (event.scale > 1 + scaleDetectionThreshold || event.scale < 1 - scaleDetectionThreshold)
338             delete this.lastDoubleTouchTime;
339
340         if (this.mostRecentNumberOfTargettedTouches == 2 && event.scale >= 1.0)
341             event.preventDefault();
342
343         var currentGestureTime = new Date();
344         var duration = (currentGestureTime - this.gestureStartTime) / 1000;
345         if (!duration)
346             return;
347
348         var velocity = Math.abs(event.scale - 1) / duration;
349         
350         var pinchOutVelocityThreshold = 2;
351         var pinchOutGestureScaleThreshold = 1.25;
352         if (velocity < pinchOutVelocityThreshold || event.scale < pinchOutGestureScaleThreshold)
353             return;
354
355         delete this.gestureStartTime;
356         this.video.webkitEnterFullscreen();
357     },
358
359     handleBaseGestureEnd: function(event) {
360         delete this.gestureStartTime;
361     },
362
363     handleWrapperTouchStart: function(event) {
364         if (event.target != this.base && event.target != this.controls.inlinePlaybackPlaceholder)
365             return;
366
367         this.mostRecentNumberOfTargettedTouches = event.targetTouches.length;
368         
369         if (this.controlsAreHidden()) {
370             this.showControls();
371             if (this.hideTimer)
372                 clearTimeout(this.hideTimer);
373             this.hideTimer = setTimeout(this.hideControls.bind(this), this.HideControlsDelay);
374         } else if (!this.canPlay())
375             this.hideControls();
376     },
377
378     handlePanelTouchStart: function(event) {
379         this.video.style.webkitUserSelect = 'none';
380     },
381
382     handlePanelTouchEnd: function(event) {
383         this.video.style.removeProperty('-webkit-user-select');
384     },
385
386     handlePanelTouchCancel: function(event) {
387         this.video.style.removeProperty('-webkit-user-select');
388     },
389
390     handleVisibilityChange: function(event) {
391         this.updateShouldListenForPlaybackTargetAvailabilityEvent();
392     },
393
394     isFullScreen: function()
395     {
396         return this.video.webkitDisplayingFullscreen;
397     },
398
399     handleFullscreenButtonClicked: function(event) {
400         if (this.isFullScreen())
401             this.video.webkitExitFullscreen();
402         else
403             this.video.webkitEnterFullscreen();
404     },
405
406     handleFullscreenTouchStart: function() {
407         this.controls.fullscreenButton.classList.add('active');
408     },
409
410     handleFullscreenTouchEnd: function(event) {
411         this.controls.fullscreenButton.classList.remove('active');
412
413         this.handleFullscreenButtonClicked();
414
415         return true;
416     },
417
418     handleFullscreenTouchCancel: function(event) {
419         this.controls.fullscreenButton.classList.remove('active');
420         return true;
421     },
422
423     handleOptimizedFullscreenButtonClicked: function(event) {
424         if (this.isFullScreen())
425             this.video.webkitExitFullscreen();
426         else
427             this.host.enterFullscreenOptimized();
428     },
429         
430     handleOptimizedFullscreenTouchStart: function() {
431         this.controls.optimizedFullscreenButton.classList.add('active');
432     },
433         
434     handleOptimizedFullscreenTouchEnd: function(event) {
435         this.controls.optimizedFullscreenButton.classList.remove('active');
436         
437         this.handleOptimizedFullscreenButtonClicked();
438         
439         return true;
440     },
441         
442     handleOptimizedFullscreenTouchCancel: function(event) {
443         this.controls.optimizedFullscreenButton.classList.remove('active');
444         return true;
445     },
446
447     handleStartPlaybackButtonTouchStart: function(event) {
448         this.controls.startPlaybackButton.classList.add('active');
449     },
450
451     handleStartPlaybackButtonTouchEnd: function(event) {
452         this.controls.startPlaybackButton.classList.remove('active');
453         if (this.video.error)
454             return true;
455
456         this.video.play();
457         this.updateControls();
458
459         return true;
460     },
461
462     handleStartPlaybackButtonTouchCancel: function(event) {
463         this.controls.startPlaybackButton.classList.remove('active');
464         return true;
465     },
466
467     handleReadyStateChange: function(event) {
468         Controller.prototype.handleReadyStateChange.call(this, event);
469         this.updateControls();
470     },
471
472     handleWirelessPlaybackChange: function(event) {
473         this.updateWirelessPlaybackStatus();
474         this.setNeedsTimelineMetricsUpdate();
475     },
476
477     handleWirelessTargetAvailableChange: function(event) {
478         var wirelessPlaybackTargetsAvailable = event.availability == "available";
479         if (this.hasWirelessPlaybackTargets === wirelessPlaybackTargetsAvailable)
480             return;
481
482         this.hasWirelessPlaybackTargets = wirelessPlaybackTargetsAvailable;
483         this.updateWirelessTargetAvailable();
484         this.setNeedsTimelineMetricsUpdate();
485     },
486
487     handleWirelessPickerButtonTouchStart: function() {
488         if (!this.video.error)
489             this.controls.wirelessTargetPicker.classList.add('active');
490     },
491
492     handleWirelessPickerButtonTouchEnd: function(event) {
493         this.controls.wirelessTargetPicker.classList.remove('active');
494         this.video.webkitShowPlaybackTargetPicker();
495         return true;
496     },
497
498     handleWirelessPickerButtonTouchCancel: function(event) {
499         this.controls.wirelessTargetPicker.classList.remove('active');
500         return true;
501     },
502
503     updateShouldListenForPlaybackTargetAvailabilityEvent: function() {
504         var shouldListen = true;
505         if (this.video.error)
506             shouldListen = false;
507         if (this.controlsType === ControllerIOS.StartPlaybackControls)
508             shouldListen = false;
509         if (!this.isAudio() && !this.video.paused && this.controlsAreHidden())
510             shouldListen = false;
511         if (document.hidden)
512             shouldListen = false;
513
514         this.setShouldListenForPlaybackTargetAvailabilityEvent(shouldListen);
515     },
516
517     updateStatusDisplay: function(event)
518     {
519         this.updateShouldListenForPlaybackTargetAvailabilityEvent();
520         this.controls.startPlaybackButton.classList.toggle(this.ClassNames.failed, this.video.error !== null);
521         Controller.prototype.updateStatusDisplay.call(this, event);
522     },
523
524     setPlaying: function(isPlaying)
525     {
526         Controller.prototype.setPlaying.call(this, isPlaying);
527         this.updateControls();
528     },
529
530     setShouldListenForPlaybackTargetAvailabilityEvent: function(shouldListen)
531     {
532         if (!window.WebKitPlaybackTargetAvailabilityEvent || this.isListeningForPlaybackTargetAvailabilityEvent == shouldListen)
533             return;
534
535         if (shouldListen && (this.shouldHaveStartPlaybackButton() || this.video.error))
536             return;
537
538         this.isListeningForPlaybackTargetAvailabilityEvent = shouldListen;
539         if (shouldListen)
540             this.listenFor(this.video, 'webkitplaybacktargetavailabilitychanged', this.handleWirelessTargetAvailableChange);
541         else
542             this.stopListeningFor(this.video, 'webkitplaybacktargetavailabilitychanged', this.handleWirelessTargetAvailableChange);
543     },
544
545     get pageScaleFactor()
546     {
547         return this._pageScaleFactor;
548     },
549
550     set pageScaleFactor(newScaleFactor)
551     {
552         if (this._pageScaleFactor === newScaleFactor)
553             return;
554
555         this._pageScaleFactor = newScaleFactor;
556
557         if (newScaleFactor) {
558             var scaleValue = 1 / newScaleFactor;
559             var scaleTransform = "scale(" + scaleValue + ")";
560             if (this.controls.startPlaybackButton)
561                 this.controls.startPlaybackButton.style.webkitTransform = scaleTransform;
562             if (this.controls.panel) {
563                 var bottomAligment = -2 * scaleValue;
564                 this.controls.panel.style.bottom = bottomAligment + "px";
565                 this.controls.panel.style.paddingBottom = -(newScaleFactor * bottomAligment) + "px";
566                 this.controls.panel.style.width = Math.ceil(newScaleFactor * 100) + "%";
567                 this.controls.panel.style.webkitTransform = scaleTransform;
568                 this.setNeedsTimelineMetricsUpdate();
569                 this.updateProgress();
570             }
571         }
572     },
573
574     handleFullscreenChange: function(event)
575     {
576         Controller.prototype.handleFullscreenChange.call(this, event);
577
578         if (!this.isFullScreen()) {
579             this.controls.inlinePlaybackPlaceholder.classList.add(this.ClassNames.hidden);
580         } else if (this.host.fullscreenMode === "optimized") {
581             var backgroundImageSVG = "url('" + this.host.mediaUIImageData("optimized-fullscreen-placeholder") + "')";
582             this.controls.inlinePlaybackPlaceholder.style.backgroundImage = backgroundImageSVG;
583             this.controls.inlinePlaybackPlaceholder.setAttribute('aria-label', "video playback placeholder");
584
585             this.controls.inlinePlaybackPlaceholder.classList.remove(this.ClassNames.hidden);
586         }
587
588         this.updateCaptionContainer();
589     },
590
591 };
592
593 Object.create(Controller.prototype).extend(ControllerIOS.prototype);
594 Object.defineProperty(ControllerIOS.prototype, 'constructor', { enumerable: false, value: ControllerIOS });