Bug 161503 - Refactor the heuristic for showing media controls to take all media sessions into account
Summary: Refactor the heuristic for showing media controls to take all media sessions ...
Status: RESOLVED FIXED
Alias: None
Product: WebKit
Classification: Unclassified
Component: Media (show other bugs)
Version: WebKit Nightly Build
Hardware: Unspecified Unspecified
: P2 Normal
Assignee: Wenson Hsieh
URL:
Keywords: InRadar
Depends on:
Blocks:
 
Reported: 2016-09-01 12:43 PDT by Wenson Hsieh
Modified: 2016-09-03 17:26 PDT (History)
4 users (show)

See Also:


Attachments
Patch (43.02 KB, patch)
2016-09-01 13:55 PDT, Wenson Hsieh
no flags Details | Formatted Diff | Diff
Patch (92.60 KB, patch)
2016-09-02 10:36 PDT, Wenson Hsieh
no flags Details | Formatted Diff | Diff
Add missing ifdef guards to fix other platforms. (92.79 KB, patch)
2016-09-02 11:11 PDT, Wenson Hsieh
no flags Details | Formatted Diff | Diff
Move main content sizing helpers back into MediaElementSession.cpp (87.99 KB, patch)
2016-09-02 13:10 PDT, Wenson Hsieh
no flags Details | Formatted Diff | Diff
Addresses review feedback (87.16 KB, patch)
2016-09-03 15:52 PDT, Wenson Hsieh
no flags Details | Formatted Diff | Diff
Patch for landing (86.86 KB, patch)
2016-09-03 16:56 PDT, Wenson Hsieh
no flags Details | Formatted Diff | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Wenson Hsieh 2016-09-01 12:43:55 PDT
Refactor the heuristic for showing media controls to take all media sessions into account
Comment 1 Wenson Hsieh 2016-09-01 13:55:42 PDT
Created attachment 287672 [details]
Patch
Comment 2 Wenson Hsieh 2016-09-01 13:58:14 PDT
<rdar://problem/28033783>
Comment 3 Wenson Hsieh 2016-09-02 10:36:43 PDT
Created attachment 287776 [details]
Patch
Comment 4 Wenson Hsieh 2016-09-02 11:11:32 PDT
Created attachment 287785 [details]
Add missing ifdef guards to fix other platforms.
Comment 5 Wenson Hsieh 2016-09-02 13:10:20 PDT
Created attachment 287805 [details]
Move main content sizing helpers back into MediaElementSession.cpp
Comment 6 Darin Adler 2016-09-03 08:03:32 PDT
Comment on attachment 287805 [details]
Move main content sizing helpers back into MediaElementSession.cpp

View in context: https://bugs.webkit.org/attachment.cgi?id=287805&action=review

The patch generally looks good, but the mediaSessionInfo() and currentSessionInfos() functions have design problems. See my comments below.

> Source/WebCore/html/HTMLMediaElement.cpp:359
> +static bool mediaSessionMayBeConfusedForMainContent(const MediaSessionInfo& session)

"confused with" is probably good grammar, but I don’t think "confused for" is

> Source/WebCore/html/HTMLMediaElement.cpp:372
> +static const PlatformMediaSession* bestMediaSessionForShowingPlaybackControlsManager()

This function should return a MediaElementSession*; it will never return any other kind of PlatformMediaSession, and the single caller downcasts to MediaElementSession.

