fxa_client/internal/commands/
send_tab.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5use super::super::telemetry;
6/// The Send Tab functionality is backed by Firefox Accounts device commands.
7/// A device shows it can handle "Send Tab" commands by advertising the "open-uri"
8/// command in its on own device record.
9/// This command data bundle contains a one-time generated `PublicCommandKeys`
10/// (while keeping locally `PrivateCommandKeys` containing the private key),
11/// wrapped by the account oldsync scope `kSync` to form a `CommandKeysPayload`.
12///
13/// When a device sends a tab to another, it decrypts that `CommandKeysPayload` using `kSync`,
14/// uses the obtained public key to encrypt the `SendTabPayload` it created that
15/// contains the tab to send and finally forms the encrypted payload that is
16/// then sent to the target device.
17use serde_derive::*;
18
19pub const COMMAND_NAME: &str = "https://identity.mozilla.com/cmd/open-uri";
20
21#[derive(Clone, Debug, Serialize, Deserialize)]
22pub struct SendTabPayload {
23    pub entries: Vec<TabHistoryEntry>,
24    #[serde(rename = "flowID", default)]
25    pub flow_id: String,
26    #[serde(rename = "streamID", default)]
27    pub stream_id: String,
28}
29
30impl From<SendTabPayload> for crate::SendTabPayload {
31    fn from(payload: SendTabPayload) -> Self {
32        crate::SendTabPayload {
33            entries: payload.entries.into_iter().map(From::from).collect(),
34            flow_id: payload.flow_id,
35            stream_id: payload.stream_id,
36        }
37    }
38}
39
40impl SendTabPayload {
41    pub fn single_tab(title: &str, url: &str) -> (Self, telemetry::SentCommand) {
42        let sent_telemetry: telemetry::SentCommand = telemetry::SentCommand::for_send_tab();
43        (
44            SendTabPayload {
45                entries: vec![TabHistoryEntry {
46                    title: title.to_string(),
47                    url: url.to_string(),
48                }],
49                flow_id: sent_telemetry.flow_id.clone(),
50                stream_id: sent_telemetry.stream_id.clone(),
51            },
52            sent_telemetry,
53        )
54    }
55}
56
57#[derive(Clone, Debug, Serialize, Deserialize)]
58pub struct TabHistoryEntry {
59    pub title: String,
60    pub url: String,
61}
62
63impl From<TabHistoryEntry> for crate::TabHistoryEntry {
64    fn from(e: TabHistoryEntry) -> Self {
65        crate::TabHistoryEntry {
66            title: e.title,
67            url: e.url,
68        }
69    }
70}
71
72#[cfg(test)]
73mod tests {
74    use super::*;
75
76    #[test]
77    fn test_minimal_parse_payload() {
78        let minimal = r#"{ "entries": []}"#;
79        let payload: SendTabPayload = serde_json::from_str(minimal).expect("should work");
80        assert_eq!(payload.flow_id, "".to_string());
81    }
82
83    #[test]
84    fn test_payload() {
85        let (payload, telem) = SendTabPayload::single_tab("title", "http://example.com");
86        let json = serde_json::to_string(&payload).expect("should work");
87        assert_eq!(telem.flow_id.len(), 12);
88        assert_eq!(telem.stream_id.len(), 12);
89        assert_ne!(telem.flow_id, telem.stream_id);
90        let p2: SendTabPayload = serde_json::from_str(&json).expect("should work");
91        // no 'PartialEq' derived so check each field individually...
92        assert_eq!(payload.entries[0].url, "http://example.com".to_string());
93        assert_eq!(payload.flow_id, p2.flow_id);
94        assert_eq!(payload.stream_id, p2.stream_id);
95    }
96}