Commit c964736f authored by Bo Liu's avatar Bo Liu Committed by Commit Bot

weblayer: Implement WebLayer.enumerateAllProfileNames

Implementation just list and returns all valid directories in the
root profile directory.

Bug: 1044581
Change-Id: I0cae48abf7ad6f2543478df582ef4e5d5b32f5b2
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2048945Reviewed-by: default avatarScott Violet <sky@chromium.org>
Commit-Queue: Bo <boliu@chromium.org>
Cr-Commit-Position: refs/heads/master@{#740466}
parent 77abb503
...@@ -12,11 +12,15 @@ import org.junit.Test; ...@@ -12,11 +12,15 @@ import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.chromium.base.test.util.CallbackHelper; import org.chromium.base.test.util.CallbackHelper;
import org.chromium.content_public.browser.test.util.Criteria;
import org.chromium.content_public.browser.test.util.CriteriaHelper;
import org.chromium.content_public.browser.test.util.TestThreadUtils; import org.chromium.content_public.browser.test.util.TestThreadUtils;
import org.chromium.weblayer.Callback;
import org.chromium.weblayer.Profile; import org.chromium.weblayer.Profile;
import org.chromium.weblayer.WebLayer; import org.chromium.weblayer.WebLayer;
import org.chromium.weblayer.shell.InstrumentationActivity; import org.chromium.weblayer.shell.InstrumentationActivity;
import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
...@@ -113,6 +117,49 @@ public class ProfileTest { ...@@ -113,6 +117,49 @@ public class ProfileTest {
callbackHelper.waitForFirst(); callbackHelper.waitForFirst();
} }
@Test
@SmallTest
public void testEnumerateAllProfileNames() throws Exception {
final String profileName = "TestEnumerateAllProfileNames";
final WebLayer weblayer = mActivityTestRule.getWebLayer();
final InstrumentationActivity activity = mActivityTestRule.launchWithProfile(profileName);
final Profile profile = TestThreadUtils.runOnUiThreadBlockingNoException(
() -> activity.getBrowser().getProfile());
Assert.assertTrue(Arrays.asList(enumerateAllProfileNames(weblayer)).contains(profileName));
TestThreadUtils.runOnUiThreadBlocking(() -> activity.finish());
CriteriaHelper.pollUiThread(new Criteria() {
@Override
public boolean isSatisfied() {
return activity.isDestroyed();
}
});
final CallbackHelper callbackHelper = new CallbackHelper();
TestThreadUtils.runOnUiThreadBlocking(
() -> profile.destroyAndDeleteDataFromDisk(callbackHelper::notifyCalled));
callbackHelper.waitForFirst();
Assert.assertFalse(Arrays.asList(enumerateAllProfileNames(weblayer)).contains(profileName));
}
private static String[] enumerateAllProfileNames(final WebLayer weblayer) throws Exception {
final String[][] holder = new String[1][];
final CallbackHelper callbackHelper = new CallbackHelper();
TestThreadUtils.runOnUiThreadBlocking(() -> {
Callback<String[]> callback = new Callback<String[]>() {
@Override
public void onResult(String[] names) {
holder[0] = names;
callbackHelper.notifyCalled();
}
};
weblayer.enumerateAllProfileNames(callback);
});
callbackHelper.waitForFirst();
return holder[0];
}
private static Collection<Profile> getAllProfiles() { private static Collection<Profile> getAllProfiles() {
return TestThreadUtils.runOnUiThreadBlockingNoException(() -> Profile.getAllProfiles()); return TestThreadUtils.runOnUiThreadBlockingNoException(() -> Profile.getAllProfiles());
} }
......
...@@ -4,8 +4,11 @@ ...@@ -4,8 +4,11 @@
package org.chromium.weblayer_private; package org.chromium.weblayer_private;
import android.webkit.ValueCallback;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import org.chromium.base.Callback;
import org.chromium.base.CollectionUtil; import org.chromium.base.CollectionUtil;
import org.chromium.base.annotations.JNINamespace; import org.chromium.base.annotations.JNINamespace;
import org.chromium.base.annotations.NativeMethods; import org.chromium.base.annotations.NativeMethods;
...@@ -28,6 +31,11 @@ public final class ProfileImpl extends IProfile.Stub { ...@@ -28,6 +31,11 @@ public final class ProfileImpl extends IProfile.Stub {
private Runnable mOnDestroyCallback; private Runnable mOnDestroyCallback;
private boolean mBeingDeleted; private boolean mBeingDeleted;
public static void enumerateAllProfileNames(ValueCallback<String[]> callback) {
final Callback<String[]> baseCallback = (String[] names) -> callback.onReceiveValue(names);
ProfileImplJni.get().enumerateAllProfileNames(baseCallback);
}
ProfileImpl(String name, Runnable onDestroyCallback) { ProfileImpl(String name, Runnable onDestroyCallback) {
if (!name.matches("^\\w*$")) { if (!name.matches("^\\w*$")) {
throw new IllegalArgumentException("Name can only contain words: " + name); throw new IllegalArgumentException("Name can only contain words: " + name);
...@@ -131,6 +139,7 @@ public final class ProfileImpl extends IProfile.Stub { ...@@ -131,6 +139,7 @@ public final class ProfileImpl extends IProfile.Stub {
@NativeMethods @NativeMethods
interface Natives { interface Natives {
void enumerateAllProfileNames(Callback<String[]> callback);
long createProfile(String name); long createProfile(String name);
void deleteProfile(long profile); void deleteProfile(long profile);
boolean deleteDataFromDisk(long nativeProfileImpl, Runnable completionCallback); boolean deleteDataFromDisk(long nativeProfileImpl, Runnable completionCallback);
......
...@@ -273,6 +273,14 @@ public final class WebLayerImpl extends IWebLayer.Stub { ...@@ -273,6 +273,14 @@ public final class WebLayerImpl extends IWebLayer.Stub {
DownloadImpl.forwardIntent(context, intent); DownloadImpl.forwardIntent(context, intent);
} }
@Override
public void enumerateAllProfileNames(IObjectWrapper valueCallback) {
StrictModeWorkaround.apply();
final ValueCallback<String[]> callback =
(ValueCallback<String[]>) ObjectWrapper.unwrap(valueCallback, ValueCallback.class);
ProfileImpl.enumerateAllProfileNames(callback);
}
/** /**
* Creates a remote context. This should only be used for backwards compatibility when the * Creates a remote context. This should only be used for backwards compatibility when the
* client was not sending the remote context. * client was not sending the remote context.
......
...@@ -71,4 +71,7 @@ interface IWebLayer { ...@@ -71,4 +71,7 @@ interface IWebLayer {
// Forwards download intent notifications to the implementation. // Forwards download intent notifications to the implementation.
void onReceivedDownloadNotification(in IObjectWrapper appContext, in Intent intent) = 11; void onReceivedDownloadNotification(in IObjectWrapper appContext, in Intent intent) = 11;
// Added in Version 82.
void enumerateAllProfileNames(in IObjectWrapper valueCallback) = 12;
} }
...@@ -4,10 +4,13 @@ ...@@ -4,10 +4,13 @@
#include "weblayer/browser/profile_impl.h" #include "weblayer/browser/profile_impl.h"
#include <string>
#include <utility> #include <utility>
#include <vector>
#include "base/bind.h" #include "base/bind.h"
#include "base/callback_forward.h" #include "base/callback_forward.h"
#include "base/files/file_enumerator.h"
#include "base/files/file_util.h" #include "base/files/file_util.h"
#include "base/path_service.h" #include "base/path_service.h"
#include "base/strings/string_util.h" #include "base/strings/string_util.h"
...@@ -57,6 +60,13 @@ bool IsNameValid(const std::string& name) { ...@@ -57,6 +60,13 @@ bool IsNameValid(const std::string& name) {
return true; return true;
} }
// Return the data path directory to profiles.
base::FilePath GetProfileRootDataDir() {
base::FilePath path;
CHECK(base::PathService::Get(DIR_USER_DATA, &path));
return path.AppendASCII("profiles");
}
#if defined(OS_POSIX) #if defined(OS_POSIX)
base::FilePath ComputeCachePath(const std::string& profile_name) { base::FilePath ComputeCachePath(const std::string& profile_name) {
base::FilePath path; base::FilePath path;
...@@ -91,6 +101,37 @@ void NukeProfileFromDisk(const std::string& profile_name, ...@@ -91,6 +101,37 @@ void NukeProfileFromDisk(const std::string& profile_name,
#endif #endif
} }
#if defined(OS_ANDROID)
// Returned path only contains the directory name.
// Invalid profile names are ignored.
std::vector<base::FilePath> ListProfileNames() {
base::FilePath root_dir = GetProfileRootDataDir();
std::vector<base::FilePath> profile_names;
base::FileEnumerator enumerator(root_dir, /*recursive=*/false,
base::FileEnumerator::DIRECTORIES);
for (base::FilePath path = enumerator.Next(); !path.empty();
path = enumerator.Next()) {
base::FilePath name = enumerator.GetInfo().GetName();
if (IsNameValid(name.MaybeAsASCII())) {
profile_names.push_back(name);
}
}
return profile_names;
}
void PassFilePathsToJavaCallback(
const base::android::ScopedJavaGlobalRef<jobject>& callback,
const std::vector<base::FilePath>& file_paths) {
std::vector<std::string> strings;
for (const auto& path : file_paths) {
strings.push_back(path.value());
}
base::android::RunObjectCallbackAndroid(
callback, base::android::ToJavaArrayOfStrings(
base::android::AttachCurrentThread(), strings));
}
#endif // OS_ANDROID
} // namespace } // namespace
class ProfileImpl::DataClearer : public content::BrowsingDataRemover::Observer { class ProfileImpl::DataClearer : public content::BrowsingDataRemover::Observer {
...@@ -147,8 +188,7 @@ ProfileImpl::ProfileImpl(const std::string& name) ...@@ -147,8 +188,7 @@ ProfileImpl::ProfileImpl(const std::string& name)
CHECK(IsNameValid(name)); CHECK(IsNameValid(name));
{ {
base::ScopedAllowBlocking allow_blocking; base::ScopedAllowBlocking allow_blocking;
CHECK(base::PathService::Get(DIR_USER_DATA, &data_path_)); data_path_ = GetProfileRootDataDir().AppendASCII(name.c_str());
data_path_ = data_path_.AppendASCII("profiles").AppendASCII(name.c_str());
if (!base::PathExists(data_path_)) if (!base::PathExists(data_path_))
base::CreateDirectory(data_path_); base::CreateDirectory(data_path_);
...@@ -268,6 +308,18 @@ static void JNI_ProfileImpl_DeleteProfile(JNIEnv* env, jlong profile) { ...@@ -268,6 +308,18 @@ static void JNI_ProfileImpl_DeleteProfile(JNIEnv* env, jlong profile) {
delete reinterpret_cast<ProfileImpl*>(profile); delete reinterpret_cast<ProfileImpl*>(profile);
} }
static void JNI_ProfileImpl_EnumerateAllProfileNames(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& callback) {
base::PostTaskAndReplyWithResult(
FROM_HERE,
{base::ThreadPool(), base::MayBlock(), base::TaskPriority::BEST_EFFORT,
base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN},
base::BindOnce(&ListProfileNames),
base::BindOnce(&PassFilePathsToJavaCallback,
base::android::ScopedJavaGlobalRef<jobject>(callback)));
}
jboolean ProfileImpl::DeleteDataFromDisk( jboolean ProfileImpl::DeleteDataFromDisk(
JNIEnv* env, JNIEnv* env,
const base::android::JavaRef<jobject>& j_completion_callback) { const base::android::JavaRef<jobject>& j_completion_callback) {
......
...@@ -357,6 +357,25 @@ public class WebLayer { ...@@ -357,6 +357,25 @@ public class WebLayer {
return Profile.of(iprofile); return Profile.of(iprofile);
} }
/**
* Return a list of Profile names currently on disk. This will not include the incognito
* profile. This will not include profiles that are being deleted from disk.
* WebLayer must be initialized before calling this.
* @since 82
*/
public void enumerateAllProfileNames(@NonNull Callback<String[]> callback) {
ThreadCheck.ensureOnUiThread();
if (getSupportedMajorVersionInternal() < 82) {
throw new UnsupportedOperationException();
}
try {
ValueCallback<String[]> valueCallback = (String[] value) -> callback.onResult(value);
mImpl.enumerateAllProfileNames(ObjectWrapper.wrap(valueCallback));
} catch (RemoteException e) {
throw new APICallException(e);
}
}
/** /**
* To enable or disable DevTools remote debugging. * To enable or disable DevTools remote debugging.
*/ */
......
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