Changeset 293658 in webkit


Ignore:
Timestamp:
May 1, 2022 6:48:01 PM (3 months ago)
Author:
Devin Rousso
Message:

[Modern Media Controls] the overflow button sometimes flickers
https://bugs.webkit.org/show_bug.cgi?id=239921
<rdar://problem/91329468>

Reviewed by Eric Carlson.

There are two things that control the visibility of the OverflowButton:

  1. whether any of the "default" actions (e.g. playback speed, chapters, etc.) are possible
  2. if any other buttons that have contextMenuOptions are dropped (i.e. there's not enough

room for it because the <video> is narrow or there are already too many buttons)

(1) is recalculated for most JS media events (e.g. whenever tracks are changed, if
the readyState changes, etc.).

(2) is recalculated in layout of MediaControls, which is (relatively) less frequent.

In the case that the only contextmenu options are provided by (2) (i.e. none of the "default"
actions are possible), the frequent recalculation of (1) will combined with the fact that
layout uses a requestAnimationFrame to delay/batch work will cause there to be a short
period of time after the recalculation of (1) and before the recalculation of (2) where
there are no contextmenu options, resulting in the OverflowButton being hidden.

  • Modules/modern-media-controls/controls/overflow-button.js:

(OverflowButton):
(OverflowButton.prototype.set visible):
(OverflowButton.prototype.set visible.isEmpty): Added.
(OverflowButton.prototype.get contextMenuOptions):
(OverflowButton.prototype.addExtraContextMenuOptions): Renamed from addContextMenuOptions.
(OverflowButton.prototype.clearExtraContextMenuOptions): Renamed from clearContextMenuOptions.
(OverflowButton.prototype.set defaultContextMenuOptions):

  • Modules/modern-media-controls/controls/inline-media-controls.js:

(InlineMediaControls.prototype.layout):

  • Modules/modern-media-controls/controls/macos-fullscreen-media-controls.js:

(MacOSFullscreenMediaControls.prototype.layout):
Instead of having a single _contextMenuOptions that is modified by both (1) and (2), have
a separate member variable for each. This way, the recalculation of (1) doesn't also clear
the state left over from the last time (2) was calculated (which will be recalculated by (2)
shortly thereafter). Use both member variables to decide whether the OverflowButton should
be visible, allowing (1) and (2) to update independent of eachother.

