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

Handle firefox flatpak installation #1597

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
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
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,25 @@ Before installing anything please read [SECURITY.md](SECURITY.md) and make sure

2. Install the Firenvim addon for your browser from [Mozilla's store](https://addons.mozilla.org/en-US/firefox/addon/firenvim/) or [Google's](https://chrome.google.com/webstore/detail/firenvim/egpjdkipkomnmjhjmdamaniclmdlobbo).

3. If you're not using a flatpak browser, you can skip this

While support for flatpaks was [recently](https://github.com/glacambre/firenvim/pull/1597) added for Firefox, other changes are still needed on the user's end.

The following folders all need to be overridden in the flatpak config for the browser:

- Whatever `:echo stdpath("config")` resolves to for you (normally `~/.config/nvim`)
- Whatever `:echo stdpath("data")` resolves to for you (normall `~/.local/share/nvim`)
- `~/.local/share/firenvim`
- The path to `nvim` if it's installed using something like homebrew (if you're using your distro's package manager you should be able to ignore)
- `/run/user/1000/firenvim` by default, or whichever path you've configured as the base for the files being edited

You can do this using [flatseal](https://flathub.org/apps/com.github.tchx84.Flatseal) (recommended) or create/amend the file in `~/.local/share/flatpak/overrides/org.mozilla.firefox` to look something similar to the below:

```conf
[Context]
filesystems=/run/user/1000/firenvim;~/.local/share/firenvim;~/.local/share/nvim;~/.config/nvim
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just thinking: this effectively breaks flatpak's sandboxing. A compromised firefox will be able to append malicious code to the user's init.lua, which will be executed outside the sandbox the next time the user runs neovim from their terminal.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The only way around it would be to copy the directories instead of symlinking them, but then you'd have to re-run it any time your config changed or you updated a plugin.

I have pondered that perhaps this PR would be better as purely documentation rather than any changes the plugin makes, then the setup is up to the user.

```

If you would rather build and install Firenvim from source, check [CONTRIBUTING.md](CONTRIBUTING.md).

#### Other browsers
Expand Down
13 changes: 11 additions & 2 deletions TROUBLESHOOTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,22 @@ If you're having issues with Firenvim, here are the steps you can take in order

## Make sure Flatpak and Snap are not preventing Firenvim from working

If your browser is installed through Snap or Flatpak, sandboxing mechanisms might be preventing the browser from starting Neovim. You can confirm this by running:
If your browser is installed through Snap, sandboxing mechanisms might be preventing the browser from starting Neovim. You can confirm this by running:

```
flatpak permissions webextensions
```

If the output of this command shows that Snap/Flatpak are preventing Firenvim from running, you need to run `flatpak permission-set webextensions firenvim snap.firefox yes` to change that.
If the output of this command shows that Snap are preventing Firenvim from running, you need to run `flatpak permission-set webextensions firenvim snap.firefox yes` to change that.

In the case of Flatpak installs, verify that you've performed the steps in the [README](https://github.com/glacambre/firenvim?tab=readme-ov-file#installing) and that the `~/.local/share/flatpak/overrides/org.mozilla.firefox` file looks something similar to the below:

```conf
[Context]
filesystems=/run/user/1000/firenvim;~/.local/share/firenvim;~/.local/share/nvim;~/.config/nvim
```

Those directories may change depending on your setup, consult the guidelines in the [README](https://github.com/glacambre/firenvim?tab=readme-ov-file#installing). Also ensure that the directory that `$ which nvim` resolves to shows up (eg if you've installed via homebrew, you'll have to add `/home/linuxbrew/.linuxbrew`)

## Make sure the neovim plugin is installed

Expand Down
185 changes: 123 additions & 62 deletions autoload/firenvim.vim
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,23 @@ function! s:get_data_dir_path() abort
return s:build_path([l:xdg_data_home, 'firenvim'])
endfunction

function! s:browser_has_flatpak_installed(browser) abort
if has('linux') && has_key(a:browser, 'flatpak_name')
let l:cmd_output = system(['flatpak', 'info', a:browser['flatpak_name']])
if l:cmd_output =~ 'error:.*not installed'
return 0
else
return 1
endif
endif

return 0
endfunction

function! s:firefox_flatpak_config_exists() abort
return isdirectory(s:build_path([$HOME, '.var', 'app', 'org.mozilla.firefox', '.mozilla']))
endfunction

function! s:firefox_config_exists() abort
let l:p = [$HOME, '.mozilla']
if has('mac')
Expand All @@ -285,13 +302,17 @@ function! s:firefox_config_exists() abort
return isdirectory(s:build_path(l:p))
endfunction

function! s:get_firefox_flatpak_manifest_dir_path() abort
return s:build_path([$HOME, '.var', 'app', 'org.mozilla.firefox', '.mozilla', 'native-messaging-hosts'])
endfunction

function! s:get_firefox_manifest_dir_path() abort
if has('mac')
return s:build_path([$HOME, 'Library', 'Application Support', 'Mozilla', 'NativeMessagingHosts'])
elseif has('win32') || s:is_wsl
return s:get_data_dir_path()
end
return s:build_path([$HOME, '.mozilla', 'native-messaging-hosts'])
if has('mac')
return s:build_path([$HOME, 'Library', 'Application Support', 'Mozilla', 'NativeMessagingHosts'])
elseif has('win32') || s:is_wsl
return s:get_data_dir_path()
end
return s:build_path([$HOME, '.mozilla', 'native-messaging-hosts'])
endfunction

function! s:librewolf_config_exists() abort
Expand Down Expand Up @@ -750,6 +771,12 @@ function! s:get_browser_configuration() abort
\ 'manifest_dir_path': function('s:get_firefox_manifest_dir_path'),
\ 'registry_key': 'HKCU:\Software\Mozilla\NativeMessagingHosts\firenvim',
\},
\'firefox-flatpak': {
\ 'has_config': s:firefox_flatpak_config_exists(),
\ 'manifest_content': function('s:get_firefox_manifest'),
\ 'manifest_dir_path': function('s:get_firefox_flatpak_manifest_dir_path'),
\ 'flatpak_name': 'org.mozilla.firefox',
\},
\'librewolf': {
\ 'has_config': s:librewolf_config_exists(),
\ 'manifest_content': function('s:get_firefox_manifest'),
Expand Down Expand Up @@ -857,64 +884,86 @@ function! firenvim#install(...) abort
continue
endif

try
let l:manifest_content = l:cur_browser['manifest_content'](l:execute_nvim_path)
let l:manifest_dir_path = l:cur_browser['manifest_dir_path']()
let l:manifest_path = s:build_path([l:manifest_dir_path, 'firenvim.json'])
catch /.*/
echo 'Aborting installation for ' . l:name . '. ' . v:exception
continue
endtry

if has('win32') || s:is_wsl
let l:manifest_path = s:build_path([l:manifest_dir_path, 'firenvim-' . l:name . '.json'])
let l:flatpak_result = s:browser_has_flatpak_installed(l:cur_browser)
if l:flatpak_result == 1
try
let l:manifest_content = l:cur_browser['manifest_content'](l:execute_nvim_path)
let l:manifest_dir_path = l:cur_browser['manifest_dir_path']()
let l:manifest_path = s:build_path([l:manifest_dir_path, 'firenvim.json'])
catch /.*/
echo 'Aborting installation for ' . l:name . ' flatpak. ' . v:exception
continue
endtry

call s:maybe_execute('mkdir', l:manifest_dir_path, 'p', 0700)
call s:maybe_execute('writefile', [l:manifest_content], l:manifest_path)
call s:maybe_execute('setfperm', l:manifest_path, 'rw-------')

echo 'Installed flatpak manifest for ' . l:name . '.'

call s:maybe_execute('system', ['ln', '-nsf', stdpath("config"), s:build_path([$HOME, '.var', 'app', l:cur_browser['flatpak_name'], "config", "nvim"])])
call s:maybe_execute('system', ['ln', '-nsf', stdpath("data"), s:build_path([$HOME, '.var', 'app', l:cur_browser['flatpak_name'], "data", "nvim"])])
else
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems that most of the code in the if l:flatpak_result branch is shared with the code in the else branch. What do you think about having a single execution path and only gating the two system calls behind an if l:flatpak_result? e.g.

endtry

call s:maybe_execute('mkdir', l:manifest_dir_path, 'p', 0700)
call s:maybe_execute('writefile', [l:manifest_content], l:manifest_path)
call s:maybe_execute('setfperm', l:manifest_path, 'rw-------')

echo 'Installed manifest for ' . l:name . '.'

if s:browser_has_flatpak_installed(l:cur_browser)
    call s:maybe_execute('system', ['ln', '-nsf', stdpath("config"), s:build_path([$HOME, '.var', 'app', l:cur_browser['flatpak_name'], "config", "nvim"])]) 
    call s:maybe_execute('system', ['ln', '-nsf', stdpath("data"), s:build_path([$HOME, '.var', 'app', l:cur_browser['flatpak_name'], "data", "nvim"])]) 
endif

if has('win32') || s:is_wsl
    ...

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That does actually make the most sense yes

try
let l:manifest_content = l:cur_browser['manifest_content'](l:execute_nvim_path)
let l:manifest_dir_path = l:cur_browser['manifest_dir_path']()
let l:manifest_path = s:build_path([l:manifest_dir_path, 'firenvim.json'])
catch /.*/
echo 'Aborting installation for ' . l:name . '. ' . v:exception
continue
endtry

if has('win32') || s:is_wsl
let l:manifest_path = s:build_path([l:manifest_dir_path, 'firenvim-' . l:name . '.json'])
endif

call s:maybe_execute('mkdir', l:manifest_dir_path, 'p', 0700)
call s:maybe_execute('writefile', [l:manifest_content], l:manifest_path)
call s:maybe_execute('setfperm', l:manifest_path, 'rw-------')

echo 'Installed native manifest for ' . l:name . '.'

if has('win32') || s:is_wsl
" On windows, also create a registry key. We do this
" by writing a powershell script to a file and
" executing it.
let l:ps1_content = s:key_to_ps1_str(l:cur_browser['registry_key'],
\ l:manifest_path)
let l:ps1_path = s:build_path([l:manifest_dir_path, l:name . '.ps1'])
echo 'Creating registry key for ' . l:name . '. This may take a while. Script: ' . l:ps1_path
call s:maybe_execute('writefile', split(l:ps1_content, "\n"), l:ps1_path)
call s:maybe_execute('setfperm', l:ps1_path, 'rwx------')
try
let o = s:maybe_execute('system', ['powershell.exe', '-NonInteractive', '-Command', '-'], readfile(l:ps1_path))
catch /powershell.exe' is not executable/
let l:failure = v:true
let l:msg = 'Error: Firenvim could not find powershell.exe'
" If the failure happened on wsl, try to use
" an absolute path
if s:is_wsl
let l:msg += ' from WSL'
try
let o = s:maybe_execute('system', ['/mnt/c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe', '-Command', '-'], readfile(l:ps1_path))
let l:failure = v:false
catch /powershell.exe' is not executable/
let l:failure = v:true
endtry
endif
let l:msg += ' on your system. Please report this issue.'
if l:failure
echomsg 'Note: created ' . l:ps1_path . " . You may try to run it manually by right-clicking from your file browser to complete Firenvim's installation."
throw l:msg
endif
endtry

if v:shell_error
echo o
endif

echo 'Created registry key for ' . l:name . '.'
endif
endif

call s:maybe_execute('mkdir', l:manifest_dir_path, 'p', 0700)
call s:maybe_execute('writefile', [l:manifest_content], l:manifest_path)
call s:maybe_execute('setfperm', l:manifest_path, 'rw-------')

echo 'Installed native manifest for ' . l:name . '.'

if has('win32') || s:is_wsl
" On windows, also create a registry key. We do this
" by writing a powershell script to a file and
" executing it.
let l:ps1_content = s:key_to_ps1_str(l:cur_browser['registry_key'],
\ l:manifest_path)
let l:ps1_path = s:build_path([l:manifest_dir_path, l:name . '.ps1'])
echo 'Creating registry key for ' . l:name . '. This may take a while. Script: ' . l:ps1_path
call s:maybe_execute('writefile', split(l:ps1_content, "\n"), l:ps1_path)
call s:maybe_execute('setfperm', l:ps1_path, 'rwx------')
try
let o = s:maybe_execute('system', ['powershell.exe', '-NonInteractive', '-Command', '-'], readfile(l:ps1_path))
catch /powershell.exe' is not executable/
let l:failure = v:true
let l:msg = 'Error: Firenvim could not find powershell.exe'
" If the failure happened on wsl, try to use
" an absolute path
if s:is_wsl
let l:msg += ' from WSL'
try
let o = s:maybe_execute('system', ['/mnt/c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe', '-Command', '-'], readfile(l:ps1_path))
let l:failure = v:false
catch /powershell.exe' is not executable/
let l:failure = v:true
endtry
endif
let l:msg += ' on your system. Please report this issue.'
if l:failure
echomsg 'Note: created ' . l:ps1_path . " . You may try to run it manually by right-clicking from your file browser to complete Firenvim's installation."
throw l:msg
endif
endtry

if v:shell_error
echo o
endif

echo 'Created registry key for ' . l:name . '.'
endif
endfor

if !s:is_wsl
Expand Down Expand Up @@ -951,6 +1000,18 @@ function! firenvim#uninstall() abort
continue
endif

let l:flatpak_result = s:browser_has_flatpak_installed(l:cur_browser)
if l:flatpak_result == 1
let l:manifest_content = l:cur_browser['manifest_content'](l:execute_nvim_path)
let l:manifest_dir_path = l:cur_browser['manifest_dir_path'](1)
let l:manifest_path = s:build_path([l:manifest_dir_path, 'firenvim.json'])

call delete(l:manifest_path)
call delete(s:build_path([$HOME, '.var', 'app', l:cur_browser['flatpak_name'], 'config', 'nvim']))
call delete(s:build_path([$HOME, '.var', 'app', l:cur_browser['flatpak_name'], 'data', 'nvim']))
echo 'Removed flatpak manifest for ' . l:name . '.'
endif

let l:manifest_dir_path = l:cur_browser['manifest_dir_path']()
let l:manifest_path = s:build_path([l:manifest_dir_path, 'firenvim.json'])

Expand Down