Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update tabs component for iOS integration #4905

Merged
merged 1 commit into from
May 24, 2022
Merged
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
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