Skip to content

Commit

Permalink
Add support for the Microsoft Store Firefox version
Browse files Browse the repository at this point in the history
  • Loading branch information
null-dev committed Feb 22, 2022
1 parent 84320e9 commit fad5d67
Show file tree
Hide file tree
Showing 11 changed files with 184 additions and 56 deletions.
9 changes: 5 additions & 4 deletions Cargo.lock

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

3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ url = "2.2.0"
chrono = "0.4"
rand = "0.8"
nng = "1.0.1"
once_cell = "1.9.0"

[target.'cfg(target_family = "unix")'.dependencies]
nix = "0.19"
Expand All @@ -43,6 +44,8 @@ features = [
"Win32_Security",
"Win32_System_Threading",
"Win32_UI_WindowsAndMessaging",
"Win32_UI_Shell",
"Win32_System_Com",
]

[package.metadata.deb]
Expand Down
1 change: 0 additions & 1 deletion src/cmd/create_profile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ use crate::profiles::{ProfilesIniState, ProfileEntry, calc_profile_id, write_pro
use crate::native_req::NativeMessageCreateProfile;
use crate::native_resp::{NativeResponse, NativeResponseProfileListProfileEntry, NativeResponseData};
use ulid::Ulid;
use std::collections::HashMap;
use std::fs;
use std::fs::OpenOptions;
use std::path::PathBuf;
Expand Down
2 changes: 1 addition & 1 deletion src/cmd/initialize.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::state::AppState;
use crate::profiles::{ProfilesIniState, write_profiles};
use crate::native_req::NativeMessageInitialize;
use crate::native_resp::{NativeResponse, NativeResponseData, write_native_response, NativeResponseWrapper, NATIVE_RESP_ID_EVENT, NativeResponseEvent, NativeResponseProfileListProfileEntry, write_native_event};
use crate::native_resp::{NativeResponse, NativeResponseData, NativeResponseEvent, NativeResponseProfileListProfileEntry, write_native_event};
use std::{thread, fs};
use crate::ipc::setup_ipc;
use crate::options::native_notify_updated_options;
Expand Down
4 changes: 3 additions & 1 deletion src/cmd/launch_profile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@ pub fn process_cmd_launch_profile(app_state: &AppState,
"The version of your browser that is currently running can no longer be found. ",
"This is usually because your browser has updated but you haven't restarted your browser recently to apply the update. ",
"Please restart your browser to resolve this issue."
))
)),
ForkBrowserProcError::COMError { .. } => NativeResponse::error_with_dbg_msg("Failed to launch browser with new profile (Windows COM error)!", e),
ForkBrowserProcError::MSIXProcessLaunchError { .. } => NativeResponse::error_with_dbg_msg("Failed to launch browser with new profile (Windows AAM error)!", e),
}
}
}
70 changes: 64 additions & 6 deletions src/config.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
// === CONFIG ===

use std::path::PathBuf;
use std::path::{Component, PathBuf};
use serde::{Deserialize, Serialize};
use cfg_if::cfg_if;
use std::fs::OpenOptions;
use once_cell::sync::Lazy;
use crate::process::get_parent_proc_path;

