improve profile isolation
This commit is contained in:
@@ -87,14 +87,75 @@ class _Filesystem {
|
|||||||
|
|
||||||
Future<void> _linkMozillaDir(Directory filesDir) async {
|
Future<void> _linkMozillaDir(Directory filesDir) async {
|
||||||
final mozillaDir = Directory(p.join(selectedProfileDir.path, 'mozilla'));
|
final mozillaDir = Directory(p.join(selectedProfileDir.path, 'mozilla'));
|
||||||
await mozillaDir.create();
|
await mozillaDir.create(recursive: true);
|
||||||
|
|
||||||
final mozillaLink = Link(p.join(filesDir.path, 'mozilla'));
|
final mozillaPath = p.join(filesDir.path, 'mozilla');
|
||||||
if (await mozillaLink.exists()) {
|
|
||||||
await mozillaLink.delete();
|
Future<void> moveAside(FileSystemEntityType type) async {
|
||||||
|
final backupPath = p.join(
|
||||||
|
filesDir.path,
|
||||||
|
'mozilla.backup.${DateTime.now().millisecondsSinceEpoch}',
|
||||||
|
);
|
||||||
|
|
||||||
|
if (type == FileSystemEntityType.directory) {
|
||||||
|
await Directory(mozillaPath).rename(backupPath);
|
||||||
|
} else {
|
||||||
|
await File(mozillaPath).rename(backupPath);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await mozillaLink.create(mozillaDir.path);
|
Future<void> createLink() async {
|
||||||
|
await Link(mozillaPath).create(mozillaDir.path);
|
||||||
|
}
|
||||||
|
|
||||||
|
final currentType = await FileSystemEntity.type(
|
||||||
|
mozillaPath,
|
||||||
|
followLinks: false,
|
||||||
|
);
|
||||||
|
|
||||||
|
switch (currentType) {
|
||||||
|
case FileSystemEntityType.notFound:
|
||||||
|
break;
|
||||||
|
case FileSystemEntityType.link:
|
||||||
|
final link = Link(mozillaPath);
|
||||||
|
try {
|
||||||
|
if (await link.target() == mozillaDir.path) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} on FileSystemException {
|
||||||
|
// Replace unreadable or broken links.
|
||||||
|
}
|
||||||
|
|
||||||
|
await link.delete();
|
||||||
|
case FileSystemEntityType.directory:
|
||||||
|
case FileSystemEntityType.file:
|
||||||
|
case FileSystemEntityType.unixDomainSock:
|
||||||
|
case FileSystemEntityType.pipe:
|
||||||
|
default:
|
||||||
|
await moveAside(currentType);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await createLink();
|
||||||
|
} on PathExistsException {
|
||||||
|
final retryType = await FileSystemEntity.type(
|
||||||
|
mozillaPath,
|
||||||
|
followLinks: false,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (retryType == FileSystemEntityType.link) {
|
||||||
|
final link = Link(mozillaPath);
|
||||||
|
if (await link.target() == mozillaDir.path) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await link.delete();
|
||||||
|
} else if (retryType != FileSystemEntityType.notFound) {
|
||||||
|
await moveAside(retryType);
|
||||||
|
}
|
||||||
|
|
||||||
|
await createLink();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _setupSqliteCache() async {
|
Future<void> _setupSqliteCache() async {
|
||||||
|
|||||||
+2
@@ -150,7 +150,9 @@ object GlobalComponents {
|
|||||||
|
|
||||||
//RustHttpConfig.setClient(lazy { newComponents.core.client })
|
//RustHttpConfig.setClient(lazy { newComponents.core.client })
|
||||||
|
|
||||||
|
if (mode == ComponentsMode.FULL) {
|
||||||
newComponents.core.engine.warmUp()
|
newComponents.core.engine.warmUp()
|
||||||
|
}
|
||||||
|
|
||||||
fun restorePreviousCustomTabs() {
|
fun restorePreviousCustomTabs() {
|
||||||
if (previousCustomTabs.isEmpty()) return
|
if (previousCustomTabs.isEmpty()) return
|
||||||
|
|||||||
+4
@@ -57,6 +57,10 @@ class ProfileContext(private val base: Context, val relativePath: String) :
|
|||||||
return customApplicationInfo
|
return customApplicationInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun getApplicationContext(): Context {
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
override fun getFilesDir(): File {
|
override fun getFilesDir(): File {
|
||||||
return customFilesDir
|
return customFilesDir
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-4
@@ -303,15 +303,14 @@ class IntentReceiverActivity : Activity() {
|
|||||||
contextId: String?,
|
contextId: String?,
|
||||||
) {
|
) {
|
||||||
val message = "This PWA was originally installed in a different profile. " +
|
val message = "This PWA was originally installed in a different profile. " +
|
||||||
"Opening it here will use your current profile's data and settings, " +
|
"Opening it here uses only your current profile's data and settings. " +
|
||||||
"which means you won't see the same content, preferences, or saved data " +
|
"The original profile's app state and saved data will not be used.\n\n" +
|
||||||
"that you had in the original profile.\n\n" +
|
|
||||||
"Do you want to proceed anyway?"
|
"Do you want to proceed anyway?"
|
||||||
|
|
||||||
AlertDialog.Builder(this)
|
AlertDialog.Builder(this)
|
||||||
.setTitle("PWA Profile Mismatch")
|
.setTitle("PWA Profile Mismatch")
|
||||||
.setMessage(message)
|
.setMessage(message)
|
||||||
.setPositiveButton("Open Anyway") { _, _ ->
|
.setPositiveButton("Open in Current Profile") { _, _ ->
|
||||||
Log.d(TAG, "User chose to open PWA despite profile mismatch")
|
Log.d(TAG, "User chose to open PWA despite profile mismatch")
|
||||||
launchPwaWithContext(url, contextId)
|
launchPwaWithContext(url, contextId)
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-4
@@ -144,7 +144,7 @@ class GeckoPwaApiImpl(
|
|||||||
|
|
||||||
val (iconBitmap, isMaskable) = loadPwaIcon(manifest)
|
val (iconBitmap, isMaskable) = loadPwaIcon(manifest)
|
||||||
|
|
||||||
val shortcutId = generateShortcutId(manifest.startUrl)
|
val shortcutId = generateShortcutId(manifest.startUrl, profileUuid)
|
||||||
val launchToken = resolveLaunchToken(
|
val launchToken = resolveLaunchToken(
|
||||||
shortcutManager = shortcutManager,
|
shortcutManager = shortcutManager,
|
||||||
shortcutId = shortcutId,
|
shortcutId = shortcutId,
|
||||||
@@ -189,11 +189,11 @@ class GeckoPwaApiImpl(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates a collision-resistant shortcut ID from a URL using SHA-256.
|
* Generates a collision-resistant shortcut ID from URL + profile using SHA-256.
|
||||||
*/
|
*/
|
||||||
private fun generateShortcutId(url: String): String {
|
private fun generateShortcutId(url: String, profileUuid: String): String {
|
||||||
val digest = MessageDigest.getInstance("SHA-256")
|
val digest = MessageDigest.getInstance("SHA-256")
|
||||||
val hash = digest.digest(url.toByteArray())
|
val hash = digest.digest("$url::$profileUuid".toByteArray())
|
||||||
val hex = hash.take(16).joinToString("") { "%02x".format(it) }
|
val hex = hash.take(16).joinToString("") { "%02x".format(it) }
|
||||||
return "pwa_$hex"
|
return "pwa_$hex"
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-3
@@ -232,9 +232,7 @@ class Core(
|
|||||||
*/
|
*/
|
||||||
val customTabsStore by lazy { CustomTabsServiceStore() }
|
val customTabsStore by lazy { CustomTabsServiceStore() }
|
||||||
|
|
||||||
// Must use the base application context (not ProfileContext) so the database
|
val webAppManifestStorage by lazy { ManifestStorage(context) }
|
||||||
// matches what WebAppLauncherActivity (from the library) uses when loading manifests.
|
|
||||||
val webAppManifestStorage by lazy { ManifestStorage(context.applicationContext) }
|
|
||||||
|
|
||||||
val webAppShortcutManager by lazy {
|
val webAppShortcutManager by lazy {
|
||||||
WebAppShortcutManager(context, client, webAppManifestStorage)
|
WebAppShortcutManager(context, client, webAppManifestStorage)
|
||||||
|
|||||||
Reference in New Issue
Block a user