Commit 3b7ed9d0 authored by Pavel Shmakov's avatar Pavel Shmakov Committed by Commit Bot

Implement Profile#clearBrowsingData

Change-Id: I8cb125cf9ee2d2f72ba22f498248dd5fa6fd5c4d
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1865334
Commit-Queue: Pavel Shmakov <pshmakov@chromium.org>
Reviewed-by: default avatarBo <boliu@chromium.org>
Cr-Commit-Position: refs/heads/master@{#709853}
parent 4caa96fb
......@@ -4,17 +4,25 @@
package org.chromium.weblayer_private;
import org.chromium.base.annotations.CalledByNative;
import org.chromium.base.annotations.JNINamespace;
import org.chromium.base.annotations.NativeMethods;
import org.chromium.weblayer_private.aidl.IObjectWrapper;
import org.chromium.weblayer_private.aidl.IProfile;
import org.chromium.weblayer_private.aidl.ObjectWrapper;
import java.util.ArrayList;
import java.util.List;
@JNINamespace("weblayer")
public final class ProfileImpl extends IProfile.Stub {
private final List<Runnable> mCurrentClearDataCallbacks = new ArrayList<>();
private final List<Runnable> mPendingClearDataCallbacks = new ArrayList<>();
private long mNativeProfile;
private Runnable mOnDestroyCallback;
ProfileImpl(String path, Runnable onDestroyCallback) {
mNativeProfile = ProfileImplJni.get().createProfile(path);
mNativeProfile = ProfileImplJni.get().createProfile(this, path);
mOnDestroyCallback = onDestroyCallback;
}
......@@ -27,17 +35,38 @@ public final class ProfileImpl extends IProfile.Stub {
}
@Override
public void clearBrowsingData() {
public void clearBrowsingData(IObjectWrapper completionCallback) {
Runnable callback = ObjectWrapper.unwrap(completionCallback, Runnable.class);
if (!mCurrentClearDataCallbacks.isEmpty()) {
// Already running a clear data job. Will have to re-run the job once it's completed,
// because new data may have been stored.
mPendingClearDataCallbacks.add(callback);
return;
}
mCurrentClearDataCallbacks.add(callback);
ProfileImplJni.get().clearBrowsingData(mNativeProfile);
}
@CalledByNative
private void onBrowsingDataCleared() {
for (Runnable callback : mCurrentClearDataCallbacks) {
callback.run();
}
mCurrentClearDataCallbacks.clear();
if (!mPendingClearDataCallbacks.isEmpty()) {
mCurrentClearDataCallbacks.addAll(mPendingClearDataCallbacks);
mPendingClearDataCallbacks.clear();
ProfileImplJni.get().clearBrowsingData(mNativeProfile);
}
}
long getNativeProfile() {
return mNativeProfile;
}
@NativeMethods
interface Natives {
long createProfile(String path);
long createProfile(ProfileImpl caller, String path);
void deleteProfile(long profile);
void clearBrowsingData(long nativeProfileImpl);
}
......
......@@ -7,5 +7,5 @@ package org.chromium.weblayer_private.aidl;
interface IProfile {
void destroy() = 0;
void clearBrowsingData() = 1;
void clearBrowsingData(in IObjectWrapper completionCallback) = 1;
}
......@@ -6,6 +6,7 @@
#include "build/build_config.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browsing_data_remover.h"
#include "content/public/browser/download_manager_delegate.h"
#include "content/public/browser/resource_context.h"
#include "weblayer/browser/browser_controller_impl.h"
......@@ -16,6 +17,10 @@
#include "weblayer/browser/java/jni/ProfileImpl_jni.h"
#endif
#if defined(OS_ANDROID)
using base::android::AttachCurrentThread;
#endif
namespace weblayer {
namespace {
......@@ -148,6 +153,36 @@ class ProfileImpl::BrowserContextImpl : public content::BrowserContext {
DISALLOW_COPY_AND_ASSIGN(BrowserContextImpl);
};
class ProfileImpl::DataClearer : public content::BrowsingDataRemover::Observer {
public:
DataClearer(content::BrowserContext* browser_context, ProfileImpl* profile)
: remover_(
content::BrowserContext::GetBrowsingDataRemover(browser_context)),
profile_(profile) {
remover_->AddObserver(this);
}
~DataClearer() override { remover_->RemoveObserver(this); }
void ClearData() {
int mask = content::BrowsingDataRemover::DATA_TYPE_COOKIES |
content::BrowsingDataRemover::DATA_TYPE_MEDIA_LICENSES;
int origin_types =
content::BrowsingDataRemover::ORIGIN_TYPE_UNPROTECTED_WEB |
content::BrowsingDataRemover::ORIGIN_TYPE_PROTECTED_WEB;
remover_->RemoveAndReply(base::Time(), base::Time::Max(), mask,
origin_types, this);
}
void OnBrowsingDataRemoverDone() override {
profile_->OnBrowsingDataCleared();
}
private:
content::BrowsingDataRemover* const remover_;
ProfileImpl* const profile_;
};
ProfileImpl::ProfileImpl(const base::FilePath& path) : path_(path) {
browser_context_ = std::make_unique<BrowserContextImpl>(path_);
}
......@@ -158,8 +193,17 @@ content::BrowserContext* ProfileImpl::GetBrowserContext() {
return browser_context_.get();
}
void ProfileImpl::OnBrowsingDataCleared() {
#if defined(OS_ANDROID)
Java_ProfileImpl_onBrowsingDataCleared(AttachCurrentThread(), java_profile_);
#endif
}
void ProfileImpl::ClearBrowsingData() {
NOTIMPLEMENTED();
if (!data_clearer_) {
data_clearer_ = std::make_unique<DataClearer>(browser_context_.get(), this);
}
data_clearer_->ClearData();
}
std::unique_ptr<Profile> Profile::Create(const base::FilePath& path) {
......@@ -167,11 +211,20 @@ std::unique_ptr<Profile> Profile::Create(const base::FilePath& path) {
}
#if defined(OS_ANDROID)
ProfileImpl::ProfileImpl(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& java_profile,
const base::android::JavaParamRef<jstring>& path)
: ProfileImpl(base::FilePath(ConvertJavaStringToUTF8(env, path))) {
java_profile_.Reset(env, java_profile);
}
static jlong JNI_ProfileImpl_CreateProfile(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& java_profile,
const base::android::JavaParamRef<jstring>& path) {
return reinterpret_cast<jlong>(new weblayer::ProfileImpl(
base::FilePath(ConvertJavaStringToUTF8(env, path))));
return reinterpret_cast<jlong>(
new weblayer::ProfileImpl(env, java_profile, path));
}
static void JNI_ProfileImpl_DeleteProfile(JNIEnv* env, jlong profile) {
......
......@@ -5,10 +5,12 @@
#ifndef WEBLAYER_BROWSER_PROFILE_IMPL_H_
#define WEBLAYER_BROWSER_PROFILE_IMPL_H_
#include "build/build_config.h"
#include "weblayer/public/profile.h"
#if defined(OS_ANDROID)
#include <jni.h>
#include "base/android/scoped_java_ref.h"
#endif
namespace content {
......@@ -28,14 +30,26 @@ class ProfileImpl : public Profile {
void ClearBrowsingData() override;
#if defined(OS_ANDROID)
ProfileImpl(JNIEnv* env,
const base::android::JavaParamRef<jobject>& java_profile,
const base::android::JavaParamRef<jstring>& path);
void ClearBrowsingData(JNIEnv* env) { ClearBrowsingData(); }
#endif
private:
class BrowserContextImpl;
class DataClearer;
void OnBrowsingDataCleared();
base::FilePath path_;
std::unique_ptr<BrowserContextImpl> browser_context_;
std::unique_ptr<DataClearer> data_clearer_;
#if defined(OS_ANDROID)
base::android::ScopedJavaGlobalRef<jobject> java_profile_;
#endif
};
} // namespace weblayer
......
......@@ -8,6 +8,7 @@ import android.os.RemoteException;
import org.chromium.weblayer_private.aidl.APICallException;
import org.chromium.weblayer_private.aidl.IProfile;
import org.chromium.weblayer_private.aidl.ObjectWrapper;
/**
* Profile holds state (typically on disk) needed for browsing. Create a
......@@ -28,10 +29,12 @@ public final class Profile {
// TODO(sky): figure out right assertion here if mImpl is non-null.
}
public void clearBrowsingData() {
public ListenableResult<Void> clearBrowsingData() {
ThreadCheck.ensureOnUiThread();
try {
mImpl.clearBrowsingData();
ListenableResult<Void> result = new ListenableResult<>();
mImpl.clearBrowsingData(ObjectWrapper.wrap((Runnable) () -> result.supplyResult(null)));
return result;
} catch (RemoteException e) {
throw new APICallException(e);
}
......
......@@ -214,6 +214,7 @@ instrumentation_test_apk("weblayer_instrumentation_test_apk") {
"javatests/src/org/chromium/weblayer/test/EventUtils.java",
"javatests/src/org/chromium/weblayer/test/FullscreenDelegateTest.java",
"javatests/src/org/chromium/weblayer/test/NavigationTest.java",
"javatests/src/org/chromium/weblayer/test/DataClearingTest.java",
"javatests/src/org/chromium/weblayer/test/SmokeTest.java",
"javatests/src/org/chromium/weblayer/test/EventUtils.java",
"javatests/src/org/chromium/weblayer/test/ExecuteScriptTest.java",
......
// Copyright 2019 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.weblayer.test;
import static org.junit.Assert.assertTrue;
import static org.chromium.content_public.browser.test.util.TestThreadUtils.runOnUiThreadBlocking;
import android.os.Bundle;
import android.support.test.filters.SmallTest;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.chromium.base.test.BaseJUnit4ClassRunner;
import org.chromium.weblayer.Profile;
import org.chromium.weblayer.shell.WebLayerShellActivity;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
/**
* Example test that just starts the weblayer shell.
*/
@RunWith(BaseJUnit4ClassRunner.class)
public class DataClearingTest {
@Rule
public WebLayerShellActivityTestRule mActivityTestRule = new WebLayerShellActivityTestRule();
@Test
@SmallTest
public void clearDataWithPersistedProfile_TriggersCallback() throws InterruptedException {
checkTriggersCallbackOnClearData("Profile");
}
@Test
@SmallTest
public void clearDataWithInMemoryProfile_TriggersCallback() throws InterruptedException {
checkTriggersCallbackOnClearData("");
}
// The tests below should rather be unit tests for ProfileImpl.
@Test
@SmallTest
public void twoSuccesiveRequestsTriggerCallbacks() throws InterruptedException {
WebLayerShellActivity activity = launchWithProfile("profile");
CountDownLatch latch = new CountDownLatch(2);
runOnUiThreadBlocking(() -> {
Profile profile = activity.getBrowserFragmentController().getProfile();
profile.clearBrowsingData().addCallback((ignored) -> latch.countDown());
profile.clearBrowsingData().addCallback((ignored) -> latch.countDown());
});
assertTrue(latch.await(3, TimeUnit.SECONDS));
}
@Test
@SmallTest
public void clearingAgainAfterClearFinished_TriggersCallback() throws InterruptedException {
WebLayerShellActivity activity = launchWithProfile("profile");
CountDownLatch latch = new CountDownLatch(1);
runOnUiThreadBlocking(() -> {
Profile profile = activity.getBrowserFragmentController().getProfile();
profile.clearBrowsingData().addCallback((v1) -> {
profile.clearBrowsingData().addCallback((v2) -> latch.countDown());
});
});
assertTrue(latch.await(3, TimeUnit.SECONDS));
}
@Test
@SmallTest
public void threeSuccesiveRequestsTriggerCallbacks() throws InterruptedException {
WebLayerShellActivity activity = launchWithProfile("profile");
CountDownLatch latch = new CountDownLatch(3);
runOnUiThreadBlocking(() -> {
Profile profile = activity.getBrowserFragmentController().getProfile();
profile.clearBrowsingData().addCallback((ignored) -> latch.countDown());
profile.clearBrowsingData().addCallback((ignored) -> latch.countDown());
profile.clearBrowsingData().addCallback((ignored) -> latch.countDown());
});
assertTrue(latch.await(3, TimeUnit.SECONDS));
}
private void checkTriggersCallbackOnClearData(String profileName) throws InterruptedException {
WebLayerShellActivity activity = launchWithProfile(profileName);
CountDownLatch latch = new CountDownLatch(1);
runOnUiThreadBlocking(() -> activity.getBrowserFragmentController().getProfile()
.clearBrowsingData().addCallback((ignored) -> latch.countDown()));
assertTrue(latch.await(3, TimeUnit.SECONDS));
}
private WebLayerShellActivity launchWithProfile(String profileName) {
Bundle extras = new Bundle();
extras.putString(WebLayerShellActivity.EXTRA_PROFILE_NAME, profileName);
String url = "data:text,foo";
WebLayerShellActivity activity = mActivityTestRule.launchShellWithUrl(url, extras);
return activity;
}
}
......@@ -11,6 +11,7 @@ import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.net.Uri;
import android.os.Bundle;
import android.support.test.InstrumentationRegistry;
import android.support.test.rule.ActivityTestRule;
......@@ -103,11 +104,12 @@ public class WebLayerShellActivityTestRule extends ActivityTestRule<WebLayerShel
}
/**
* Starts the WebLayer activity and completely loads the given URL (this calls
* navigateAndWait()).
* Starts the WebLayer activity with the given extras Bundle and completely loads the given URL
* (this calls navigateAndWait()).
*/
public WebLayerShellActivity launchShellWithUrl(String url) {
public WebLayerShellActivity launchShellWithUrl(String url, Bundle extras) {
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.putExtras(extras);
intent.addCategory(Intent.CATEGORY_LAUNCHER);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
// Prevent URL from being loaded on start.
......@@ -121,6 +123,14 @@ public class WebLayerShellActivityTestRule extends ActivityTestRule<WebLayerShel
return activity;
}
/**
* Starts the WebLayer activity and completely loads the given URL (this calls
* navigateAndWait()).
*/
public WebLayerShellActivity launchShellWithUrl(String url) {
return launchShellWithUrl(url, new Bundle());
}
/**
* Loads the given URL in the shell.
*/
......
......@@ -51,6 +51,8 @@ public class WebLayerShellActivity extends FragmentActivity {
private static final String TAG = "WebLayerShell";
private static final String KEY_MAIN_VIEW_ID = "mainViewId";
public static final String EXTRA_PROFILE_NAME = "EXTRA_PROFILE_NAME";
private Profile mProfile;
private BrowserFragmentController mBrowserFragmentController;
private BrowserController mBrowserController;
......@@ -255,8 +257,14 @@ public class WebLayerShellActivity extends FragmentActivity {
}
}
File profile = new File(getFilesDir(), "defaultProfile");
BrowserFragment fragment = WebLayer.createBrowserFragment(profile.getPath());
String profileName = getIntent().hasExtra(EXTRA_PROFILE_NAME)
? getIntent().getStringExtra(EXTRA_PROFILE_NAME) : "DefaultProfile";
String profilePath = null;
if (!TextUtils.isEmpty(profileName)) {
profilePath = new File(getFilesDir(), profileName).getPath();
} // else create an in-memory Profile.
BrowserFragment fragment = WebLayer.createBrowserFragment(profilePath);
FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.add(mMainViewId, fragment);
......
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