Location:
trunk/Source/WebCore
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebCore/ChangeLog

    r293650 r293658  
     12022-05-01  Devin Rousso  <drousso@apple.com>
     2
     3        [Modern Media Controls] the overflow button sometimes flickers
     4        https://bugs.webkit.org/show_bug.cgi?id=239921
     5        <rdar://problem/91329468>
     6
     7        Reviewed by Eric Carlson.
     8
     9        There are two things that control the visibility of the `OverflowButton`:
     10        1. whether any of the "default" actions (e.g. playback speed, chapters, etc.) are possible
     11        2. if any other buttons that have `contextMenuOptions` are dropped (i.e. there's not enough
     12        room for it because the `<video>` is narrow or there are already too many buttons)
     13
     14        (1) is recalculated for most JS media events (e.g. whenever tracks are changed, if
     15        the `readyState` changes, etc.).
     16
     17        (2) is recalculated in `layout` of `MediaControls`, which is (relatively) less frequent.
     18
     19        In the case that the only contextmenu options are provided by (2) (i.e. none of the "default"
     20        actions are possible), the frequent recalculation of (1) will combined with the fact that
     21        `layout` uses a `requestAnimationFrame` to delay/batch work will cause there to be a short
     22        period of time after the recalculation of (1) and before the recalculation of (2) where
     23        there are no contextmenu options, resulting in the `OverflowButton` being hidden.
     24
     25        * Modules/modern-media-controls/controls/overflow-button.js:
     26        (OverflowButton):
     27        (OverflowButton.prototype.set visible):
     28        (OverflowButton.prototype.set visible.isEmpty): Added.
     29        (OverflowButton.prototype.get contextMenuOptions):
     30        (OverflowButton.prototype.addExtraContextMenuOptions): Renamed from `addContextMenuOptions`.
     31        (OverflowButton.prototype.clearExtraContextMenuOptions): Renamed from `clearContextMenuOptions`.
     32        (OverflowButton.prototype.set defaultContextMenuOptions):
     33        * Modules/modern-media-controls/controls/inline-media-controls.js:
     34        (InlineMediaControls.prototype.layout):
     35        * Modules/modern-media-controls/controls/macos-fullscreen-media-controls.js:
     36        (MacOSFullscreenMediaControls.prototype.layout):
     37        Instead of having a single `_contextMenuOptions` that is modified by both (1) and (2), have
     38        a separate member variable for each. This way, the recalculation of (1) doesn't also clear
     39        the state left over from the last time (2) was calculated (which will be recalculated by (2)
     40        shortly thereafter). Use both member variables to decide whether the `OverflowButton` should
     41        be `visible`, allowing (1) and (2) to update independent of eachother.
     42
    1432022-04-30  Andres Gonzalez  <andresg_22@apple.com>
    244
  • trunk/Source/WebCore/Modules/modern-media-controls/controls/inline-media-controls.js

    r289751 r293658  
    170170        this.rightContainer.children.concat(this.leftContainer.children).forEach(button => delete button.dropped);
    171171        this.muteButton.style = this.preferredMuteButtonStyle;
    172         this.overflowButton.clearContextMenuOptions();
     172        this.overflowButton.clearExtraContextMenuOptions();
    173173
    174174        for (let button of this._droppableButtons()) {
     
    189189
    190190            if (button !== this.overflowButton)
    191                 this.overflowButton.addContextMenuOptions(button.contextMenuOptions);
     191                this.overflowButton.addExtraContextMenuOptions(button.contextMenuOptions);
    192192        }
    193193
     
    201201
    202202            button.dropped = true;
    203             this.overflowButton.addContextMenuOptions(button.contextMenuOptions);
     203            this.overflowButton.addExtraContextMenuOptions(button.contextMenuOptions);
    204204        }
    205205
  • trunk/Source/WebCore/Modules/modern-media-controls/controls/macos-fullscreen-media-controls.js

    r292730 r293658  
    115115
    116116        this._rightContainer.children.forEach(button => delete button.dropped)
    117         this.overflowButton.clearContextMenuOptions();
     117        this.overflowButton.clearExtraContextMenuOptions();
    118118
    119119        this._leftContainer.visible = this.muteButton.enabled;
     
    129129
    130130            button.dropped = true;
    131             this.overflowButton.addContextMenuOptions(button.contextMenuOptions);
     131            this.overflowButton.addExtraContextMenuOptions(button.contextMenuOptions);
    132132        }
    133133
  • trunk/Source/WebCore/Modules/modern-media-controls/controls/overflow-button.js

    r290133 r293658  
    3535        });
    3636
    37         this.clearContextMenuOptions();
    3837        this._defaultContextMenuOptions = {};
     38        this._extraContextMenuOptions = {};
    3939    }
    4040
     
    4848    set visible(flag)
    4949    {
    50         let hasContextMenuOptions = false;
    51         for (let key in this._contextMenuOptions) {
    52             hasContextMenuOptions = true;
    53             break;
     50        function isEmpty(contextMenuOptions) {
     51            for (let key in contextMenuOptions)
     52                return false;
     53            return true;
    5454        }
    5555
    56         super.visible = flag && hasContextMenuOptions;
     56        super.visible = flag && (!isEmpty(this._defaultContextMenuOptions) || !isEmpty(this._extraContextMenuOptions));
    5757    }
    5858
    5959    get contextMenuOptions()
    6060    {
    61         return this._contextMenuOptions;
     61        return {
     62            ...this._defaultContextMenuOptions,
     63            ...this._extraContextMenuOptions,
     64        };
    6265    }
    6366
    64     addContextMenuOptions(contextMenuOptions)
     67    addExtraContextMenuOptions(contextMenuOptions)
    6568    {
    6669        if (!this.enabled)
     
    6871
    6972        for (let key in contextMenuOptions)
    70             this._contextMenuOptions[key] = contextMenuOptions[key];
     73            this._extraContextMenuOptions[key] = contextMenuOptions[key];
    7174
    7275        this.visible = true;
    7376    }
    7477
    75     clearContextMenuOptions()
     78    clearExtraContextMenuOptions()
    7679    {
    77         this._contextMenuOptions = {};
     80        this._extraContextMenuOptions = {};
    7881
    7982        this.visible = false;
    80 
    81         this.addContextMenuOptions(this._defaultContextMenuOptions);
    8283    }
    8384
     
    8687        this._defaultContextMenuOptions = defaultContextMenuOptions || {};
    8788
    88         this.clearContextMenuOptions();
    89 
    90         if (this.layoutDelegate)
    91             this.layoutDelegate.needsLayout = true;
     89        this.visible = true;
    9290    }
    9391
Note: See TracChangeset for help on using the changeset viewer.