OLD | NEW |
---|---|
1 /* | 1 /* |
2 * Copyright (C) 2011, 2012 Apple Inc. All rights reserved. | 2 * Copyright (C) 2011, 2012 Apple Inc. All rights reserved. |
3 * Copyright (C) 2011, 2012 Google Inc. All rights reserved. | 3 * Copyright (C) 2011, 2012 Google Inc. All rights reserved. |
4 * | 4 * |
5 * Redistribution and use in source and binary forms, with or without | 5 * Redistribution and use in source and binary forms, with or without |
6 * modification, are permitted provided that the following conditions | 6 * modification, are permitted provided that the following conditions |
7 * are met: | 7 * are met: |
8 * 1. Redistributions of source code must retain the above copyright | 8 * 1. Redistributions of source code must retain the above copyright |
9 * notice, this list of conditions and the following disclaimer. | 9 * notice, this list of conditions and the following disclaimer. |
10 * 2. Redistributions in binary form must reproduce the above copyright | 10 * 2. Redistributions in binary form must reproduce the above copyright |
(...skipping 25 matching lines...) Expand all Loading... | |
36 #include "core/html/MediaController.h" | 36 #include "core/html/MediaController.h" |
37 #include "core/html/track/TextTrackContainer.h" | 37 #include "core/html/track/TextTrackContainer.h" |
38 #include "core/layout/LayoutTheme.h" | 38 #include "core/layout/LayoutTheme.h" |
39 | 39 |
40 namespace blink { | 40 namespace blink { |
41 | 41 |
42 // If you change this value, then also update the corresponding value in | 42 // If you change this value, then also update the corresponding value in |
43 // LayoutTests/media/media-controls.js. | 43 // LayoutTests/media/media-controls.js. |
44 static const double timeWithoutMouseMovementBeforeHidingMediaControls = 3; | 44 static const double timeWithoutMouseMovementBeforeHidingMediaControls = 3; |
45 | 45 |
46 // Count of number open batches for controls visibility. | |
47 static int s_batchDepth = 0; | |
philipj_slow
2015/08/05 08:41:05
This can be a static member of BatchedControlUpdat
liberato (no reviews please)
2015/08/05 16:36:40
that was my preference as well. i patterned after
| |
48 | |
46 static bool shouldShowFullscreenButton(const HTMLMediaElement& mediaElement) | 49 static bool shouldShowFullscreenButton(const HTMLMediaElement& mediaElement) |
47 { | 50 { |
48 // Unconditionally allow the user to exit fullscreen if we are in it | 51 // Unconditionally allow the user to exit fullscreen if we are in it |
49 // now. Especially on android, when we might not yet know if | 52 // now. Especially on android, when we might not yet know if |
50 // fullscreen is supported, we sometimes guess incorrectly and show | 53 // fullscreen is supported, we sometimes guess incorrectly and show |
51 // the button earlier, and we don't want to remove it here if the | 54 // the button earlier, and we don't want to remove it here if the |
52 // user chose to enter fullscreen. crbug.com/500732 . | 55 // user chose to enter fullscreen. crbug.com/500732 . |
53 if (mediaElement.isFullscreen()) | 56 if (mediaElement.isFullscreen()) |
54 return true; | 57 return true; |
55 | 58 |
56 if (!mediaElement.hasVideo()) | 59 if (!mediaElement.hasVideo()) |
57 return false; | 60 return false; |
58 | 61 |
59 if (!Fullscreen::fullscreenEnabled(mediaElement.document())) | 62 if (!Fullscreen::fullscreenEnabled(mediaElement.document())) |
60 return false; | 63 return false; |
61 | 64 |
62 return true; | 65 return true; |
63 } | 66 } |
64 | 67 |
68 static bool preferHiddenVolumeControls(const Document& document) | |
69 { | |
70 return !document.settings() || document.settings()->preferHiddenVolumeContro ls(); | |
71 } | |
72 | |
73 class MediaControls::BatchedControlUpdate { | |
74 WTF_MAKE_NONCOPYABLE(BatchedControlUpdate); | |
75 public: | |
76 BatchedControlUpdate(MediaControls* controls) : m_controls(controls) | |
77 { | |
78 ASSERT(isMainThread()); | |
79 ASSERT(s_batchDepth >= 0); | |
80 ++s_batchDepth; | |
81 } | |
82 ~BatchedControlUpdate() | |
83 { | |
84 ASSERT(isMainThread()); | |
85 ASSERT(s_batchDepth > 0); | |
86 if (!(--s_batchDepth)) | |
87 m_controls->computeWhichControlsFit(); | |
88 } | |
89 | |
90 private: | |
91 MediaControls* m_controls; | |
92 }; | |
93 | |
65 MediaControls::MediaControls(HTMLMediaElement& mediaElement) | 94 MediaControls::MediaControls(HTMLMediaElement& mediaElement) |
66 : HTMLDivElement(mediaElement.document()) | 95 : HTMLDivElement(mediaElement.document()) |
67 , m_mediaElement(&mediaElement) | 96 , m_mediaElement(&mediaElement) |
68 , m_overlayEnclosure(nullptr) | 97 , m_overlayEnclosure(nullptr) |
69 , m_overlayPlayButton(nullptr) | 98 , m_overlayPlayButton(nullptr) |
70 , m_overlayCastButton(nullptr) | 99 , m_overlayCastButton(nullptr) |
71 , m_enclosure(nullptr) | 100 , m_enclosure(nullptr) |
72 , m_panel(nullptr) | 101 , m_panel(nullptr) |
73 , m_playButton(nullptr) | 102 , m_playButton(nullptr) |
74 , m_timeline(nullptr) | 103 , m_timeline(nullptr) |
75 , m_currentTimeDisplay(nullptr) | 104 , m_currentTimeDisplay(nullptr) |
76 , m_durationDisplay(nullptr) | 105 , m_durationDisplay(nullptr) |
77 , m_muteButton(nullptr) | 106 , m_muteButton(nullptr) |
78 , m_volumeSlider(nullptr) | 107 , m_volumeSlider(nullptr) |
79 , m_toggleClosedCaptionsButton(nullptr) | 108 , m_toggleClosedCaptionsButton(nullptr) |
80 , m_castButton(nullptr) | 109 , m_castButton(nullptr) |
81 , m_fullScreenButton(nullptr) | 110 , m_fullScreenButton(nullptr) |
82 , m_hideMediaControlsTimer(this, &MediaControls::hideMediaControlsTimerFired ) | 111 , m_hideMediaControlsTimer(this, &MediaControls::hideMediaControlsTimerFired ) |
83 , m_hideTimerBehaviorFlags(IgnoreNone) | 112 , m_hideTimerBehaviorFlags(IgnoreNone) |
84 , m_isMouseOverControls(false) | 113 , m_isMouseOverControls(false) |
85 , m_isPausedForScrubbing(false) | 114 , m_isPausedForScrubbing(false) |
115 , m_panelWidthChangedTimer(this, &MediaControls::panelWidthChangedTimerFired ) | |
116 , m_panelWidth(0) | |
117 , m_allowHiddenVolumeControls(RuntimeEnabledFeatures::newMediaPlaybackUiEnab led()) | |
118 , m_keepMuteButton(false) | |
86 { | 119 { |
87 } | 120 } |
88 | 121 |
89 PassRefPtrWillBeRawPtr<MediaControls> MediaControls::create(HTMLMediaElement& me diaElement) | 122 PassRefPtrWillBeRawPtr<MediaControls> MediaControls::create(HTMLMediaElement& me diaElement) |
90 { | 123 { |
91 RefPtrWillBeRawPtr<MediaControls> controls = adoptRefWillBeNoop(new MediaCon trols(mediaElement)); | 124 RefPtrWillBeRawPtr<MediaControls> controls = adoptRefWillBeNoop(new MediaCon trols(mediaElement)); |
92 controls->setShadowPseudoId(AtomicString("-webkit-media-controls", AtomicStr ing::ConstructFromLiteral)); | 125 controls->setShadowPseudoId(AtomicString("-webkit-media-controls", AtomicStr ing::ConstructFromLiteral)); |
93 controls->initializeControls(); | 126 controls->initializeControls(); |
94 return controls.release(); | 127 return controls.release(); |
95 } | 128 } |
96 | 129 |
97 // The media controls DOM structure looks like: | 130 // The media controls DOM structure looks like: |
98 // | 131 // |
99 // MediaControls (-webkit-media-controls) | 132 // MediaControls (-webkit-media-controls) |
100 // +-MediaControlOverlayEnclosureElement (-webkit-media-controls-o verlay-enclosure) | 133 // +-MediaControlOverlayEnclosureElement (-webkit-media-controls-o verlay-enclosure) |
101 // | +-MediaControlOverlayPlayButtonElement (-webkit-media-controls-o verlay-play-button) | 134 // | +-MediaControlOverlayPlayButtonElement (-webkit-media-controls-o verlay-play-button) |
102 // | | {if mediaControlsOverlayPlayButtonEnabled} | 135 // | | {if mediaControlsOverlayPlayButtonEnabled} |
103 // | \-MediaControlCastButtonElement (-internal-media-controls -overlay-cast-button) | 136 // | \-MediaControlCastButtonElement (-internal-media-controls -overlay-cast-button) |
104 // \-MediaControlPanelEnclosureElement (-webkit-media-controls-e nclosure) | 137 // \-MediaControlPanelEnclosureElement (-webkit-media-controls-e nclosure) |
105 // \-MediaControlPanelElement (-webkit-media-controls-p anel) | 138 // \-MediaControlPanelElement (-webkit-media-controls-p anel) |
106 // +-MediaControlPlayButtonElement (-webkit-media-controls-p lay-button) | 139 // +-MediaControlPlayButtonElement (-webkit-media-controls-p lay-button) |
140 // | {if !RTE::newMediaPlaybackUi()} | |
107 // +-MediaControlTimelineElement (-webkit-media-controls-t imeline) | 141 // +-MediaControlTimelineElement (-webkit-media-controls-t imeline) |
108 // +-MediaControlCurrentTimeDisplayElement (-webkit-media-controls-c urrent-time-display) | 142 // +-MediaControlCurrentTimeDisplayElement (-webkit-media-controls-c urrent-time-display) |
109 // +-MediaControlTimeRemainingDisplayElement (-webkit-media-controls-t ime-remaining-display) | 143 // +-MediaControlTimeRemainingDisplayElement (-webkit-media-controls-t ime-remaining-display) |
144 // | {if RTE::newMediaPlaybackUi()} | |
145 // +-MediaControlTimelineElement (-webkit-media-controls-t imeline) | |
110 // +-MediaControlMuteButtonElement (-webkit-media-controls-m ute-button) | 146 // +-MediaControlMuteButtonElement (-webkit-media-controls-m ute-button) |
111 // +-MediaControlVolumeSliderElement (-webkit-media-controls-v olume-slider) | 147 // +-MediaControlVolumeSliderElement (-webkit-media-controls-v olume-slider) |
112 // +-MediaControlToggleClosedCaptionsButtonElement (-webkit-media-controls-t oggle-closed-captions-button) | 148 // +-MediaControlToggleClosedCaptionsButtonElement (-webkit-media-controls-t oggle-closed-captions-button) |
113 // +-MediaControlCastButtonElement (-internal-media-controls -cast-button) | 149 // +-MediaControlCastButtonElement (-internal-media-controls -cast-button) |
114 // \-MediaControlFullscreenButtonElement (-webkit-media-controls-f ullscreen-button) | 150 // \-MediaControlFullscreenButtonElement (-webkit-media-controls-f ullscreen-button) |
115 void MediaControls::initializeControls() | 151 void MediaControls::initializeControls() |
116 { | 152 { |
153 const bool useNewUi = RuntimeEnabledFeatures::newMediaPlaybackUiEnabled(); | |
117 RefPtrWillBeRawPtr<MediaControlOverlayEnclosureElement> overlayEnclosure = M ediaControlOverlayEnclosureElement::create(*this); | 154 RefPtrWillBeRawPtr<MediaControlOverlayEnclosureElement> overlayEnclosure = M ediaControlOverlayEnclosureElement::create(*this); |
118 | 155 |
119 if (document().settings() && document().settings()->mediaControlsOverlayPlay ButtonEnabled()) { | 156 if (document().settings() && document().settings()->mediaControlsOverlayPlay ButtonEnabled()) { |
120 RefPtrWillBeRawPtr<MediaControlOverlayPlayButtonElement> overlayPlayButt on = MediaControlOverlayPlayButtonElement::create(*this); | 157 RefPtrWillBeRawPtr<MediaControlOverlayPlayButtonElement> overlayPlayButt on = MediaControlOverlayPlayButtonElement::create(*this); |
121 m_overlayPlayButton = overlayPlayButton.get(); | 158 m_overlayPlayButton = overlayPlayButton.get(); |
122 overlayEnclosure->appendChild(overlayPlayButton.release()); | 159 overlayEnclosure->appendChild(overlayPlayButton.release()); |
123 } | 160 } |
124 | 161 |
125 RefPtrWillBeRawPtr<MediaControlCastButtonElement> overlayCastButton = MediaC ontrolCastButtonElement::create(*this, true); | 162 RefPtrWillBeRawPtr<MediaControlCastButtonElement> overlayCastButton = MediaC ontrolCastButtonElement::create(*this, true); |
126 m_overlayCastButton = overlayCastButton.get(); | 163 m_overlayCastButton = overlayCastButton.get(); |
127 overlayEnclosure->appendChild(overlayCastButton.release()); | 164 overlayEnclosure->appendChild(overlayCastButton.release()); |
128 | 165 |
129 m_overlayEnclosure = overlayEnclosure.get(); | 166 m_overlayEnclosure = overlayEnclosure.get(); |
130 appendChild(overlayEnclosure.release()); | 167 appendChild(overlayEnclosure.release()); |
131 | 168 |
132 // Create an enclosing element for the panel so we can visually offset the c ontrols correctly. | 169 // Create an enclosing element for the panel so we can visually offset the c ontrols correctly. |
133 RefPtrWillBeRawPtr<MediaControlPanelEnclosureElement> enclosure = MediaContr olPanelEnclosureElement::create(*this); | 170 RefPtrWillBeRawPtr<MediaControlPanelEnclosureElement> enclosure = MediaContr olPanelEnclosureElement::create(*this); |
134 | 171 |
135 RefPtrWillBeRawPtr<MediaControlPanelElement> panel = MediaControlPanelElemen t::create(*this); | 172 RefPtrWillBeRawPtr<MediaControlPanelElement> panel = MediaControlPanelElemen t::create(*this); |
136 | 173 |
137 RefPtrWillBeRawPtr<MediaControlPlayButtonElement> playButton = MediaControlP layButtonElement::create(*this); | 174 RefPtrWillBeRawPtr<MediaControlPlayButtonElement> playButton = MediaControlP layButtonElement::create(*this); |
138 m_playButton = playButton.get(); | 175 m_playButton = playButton.get(); |
139 panel->appendChild(playButton.release()); | 176 panel->appendChild(playButton.release()); |
140 | 177 |
141 RefPtrWillBeRawPtr<MediaControlTimelineElement> timeline = MediaControlTimel ineElement::create(*this); | 178 RefPtrWillBeRawPtr<MediaControlTimelineElement> timeline = MediaControlTimel ineElement::create(*this); |
142 m_timeline = timeline.get(); | 179 m_timeline = timeline.get(); |
143 panel->appendChild(timeline.release()); | 180 // In old UX, timeline is before the time / duration text. |
181 if (!useNewUi) | |
182 panel->appendChild(timeline.release()); | |
183 // else we will attach it later. | |
144 | 184 |
145 RefPtrWillBeRawPtr<MediaControlCurrentTimeDisplayElement> currentTimeDisplay = MediaControlCurrentTimeDisplayElement::create(*this); | 185 RefPtrWillBeRawPtr<MediaControlCurrentTimeDisplayElement> currentTimeDisplay = MediaControlCurrentTimeDisplayElement::create(*this); |
146 m_currentTimeDisplay = currentTimeDisplay.get(); | 186 m_currentTimeDisplay = currentTimeDisplay.get(); |
147 m_currentTimeDisplay->hide(); | 187 m_currentTimeDisplay->setIsWanted(useNewUi); |
148 panel->appendChild(currentTimeDisplay.release()); | 188 panel->appendChild(currentTimeDisplay.release()); |
149 | 189 |
150 RefPtrWillBeRawPtr<MediaControlTimeRemainingDisplayElement> durationDisplay = MediaControlTimeRemainingDisplayElement::create(*this); | 190 RefPtrWillBeRawPtr<MediaControlTimeRemainingDisplayElement> durationDisplay = MediaControlTimeRemainingDisplayElement::create(*this); |
151 m_durationDisplay = durationDisplay.get(); | 191 m_durationDisplay = durationDisplay.get(); |
152 panel->appendChild(durationDisplay.release()); | 192 panel->appendChild(durationDisplay.release()); |
153 | 193 |
194 // Timeline is after the time / duration text if newMediaPlaybackUiEnabled. | |
195 if (useNewUi) | |
196 panel->appendChild(timeline.release()); | |
197 | |
154 RefPtrWillBeRawPtr<MediaControlMuteButtonElement> muteButton = MediaControlM uteButtonElement::create(*this); | 198 RefPtrWillBeRawPtr<MediaControlMuteButtonElement> muteButton = MediaControlM uteButtonElement::create(*this); |
155 m_muteButton = muteButton.get(); | 199 m_muteButton = muteButton.get(); |
156 panel->appendChild(muteButton.release()); | 200 panel->appendChild(muteButton.release()); |
201 if (m_allowHiddenVolumeControls && preferHiddenVolumeControls(document())) | |
202 m_muteButton->setIsWanted(false); | |
157 | 203 |
158 RefPtrWillBeRawPtr<MediaControlVolumeSliderElement> slider = MediaControlVol umeSliderElement::create(*this); | 204 RefPtrWillBeRawPtr<MediaControlVolumeSliderElement> slider = MediaControlVol umeSliderElement::create(*this); |
159 m_volumeSlider = slider.get(); | 205 m_volumeSlider = slider.get(); |
160 panel->appendChild(slider.release()); | 206 panel->appendChild(slider.release()); |
207 if (m_allowHiddenVolumeControls && preferHiddenVolumeControls(document())) | |
208 m_volumeSlider->setIsWanted(false); | |
161 | 209 |
162 RefPtrWillBeRawPtr<MediaControlToggleClosedCaptionsButtonElement> toggleClos edCaptionsButton = MediaControlToggleClosedCaptionsButtonElement::create(*this); | 210 RefPtrWillBeRawPtr<MediaControlToggleClosedCaptionsButtonElement> toggleClos edCaptionsButton = MediaControlToggleClosedCaptionsButtonElement::create(*this); |
163 m_toggleClosedCaptionsButton = toggleClosedCaptionsButton.get(); | 211 m_toggleClosedCaptionsButton = toggleClosedCaptionsButton.get(); |
164 panel->appendChild(toggleClosedCaptionsButton.release()); | 212 panel->appendChild(toggleClosedCaptionsButton.release()); |
165 | 213 |
166 RefPtrWillBeRawPtr<MediaControlCastButtonElement> castButton = MediaControlC astButtonElement::create(*this, false); | 214 RefPtrWillBeRawPtr<MediaControlCastButtonElement> castButton = MediaControlC astButtonElement::create(*this, false); |
167 m_castButton = castButton.get(); | 215 m_castButton = castButton.get(); |
168 panel->appendChild(castButton.release()); | 216 panel->appendChild(castButton.release()); |
169 | 217 |
170 RefPtrWillBeRawPtr<MediaControlFullscreenButtonElement> fullscreenButton = M ediaControlFullscreenButtonElement::create(*this); | 218 RefPtrWillBeRawPtr<MediaControlFullscreenButtonElement> fullscreenButton = M ediaControlFullscreenButtonElement::create(*this); |
171 m_fullScreenButton = fullscreenButton.get(); | 219 m_fullScreenButton = fullscreenButton.get(); |
172 panel->appendChild(fullscreenButton.release()); | 220 panel->appendChild(fullscreenButton.release()); |
173 | 221 |
174 m_panel = panel.get(); | 222 m_panel = panel.get(); |
175 enclosure->appendChild(panel.release()); | 223 enclosure->appendChild(panel.release()); |
176 | 224 |
177 m_enclosure = enclosure.get(); | 225 m_enclosure = enclosure.get(); |
178 appendChild(enclosure.release()); | 226 appendChild(enclosure.release()); |
179 } | 227 } |
180 | 228 |
181 void MediaControls::reset() | 229 void MediaControls::reset() |
182 { | 230 { |
183 double duration = mediaElement().duration(); | 231 const bool useNewUi = RuntimeEnabledFeatures::newMediaPlaybackUiEnabled(); |
232 BatchedControlUpdate batch(this); | |
233 | |
234 m_allowHiddenVolumeControls = useNewUi; | |
235 m_keepMuteButton = false; | |
236 | |
237 const double duration = mediaElement().duration(); | |
184 m_durationDisplay->setInnerText(LayoutTheme::theme().formatMediaControlsTime (duration), ASSERT_NO_EXCEPTION); | 238 m_durationDisplay->setInnerText(LayoutTheme::theme().formatMediaControlsTime (duration), ASSERT_NO_EXCEPTION); |
185 m_durationDisplay->setCurrentValue(duration); | 239 m_durationDisplay->setCurrentValue(duration); |
186 | 240 |
241 if (useNewUi) { | |
242 // Show everything that we might hide. | |
243 // If we don't have a duration, then mark it to be hidden. For the | |
244 // old UI case, want / don't want is the same as show / hide since | |
245 // it is never marked as not fitting. | |
246 m_durationDisplay->setIsWanted(std::isfinite(duration)); | |
247 m_currentTimeDisplay->setIsWanted(true); | |
248 m_timeline->setIsWanted(true); | |
249 } | |
250 | |
187 updatePlayState(); | 251 updatePlayState(); |
188 | 252 |
189 updateCurrentTimeDisplay(); | 253 updateCurrentTimeDisplay(); |
190 | 254 |
191 m_timeline->setDuration(duration); | 255 m_timeline->setDuration(duration); |
192 m_timeline->setPosition(mediaElement().currentTime()); | 256 m_timeline->setPosition(mediaElement().currentTime()); |
193 | 257 |
194 if (!mediaElement().hasAudio()) | |
195 m_volumeSlider->hide(); | |
196 else | |
197 m_volumeSlider->show(); | |
198 updateVolume(); | 258 updateVolume(); |
199 | 259 |
200 refreshClosedCaptionsButtonVisibility(); | 260 refreshClosedCaptionsButtonVisibility(); |
201 | 261 |
202 if (shouldShowFullscreenButton(mediaElement())) | 262 m_fullScreenButton->setIsWanted(shouldShowFullscreenButton(mediaElement())); |
203 m_fullScreenButton->show(); | |
204 else | |
205 m_fullScreenButton->hide(); | |
206 | 263 |
207 refreshCastButtonVisibility(); | 264 refreshCastButtonVisibilityWithoutUpdate(); |
208 makeOpaque(); | 265 makeOpaque(); |
266 | |
267 // Set the panel width here, and force a layout, before the controls update . | |
268 m_panelWidth = m_panel->clientWidth(); | |
209 } | 269 } |
210 | 270 |
211 LayoutObject* MediaControls::layoutObjectForTextTrackLayout() | 271 LayoutObject* MediaControls::layoutObjectForTextTrackLayout() |
212 { | 272 { |
213 return m_panel->layoutObject(); | 273 return m_panel->layoutObject(); |
214 } | 274 } |
215 | 275 |
216 void MediaControls::show() | 276 void MediaControls::show() |
217 { | 277 { |
218 makeOpaque(); | 278 makeOpaque(); |
219 m_panel->show(); | 279 m_panel->setIsWanted(true); |
220 m_panel->setIsDisplayed(true); | 280 m_panel->setIsDisplayed(true); |
221 if (m_overlayPlayButton) | 281 if (m_overlayPlayButton) |
222 m_overlayPlayButton->updateDisplayType(); | 282 m_overlayPlayButton->updateDisplayType(); |
223 } | 283 } |
224 | 284 |
225 void MediaControls::mediaElementFocused() | 285 void MediaControls::mediaElementFocused() |
226 { | 286 { |
227 if (mediaElement().shouldShowControls()) { | 287 if (mediaElement().shouldShowControls()) { |
228 show(); | 288 show(); |
229 resetHideMediaControlsTimer(); | 289 resetHideMediaControlsTimer(); |
230 } | 290 } |
231 } | 291 } |
232 | 292 |
233 void MediaControls::hide() | 293 void MediaControls::hide() |
234 { | 294 { |
235 m_panel->hide(); | 295 m_panel->setIsWanted(false); |
236 m_panel->setIsDisplayed(false); | 296 m_panel->setIsDisplayed(false); |
237 if (m_overlayPlayButton) | 297 if (m_overlayPlayButton) |
238 m_overlayPlayButton->hide(); | 298 m_overlayPlayButton->setIsWanted(false); |
239 } | 299 } |
240 | 300 |
241 void MediaControls::makeOpaque() | 301 void MediaControls::makeOpaque() |
242 { | 302 { |
243 m_panel->makeOpaque(); | 303 m_panel->makeOpaque(); |
244 } | 304 } |
245 | 305 |
246 void MediaControls::makeTransparent() | 306 void MediaControls::makeTransparent() |
247 { | 307 { |
248 m_panel->makeTransparent(); | 308 m_panel->makeTransparent(); |
309 m_overlayCastButton->setIsWanted(false); | |
249 } | 310 } |
250 | 311 |
251 bool MediaControls::shouldHideMediaControls(unsigned behaviorFlags) const | 312 bool MediaControls::shouldHideMediaControls(unsigned behaviorFlags) const |
252 { | 313 { |
253 // Never hide for a media element without visual representation. | 314 // Never hide for a media element without visual representation. |
254 if (!mediaElement().hasVideo() || mediaElement().isPlayingRemotely()) | 315 if (!mediaElement().hasVideo() || mediaElement().isPlayingRemotely()) |
255 return false; | 316 return false; |
256 // Don't hide if the mouse is over the controls. | 317 // Don't hide if the mouse is over the controls. |
257 const bool ignoreControlsHover = behaviorFlags & IgnoreControlsHover; | 318 const bool ignoreControlsHover = behaviorFlags & IgnoreControlsHover; |
258 if (!ignoreControlsHover && m_panel->hovered()) | 319 if (!ignoreControlsHover && m_panel->hovered()) |
259 return false; | 320 return false; |
260 // Don't hide if the mouse is over the video area. | 321 // Don't hide if the mouse is over the video area. |
261 const bool ignoreVideoHover = behaviorFlags & IgnoreVideoHover; | 322 const bool ignoreVideoHover = behaviorFlags & IgnoreVideoHover; |
262 if (!ignoreVideoHover && m_isMouseOverControls) | 323 if (!ignoreVideoHover && m_isMouseOverControls) |
263 return false; | 324 return false; |
264 // Don't hide if focus is on the HTMLMediaElement or within the | 325 // Don't hide if focus is on the HTMLMediaElement or within the |
265 // controls/shadow tree. (Perform the checks separately to avoid going | 326 // controls/shadow tree. (Perform the checks separately to avoid going |
266 // through all the potential ancestor hosts for the focused element.) | 327 // through all the potential ancestor hosts for the focused element.) |
267 const bool ignoreFocus = behaviorFlags & IgnoreFocus; | 328 const bool ignoreFocus = behaviorFlags & IgnoreFocus; |
268 if (!ignoreFocus && (mediaElement().focused() || contains(document().focused Element()))) | 329 if (!ignoreFocus && (mediaElement().focused() || contains(document().focused Element()))) |
269 return false; | 330 return false; |
270 return true; | 331 return true; |
271 } | 332 } |
272 | 333 |
273 void MediaControls::playbackStarted() | 334 void MediaControls::playbackStarted() |
274 { | 335 { |
275 m_currentTimeDisplay->show(); | 336 BatchedControlUpdate batch(this); |
276 m_durationDisplay->hide(); | 337 |
338 if (!RuntimeEnabledFeatures::newMediaPlaybackUiEnabled()) { | |
339 m_currentTimeDisplay->setIsWanted(true); | |
340 m_durationDisplay->setIsWanted(false); | |
341 } | |
277 | 342 |
278 updatePlayState(); | 343 updatePlayState(); |
279 m_timeline->setPosition(mediaElement().currentTime()); | 344 m_timeline->setPosition(mediaElement().currentTime()); |
280 updateCurrentTimeDisplay(); | 345 updateCurrentTimeDisplay(); |
281 | 346 |
282 startHideMediaControlsTimer(); | 347 startHideMediaControlsTimer(); |
283 } | 348 } |
284 | 349 |
285 void MediaControls::playbackProgressed() | 350 void MediaControls::playbackProgressed() |
286 { | 351 { |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
327 mediaElement().togglePlayState(); | 392 mediaElement().togglePlayState(); |
328 } | 393 } |
329 } | 394 } |
330 | 395 |
331 void MediaControls::updateCurrentTimeDisplay() | 396 void MediaControls::updateCurrentTimeDisplay() |
332 { | 397 { |
333 double now = mediaElement().currentTime(); | 398 double now = mediaElement().currentTime(); |
334 double duration = mediaElement().duration(); | 399 double duration = mediaElement().duration(); |
335 | 400 |
336 // After seek, hide duration display and show current time. | 401 // After seek, hide duration display and show current time. |
337 if (now > 0) { | 402 if (!RuntimeEnabledFeatures::newMediaPlaybackUiEnabled() && now > 0) { |
338 m_currentTimeDisplay->show(); | 403 BatchedControlUpdate batch(this); |
339 m_durationDisplay->hide(); | 404 m_currentTimeDisplay->setIsWanted(true); |
405 m_durationDisplay->setIsWanted(false); | |
340 } | 406 } |
341 | 407 |
342 // Allow the theme to format the time. | 408 // Allow the theme to format the time. |
343 m_currentTimeDisplay->setInnerText(LayoutTheme::theme().formatMediaControlsC urrentTime(now, duration), IGNORE_EXCEPTION); | 409 m_currentTimeDisplay->setInnerText(LayoutTheme::theme().formatMediaControlsC urrentTime(now, duration), IGNORE_EXCEPTION); |
344 m_currentTimeDisplay->setCurrentValue(now); | 410 m_currentTimeDisplay->setCurrentValue(now); |
345 } | 411 } |
346 | 412 |
347 void MediaControls::updateVolume() | 413 void MediaControls::updateVolume() |
348 { | 414 { |
349 m_muteButton->updateDisplayType(); | 415 m_muteButton->updateDisplayType(); |
350 // Invalidate the mute button because it paints differently according to vol ume. | 416 // Invalidate the mute button because it paints differently according to vol ume. |
351 if (LayoutObject* layoutObject = m_muteButton->layoutObject()) | 417 if (LayoutObject* layoutObject = m_muteButton->layoutObject()) |
352 layoutObject->setShouldDoFullPaintInvalidation(); | 418 layoutObject->setShouldDoFullPaintInvalidation(); |
353 | 419 |
354 if (mediaElement().muted()) | 420 if (mediaElement().muted()) |
355 m_volumeSlider->setVolume(0); | 421 m_volumeSlider->setVolume(0); |
356 else | 422 else |
357 m_volumeSlider->setVolume(mediaElement().volume()); | 423 m_volumeSlider->setVolume(mediaElement().volume()); |
424 | |
425 // Update the visibility of our audio elements. | |
426 // We never want the volume slider if there's no audio. | |
427 // If there is audio, then we want it unless hiding audio is enabled and | |
428 // we prefer to hide it. | |
429 BatchedControlUpdate batch(this); | |
430 m_volumeSlider->setIsWanted(mediaElement().hasAudio() | |
431 && !(m_allowHiddenVolumeControls && preferHiddenVolumeControls(document( )))); | |
432 | |
433 // The mute button is a little more complicated. If enableNewMediaPlaybackU i | |
434 // is true, then we choose to hide or show the mute button to save space. | |
435 // If enableNew* is not set, then we never touch the mute button, and | |
436 // instead leave it to the CSS. | |
437 // Note that this is why m_allowHiddenVolumeControls isn't rolled into prefe r...(). | |
438 if (m_allowHiddenVolumeControls) { | |
439 // If there is no audio track, then hide the mute button. If there | |
440 // is an audio track, then we always show the mute button unless | |
441 // we prefer to hide it and the media isn't muted. If it's muted, | |
442 // then we show it to let the user unmute it. In this case, we don't | |
443 // want to re-hide the mute button later. | |
444 m_keepMuteButton |= mediaElement().muted(); | |
445 m_muteButton->setIsWanted(mediaElement().hasAudio() | |
446 && (!preferHiddenVolumeControls(document()) || m_keepMuteButton)); | |
447 } | |
448 | |
358 // Invalidate the volume slider because it paints differently according to v olume. | 449 // Invalidate the volume slider because it paints differently according to v olume. |
359 if (LayoutObject* layoutObject = m_volumeSlider->layoutObject()) | 450 if (LayoutObject* layoutObject = m_volumeSlider->layoutObject()) |
360 layoutObject->setShouldDoFullPaintInvalidation(); | 451 layoutObject->setShouldDoFullPaintInvalidation(); |
361 } | 452 } |
362 | 453 |
363 void MediaControls::changedClosedCaptionsVisibility() | 454 void MediaControls::changedClosedCaptionsVisibility() |
364 { | 455 { |
365 m_toggleClosedCaptionsButton->updateDisplayType(); | 456 m_toggleClosedCaptionsButton->updateDisplayType(); |
366 } | 457 } |
367 | 458 |
368 void MediaControls::refreshClosedCaptionsButtonVisibility() | 459 void MediaControls::refreshClosedCaptionsButtonVisibility() |
369 { | 460 { |
370 if (mediaElement().hasClosedCaptions()) | 461 m_toggleClosedCaptionsButton->setIsWanted(mediaElement().hasClosedCaptions() ); |
371 m_toggleClosedCaptionsButton->show(); | 462 BatchedControlUpdate batch(this); |
372 else | |
373 m_toggleClosedCaptionsButton->hide(); | |
374 } | 463 } |
375 | 464 |
376 static Element* elementFromCenter(Element& element) | 465 static Element* elementFromCenter(Element& element) |
377 { | 466 { |
378 ClientRect* clientRect = element.getBoundingClientRect(); | 467 ClientRect* clientRect = element.getBoundingClientRect(); |
379 int centerX = static_cast<int>((clientRect->left() + clientRect->right()) / 2); | 468 int centerX = static_cast<int>((clientRect->left() + clientRect->right()) / 2); |
380 int centerY = static_cast<int>((clientRect->top() + clientRect->bottom()) / 2); | 469 int centerY = static_cast<int>((clientRect->top() + clientRect->bottom()) / 2); |
381 | 470 |
382 return element.document().elementFromPoint(centerX , centerY); | 471 return element.document().elementFromPoint(centerX , centerY); |
383 } | 472 } |
384 | 473 |
385 void MediaControls::tryShowOverlayCastButton() | 474 void MediaControls::tryShowOverlayCastButton() |
386 { | 475 { |
387 // The element needs to be shown to have its dimensions and position. | 476 // The element needs to be shown to have its dimensions and position. |
388 m_overlayCastButton->show(); | 477 m_overlayCastButton->setIsWanted(true); |
389 | |
390 if (elementFromCenter(*m_overlayCastButton) != &mediaElement()) | 478 if (elementFromCenter(*m_overlayCastButton) != &mediaElement()) |
391 m_overlayCastButton->hide(); | 479 m_overlayCastButton->setIsWanted(false); |
392 } | 480 } |
393 | 481 |
394 void MediaControls::refreshCastButtonVisibility() | 482 void MediaControls::refreshCastButtonVisibility() |
395 { | 483 { |
484 refreshCastButtonVisibilityWithoutUpdate(); | |
485 BatchedControlUpdate batch(this); | |
486 } | |
487 | |
488 void MediaControls::refreshCastButtonVisibilityWithoutUpdate() | |
489 { | |
396 if (mediaElement().hasRemoteRoutes()) { | 490 if (mediaElement().hasRemoteRoutes()) { |
397 // The reason for the autoplay test is that some pages (e.g. vimeo.com) have an autoplay background video, which | 491 // The reason for the autoplay test is that some pages (e.g. vimeo.com) have an autoplay background video, which |
398 // doesn't autoplay on Chrome for Android (we prevent it) so starts paus ed. In such cases we don't want to automatically | 492 // doesn't autoplay on Chrome for Android (we prevent it) so starts paus ed. In such cases we don't want to automatically |
399 // show the cast button, since it looks strange and is unlikely to corre spond with anything the user wants to do. | 493 // show the cast button, since it looks strange and is unlikely to corre spond with anything the user wants to do. |
400 // If a user does want to cast a paused autoplay video then they can sti ll do so by touching or clicking on the | 494 // If a user does want to cast a paused autoplay video then they can sti ll do so by touching or clicking on the |
401 // video, which will cause the cast button to appear. | 495 // video, which will cause the cast button to appear. |
402 if (!mediaElement().shouldShowControls() && !mediaElement().autoplay() & & mediaElement().paused()) { | 496 if (!mediaElement().shouldShowControls() && !mediaElement().autoplay() & & mediaElement().paused()) { |
403 showOverlayCastButton(); | 497 // Note that this is a case where we add the overlay cast button |
498 // without wanting the panel cast button. We depend on the fact | |
499 // that computeWhichControlsFit() won't change overlay cast button | |
500 // visibility in the case where the cast button isn't wanted. | |
501 // We don't call compute...() here, but it will be called as | |
502 // non-cast changes (e.g., resize) occur. If the panel button | |
503 // is shown, however, compute...() will take control of the | |
504 // overlay cast button if it needs to hide it from the panel. | |
505 tryShowOverlayCastButton(); | |
506 m_castButton->setIsWanted(false); | |
404 } else if (mediaElement().shouldShowControls()) { | 507 } else if (mediaElement().shouldShowControls()) { |
405 m_overlayCastButton->hide(); | 508 m_overlayCastButton->setIsWanted(false); |
406 m_castButton->show(); | 509 m_castButton->setIsWanted(true); |
407 // Check that the cast button actually fits on the bar. | 510 // Check that the cast button actually fits on the bar. For the |
408 if (m_fullScreenButton->getBoundingClientRect()->right() > m_panel-> getBoundingClientRect()->right()) { | 511 // newMediaPlaybackUiEnabled case, we let computeWhichControlsFit() |
409 m_castButton->hide(); | 512 // handle this. |
513 if ( !RuntimeEnabledFeatures::newMediaPlaybackUiEnabled() | |
514 && m_fullScreenButton->getBoundingClientRect()->right() > m_pane l->getBoundingClientRect()->right()) { | |
515 m_castButton->setIsWanted(false); | |
410 tryShowOverlayCastButton(); | 516 tryShowOverlayCastButton(); |
411 } | 517 } |
412 } | 518 } |
413 } else { | 519 } else { |
414 m_castButton->hide(); | 520 m_castButton->setIsWanted(false); |
415 m_overlayCastButton->hide(); | 521 m_overlayCastButton->setIsWanted(false); |
416 } | 522 } |
417 } | 523 } |
418 | 524 |
419 void MediaControls::showOverlayCastButton() | 525 void MediaControls::showOverlayCastButton() |
420 { | 526 { |
421 tryShowOverlayCastButton(); | 527 tryShowOverlayCastButton(); |
422 resetHideMediaControlsTimer(); | 528 resetHideMediaControlsTimer(); |
423 } | 529 } |
424 | 530 |
425 void MediaControls::enteredFullscreen() | 531 void MediaControls::enteredFullscreen() |
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
496 unsigned behaviorFlags = m_hideTimerBehaviorFlags | IgnoreFocus | IgnoreVide oHover; | 602 unsigned behaviorFlags = m_hideTimerBehaviorFlags | IgnoreFocus | IgnoreVide oHover; |
497 m_hideTimerBehaviorFlags = IgnoreNone; | 603 m_hideTimerBehaviorFlags = IgnoreNone; |
498 | 604 |
499 if (mediaElement().togglePlayStateWillPlay()) | 605 if (mediaElement().togglePlayStateWillPlay()) |
500 return; | 606 return; |
501 | 607 |
502 if (!shouldHideMediaControls(behaviorFlags)) | 608 if (!shouldHideMediaControls(behaviorFlags)) |
503 return; | 609 return; |
504 | 610 |
505 makeTransparent(); | 611 makeTransparent(); |
506 m_overlayCastButton->hide(); | |
507 } | 612 } |
508 | 613 |
509 void MediaControls::startHideMediaControlsTimer() | 614 void MediaControls::startHideMediaControlsTimer() |
510 { | 615 { |
511 m_hideMediaControlsTimer.startOneShot(timeWithoutMouseMovementBeforeHidingMe diaControls, FROM_HERE); | 616 m_hideMediaControlsTimer.startOneShot(timeWithoutMouseMovementBeforeHidingMe diaControls, FROM_HERE); |
512 } | 617 } |
513 | 618 |
514 void MediaControls::stopHideMediaControlsTimer() | 619 void MediaControls::stopHideMediaControlsTimer() |
515 { | 620 { |
516 m_hideMediaControlsTimer.stop(); | 621 m_hideMediaControlsTimer.stop(); |
(...skipping 10 matching lines...) Expand all Loading... | |
527 bool MediaControls::containsRelatedTarget(Event* event) | 632 bool MediaControls::containsRelatedTarget(Event* event) |
528 { | 633 { |
529 if (!event->isMouseEvent()) | 634 if (!event->isMouseEvent()) |
530 return false; | 635 return false; |
531 EventTarget* relatedTarget = toMouseEvent(event)->relatedTarget(); | 636 EventTarget* relatedTarget = toMouseEvent(event)->relatedTarget(); |
532 if (!relatedTarget) | 637 if (!relatedTarget) |
533 return false; | 638 return false; |
534 return contains(relatedTarget->toNode()); | 639 return contains(relatedTarget->toNode()); |
535 } | 640 } |
536 | 641 |
642 void MediaControls::notifyPanelWidthChanged(const LayoutUnit& newWidth) | |
643 { | |
644 // Don't bother to do any work if this matches the most recent panel | |
645 // width, since we're called after layout. | |
646 // Note that this code permits a bad frame on resize, since it is | |
647 // run after the relayout / paint happens. It would be great to improve | |
648 // this, but it would be even greater to move this code entirely to | |
649 // JS and fix it there. | |
650 const int panelWidth = newWidth.toInt(); | |
651 | |
652 if (!RuntimeEnabledFeatures::newMediaPlaybackUiEnabled()) | |
653 return; | |
654 | |
655 m_panelWidth = panelWidth; | |
656 | |
657 // Adjust for effective zoom. | |
658 if (!m_panel->layoutObject() || !m_panel->layoutObject()->style()) | |
659 return; | |
660 m_panelWidth = ceil(m_panelWidth / m_panel->layoutObject()->style()->effecti veZoom()); | |
661 | |
662 m_panelWidthChangedTimer.startOneShot(0, FROM_HERE); | |
663 } | |
664 | |
665 void MediaControls::panelWidthChangedTimerFired(Timer<MediaControls>*) | |
666 { | |
667 computeWhichControlsFit(); | |
668 } | |
669 | |
670 void MediaControls::computeWhichControlsFit() | |
671 { | |
672 // Hide all controls that don't fit, and show the ones that do. | |
673 // This might be better suited for a layout, but since JS media controls | |
674 // won't benefit from that anwyay, we just do it here like JS will. | |
675 | |
676 if (!RuntimeEnabledFeatures::newMediaPlaybackUiEnabled()) | |
677 return; | |
678 | |
679 if (!m_panelWidth) | |
680 return; | |
681 | |
682 // Controls that we'll hide / show, in order of decreasing priority. | |
683 MediaControlElement* elements[] = { | |
684 m_playButton.get(), | |
685 m_toggleClosedCaptionsButton.get(), | |
686 m_fullScreenButton.get(), | |
687 m_timeline.get(), | |
688 m_currentTimeDisplay.get(), | |
689 m_volumeSlider.get(), | |
690 m_castButton.get(), | |
691 m_muteButton.get(), | |
692 m_durationDisplay.get(), | |
693 }; | |
694 | |
695 int usedWidth = 0; | |
696 bool droppedCastButton = false; | |
697 // Assume that all controls require 48px. Ideally, we could get this | |
698 // the computed style, but that requires the controls to be shown. | |
699 const int minimumWidth = 48; | |
700 for (MediaControlElement* element : elements) { | |
701 if (!element) | |
702 continue; | |
703 | |
704 if (element->isWanted()) { | |
705 if (usedWidth + minimumWidth <= m_panelWidth) { | |
706 element->setDoesFit(true); | |
707 usedWidth += minimumWidth; | |
708 } else { | |
709 element->setDoesFit(false); | |
710 if (element == m_castButton.get()) | |
711 droppedCastButton = true; | |
712 } | |
713 } | |
714 } | |
715 | |
716 // Special case for cast: if we want a cast button but dropped it, then | |
717 // show the overlay cast button instead. | |
718 if (m_castButton->isWanted()) | |
719 m_overlayCastButton->setIsWanted(droppedCastButton); | |
720 } | |
721 | |
722 void MediaControls::setAllowHiddenVolumeControls(bool allow) | |
723 { | |
724 m_allowHiddenVolumeControls = allow; | |
725 // Clear the 'keep muted flag', for tests. | |
726 m_keepMuteButton = false; | |
727 // Update the controls visibility. | |
728 updateVolume(); | |
729 } | |
730 | |
537 DEFINE_TRACE(MediaControls) | 731 DEFINE_TRACE(MediaControls) |
538 { | 732 { |
539 visitor->trace(m_mediaElement); | 733 visitor->trace(m_mediaElement); |
540 visitor->trace(m_panel); | 734 visitor->trace(m_panel); |
541 visitor->trace(m_overlayPlayButton); | 735 visitor->trace(m_overlayPlayButton); |
542 visitor->trace(m_overlayEnclosure); | 736 visitor->trace(m_overlayEnclosure); |
543 visitor->trace(m_playButton); | 737 visitor->trace(m_playButton); |
544 visitor->trace(m_currentTimeDisplay); | 738 visitor->trace(m_currentTimeDisplay); |
545 visitor->trace(m_timeline); | 739 visitor->trace(m_timeline); |
546 visitor->trace(m_muteButton); | 740 visitor->trace(m_muteButton); |
547 visitor->trace(m_volumeSlider); | 741 visitor->trace(m_volumeSlider); |
548 visitor->trace(m_toggleClosedCaptionsButton); | 742 visitor->trace(m_toggleClosedCaptionsButton); |
549 visitor->trace(m_fullScreenButton); | 743 visitor->trace(m_fullScreenButton); |
550 visitor->trace(m_durationDisplay); | 744 visitor->trace(m_durationDisplay); |
551 visitor->trace(m_enclosure); | 745 visitor->trace(m_enclosure); |
552 visitor->trace(m_castButton); | 746 visitor->trace(m_castButton); |
553 visitor->trace(m_overlayCastButton); | 747 visitor->trace(m_overlayCastButton); |
554 HTMLDivElement::trace(visitor); | 748 HTMLDivElement::trace(visitor); |
555 } | 749 } |
556 | 750 |
557 } | 751 } |
OLD | NEW |