chromium/chrome/android/java/src/org/chromium/chrome/browser/history/BrowsingHistoryBridge.java

// Copyright 2016 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.history;

import org.jni_zero.CalledByNative;
import org.jni_zero.JniType;
import org.jni_zero.NativeMethods;

import org.chromium.base.Callback;
import org.chromium.base.metrics.RecordHistogram;
import org.chromium.chrome.browser.profiles.Profile;
import org.chromium.components.browsing_data.DeleteBrowsingDataAction;
import org.chromium.url.GURL;

import java.util.ArrayList;
import java.util.List;

/** The JNI bridge for Android to fetch and manipulate browsing history. */
public class BrowsingHistoryBridge implements HistoryProvider {
    private BrowsingHistoryObserver mObserver;
    private long mNativeHistoryBridge;
    private boolean mRemovingItems;
    private boolean mHasPendingRemoveRequest;

    public BrowsingHistoryBridge(Profile profile) {
        mNativeHistoryBridge =
                BrowsingHistoryBridgeJni.get().init(BrowsingHistoryBridge.this, profile);
    }

    @Override
    public void setObserver(BrowsingHistoryObserver observer) {
        mObserver = observer;
    }

    @Override
    public void destroy() {
        if (mNativeHistoryBridge != 0) {
            BrowsingHistoryBridgeJni.get()
                    .destroy(mNativeHistoryBridge, BrowsingHistoryBridge.this);
            mNativeHistoryBridge = 0;
        }
    }

    @Override
    public void queryHistory(String query, String appId) {
        BrowsingHistoryBridgeJni.get()
                .queryHistory(
                        mNativeHistoryBridge,
                        BrowsingHistoryBridge.this,
                        new ArrayList<HistoryItem>(),
                        query,
                        appId,
                        false);
    }

    @Override
    public void queryHistoryForHost(String hostName) {
        BrowsingHistoryBridgeJni.get()
                .queryHistory(
                        mNativeHistoryBridge,
                        BrowsingHistoryBridge.this,
                        new ArrayList<HistoryItem>(),
                        hostName,
                        null,
                        true);
    }

    @Override
    public void queryHistoryContinuation() {
        BrowsingHistoryBridgeJni.get()
                .queryHistoryContinuation(
                        mNativeHistoryBridge,
                        BrowsingHistoryBridge.this,
                        new ArrayList<HistoryItem>());
    }

    @Override
    public void queryApps() {
        BrowsingHistoryBridgeJni.get()
                .getAllAppIds(
                        mNativeHistoryBridge, BrowsingHistoryBridge.this, new ArrayList<String>());
    }

    @CalledByNative
    public static void addAppIdToList(List<String> items, String appId) {
        items.add(appId);
    }

    @Override
    public void getLastVisitToHostBeforeRecentNavigations(
            String hostName, Callback<Long> callback) {
        BrowsingHistoryBridgeJni.get()
                .getLastVisitToHostBeforeRecentNavigations(
                        mNativeHistoryBridge, BrowsingHistoryBridge.this, hostName, callback);
    }

    @Override
    public void markItemForRemoval(HistoryItem item) {
        BrowsingHistoryBridgeJni.get()
                .markItemForRemoval(
                        mNativeHistoryBridge,
                        BrowsingHistoryBridge.this,
                        item.getUrl(),
                        item.getAppId(),
                        item.getNativeTimestamps());
    }

    @Override
    public void removeItems() {
        // Only one remove request may be in-flight at any given time. If items are currently being
        // removed, queue the new request and return early.
        if (mRemovingItems) {
            mHasPendingRemoveRequest = true;
            return;
        }
        mRemovingItems = true;
        mHasPendingRemoveRequest = false;

        RecordHistogram.recordEnumeratedHistogram(
                "Privacy.DeleteBrowsingData.Action",
                DeleteBrowsingDataAction.HISTORY_PAGE_ENTRIES,
                DeleteBrowsingDataAction.MAX_VALUE);

        BrowsingHistoryBridgeJni.get()
                .removeItems(mNativeHistoryBridge, BrowsingHistoryBridge.this);
    }

    @CalledByNative
    public static void createHistoryItemAndAddToList(
            List<HistoryItem> items,
            GURL url,
            String domain,
            String title,
            String appId,
            long mostRecentJavaTimestamp,
            long[] nativeTimestamps,
            boolean blockedVisit) {
        items.add(
                new HistoryItem(
                        url,
                        domain,
                        title,
                        appId,
                        mostRecentJavaTimestamp,
                        nativeTimestamps,
                        blockedVisit));
    }

    @CalledByNative
    public void onQueryHistoryComplete(List<HistoryItem> items, boolean hasMorePotentialMatches) {
        if (mObserver != null) mObserver.onQueryHistoryComplete(items, hasMorePotentialMatches);
    }

    @CalledByNative
    public void onQueryAppsComplete(List<String> items) {
        if (mObserver != null) mObserver.onQueryAppsComplete(items);
    }

    @CalledByNative
    public void onRemoveComplete() {
        mRemovingItems = false;
        if (mHasPendingRemoveRequest) removeItems();
    }

    @CalledByNative
    public void onRemoveFailed() {
        mRemovingItems = false;
        if (mHasPendingRemoveRequest) removeItems();
        // TODO(twellington): handle remove failures.
    }

    @CalledByNative
    public void onHistoryDeleted() {
        if (mObserver != null) mObserver.onHistoryDeleted();
    }

    @CalledByNative
    public void hasOtherFormsOfBrowsingData(boolean hasOtherForms) {
        if (mObserver != null) {
            mObserver.hasOtherFormsOfBrowsingData(hasOtherForms);
        }
    }

    @NativeMethods
    interface Natives {
        long init(BrowsingHistoryBridge caller, @JniType("Profile*") Profile profile);

        void destroy(long nativeBrowsingHistoryBridge, BrowsingHistoryBridge caller);

        void queryHistory(
                long nativeBrowsingHistoryBridge,
                BrowsingHistoryBridge caller,
                List<HistoryItem> historyItems,
                String query,
                String appId,
                boolean hostOnly);

        void queryHistoryContinuation(
                long nativeBrowsingHistoryBridge,
                BrowsingHistoryBridge caller,
                List<HistoryItem> historyItems);

        void getLastVisitToHostBeforeRecentNavigations(
                long nativeBrowsingHistoryBridge,
                BrowsingHistoryBridge caller,
                String hostName,
                Callback<Long> callback);

        void markItemForRemoval(
                long nativeBrowsingHistoryBridge,
                BrowsingHistoryBridge caller,
                GURL url,
                String appId,
                long[] nativeTimestamps);

        void removeItems(long nativeBrowsingHistoryBridge, BrowsingHistoryBridge caller);

        void getAllAppIds(
                long nativeBrowsingHistoryBridge,
                BrowsingHistoryBridge caller,
                List<String> appIds);
    }
}