chromium/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/TabObscuringHandler.java

// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package org.chromium.chrome.browser.tab;

import org.chromium.base.ObserverList;
import org.chromium.ui.util.TokenHolder;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

/**
 * Passes around the ability to set a view that is obscuring all tabs and optionally also the
 * toolbar.
 */
public class TabObscuringHandler {
    @Retention(RetentionPolicy.SOURCE)
    public @interface Target {
        // Specifies that only the web content is covered or obscured.
        int TAB_CONTENT = 1;
        // Specifies that the whole screen including the top toolbar container is covered or
        // obscured.
        int ALL_TABS_AND_TOOLBAR = 2;
    }

    /** Represents a view that obscured a tab. */
    public static final class Token {
        private final @Target int mTarget;
        private final int mToken;

        private Token(@Target int target, int token) {
            assert token != TokenHolder.INVALID_TOKEN;
            this.mTarget = target;
            this.mToken = token;
        }
    }

    /** Interface for the observers of the tab-obscuring state change. */
    public interface Observer {
        /**
         * @param obscureTabContent {@code true} if tabs are obscured by another view.
         * @param obscureToolbar {@code true} if the top toolbar is obscured by another view.
         */
        void updateObscured(boolean obscureTabContent, boolean obscureToolbar);
    }

    /** A mechanism for distributing unique tokens to users of this system. */
    private final TokenHolder mTabContentTokenHolder;

    private final TokenHolder mAllTabsAndToolbarTokenHolder;

    private final ObserverList<Observer> mVisibilityObservers = new ObserverList<>();

    /** Default constructor */
    public TabObscuringHandler() {
        mTabContentTokenHolder = new TokenHolder(this::notifyUpdate);
        mAllTabsAndToolbarTokenHolder = new TokenHolder(this::notifyUpdate);
    }

    /**
     * Notify the system that there is a feature obscuring all visible tabs for accessibility. As
     * long as this set is nonempty, all tabs should be hidden from the accessibility tree.
     *
     * @return A token to hold while the feature is obscuring all tabs. This token is required to
     *         un-obscure the tabs.
     */
    public Token obscure(@Target int target) {
        int token = TokenHolder.INVALID_TOKEN;
        switch (target) {
            case Target.TAB_CONTENT:
                token = mTabContentTokenHolder.acquireToken();
                break;
            case Target.ALL_TABS_AND_TOOLBAR:
                token = mAllTabsAndToolbarTokenHolder.acquireToken();
                break;
        }
        return new Token(target, token);
    }

    /**
     * Remove a feature that previously obscured the content of all tabs.
     *
     * @param token The unique token that identified the feature (acquired in
     *              {@link #obscure(int)} ()}.
     */
    public void unobscure(Token token) {
        assert token != null;
        switch (token.mTarget) {
            case Target.TAB_CONTENT:
                mTabContentTokenHolder.releaseToken(token.mToken);
                break;
            case Target.ALL_TABS_AND_TOOLBAR:
                mAllTabsAndToolbarTokenHolder.releaseToken(token.mToken);
                break;
        }
    }

    /** @return Whether or not any features obscure all tabs. */
    public boolean isTabContentObscured() {
        return mTabContentTokenHolder.hasTokens() || mAllTabsAndToolbarTokenHolder.hasTokens();
    }

    /** @return Whether or not any features is obscuring the toolbar. */
    public boolean isToolbarObscured() {
        return mAllTabsAndToolbarTokenHolder.hasTokens();
    }

    /**
     * Add {@link Observer} object.
     * @param observer Observer object monitoring tab visibility.
     */
    public void addObserver(Observer observer) {
        mVisibilityObservers.addObserver(observer);
    }

    /**
     * Remove {@link Observer} object.
     * @param observer Observer object monitoring tab visibility.
     */
    public void removeObserver(Observer observer) {
        mVisibilityObservers.removeObserver(observer);
    }

    /** Notify all the observers of the visibility update. */
    private void notifyUpdate() {
        for (Observer observer : mVisibilityObservers) {
            observer.updateObscured(isTabContentObscured(), isToolbarObscured());
        }
    }
}