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