#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct Config {
Expand All @@ -13,25 +15,63 @@ pub struct Config {

impl Config {
pub fn browser_profile_dir(&self) -> PathBuf {
self.browser_profile_dir.clone().unwrap_or_else(get_default_browser_profile_folder)
self.browser_profile_dir.clone()
.unwrap_or_else(|| get_default_browser_profile_folder().clone())
}
pub fn browser_binary(&self) -> Option<&PathBuf> {
self.browser_binary.as_ref()
}

pub fn profiles_ini_path(&self) -> PathBuf {
let mut profiles_ini = self.browser_profile_dir().clone();
let mut profiles_ini = self.browser_profile_dir();
profiles_ini.push("profiles.ini");
return profiles_ini;
}
pub fn installs_ini_path(&self) -> PathBuf {
let mut installs_ini = self.browser_profile_dir().clone();
let mut installs_ini = self.browser_profile_dir();
installs_ini.push("installs.ini");
return installs_ini;
}
}

fn get_default_browser_profile_folder() -> PathBuf {
// Detect if Firefox is installed from Microsoft Store
#[cfg(target_os = "windows")]
static MSIX_PACKAGE: Lazy<Result<String, String>> = Lazy::new(|| {
get_parent_proc_path()
.map_err(|e| format!("get_parent_proc_path failed: {:?}", e))
.and_then(|p| {
// Windows path looks like this:
// [Prefix(PrefixComponent { raw: "C:", parsed: Disk(67) }), RootDir, Normal("Program Files"), Normal("WindowsApps"), Normal("Mozilla.Firefox_97.0.1.0_x64__n80bbvh6b1yt2"), Normal("VFS"), Normal("ProgramFiles"), Normal("Firefox Package Root"), Normal("firefox.exe")]
let components: Vec<Component> = p.components()
// Skip beginning of path until we get to the root dir (e.g. the C: prefix)
.skip_while(|c| !matches!(c, Component::RootDir))
.skip(1) // Now skip the root dir
.take(3) // Take the "Program Files", "WindowsApps" and package entries
.collect();

if let [
Component::Normal(p1),
Component::Normal(p2),
Component::Normal(package)
] = components[..] {
if p1 == "Program Files" && p2 == "WindowsApps" {
if let Some(package) = package.to_str() {
if let [Some(pname_sep), Some(pid_sep)] = [package.find("_"), package.rfind("_")] {
return Ok(format!("{}_{}", &package[..pname_sep], &package[pid_sep + 1..]))
}
}
}
}

Err(format!("Browser path is not in MSIX format, components: {:?}!", components))
})
});
#[cfg(target_os = "windows")]
pub fn get_msix_package() -> Result<&'static String, &'static String> {
MSIX_PACKAGE.as_ref()
}

static DEFAULT_BROWSER_PROFILE_FOLDER: Lazy<PathBuf> = Lazy::new(|| {
let user_dirs = directories::UserDirs::new()
.expect("Unable to determine user folder!");

Expand All @@ -45,7 +85,22 @@ fn get_default_browser_profile_folder() -> PathBuf {
result.push("Application Support");
result.push("Firefox");
} else if #[cfg(target_os = "windows")] {
result.push("AppData");
match MSIX_PACKAGE.as_ref() {
Ok(msix_package) => {
log::trace!("Detected MSIX package: {}", msix_package);

result.push("AppData");
result.push("Local");
result.push("Packages");
result.push(msix_package);
result.push("LocalCache");
}
Err(e) => {
log::trace!("Did not detect MSIX package: {}", e);

result.push("AppData");
}
}
result.push("Roaming");
result.push("Mozilla");
result.push("Firefox");
Expand All @@ -55,6 +110,9 @@ fn get_default_browser_profile_folder() -> PathBuf {
}
log::trace!("Found default browser profile dir: {:?}", result);
return result;
});
fn get_default_browser_profile_folder() -> &'static PathBuf {
&DEFAULT_BROWSER_PROFILE_FOLDER
}

impl Default for Config {
Expand Down
13 changes: 4 additions & 9 deletions src/ipc.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,14 @@
use std::{io, fs, thread};
use std::{io, thread};
use crate::state::AppState;
use byteorder::{WriteBytesExt, NetworkEndian, ReadBytesExt};
use std::io::{Write, Read, BufReader, BufWriter};
use std::path::PathBuf;
use std::time::Duration;
use crate::native_resp::{write_native_response, NativeResponseWrapper, NATIVE_RESP_ID_EVENT, NativeResponse, NativeResponseEvent, NativeResponseProfileListProfileEntry, write_native_event};
use crate::native_resp::{NATIVE_RESP_ID_EVENT, NativeResponse, NativeResponseEvent, NativeResponseProfileListProfileEntry, write_native_event};
use crate::profiles::{read_profiles, ProfilesIniState};
use crate::options::{read_global_options, native_notify_updated_options};
use crate::storage::{options_data_path, global_options_data_path};
use crate::storage::{global_options_data_path};
use cfg_if::cfg_if;
use nng::{Message, Protocol, Socket};
use nng::options::{Options, RecvTimeout, SendTimeout};
use serde::{Serialize, Deserialize};
use serde_json::value::Serializer;
use crate::process::fork_browser_proc;

// === IPC ===
Expand Down Expand Up @@ -146,7 +142,6 @@ fn handle_ipc_cmd_focus_window(app_state: &AppState, cmd: FocusWindowCommand) {

#[derive(Debug)]
pub enum IpcError {
NotRunning,
BadStatus,
SerializationError(serde_cbor::Error),
IoError(io::Error),
Expand All @@ -164,7 +159,7 @@ fn send_ipc_cmd(app_state: &AppState, target_profile_id: &str, cmd: IPCCommand)
let socket_name = get_ipc_socket_name(target_profile_id, false)
.map_err(IpcError::IoError)?;

let mut conn = Socket::new(Protocol::Req0).map_err(IpcError::NetworkError)?;
let conn = Socket::new(Protocol::Req0).map_err(IpcError::NetworkError)?;
conn.set_opt::<SendTimeout>(Some(Duration::from_millis(500)));
conn.set_opt::<RecvTimeout>(Some(Duration::from_millis(3000)));
conn.dial(&socket_name).map_err(IpcError::NetworkError)?;
Expand Down
25 changes: 23 additions & 2 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ mod process;
extern crate ini;
extern crate serde;
extern crate serde_json;
extern crate byteorder;
extern crate directories;
extern crate fs2;
extern crate cfg_if;
Expand All @@ -25,14 +24,25 @@ extern crate chrono;
extern crate rand;
extern crate serde_cbor;

cfg_if! {
if #[cfg(target_family = "unix")] {
extern crate nix;
extern crate libc;
} else if #[cfg(target_family = "windows")] {
extern crate windows;
use windows::Win32::System::Com::{CoInitializeEx, COINIT_MULTITHREADED};
}
}

use std::{io, env};
use std::fs;
use cfg_if::cfg_if;
use directories::ProjectDirs;
use rand::Rng;
use crate::config::{read_configuration};
use crate::profiles::read_profiles;
use crate::state::AppState;
use crate::native_resp::{NativeResponseEvent, write_native_response, NativeResponseWrapper, NATIVE_RESP_ID_EVENT, NativeResponse, write_native_event};
use crate::native_resp::{NativeResponseEvent, write_native_response, NativeResponseWrapper, NativeResponse, write_native_event};
use crate::cmd::execute_cmd_for_message;
use crate::native_req::{read_incoming_message, NativeMessage};

Expand Down Expand Up @@ -111,6 +121,17 @@ fn main() {

log::trace!("Finished setup logging (app version: {}).", APP_VERSION);

// Initialize Windows COM library
cfg_if! {
if #[cfg(target_family = "windows")] {
if let Err(e) = unsafe { CoInitializeEx(std::ptr::null(), COINIT_MULTITHREADED) } {
log::trace!("Windows COM library initialization failure, continuing anyway: {:?}", e);
} else {
log::trace!("Windows COM library initialized.");
}
}
}

// Find extension ID
let args: Vec<String> = env::args().collect();
let extension_id = args.get(2);
Expand Down
2 changes: 1 addition & 1 deletion src/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use serde_json::Value;
use std::fs::OpenOptions;
use std::io;
use crate::storage::global_options_data_path;
use crate::native_resp::{write_native_response, NativeResponseWrapper, write_native_event, NativeResponseEvent};
use crate::native_resp::{write_native_event, NativeResponseEvent};
use crate::state::AppState;

// === GLOBAL OPTIONS ===
Expand Down
Loading

0 comments on commit fad5d67

Please sign in to comment.