Commit 88733983 authored by ckitagawa's avatar ckitagawa Committed by Commit Bot

[Paint Preview] Tab Service Java side

This CL implements the Java side integration for the TabService.

Bug: 1058093
Change-Id: Iaa8f007a2a16ac10f87466eb7d5d49f05e112576
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2090298
Commit-Queue: Calder Kitagawa <ckitagawa@chromium.org>
Reviewed-by: default avatarMichael Thiessen <mthiesse@chromium.org>
Cr-Commit-Position: refs/heads/master@{#750542}
parent bfabce08
...@@ -2788,6 +2788,8 @@ generate_jni("chrome_jni_headers") { ...@@ -2788,6 +2788,8 @@ generate_jni("chrome_jni_headers") {
"java/src/org/chromium/chrome/browser/page_info/PageInfoController.java", "java/src/org/chromium/chrome/browser/page_info/PageInfoController.java",
"java/src/org/chromium/chrome/browser/paint_preview/services/PaintPreviewDemoService.java", "java/src/org/chromium/chrome/browser/paint_preview/services/PaintPreviewDemoService.java",
"java/src/org/chromium/chrome/browser/paint_preview/services/PaintPreviewDemoServiceFactory.java", "java/src/org/chromium/chrome/browser/paint_preview/services/PaintPreviewDemoServiceFactory.java",
"java/src/org/chromium/chrome/browser/paint_preview/services/PaintPreviewTabService.java",
"java/src/org/chromium/chrome/browser/paint_preview/services/PaintPreviewTabServiceFactory.java",
"java/src/org/chromium/chrome/browser/partnerbookmarks/PartnerBookmarksReader.java", "java/src/org/chromium/chrome/browser/partnerbookmarks/PartnerBookmarksReader.java",
"java/src/org/chromium/chrome/browser/partnercustomizations/PartnerBrowserCustomizations.java", "java/src/org/chromium/chrome/browser/partnercustomizations/PartnerBrowserCustomizations.java",
"java/src/org/chromium/chrome/browser/password_manager/AccountChooserDialog.java", "java/src/org/chromium/chrome/browser/password_manager/AccountChooserDialog.java",
......
...@@ -1224,6 +1224,8 @@ chrome_java_sources = [ ...@@ -1224,6 +1224,8 @@ chrome_java_sources = [
"java/src/org/chromium/chrome/browser/paint_preview/PaintPreviewTabHelper.java", "java/src/org/chromium/chrome/browser/paint_preview/PaintPreviewTabHelper.java",
"java/src/org/chromium/chrome/browser/paint_preview/services/PaintPreviewDemoService.java", "java/src/org/chromium/chrome/browser/paint_preview/services/PaintPreviewDemoService.java",
"java/src/org/chromium/chrome/browser/paint_preview/services/PaintPreviewDemoServiceFactory.java", "java/src/org/chromium/chrome/browser/paint_preview/services/PaintPreviewDemoServiceFactory.java",
"java/src/org/chromium/chrome/browser/paint_preview/services/PaintPreviewTabService.java",
"java/src/org/chromium/chrome/browser/paint_preview/services/PaintPreviewTabServiceFactory.java",
"java/src/org/chromium/chrome/browser/partnerbookmarks/PartnerBookmark.java", "java/src/org/chromium/chrome/browser/partnerbookmarks/PartnerBookmark.java",
"java/src/org/chromium/chrome/browser/partnerbookmarks/PartnerBookmarksFaviconThrottle.java", "java/src/org/chromium/chrome/browser/partnerbookmarks/PartnerBookmarksFaviconThrottle.java",
"java/src/org/chromium/chrome/browser/partnerbookmarks/PartnerBookmarksProviderIterator.java", "java/src/org/chromium/chrome/browser/partnerbookmarks/PartnerBookmarksProviderIterator.java",
......
...@@ -305,6 +305,7 @@ chrome_test_java_sources = [ ...@@ -305,6 +305,7 @@ chrome_test_java_sources = [
"javatests/src/org/chromium/chrome/browser/page_info/ConnectionInfoPopupTest.java", "javatests/src/org/chromium/chrome/browser/page_info/ConnectionInfoPopupTest.java",
"javatests/src/org/chromium/chrome/browser/page_info/CookieControlsViewTest.java", "javatests/src/org/chromium/chrome/browser/page_info/CookieControlsViewTest.java",
"javatests/src/org/chromium/chrome/browser/page_info/PageInfoControllerTest.java", "javatests/src/org/chromium/chrome/browser/page_info/PageInfoControllerTest.java",
"javatests/src/org/chromium/chrome/browser/paint_preview/services/PaintPreviewTabServiceTest.java",
"javatests/src/org/chromium/chrome/browser/partnercustomizations/BasePartnerBrowserCustomizationIntegrationTestRule.java", "javatests/src/org/chromium/chrome/browser/partnercustomizations/BasePartnerBrowserCustomizationIntegrationTestRule.java",
"javatests/src/org/chromium/chrome/browser/partnercustomizations/BasePartnerBrowserCustomizationUnitTestRule.java", "javatests/src/org/chromium/chrome/browser/partnercustomizations/BasePartnerBrowserCustomizationUnitTestRule.java",
"javatests/src/org/chromium/chrome/browser/partnercustomizations/PartnerDisableBookmarksEditingUnitTest.java", "javatests/src/org/chromium/chrome/browser/partnercustomizations/PartnerDisableBookmarksEditingUnitTest.java",
......
// Copyright 2020 The Chromium Authors. All rights reserved.
// 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.paint_preview.services;
import org.chromium.base.Callback;
import org.chromium.base.annotations.CalledByNative;
import org.chromium.base.annotations.JNINamespace;
import org.chromium.base.annotations.NativeMethods;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.tab.TabHidingType;
import org.chromium.chrome.browser.tabmodel.TabModel;
import org.chromium.chrome.browser.tabmodel.TabModelSelector;
import org.chromium.chrome.browser.tabmodel.TabModelSelectorTabObserver;
import org.chromium.components.paintpreview.browser.NativePaintPreviewServiceProvider;
import org.chromium.content_public.browser.WebContents;
/**
* The Java-side implementations of paint_preview_tab_service.cc. The C++ side owns and controls
* the lifecycle of the Java implementation.
* This class provides the required functionalities for capturing the Paint Preview representation
* of a tab.
*/
@JNINamespace("paint_preview")
public class PaintPreviewTabService implements NativePaintPreviewServiceProvider {
private long mNativePaintPreviewTabService;
private TabModelSelectorTabObserver mTabModelSelectorTabObserver;
private class PaintPreviewTabServiceTabModelSelectorTabObserver
extends TabModelSelectorTabObserver {
private PaintPreviewTabService mTabService;
private PaintPreviewTabServiceTabModelSelectorTabObserver(
PaintPreviewTabService tabService, TabModelSelector tabModelSelector) {
super(tabModelSelector);
mTabService = tabService;
}
/**
* TODO(crbug/1061190): this may be inconsistent in some cases. If it fails to capture we
* should look at alternative triggering mechanisms.
*/
@Override
public void onHidden(Tab tab, @TabHidingType int hidingType) {
if (qualifiesForCapture(tab)) {
mTabService.captureTab(tab, success -> {
if (!success) {
// Treat the tab as if it was closed to cleanup any partial capture data.
mTabService.tabClosed(tab);
}
});
}
}
@Override
public void onTabUnregistered(Tab tab) {
mTabService.tabClosed(tab);
}
private boolean qualifiesForCapture(Tab tab) {
return !tab.isIncognito() && !tab.isNativePage() && !tab.isShowingErrorPage()
&& tab.getWebContents() != null;
}
}
@CalledByNative
private PaintPreviewTabService(long nativePaintPreviewTabService) {
mNativePaintPreviewTabService = nativePaintPreviewTabService;
}
@CalledByNative
private void onNativeDestroyed() {
mNativePaintPreviewTabService = 0;
}
@Override
public long getNativeService() {
return mNativePaintPreviewTabService;
}
/**
* Returns whether there exists a capture for the tab ID.
* @param tabId the id for the tab requested.
* @param successCallback returns true if there is a capture for the tab.
*/
public void hasCaptureForTab(int tabId, Callback<Boolean> successCallback) {
if (mNativePaintPreviewTabService == 0) return;
PaintPreviewTabServiceJni.get().hasCaptureForTab(
mNativePaintPreviewTabService, tabId, successCallback);
}
/**
* Should be called when all tabs are restored. Registers a {@link TabModelSelectorTabObserver}
* for the regular to capture and delete paint previews as needed. Audits restored tabs to
* remove any failed deletions. deletions.
* @param tabModelSelector the TabModelSelector for the activity.
*/
public void onRestoreCompleted(TabModelSelector tabModelSelector) {
mTabModelSelectorTabObserver =
new PaintPreviewTabServiceTabModelSelectorTabObserver(this, tabModelSelector);
TabModel regularTabModel = tabModelSelector.getModel(/*incognito*/ false);
int tabCount = regularTabModel.getCount();
int[] tabIds = new int[tabCount];
for (int i = 0; i < tabCount; i++) {
Tab tab = regularTabModel.getTabAt(i);
tabIds[i] = tab.getId();
}
auditArtifacts(tabIds);
}
private void captureTab(Tab tab, Callback<Boolean> successCallback) {
if (mNativePaintPreviewTabService == 0) {
successCallback.onResult(false);
return;
}
PaintPreviewTabServiceJni.get().captureTab(
mNativePaintPreviewTabService, tab.getId(), tab.getWebContents(), successCallback);
}
private void tabClosed(Tab tab) {
if (mNativePaintPreviewTabService == 0) return;
PaintPreviewTabServiceJni.get().tabClosed(mNativePaintPreviewTabService, tab.getId());
}
private void auditArtifacts(int[] activeTabIds) {
if (mNativePaintPreviewTabService == 0) return;
PaintPreviewTabServiceJni.get().auditArtifacts(mNativePaintPreviewTabService, activeTabIds);
}
@NativeMethods
interface Natives {
void captureTab(long nativePaintPreviewTabService, int tabId, WebContents webContents,
Callback<Boolean> successCallback);
void tabClosed(long nativePaintPreviewTabService, int tabId);
void hasCaptureForTab(
long nativePaintPreviewTabService, int tabId, Callback<Boolean> successCallback);
void auditArtifacts(long nativePaintPreviewTabService, int[] activeTabIds);
}
}
// Copyright 2020 The Chromium Authors. All rights reserved.
// 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.paint_preview.services;
import org.chromium.base.annotations.JNINamespace;
import org.chromium.base.annotations.NativeMethods;
/**
* The Java-side implementations of paint_preview_tab_service_factory.cc. Provides an instance of
* {@link PaintPreviewTabService}.
*/
@JNINamespace("paint_preview")
public class PaintPreviewTabServiceFactory {
public static PaintPreviewTabService getServiceInstance() {
return PaintPreviewTabServiceFactoryJni.get().getServiceInstanceForCurrentProfile();
}
@NativeMethods
interface Natives {
PaintPreviewTabService getServiceInstanceForCurrentProfile();
}
}
file://components/paint_preview/OWNERS
# COMPONENT: Internals>FreezeDriedTabs
// Copyright 2020 The Chromium Authors. All rights reserved.
// 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.paint_preview.services;
import android.support.test.filters.MediumTest;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.chromium.base.test.util.CallbackHelper;
import org.chromium.base.test.util.CommandLineFlags;
import org.chromium.base.test.util.Feature;
import org.chromium.base.test.util.ScalableTimeout;
import org.chromium.chrome.browser.ChromeActivity;
import org.chromium.chrome.browser.flags.ChromeSwitches;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.tabmodel.TabModel;
import org.chromium.chrome.browser.tabmodel.TabModelSelector;
import org.chromium.chrome.test.ChromeActivityTestRule;
import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
import org.chromium.content_public.browser.LoadUrlParams;
import org.chromium.content_public.browser.test.util.CriteriaHelper;
import org.chromium.content_public.browser.test.util.TestThreadUtils;
import org.chromium.net.test.EmbeddedTestServer;
/** Tests for the Paint Preview Tab Manager. */
@RunWith(ChromeJUnit4ClassRunner.class)
@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
public class PaintPreviewTabServiceTest {
private static final long TIMEOUT_MS = ScalableTimeout.scaleTimeout(5000);
private static final long POLLING_INTERVAL_MS = ScalableTimeout.scaleTimeout(500);
@Rule
public final ChromeActivityTestRule<ChromeActivity> mActivityTestRule =
new ChromeActivityTestRule<>(ChromeActivity.class);
private TabModelSelector mTabModelSelector;
private TabModel mTabModel;
private Tab mTab;
private PaintPreviewTabService mPaintPreviewTabService;
@Before
public void setUp() throws Exception {
mActivityTestRule.startMainActivityOnBlankPage();
mTab = mActivityTestRule.getActivity().getActivityTab();
mTabModelSelector = mActivityTestRule.getActivity().getTabModelSelector();
mTabModel = mTabModelSelector.getModel(/*incognito*/ false);
}
/**
* Holder for boolean data manipulated in lambdas.
*/
public class Success {
private boolean mOk;
public Success() {
mOk = false;
}
public boolean get() {
return mOk;
}
public void set(boolean ok) {
mOk = ok;
}
}
/**
* Verifies that a Tab's contents are captured when the page is loaded and subsequently deleted
* when the tab is closed.
*/
@Test
@MediumTest
@Feature({"PaintPreview"})
public void testCapturedAndDeleted() throws Exception {
EmbeddedTestServer testServer = mActivityTestRule.getTestServer();
final String url = testServer.getURL("/chrome/test/data/android/about.html");
TestThreadUtils.runOnUiThreadBlocking(() -> {
mPaintPreviewTabService = PaintPreviewTabServiceFactory.getServiceInstance();
mPaintPreviewTabService.onRestoreCompleted(mTabModelSelector);
mTab.loadUrl(new LoadUrlParams(url));
});
// This will hide mTab so that a capture occurs.
mActivityTestRule.loadUrlInNewTab(url);
int tabId = mTab.getId();
Success ok = new Success();
CriteriaHelper.pollUiThread(() -> {
if (!ok.get()) {
mPaintPreviewTabService.hasCaptureForTab(
tabId, has_capture -> { ok.set(has_capture); });
}
return ok.get();
}, "Paint Preview didn't get captured.", TIMEOUT_MS, POLLING_INTERVAL_MS);
CallbackHelper callbackHelper = new CallbackHelper();
int callCount = callbackHelper.getCallCount();
TestThreadUtils.runOnUiThreadBlocking(() -> { mTabModel.closeTab(mTab); });
ok.set(false);
CriteriaHelper.pollUiThread(() -> {
if (!ok.get()) {
mPaintPreviewTabService.hasCaptureForTab(
tabId, has_capture -> { ok.set(!has_capture); });
}
return ok.get();
}, "Paint Preview didn't get deleted.", TIMEOUT_MS, POLLING_INTERVAL_MS);
}
}
...@@ -1114,6 +1114,10 @@ jumbo_static_library("browser") { ...@@ -1114,6 +1114,10 @@ jumbo_static_library("browser") {
"paint_preview/services/paint_preview_demo_service.h", "paint_preview/services/paint_preview_demo_service.h",
"paint_preview/services/paint_preview_demo_service_factory.cc", "paint_preview/services/paint_preview_demo_service_factory.cc",
"paint_preview/services/paint_preview_demo_service_factory.h", "paint_preview/services/paint_preview_demo_service_factory.h",
"paint_preview/services/paint_preview_tab_service.cc",
"paint_preview/services/paint_preview_tab_service.h",
"paint_preview/services/paint_preview_tab_service_factory.cc",
"paint_preview/services/paint_preview_tab_service_factory.h",
"password_manager/account_storage/account_password_store_factory.cc", "password_manager/account_storage/account_password_store_factory.cc",
"password_manager/account_storage/account_password_store_factory.h", "password_manager/account_storage/account_password_store_factory.h",
"password_manager/biometric_authenticator_android.cc", "password_manager/biometric_authenticator_android.cc",
......
# Copyright 2020 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import("//testing/test.gni")
assert(!is_ios, "Paint Previews are not supported on iOS.")
source_set("services") {
sources = [
"paint_preview_tab_service.cc",
"paint_preview_tab_service.h",
]
deps = [ "//base" ]
public_deps = [
"//components/paint_preview/browser",
"//components/paint_preview/common",
"//components/paint_preview/common/mojom",
"//components/paint_preview/common/proto",
"//components/paint_preview/public",
"//components/services/paint_preview_compositor/public/mojom",
]
}
source_set("unit_tests") {
testonly = true
sources = [ "paint_preview_tab_service_unittest.cc" ]
deps = [
":services",
"//base",
"//base/test:test_support",
"//components/paint_preview/common:test_utils",
"//content/public/browser",
"//content/test:test_support",
"//testing/gmock",
"//testing/gtest",
]
}
...@@ -14,8 +14,28 @@ ...@@ -14,8 +14,28 @@
#include "components/paint_preview/browser/file_manager.h" #include "components/paint_preview/browser/file_manager.h"
#include "ui/gfx/geometry/rect.h" #include "ui/gfx/geometry/rect.h"
#if defined(OS_ANDROID)
#include "base/android/callback_android.h"
#include "base/android/jni_android.h"
#include "base/android/jni_array.h"
#include "base/logging.h"
#include "chrome/android/chrome_jni_headers/PaintPreviewTabService_jni.h"
#endif // defined(OS_ANDROID)
namespace paint_preview { namespace paint_preview {
namespace {
#if defined(OS_ANDROID)
void JavaBooleanCallbackAdapter(base::OnceCallback<void(bool)> callback,
PaintPreviewTabService::Status status) {
DVLOG(1) << "Capture finished with status: " << status;
std::move(callback).Run(status == PaintPreviewTabService::Status::kOk);
}
#endif // defined(OS_ANDROID)
} // namespace
PaintPreviewTabService::PaintPreviewTabService( PaintPreviewTabService::PaintPreviewTabService(
const base::FilePath& profile_dir, const base::FilePath& profile_dir,
base::StringPiece ascii_feature_name, base::StringPiece ascii_feature_name,
...@@ -24,9 +44,21 @@ PaintPreviewTabService::PaintPreviewTabService( ...@@ -24,9 +44,21 @@ PaintPreviewTabService::PaintPreviewTabService(
: PaintPreviewBaseService(profile_dir, : PaintPreviewBaseService(profile_dir,
ascii_feature_name, ascii_feature_name,
std::move(policy), std::move(policy),
is_off_the_record) {} is_off_the_record) {
#if defined(OS_ANDROID)
JNIEnv* env = base::android::AttachCurrentThread();
java_ref_.Reset(Java_PaintPreviewTabService_Constructor(
env, reinterpret_cast<intptr_t>(this)));
#endif // defined(OS_ANDROID)
}
PaintPreviewTabService::~PaintPreviewTabService() = default; PaintPreviewTabService::~PaintPreviewTabService() {
#if defined(OS_ANDROID)
JNIEnv* env = base::android::AttachCurrentThread();
Java_PaintPreviewTabService_onNativeDestroyed(env, java_ref_);
java_ref_.Reset();
#endif // defined(OS_ANDROID)
}
void PaintPreviewTabService::CaptureTab(int tab_id, void PaintPreviewTabService::CaptureTab(int tab_id,
content::WebContents* contents, content::WebContents* contents,
...@@ -39,7 +71,8 @@ void PaintPreviewTabService::CaptureTab(int tab_id, ...@@ -39,7 +71,8 @@ void PaintPreviewTabService::CaptureTab(int tab_id,
true), true),
base::BindOnce(&PaintPreviewTabService::CaptureTabInternal, base::BindOnce(&PaintPreviewTabService::CaptureTabInternal,
weak_ptr_factory_.GetWeakPtr(), key, weak_ptr_factory_.GetWeakPtr(), key,
base::Unretained(contents), std::move(callback))); contents->GetMainFrame()->GetFrameTreeNodeId(),
std::move(callback)));
} }
void PaintPreviewTabService::TabClosed(int tab_id) { void PaintPreviewTabService::TabClosed(int tab_id) {
...@@ -49,6 +82,16 @@ void PaintPreviewTabService::TabClosed(int tab_id) { ...@@ -49,6 +82,16 @@ void PaintPreviewTabService::TabClosed(int tab_id) {
file_manager->CreateKey(tab_id))); file_manager->CreateKey(tab_id)));
} }
void PaintPreviewTabService::HasCaptureForTab(int tab_id,
BooleanCallback callback) {
auto file_manager = GetFileManager();
GetTaskRunner()->PostTaskAndReplyWithResult(
FROM_HERE,
base::BindOnce(&FileManager::CaptureExists, file_manager,
file_manager->CreateKey(tab_id)),
std::move(callback));
}
void PaintPreviewTabService::AuditArtifacts( void PaintPreviewTabService::AuditArtifacts(
const std::vector<int>& active_tab_ids) { const std::vector<int>& active_tab_ids) {
GetTaskRunner()->PostTaskAndReplyWithResult( GetTaskRunner()->PostTaskAndReplyWithResult(
...@@ -56,16 +99,60 @@ void PaintPreviewTabService::AuditArtifacts( ...@@ -56,16 +99,60 @@ void PaintPreviewTabService::AuditArtifacts(
base::BindOnce(&PaintPreviewTabService::RunAudit, base::BindOnce(&PaintPreviewTabService::RunAudit,
weak_ptr_factory_.GetWeakPtr(), active_tab_ids)); weak_ptr_factory_.GetWeakPtr(), active_tab_ids));
} }
#if defined(OS_ANDROID)
void PaintPreviewTabService::CaptureTab(
JNIEnv* env,
jint j_tab_id,
const base::android::JavaParamRef<jobject>& j_web_contents,
const base::android::JavaParamRef<jobject>& j_callback) {
content::WebContents* web_contents =
content::WebContents::FromJavaWebContents(j_web_contents);
CaptureTab(static_cast<int>(j_tab_id), web_contents,
base::BindOnce(
&JavaBooleanCallbackAdapter,
base::BindOnce(
&base::android::RunBooleanCallbackAndroid,
base::android::ScopedJavaGlobalRef<jobject>(j_callback))));
}
void PaintPreviewTabService::TabClosed(JNIEnv* env, jint j_tab_id) {
TabClosed(static_cast<int>(j_tab_id));
}
void PaintPreviewTabService::HasCaptureForTab(
JNIEnv* env,
jint j_tab_id,
const base::android::JavaParamRef<jobject>& j_callback) {
HasCaptureForTab(
static_cast<int>(j_tab_id),
base::BindOnce(&base::android::RunBooleanCallbackAndroid,
base::android::ScopedJavaGlobalRef<jobject>(j_callback)));
}
void PaintPreviewTabService::AuditArtifacts(
JNIEnv* env,
const base::android::JavaParamRef<jintArray>& j_tab_ids) {
std::vector<int> tab_ids;
base::android::JavaIntArrayToIntVector(env, j_tab_ids, &tab_ids);
AuditArtifacts(tab_ids);
}
#endif // defined(OS_ANDROID)
void PaintPreviewTabService::CaptureTabInternal( void PaintPreviewTabService::CaptureTabInternal(
const DirectoryKey& key, const DirectoryKey& key,
content::WebContents* contents, int frame_tree_node_id,
FinishedCallback callback, FinishedCallback callback,
const base::Optional<base::FilePath>& file_path) { const base::Optional<base::FilePath>& file_path) {
if (!file_path.has_value()) { if (!file_path.has_value()) {
std::move(callback).Run(Status::kDirectoryCreationFailed); std::move(callback).Run(Status::kDirectoryCreationFailed);
return; return;
} }
auto* contents =
content::WebContents::FromFrameTreeNodeId(frame_tree_node_id);
if (!contents) {
std::move(callback).Run(Status::kWebContentsGone);
return;
}
CapturePaintPreview( CapturePaintPreview(
contents, file_path.value(), gfx::Rect(0, 0, 0, 0), contents, file_path.value(), gfx::Rect(0, 0, 0, 0),
base::BindOnce(&PaintPreviewTabService::OnCaptured, base::BindOnce(&PaintPreviewTabService::OnCaptured,
......
...@@ -15,10 +15,16 @@ ...@@ -15,10 +15,16 @@
#include "base/memory/weak_ptr.h" #include "base/memory/weak_ptr.h"
#include "base/optional.h" #include "base/optional.h"
#include "base/strings/string_piece.h" #include "base/strings/string_piece.h"
#include "build/build_config.h"
#include "components/paint_preview/browser/paint_preview_base_service.h" #include "components/paint_preview/browser/paint_preview_base_service.h"
#include "components/paint_preview/browser/paint_preview_policy.h" #include "components/paint_preview/browser/paint_preview_policy.h"
#include "components/paint_preview/common/proto/paint_preview.pb.h" #include "components/paint_preview/common/proto/paint_preview.pb.h"
#if defined(os_android)
#include "base/android/jni_android.h"
#include "base/android/scoped_java_ref.h"
#endif // defined(os_android)
namespace content { namespace content {
class WebContents; class WebContents;
} // namespace content } // namespace content
...@@ -41,9 +47,11 @@ class PaintPreviewTabService : public PaintPreviewBaseService { ...@@ -41,9 +47,11 @@ class PaintPreviewTabService : public PaintPreviewBaseService {
kDirectoryCreationFailed = 1, kDirectoryCreationFailed = 1,
kCaptureFailed = 2, kCaptureFailed = 2,
kProtoSerializationFailed = 3, kProtoSerializationFailed = 3,
kWebContentsGone = 4,
}; };
using FinishedCallback = base::OnceCallback<void(Status)>; using FinishedCallback = base::OnceCallback<void(Status)>;
using BooleanCallback = base::OnceCallback<void(bool)>;
// Captures a Paint Preview of |contents| which should be associated with // Captures a Paint Preview of |contents| which should be associated with
// |tab_id| for storage. |callback| is invoked on completion to indicate // |tab_id| for storage. |callback| is invoked on completion to indicate
...@@ -56,6 +64,9 @@ class PaintPreviewTabService : public PaintPreviewBaseService { ...@@ -56,6 +64,9 @@ class PaintPreviewTabService : public PaintPreviewBaseService {
// when a tab is closed to ensure the captured contents don't outlive the tab. // when a tab is closed to ensure the captured contents don't outlive the tab.
void TabClosed(int tab_id); void TabClosed(int tab_id);
// Checks if there is a capture taken for |tab_id|.
void HasCaptureForTab(int tab_id, BooleanCallback callback);
// This should be called on startup with a list of restored tab ids // This should be called on startup with a list of restored tab ids
// (|active_tab_ids|). This performs an audit over all Paint Previews stored // (|active_tab_ids|). This performs an audit over all Paint Previews stored
// by this service and destroys any that don't correspond to active tabs. This // by this service and destroys any that don't correspond to active tabs. This
...@@ -63,9 +74,25 @@ class PaintPreviewTabService : public PaintPreviewBaseService { ...@@ -63,9 +74,25 @@ class PaintPreviewTabService : public PaintPreviewBaseService {
// occurred. // occurred.
void AuditArtifacts(const std::vector<int>& active_tab_ids); void AuditArtifacts(const std::vector<int>& active_tab_ids);
#if defined(OS_ANDROID)
// JNI wrapped versions of the above methods
void CaptureTab(JNIEnv* env,
jint j_tab_id,
const base::android::JavaParamRef<jobject>& j_web_contents,
const base::android::JavaParamRef<jobject>& j_callback);
void TabClosed(JNIEnv* env, jint j_tab_id);
void HasCaptureForTab(JNIEnv* env,
jint j_tab_id,
const base::android::JavaParamRef<jobject>& j_callback);
void AuditArtifacts(JNIEnv* env,
const base::android::JavaParamRef<jintArray>& j_tab_ids);
base::android::ScopedJavaGlobalRef<jobject> GetJavaRef() { return java_ref_; }
#endif // defined(OS_ANDROID)
private: private:
void CaptureTabInternal(const DirectoryKey& key, void CaptureTabInternal(const DirectoryKey& key,
content::WebContents* contents, int frame_tree_node_id,
FinishedCallback callback, FinishedCallback callback,
const base::Optional<base::FilePath>& file_path); const base::Optional<base::FilePath>& file_path);
...@@ -79,6 +106,9 @@ class PaintPreviewTabService : public PaintPreviewBaseService { ...@@ -79,6 +106,9 @@ class PaintPreviewTabService : public PaintPreviewBaseService {
void RunAudit(const std::vector<int>& active_tab_ids, void RunAudit(const std::vector<int>& active_tab_ids,
const base::flat_set<DirectoryKey>& in_use_keys); const base::flat_set<DirectoryKey>& in_use_keys);
#if defined(OS_ANDROID)
base::android::ScopedJavaGlobalRef<jobject> java_ref_;
#endif // defined(OS_ANDROID)
base::WeakPtrFactory<PaintPreviewTabService> weak_ptr_factory_{this}; base::WeakPtrFactory<PaintPreviewTabService> weak_ptr_factory_{this};
}; };
......
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/paint_preview/services/paint_preview_tab_service_factory.h"
#include "build/build_config.h"
#include "chrome/browser/paint_preview/services/paint_preview_tab_service.h"
#include "components/keyed_service/core/simple_dependency_manager.h"
#include "components/keyed_service/core/simple_factory_key.h"
#if defined(OS_ANDROID)
#include "base/android/jni_android.h"
#include "base/android/scoped_java_ref.h"
#include "chrome/android/chrome_jni_headers/PaintPreviewTabServiceFactory_jni.h"
#include "chrome/browser/profiles/profile_key.h"
#include "chrome/browser/profiles/profile_manager.h"
#endif // defined(OS_ANDROID)
namespace paint_preview {
namespace {
constexpr char kFeatureDirname[] = "tab_service";
} // namespace
// static
PaintPreviewTabServiceFactory* PaintPreviewTabServiceFactory::GetInstance() {
return base::Singleton<PaintPreviewTabServiceFactory>::get();
}
// static
paint_preview::PaintPreviewTabService*
PaintPreviewTabServiceFactory::GetServiceInstance(SimpleFactoryKey* key) {
return static_cast<paint_preview::PaintPreviewTabService*>(
GetInstance()->GetServiceForKey(key, true));
}
PaintPreviewTabServiceFactory::PaintPreviewTabServiceFactory()
: SimpleKeyedServiceFactory("PaintPreviewTabService",
SimpleDependencyManager::GetInstance()) {}
PaintPreviewTabServiceFactory::~PaintPreviewTabServiceFactory() = default;
std::unique_ptr<KeyedService>
PaintPreviewTabServiceFactory::BuildServiceInstanceFor(
SimpleFactoryKey* key) const {
// Prevent this working off the record.
if (key->IsOffTheRecord())
return nullptr;
// TODO(crbug/1060556): Inject a useful policy.
return std::make_unique<paint_preview::PaintPreviewTabService>(
key->GetPath(), kFeatureDirname, nullptr, key->IsOffTheRecord());
}
SimpleFactoryKey* PaintPreviewTabServiceFactory::GetKeyToUse(
SimpleFactoryKey* key) const {
return key;
}
#if defined(OS_ANDROID)
base::android::ScopedJavaLocalRef<jobject>
JNI_PaintPreviewTabServiceFactory_GetServiceInstanceForCurrentProfile(
JNIEnv* env) {
ProfileKey* profile_key =
ProfileManager::GetLastUsedProfile()->GetProfileKey();
base::android::ScopedJavaGlobalRef<jobject> java_ref =
PaintPreviewTabServiceFactory::GetServiceInstance(profile_key)
->GetJavaRef();
return base::android::ScopedJavaLocalRef<jobject>(java_ref);
}
#endif // defined(OS_ANDROID)
} // namespace paint_preview
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_PAINT_PREVIEW_SERVICES_PAINT_PREVIEW_TAB_SERVICE_FACTORY_H_
#define CHROME_BROWSER_PAINT_PREVIEW_SERVICES_PAINT_PREVIEW_TAB_SERVICE_FACTORY_H_
#include <memory>
#include "base/memory/singleton.h"
#include "components/keyed_service/core/keyed_service.h"
#include "components/keyed_service/core/simple_keyed_service_factory.h"
class SimpleFactoryKey;
namespace paint_preview {
class PaintPreviewTabService;
// Factory to create one PaintPreviewTabService per profile key.
class PaintPreviewTabServiceFactory : public SimpleKeyedServiceFactory {
public:
static PaintPreviewTabServiceFactory* GetInstance();
static paint_preview::PaintPreviewTabService* GetServiceInstance(
SimpleFactoryKey* key);
PaintPreviewTabServiceFactory(const PaintPreviewTabServiceFactory&) = delete;
PaintPreviewTabServiceFactory& operator=(
const PaintPreviewTabServiceFactory&) = delete;
private:
friend struct base::DefaultSingletonTraits<PaintPreviewTabServiceFactory>;
PaintPreviewTabServiceFactory();
~PaintPreviewTabServiceFactory() override;
// SimpleKeyedServiceFactory:
std::unique_ptr<KeyedService> BuildServiceInstanceFor(
SimpleFactoryKey* key) const override;
SimpleFactoryKey* GetKeyToUse(SimpleFactoryKey* key) const override;
};
} // namespace paint_preview
#endif // CHROME_BROWSER_PAINT_PREVIEW_SERVICES_PAINT_PREVIEW_TAB_SERVICE_FACTORY_H_
...@@ -65,6 +65,20 @@ std::vector<base::FilePath> ListDir(const base::FilePath& path) { ...@@ -65,6 +65,20 @@ std::vector<base::FilePath> ListDir(const base::FilePath& path) {
return files; return files;
} }
bool CaptureExists(PaintPreviewTabService* service, int tab_id) {
bool out = false;
base::RunLoop loop;
service->HasCaptureForTab(
tab_id, base::BindOnce(
[](base::OnceClosure quit, bool* out, bool success) {
*out = success;
std::move(quit).Run();
},
loop.QuitClosure(), &out));
loop.Run();
return out;
}
} // namespace } // namespace
class PaintPreviewTabServiceTest : public ChromeRenderViewHostTestHarness { class PaintPreviewTabServiceTest : public ChromeRenderViewHostTestHarness {
...@@ -121,6 +135,8 @@ TEST_F(PaintPreviewTabServiceTest, CaptureTab) { ...@@ -121,6 +135,8 @@ TEST_F(PaintPreviewTabServiceTest, CaptureTab) {
loop.QuitClosure())); loop.QuitClosure()));
loop.Run(); loop.Run();
EXPECT_TRUE(CaptureExists(service, kTabId));
auto file_manager = service->GetFileManager(); auto file_manager = service->GetFileManager();
auto key = file_manager->CreateKey(kTabId); auto key = file_manager->CreateKey(kTabId);
service->GetTaskRunner()->PostTaskAndReplyWithResult( service->GetTaskRunner()->PostTaskAndReplyWithResult(
...@@ -130,6 +146,7 @@ TEST_F(PaintPreviewTabServiceTest, CaptureTab) { ...@@ -130,6 +146,7 @@ TEST_F(PaintPreviewTabServiceTest, CaptureTab) {
content::RunAllTasksUntilIdle(); content::RunAllTasksUntilIdle();
service->TabClosed(kTabId); service->TabClosed(kTabId);
EXPECT_FALSE(CaptureExists(service, kTabId));
content::RunAllTasksUntilIdle(); content::RunAllTasksUntilIdle();
service->GetTaskRunner()->PostTaskAndReplyWithResult( service->GetTaskRunner()->PostTaskAndReplyWithResult(
FROM_HERE, FROM_HERE,
......
...@@ -3256,6 +3256,7 @@ test("unit_tests") { ...@@ -3256,6 +3256,7 @@ test("unit_tests") {
"../browser/page_load_metrics/observers/third_party_metrics_observer_unittest.cc", "../browser/page_load_metrics/observers/third_party_metrics_observer_unittest.cc",
"../browser/page_load_metrics/observers/ukm_page_load_metrics_observer_unittest.cc", "../browser/page_load_metrics/observers/ukm_page_load_metrics_observer_unittest.cc",
"../browser/paint_preview/services/paint_preview_demo_service_unittest.cc", "../browser/paint_preview/services/paint_preview_demo_service_unittest.cc",
"../browser/paint_preview/services/paint_preview_tab_service_unittest.cc",
"../browser/password_manager/chrome_password_manager_client_unittest.cc", "../browser/password_manager/chrome_password_manager_client_unittest.cc",
"../browser/password_manager/password_store_x_unittest.cc", "../browser/password_manager/password_store_x_unittest.cc",
"../browser/performance_hints/performance_hints_observer_unittest.cc", "../browser/performance_hints/performance_hints_observer_unittest.cc",
...@@ -3652,7 +3653,6 @@ test("unit_tests") { ...@@ -3652,7 +3653,6 @@ test("unit_tests") {
"//chrome:strings", "//chrome:strings",
"//chrome/browser/media/router:unittests", "//chrome/browser/media/router:unittests",
"//chrome/browser/notifications:unit_tests", "//chrome/browser/notifications:unit_tests",
"//chrome/browser/paint_preview/services:unit_tests",
"//chrome/browser/payments:unittests", "//chrome/browser/payments:unittests",
"//chrome/browser/updates/announcement_notification:unit_tests", "//chrome/browser/updates/announcement_notification:unit_tests",
"//chrome/common:test_support", "//chrome/common:test_support",
......
...@@ -76,6 +76,21 @@ bool FileManager::DirectoryExists(const DirectoryKey& key) const { ...@@ -76,6 +76,21 @@ bool FileManager::DirectoryExists(const DirectoryKey& key) const {
return GetPathForKey(key, &path) != StorageType::kNone; return GetPathForKey(key, &path) != StorageType::kNone;
} }
bool FileManager::CaptureExists(const DirectoryKey& key) const {
DCHECK(io_task_runner_->RunsTasksInCurrentSequence());
base::FilePath path;
StorageType storage_type = GetPathForKey(key, &path);
switch (storage_type) {
case kDirectory:
return base::PathExists(path.AppendASCII(kProtoName));
case kZip:
return true;
case kNone: // fallthrough;
default:
return false;
}
}
base::Optional<base::FilePath> FileManager::CreateOrGetDirectory( base::Optional<base::FilePath> FileManager::CreateOrGetDirectory(
const DirectoryKey& key, const DirectoryKey& key,
bool clear) const { bool clear) const {
......
...@@ -50,6 +50,10 @@ class FileManager : public base::RefCountedThreadSafe<FileManager> { ...@@ -50,6 +50,10 @@ class FileManager : public base::RefCountedThreadSafe<FileManager> {
// Returns true if the directory for |key| exists. // Returns true if the directory for |key| exists.
bool DirectoryExists(const DirectoryKey& key) const; bool DirectoryExists(const DirectoryKey& key) const;
// Returns true if there is a capture for |key| i.e. there exists a proto or
// zip file.
bool CaptureExists(const DirectoryKey& key) const;
// Creates or gets a subdirectory under |root_directory| for |key| and // Creates or gets a subdirectory under |root_directory| for |key| and
// assigns it to |directory|. The directory will be wiped if |clear| is true. // assigns it to |directory|. The directory will be wiped if |clear| is true.
// Returns a path on success or nullopt on failure. If the directory was // Returns a path on success or nullopt on failure. If the directory was
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment