Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .buildconfig-android.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,13 @@ projects:
- name: autofill
type: aar
description: Addresses and Credit Cards autofill.
containers:
path: components/containers/android
artifactId: containers
publications:
- name: containers
type: aar
description: Storage for Firefox containers.
crashtest:
path: components/crashtest/android
artifactId: crashtest
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@

## ✨ What's Changed ✨

### Containers

- Created a new component, `containers`, holding the list of Firefox containers and the format they are stored in.

### Autofill

- `update_address()` now sets `time_last_modified` to the time of the update, matching `update_credit_card()` and `update_passport()`.
Expand Down
14 changes: 14 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ members = [
"components/as-ohttp-client",
"components/autofill",
"components/breach-alerts",
"components/containers",
"components/context_id",
"components/crashtest",
"components/example",
Expand Down Expand Up @@ -109,6 +110,7 @@ default-members = [
"components/as-ohttp-client",
"components/autofill",
"components/breach-alerts",
"components/containers",
"components/context_id",
"components/crashtest",
"components/fxa-client",
Expand Down
15 changes: 15 additions & 0 deletions components/containers/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[package]
name = "containers"
description = "Storage for Firefox containers"
version = "0.1.0"
edition = "2021"
license = "MPL-2.0"

[dependencies]
error-support = { path = "../support/error" }
idna = "1"
parking_lot = "0.12"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
thiserror = "2"
uniffi = { version = "0.31" }
27 changes: 27 additions & 0 deletions components/containers/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Containers

Storage for Firefox containers.

The component never touches the filesystem. `ContainersStore` hands the
serialized document to a callback, and the embedder decides where it goes and
how durably.

It owns the list, not the behaviour. Everything that gives a container its
meaning stays outside:

- the origin attributes that isolate its cookies and storage
- clearing that storage when a container is removed
- resolving the localized labels of the shipped containers
- closing the tabs that belong to one

## Tests

Tests are run with

```shell
cargo test -p containers
```

## Bugs

We use Bugzilla to track bugs and feature work. You can use [this link](https://bugzilla.mozilla.org/enter_bug.cgi?product=Firefox&component=Containers) to file bugs in the `Firefox :: Containers` bug component.
10 changes: 10 additions & 0 deletions components/containers/android/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
apply from: "$appServicesRootDir/build-scripts/component-common.gradle"
apply from: "$publishDir/publish.gradle"

android {
namespace 'org.mozilla.appservices.containers'
}

ext.configureUniFFIBindgen("containers")
ext.dependsOnTheMegazord()
ext.configurePublish(appServicesGroupId, project.name, project.ext.description)
22 changes: 22 additions & 0 deletions components/containers/android/proguard-rules.pro
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html

# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}

# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable

# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

6 changes: 6 additions & 0 deletions components/containers/android/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<manifest xmlns:android="http://schemas.android.com/apk/res/android"/>
44 changes: 44 additions & 0 deletions components/containers/src/container.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use crate::data::Identity;

/// How a container gets its label.
#[derive(Clone, Debug, PartialEq, Eq, uniffi::Enum)]
pub enum ContainerLabel {
/// Empty only for a stored container that carries no usable label at all.
Name { name: String },
/// An identifier the embedder resolves against its own catalogue.
L10nId { l10n_id: String },
}

/// A container as the embedder sees it.
#[derive(Clone, Debug, PartialEq, Eq, uniffi::Record)]
pub struct Container {
pub user_context_id: u32,
pub is_public: bool,
pub icon: String,
pub color: String,
pub label: ContainerLabel,
}

impl Container {
pub(crate) fn from_identity(identity: &Identity) -> Self {
Self {
user_context_id: identity.user_context_id,
is_public: identity.public,
icon: identity.icon.clone(),
color: identity.color.clone(),
label: match (&identity.name, &identity.l10n_id) {
(Some(name), _) if !name.is_empty() => ContainerLabel::Name { name: name.clone() },
(_, Some(l10n_id)) => ContainerLabel::L10nId {
l10n_id: l10n_id.clone(),
},
_ => ContainerLabel::Name {
name: String::new(),
},
},
}
}
}
67 changes: 67 additions & 0 deletions components/containers/src/data.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use std::collections::BTreeMap;

use serde::{Deserialize, Serialize};
use serde_json::{Map, Value};

pub(crate) const LATEST_VERSION: u32 = 6;

/// Reserved for the IndexedDB backend of the extension storage.local API. Never
/// reassign it: extensions would lose access to data stored under it.
pub(crate) const MAX_USER_CONTEXT_ID: u32 = u32::MAX;

/// Fields that no version of the format knows about are round-tripped verbatim
/// through `extra`, so that a document written by a newer Firefox keeps its
/// data when an older one rewrites it.
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct Identity {
#[serde(default)]
pub user_context_id: u32,
#[serde(default)]
pub public: bool,
#[serde(default)]
pub icon: String,
#[serde(default)]
pub color: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub l10n_id: Option<String>,
#[serde(flatten)]
pub extra: Map<String, Value>,
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct ContainersData {
#[serde(default)]
pub version: u32,
#[serde(default)]
pub last_user_context_id: u32,
#[serde(default)]
pub identities: Vec<Identity>,
#[serde(default)]
pub site_associations: BTreeMap<String, u32>,
#[serde(flatten)]
pub extra: Map<String, Value>,
}

impl ContainersData {
pub(crate) fn public_identities(&self) -> impl Iterator<Item = &Identity> {
self.identities.iter().filter(|identity| identity.public)
}

pub(crate) fn private_identities(&self) -> impl Iterator<Item = &Identity> {
self.identities.iter().filter(|identity| !identity.public)
}

pub(crate) fn find_private_by_name(&self, name: &str) -> Option<&Identity> {
self.identities
.iter()
.find(|identity| !identity.public && identity.name.as_deref() == Some(name))
}
}
Loading
Loading