chromium/android_webview/java/src/org/chromium/android_webview/AwContentsOriginMatcher.java

// Copyright 2023 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.android_webview;

import android.net.Uri;

import org.jni_zero.JNINamespace;
import org.jni_zero.NativeMethods;

import java.util.List;

/**
 * A thread-safe thin Java facing implementation of aw_contents_origin_matcher.h This API sends all
 * the data and origin matching requests over to the native implementation so that we can have a
 * standard implementation for our origin matching rules.
 *
 * <p>This class owns a native object so it is critical that when you are done using this, it needs
 * to be cleaned up with the {@link AwContentsOriginMatcher#destroy} method.
 */
@JNINamespace("android_webview")
public class AwContentsOriginMatcher {
    private final Object mLock = new Object();

    private long mNative;

    public AwContentsOriginMatcher() {
        mNative = AwContentsOriginMatcherJni.get().init(this);
    }

    /** Returns if any of the rules in the origin matcher currently match the uri provided. */
    public boolean matchesOrigin(Uri origin) {
        return matchesOriginLocked(origin);
    }

    /**
     * Updates the rules to the list provided. If any badly formed rules are provided, the rules
     * will not be updated and the bad rules will be returned.
     */
    public String[] updateRuleList(final List<String> rules) {
        return updateRuleListLocked(rules);
    }

    /**
     * This method _must_ be called when you are finished with the origin matcher.
     *
     * <p>It will clean up any native references of your code.
     */
    public void destroy() {
        destroyLocked();
    }

    private void ensureNativeExists() {
        if (mNative == 0) {
            throw new IllegalStateException(
                    "AwContentsOriginMatcher did not have access to native implementation. "
                            + "Ensure you don't call this method after cleanup.");
        }
    }

    private boolean matchesOriginLocked(Uri origin) {
        synchronized (mLock) {
            ensureNativeExists();
            return AwContentsOriginMatcherJni.get().matchesOrigin(mNative, origin.toString());
        }
    }

    private String[] updateRuleListLocked(final List<String> rules) {
        synchronized (mLock) {
            ensureNativeExists();
            return AwContentsOriginMatcherJni.get()
                    .updateRuleList(mNative, rules.toArray(new String[] {}));
        }
    }

    private void destroyLocked() {
        synchronized (mLock) {
            ensureNativeExists();
            AwContentsOriginMatcherJni.get().destroy(mNative);
            mNative = 0;
        }
    }

    @NativeMethods
    interface Natives {
        long init(AwContentsOriginMatcher caller);

        boolean matchesOrigin(long nativeAwContentsOriginMatcher, String origin);

        // Returns the list of invalid rules.
        // If there are bad rules, no update is performed
        String[] updateRuleList(long nativeAwContentsOriginMatcher, String[] rules);

        void destroy(long nativeAwContentsOriginMatcher);
    }
}