Skip to content

Commit

Permalink
Updated tabs component for iOS integration
Browse files Browse the repository at this point in the history
  • Loading branch information
lougeniaC64 committed May 16, 2022
1 parent c23f5cf commit 95d0deb
Show file tree
Hide file tree
Showing 11 changed files with 138 additions and 37 deletions.
4 changes: 3 additions & 1 deletion components/sync15/ios/SyncUnlockInfo.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,14 @@ open class SyncUnlockInfo {
public var syncKey: String
public var tokenserverURL: String
public var loginEncryptionKey: String
public var tabsLocalId: String

public init(kid: String, fxaAccessToken: String, syncKey: String, tokenserverURL: String, loginEncryptionKey: String) {
public init(kid: String, fxaAccessToken: String, syncKey: String, tokenserverURL: String, loginEncryptionKey: String, tabsLocalId: String) {
self.kid = kid
self.fxaAccessToken = fxaAccessToken
self.syncKey = syncKey
self.tokenserverURL = tokenserverURL
self.loginEncryptionKey = loginEncryptionKey
self.tabsLocalId = tabsLocalId
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/* 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/. */

package mozilla.appservices.remotetabs

// We needed to rename the tabs `DeviceType` as it conflicts with the `DeviceType` we are exposing for FxA
// in iOS. However renaming `DeviceType` to `TabsDeviceType` creates a breaking change for the Android code.
// So we are aliasing `TabsDeviceType` back to `DeviceType` in order to prevent the breaking change.
typealias DeviceType = TabsDeviceType
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/* 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/. */

package mozilla.appservices.remotetabs

// We needed to rename the Rust `RemoteTab` struct to `RemoteTabRecord` in order to circumvent the naming conflict in
// iOS with the native `RemoteTab` struct. But that creates a breaking change for the Android code. So we are aliasing
// `RemoteTabRecord` back to `RemoteTab` to prevent a breaking change.
typealias RemoteTab = RemoteTabRecord
47 changes: 47 additions & 0 deletions components/tabs/ios/Tabs/Tabs.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/* 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/. */

import Foundation

open class TabsStorage {
private var store: TabsStore
private let queue = DispatchQueue(label: "com.mozilla.tabs-storage")

public init(databasePath: String) {
store = TabsStore(path: databasePath)
}

/// Get all tabs by client.
open func getAll() -> [ClientRemoteTabs] {
return queue.sync {
return self.store.getAll()
}
}

/// Set the local tabs.
open func setLocalTabs(remoteTabs: [RemoteTabRecord]) {
queue.sync {
self.store.setLocalTabs(remoteTabs: remoteTabs)
}
}

open func reset() throws {
try queue.sync {
try self.store.reset()
}
}

open func sync(unlockInfo: SyncUnlockInfo) throws -> String {
return try queue.sync {
return try self.store
.sync(
keyId: unlockInfo.kid,
accessToken: unlockInfo.fxaAccessToken,
syncKey: unlockInfo.syncKey,
tokenserverUrl: unlockInfo.tokenserverURL,
localId: unlockInfo.tabsLocalId
)
}
}
}
15 changes: 5 additions & 10 deletions components/tabs/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#[derive(Debug, thiserror::Error)]
pub enum ErrorKind {
pub enum TabsError {
#[error("Error synchronizing: {0}")]
SyncAdapterError(#[from] sync15::Error),

#[error("Sync reset error: {0}")]
SyncResetError(#[from] anyhow::Error),

#[error("Error parsing JSON data: {0}")]
JsonError(#[from] serde_json::Error),

Expand All @@ -20,12 +23,4 @@ pub enum ErrorKind {
OpenDatabaseError(#[from] sql_support::open_database::Error),
}

error_support::define_error! {
ErrorKind {
(SyncAdapterError, sync15::Error),
(JsonError, serde_json::Error),
(UrlParseError, url::ParseError),
(SqlError, rusqlite::Error),
(OpenDatabaseError, sql_support::open_database::Error),
}
}
pub type Result<T> = std::result::Result<T, TabsError>;
4 changes: 2 additions & 2 deletions components/tabs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ mod sync;

uniffi_macros::include_scaffolding!("tabs");

pub use crate::storage::{ClientRemoteTabs, RemoteTab};
pub use crate::storage::{ClientRemoteTabs, RemoteTabRecord, TabsDeviceType};
pub use crate::sync::engine::TabsEngine;
pub use crate::sync::store::TabsStore;
pub use error::{Error, ErrorKind, Result};
use error::TabsError;
use sync15::DeviceType;

pub use crate::sync::store::get_registered_sync_engine;
3 changes: 3 additions & 0 deletions components/tabs/src/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ use sql_support::ConnExt;
use std::cell::RefCell;
use std::path::{Path, PathBuf};

pub type TabsDeviceType = crate::DeviceType;
pub type RemoteTabRecord = RemoteTab;

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct RemoteTab {
pub title: String,
Expand Down
33 changes: 23 additions & 10 deletions components/tabs/src/sync/store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,7 @@ use interrupt_support::NeverInterrupts;
use std::cell::RefCell;
use std::path::Path;
use std::sync::{Arc, Mutex, Weak};
use sync15::{
sync_multiple, telemetry, KeyBundle, MemoryCachedState, Sync15StorageClientInit, SyncEngine,
SyncEngineId,
};
use sync15::{sync_multiple, EngineSyncAssociation, MemoryCachedState, SyncEngine, SyncEngineId};

// Our "sync manager" will use whatever is stashed here.
lazy_static::lazy_static! {
Expand Down Expand Up @@ -69,21 +66,37 @@ impl TabsStore {
self.storage.lock().unwrap().get_remote_tabs()
}

pub fn reset(self: Arc<Self>) -> Result<()> {
let engine = TabsEngine::new(Arc::clone(&self));
engine.reset(&EngineSyncAssociation::Disconnected)?;
Ok(())
}

/// A convenience wrapper around sync_multiple.
pub fn sync(
self: Arc<Self>,
storage_init: &Sync15StorageClientInit,
root_sync_key: &KeyBundle,
local_id: &str,
) -> Result<telemetry::SyncTelemetryPing> {
key_id: String,
access_token: String,
sync_key: String,
tokenserver_url: String,
local_id: String,
) -> Result<String> {
let mut mem_cached_state = MemoryCachedState::default();
let mut engine = TabsEngine::new(Arc::clone(&self));

// Since we are syncing without the sync manager, there's no
// command processor, therefore no clients engine, and in
// consequence `TabsStore::prepare_for_sync` is never called
// which means our `local_id` will never be set.
// Do it here.
engine.local_id = RefCell::new(local_id.to_owned());
engine.local_id = RefCell::new(local_id);

let storage_init = &sync15::Sync15StorageClientInit {
key_id,
access_token,
tokenserver_url: url::Url::parse(tokenserver_url.as_str())?,
};
let root_sync_key = &sync15::KeyBundle::from_ksync_base64(sync_key.as_str())?;

let mut result = sync_multiple(
&[&engine],
Expand All @@ -103,7 +116,7 @@ impl TabsStore {
return Err(e.into());
}
match result.engine_results.remove("tabs") {
None | Some(Ok(())) => Ok(result.telemetry),
None | Some(Ok(())) => Ok(serde_json::to_string(&result.telemetry)?),
Some(Err(e)) => Err(e.into()),
}
}
Expand Down
26 changes: 21 additions & 5 deletions components/tabs/src/tabs.udl
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,41 @@ namespace tabs {

};

[Error]
enum TabsError {
"SyncAdapterError",
"SyncResetError",
"JsonError",
"UrlParseError",
"SqlError",
"OpenDatabaseError",
};


interface TabsStore {
constructor(string path);

sequence<ClientRemoteTabs> get_all();

void set_local_tabs(sequence<RemoteTab> remote_tabs);
void set_local_tabs(sequence<RemoteTabRecord> remote_tabs);

[Self=ByArc]
void register_with_sync_manager();

[Throws=TabsError, Self=ByArc]
void reset();

[Throws=TabsError, Self=ByArc]
string sync(string key_id, string access_token, string sync_key, string tokenserver_url, string local_id);
};

// Note that this enum is duplicated in fxa-client.udl (although the underlying type *is*
// shared). This duplication exists because there's no direct dependency between that crate and
// this one. We can probably remove the duplication when sync15 gets a .udl file, then we could
// reference it via an `[Extern=...]typedef`
enum DeviceType { "Desktop", "Mobile", "Tablet", "VR", "TV", "Unknown" };
enum TabsDeviceType { "Desktop", "Mobile", "Tablet", "VR", "TV", "Unknown" };

dictionary RemoteTab {
dictionary RemoteTabRecord {
string title;
sequence<string> url_history;
string? icon;
Expand All @@ -30,6 +46,6 @@ dictionary RemoteTab {
dictionary ClientRemoteTabs {
string client_id;
string client_name;
DeviceType device_type;
sequence<RemoteTab> remote_tabs;
TabsDeviceType device_type;
sequence<RemoteTabRecord> remote_tabs;
};
2 changes: 1 addition & 1 deletion examples/cli-support/src/fxa_creds.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ pub fn get_default_fxa_config() -> Config {
Config::release(CLIENT_ID, REDIRECT_URI)
}

fn get_account_and_token(
pub fn get_account_and_token(
config: Config,
cred_file: &str,
) -> Result<(FirefoxAccount, AccessTokenInfo)> {
Expand Down
21 changes: 13 additions & 8 deletions examples/tabs-sync/src/tabs-sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@

#![warn(rust_2018_idioms)]

use cli_support::fxa_creds::{get_cli_fxa, get_default_fxa_config};
use cli_support::fxa_creds::{get_account_and_token, get_cli_fxa, get_default_fxa_config};
use cli_support::prompt::prompt_char;
use std::path::Path;
use std::sync::Arc;
use structopt::StructOpt;
use tabs::{RemoteTab, TabsStore};
use tabs::{RemoteTabRecord, TabsStore};

use anyhow::Result;

Expand Down Expand Up @@ -42,6 +42,9 @@ fn main() -> Result<()> {
cli_support::init_logging();
let opts = Opts::from_args();

let (_, token_info) = get_account_and_token(get_default_fxa_config(), &opts.creds_file)?;
let sync_key = token_info.key.unwrap().kid;

let mut cli_fxa = get_cli_fxa(get_default_fxa_config(), &opts.creds_file)?;
let device_id = cli_fxa.account.get_current_device_id()?;

Expand Down Expand Up @@ -86,9 +89,11 @@ fn main() -> Result<()> {
'S' | 's' => {
log::info!("Syncing!");
match Arc::clone(&store).sync(
&cli_fxa.client_init,
&cli_fxa.root_sync_key,
&device_id,
cli_fxa.client_init.clone().key_id,
cli_fxa.client_init.clone().access_token,
sync_key.clone(),
cli_fxa.client_init.tokenserver_url.to_string(),
device_id.clone(),
) {
Err(e) => {
log::warn!("Sync failed! {}", e);
Expand Down Expand Up @@ -118,7 +123,7 @@ fn main() -> Result<()> {
}

#[cfg(feature = "with-clipboard")]
fn read_local_state() -> Vec<RemoteTab> {
fn read_local_state() -> Vec<RemoteTabRecord> {
use clipboard::{ClipboardContext, ClipboardProvider};
println!("Please run the following command in the Firefox Browser Toolbox and copy it.");
println!(
Expand Down Expand Up @@ -151,7 +156,7 @@ fn read_local_state() -> Vec<RemoteTab> {
.iter()
.map(|u| u.as_str().unwrap().to_owned())
.collect();
local_state.push(RemoteTab {
local_state.push(RemoteTabRecord {
title,
url_history,
icon,
Expand All @@ -162,7 +167,7 @@ fn read_local_state() -> Vec<RemoteTab> {
}

#[cfg(not(feature = "with-clipboard"))]
fn read_local_state() -> Vec<RemoteTab> {
fn read_local_state() -> Vec<RemoteTabRecord> {
println!("This module is build without the `clipboard` feature, so we can't");
println!("read the local state.");
vec![]
Expand Down

0 comments on commit 95d0deb

Please sign in to comment.