> Source/WebCore/html/HTMLMediaElement.cpp:376
> +    for (MediaSessionInfo info : PlatformMediaSessionManager::sharedManager().currentSessionInfos()) {

This unnecessarily makes a copy of each MediaSessionInfo as we iterate the vector. Instead the type should be MediaSessionInfo& or, better, just auto&, to use the objects in place within the vector.

> Source/WebCore/html/HTMLMediaElement.cpp:7233
> +    const PlatformMediaSession* bestMediaSession = bestMediaSessionForShowingPlaybackControlsManager();

auto* would be better than writing out the type here

> Source/WebCore/html/MediaElementSession.cpp:221
> +    RenderElement* renderer = m_element.renderer();

It’s generally incorrect to access the renderer for an element without having a guarantee that the code is only called when style is updated and layout is up to date. Putting calls to renderer behind abstractions like the session increases the risk that we will do that incorrectly. We should keep calls from the session to the renderer to the absolute minimum. Ideally the element calls its own renderer, not through an intermediary object, and it should do so only in functions called when renderer is known to be up to date.

There’s also a type problem here. Putting a renderer into a local variable that is specifically RenderElement* throws away type information. The renderer of an HTMLMediaElement is guaranteed to be a RenderMedia*, which both has more operations than what RenderElement* has, and might even have more efficient less polymorphic versions of some functions that RenderElement has. That’s why best style is to use auto* rather than RenderElement* in a case like this.

> Source/WebCore/platform/audio/PlatformMediaSession.h:170
> +    virtual void resetPlaybackSessionState() { };

No semicolon after a function body like this (see other functions above and below).

> Source/WebCore/platform/audio/PlatformMediaSession.h:192
> +struct MediaSessionInfo {

"info" is typically not a great name for any class; it’s mostly vacuous since all objects contain information. And this class is more than just some information about a media session; it contains a pointer to a specific media session object. It’s sort of “a media session with some precomputed attributes of that session”, which is only good because we happen to know the exact set that one particular algorithm needs. This is kind of a peculiar API. It has all the things that one particular caller happens to need, so it’s quite specific to its one use, but it masquerades as a general purpose collection of information.

It’s a poor design pattern to vend objects with raw pointers; keeping one around too long and using the pointer can lead to a serious bug, and really there is no reason to do that here.

There is a kind of false abstraction here, because MediaSession objects that are not MediaElementSession return an info structure but these never are used for anything; just immediately ruled out. The code in HTMLMediaElement already knows it’s specifically only interested in MediaElementSession.

A better design would be to add member functions to MediaElementSession that answer any of these questions as needed to make that code clear, then any structure needed to collect these and sort them would be something that the file defining bestMediaSessionForShowingPlaybackControlsManager would define locally for its purposes as needed.

> Source/WebCore/platform/audio/PlatformMediaSession.h:201
> +    MediaSessionInfo(const PlatformMediaSession* session, bool canShowControlsManager, bool isVisibleInViewport, bool isPlayingWithAudio, bool isLargeEnoughForMainContent, double timeOfLastUserInteraction)
> +        : session(session)
> +        , canShowControlsManager(canShowControlsManager)
> +        , isVisibleInViewport(isVisibleInViewport)
> +        , isPlayingWithAudio(isPlayingWithAudio)
> +        , isLargeEnoughForMainContent(isLargeEnoughForMainContent)
> +        , timeOfLastUserInteraction(timeOfLastUserInteraction)
> +    {
> +    }

A struct does not need a constructor like this; callers can simply use aggregate initialization even without this.
Comment 7 Wenson Hsieh 2016-09-03 15:37:39 PDT
Comment on attachment 287805 [details]
Move main content sizing helpers back into MediaElementSession.cpp

View in context: https://bugs.webkit.org/attachment.cgi?id=287805&action=review

>> Source/WebCore/html/HTMLMediaElement.cpp:359
>> +static bool mediaSessionMayBeConfusedForMainContent(const MediaSessionInfo& session)
> 
> "confused with" is probably good grammar, but I don’t think "confused for" is

True -- changed to "mediaSessionMayBeConfusedWithMainContent"

>> Source/WebCore/html/HTMLMediaElement.cpp:372
>> +static const PlatformMediaSession* bestMediaSessionForShowingPlaybackControlsManager()
> 
> This function should return a MediaElementSession*; it will never return any other kind of PlatformMediaSession, and the single caller downcasts to MediaElementSession.

Changed to return a MediaElementSession*.

>> Source/WebCore/html/HTMLMediaElement.cpp:376
>> +    for (MediaSessionInfo info : PlatformMediaSessionManager::sharedManager().currentSessionInfos()) {
> 
> This unnecessarily makes a copy of each MediaSessionInfo as we iterate the vector. Instead the type should be MediaSessionInfo& or, better, just auto&, to use the objects in place within the vector.

Sounds good -- changed to use auto&.

> Source/WebCore/html/HTMLMediaElement.cpp:387
> +    MediaSessionInfo strongestSessionCandidate = candidateSessionInfos.first();

MediaSessionInfo -> auto

>> Source/WebCore/html/HTMLMediaElement.cpp:7233
>> +    const PlatformMediaSession* bestMediaSession = bestMediaSessionForShowingPlaybackControlsManager();
> 
> auto* would be better than writing out the type here

Changed to use auto.

>> Source/WebCore/html/MediaElementSession.cpp:221
>> +    RenderElement* renderer = m_element.renderer();
> 
> It’s generally incorrect to access the renderer for an element without having a guarantee that the code is only called when style is updated and layout is up to date. Putting calls to renderer behind abstractions like the session increases the risk that we will do that incorrectly. We should keep calls from the session to the renderer to the absolute minimum. Ideally the element calls its own renderer, not through an intermediary object, and it should do so only in functions called when renderer is known to be up to date.
> 
> There’s also a type problem here. Putting a renderer into a local variable that is specifically RenderElement* throws away type information. The renderer of an HTMLMediaElement is guaranteed to be a RenderMedia*, which both has more operations than what RenderElement* has, and might even have more efficient less polymorphic versions of some functions that RenderElement has. That’s why best style is to use auto* rather than RenderElement* in a case like this.

I see -- this is concerning! Our current heuristic for determining whether autoplaying videos should present media controls goes through a main content check that uses the element renderer (asking for width, height, etc.) so the renderer being out of date when updating the controls manager is a problem we may have today; perhaps what we need to do when scheduling an update is to check if the renderer needs to be updated, and defer the update until after the renderer updates. I'll add a comment describing this.

To the second point, changed RenderElement* to auto.

>> Source/WebCore/platform/audio/PlatformMediaSession.h:170
>> +    virtual void resetPlaybackSessionState() { };
> 
> No semicolon after a function body like this (see other functions above and below).

Whoops, good catch. Fixed!

>> Source/WebCore/platform/audio/PlatformMediaSession.h:192
>> +struct MediaSessionInfo {
> 
> "info" is typically not a great name for any class; it’s mostly vacuous since all objects contain information. And this class is more than just some information about a media session; it contains a pointer to a specific media session object. It’s sort of “a media session with some precomputed attributes of that session”, which is only good because we happen to know the exact set that one particular algorithm needs. This is kind of a peculiar API. It has all the things that one particular caller happens to need, so it’s quite specific to its one use, but it masquerades as a general purpose collection of information.
> 
> It’s a poor design pattern to vend objects with raw pointers; keeping one around too long and using the pointer can lead to a serious bug, and really there is no reason to do that here.
> 
> There is a kind of false abstraction here, because MediaSession objects that are not MediaElementSession return an info structure but these never are used for anything; just immediately ruled out. The code in HTMLMediaElement already knows it’s specifically only interested in MediaElementSession.
> 
> A better design would be to add member functions to MediaElementSession that answer any of these questions as needed to make that code clear, then any structure needed to collect these and sort them would be something that the file defining bestMediaSessionForShowingPlaybackControlsManager would define locally for its purposes as needed.

This makes sense. I've changed MediaElementSession to have member functions that answer questions asked when determining which session is the best candidate. To ensure that we don't keep asking the session for the same thing, I've moved session info MediaElementSessionInfo to be defined in the implementation, so it's only used when computing the best candidate session.

>> Source/WebCore/platform/audio/PlatformMediaSession.h:201
>> +    }
> 
> A struct does not need a constructor like this; callers can simply use aggregate initialization even without this.

Removed, since I'm not relying on a copy constructor here anymore.

> Source/WebKit2/WebProcess/cocoa/WebPlaybackSessionManager.mm:422
> +    HTMLMediaElement *element = ensureModel(contextId).mediaElement();

HTMLMediaElement * -> auto
Comment 8 Wenson Hsieh 2016-09-03 15:52:27 PDT
Created attachment 287872 [details]
Addresses review feedback
Comment 9 Darin Adler 2016-09-03 16:17:27 PDT
Comment on attachment 287872 [details]
Addresses review feedback

View in context: https://bugs.webkit.org/attachment.cgi?id=287872&action=review

> Source/WebCore/html/MediaElementSession.cpp:294
> +bool MediaElementSession::isVisibleInViewportOrFullscreen() const
> +{
> +    if (m_element.isFullscreen())
> +        return true;
> +
> +    auto renderer = m_element.renderer();
> +    return renderer && renderer->visibleInViewportState() == RenderElement::VisibleInViewport;
> +}

Not sure we need this function to be a member of session. It’s a question about the media element, not something specific to the session, and there is not any logic shared with other functions in the session class. I think it could be done inside HTMLMediaElement.cpp. This would have the benefit of moving the renderer access into the element class rather than hiding it under a layer of abstraction.

But I am not sure about this suggestion.

> Source/WebCore/html/MediaElementSession.cpp:299
> +bool MediaElementSession::isPlayingAudio() const
> +{
> +    return m_element.isPlaying() && m_element.hasAudio() && !m_element.muted();
> +}

Ditto.
Comment 10 Wenson Hsieh 2016-09-03 16:54:48 PDT
Comment on attachment 287872 [details]
Addresses review feedback

View in context: https://bugs.webkit.org/attachment.cgi?id=287872&action=review

>> Source/WebCore/html/MediaElementSession.cpp:294
>> +}
> 
> Not sure we need this function to be a member of session. It’s a question about the media element, not something specific to the session, and there is not any logic shared with other functions in the session class. I think it could be done inside HTMLMediaElement.cpp. This would have the benefit of moving the renderer access into the element class rather than hiding it under a layer of abstraction.
> 
> But I am not sure about this suggestion.

I think this makes sense, especially since the helper that uses this is in HTMLMediaElement in the first place. Moved to HTMLMediaElement.

>> Source/WebCore/html/MediaElementSession.cpp:299
>> +}
> 
> Ditto.

Moved to HTMLMediaElement.
Comment 11 Wenson Hsieh 2016-09-03 16:56:13 PDT
Created attachment 287874 [details]
Patch for landing
Comment 12 WebKit Commit Bot 2016-09-03 17:26:11 PDT
Comment on attachment 287874 [details]
Patch for landing

Clearing flags on attachment: 287874

Committed r205412: <http://trac.webkit.org/changeset/205412>
Comment 13 WebKit Commit Bot 2016-09-03 17:26:15 PDT
All reviewed patches have been landed.  Closing bug.