├── LICENSE ├── README.md ├── crowdin.yml ├── docs ├── README.md ├── docs │ ├── _headers │ ├── about │ │ ├── contributing.md │ │ ├── current-limitations.md │ │ ├── how-it-works.md │ │ ├── privacy-policy.md │ │ └── supported-features.md │ ├── assets │ │ ├── icons │ │ │ ├── favicon-maskable.svg │ │ │ ├── favicon-monochrome.svg │ │ │ ├── favicon.svg │ │ │ └── logo.svg │ │ ├── javascripts │ │ │ ├── feedback.js │ │ │ └── statistics.js │ │ ├── site.webmanifest │ │ └── stylesheets │ │ │ └── theme.css │ ├── help │ │ ├── faq.md │ │ ├── support.md │ │ └── troubleshooting.md │ ├── index.md │ ├── installation │ │ ├── extension.md │ │ ├── native.md │ │ ├── requirements.md │ │ └── updates.md │ ├── resources │ │ ├── installation-directories.md │ │ ├── profile-properties.md │ │ ├── specific-website-tips.md │ │ └── web-app-properties.md │ ├── robots.txt │ └── user-guide │ │ ├── browser.md │ │ ├── console.md │ │ └── extension.md ├── hooks │ ├── breadcrumbs.py │ ├── faq.py │ └── home.py ├── mkdocs.yml ├── overrides │ ├── 404.html │ └── main.html ├── requirements.in └── requirements.txt ├── extension ├── .browserslistrc ├── .eslintrc ├── .parcelrc ├── LICENSE ├── README.md ├── package.json ├── src │ ├── _locales │ │ ├── en │ │ │ └── messages.json │ │ ├── fr │ │ │ └── messages.json │ │ ├── id │ │ │ └── messages.json │ │ ├── ja │ │ │ └── messages.json │ │ ├── pt-BR │ │ │ └── messages.json │ │ ├── ru │ │ │ └── messages.json │ │ ├── sl │ │ │ └── messages.json │ │ ├── uk │ │ │ └── messages.json │ │ ├── zh-CN │ │ │ └── messages.json │ │ └── zh-TW │ │ │ └── messages.json │ ├── background.js │ ├── content.js │ ├── images │ │ ├── addon-logo.svg │ │ ├── browser-action.svg │ │ ├── page-action-install.svg │ │ └── page-action-launch.svg │ ├── manifest.json │ ├── setup │ │ ├── install.html │ │ ├── install.js │ │ ├── install.scss │ │ ├── instructions.html │ │ ├── instructions.js │ │ ├── instructions.scss │ │ ├── setup.scss │ │ ├── update.html │ │ ├── update.js │ │ └── update.scss │ ├── sites │ │ ├── categories.js │ │ ├── install.html │ │ ├── install.js │ │ ├── install.scss │ │ ├── launch.html │ │ ├── launch.js │ │ ├── launch.scss │ │ ├── manage.html │ │ ├── manage.js │ │ ├── manage.scss │ │ └── popup.scss │ ├── styles │ │ ├── bootstrap.css │ │ └── bootstrap.scss │ ├── utils.js │ └── utils │ │ ├── errors.js │ │ ├── i18n.js │ │ └── i18nHtml.js ├── tools │ ├── icons │ │ ├── _css.hbs │ │ └── generate.js │ ├── set-version.js │ └── transformers │ │ ├── filename.js │ │ ├── locale.js │ │ └── treeshake.js └── yarn.lock └── native ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── Makefile.toml ├── README.md ├── assets ├── Metropolis-SemiBold.otf ├── icon-mask-macos.png └── icon-shadow-macos.png ├── build.rs ├── manifests ├── bsd.json ├── linux.json ├── macos.json └── windows.json ├── packages ├── appstream │ ├── si.filips.FirefoxPWA.metainfo.xml │ └── si.filips.FirefoxPWA.svg ├── brew │ └── configure.sh ├── choco │ ├── firefoxpwa.nuspec │ ├── legal │ │ ├── LICENSE.txt │ │ └── VERIFICATION.txt │ └── tools │ │ └── chocolateyinstall.ps1 ├── deb │ ├── copyright │ ├── description │ ├── postinst │ └── postrm ├── gentoo │ └── firefoxpwa.ebuild ├── paf │ ├── PWAsForFirefoxHelpers │ │ ├── Cargo.lock │ │ ├── Cargo.toml │ │ └── src │ │ │ ├── icon.ico │ │ │ └── main.rs │ └── PWAsForFirefoxPortable │ │ ├── App │ │ └── AppInfo │ │ │ ├── EULA.txt │ │ │ ├── Launcher │ │ │ └── PWAsForFirefoxPortable.ini │ │ │ ├── appicon.ico │ │ │ ├── appicon_128.png │ │ │ ├── appicon_16.png │ │ │ ├── appicon_256.png │ │ │ ├── appicon_32.png │ │ │ ├── appicon_75.png │ │ │ ├── appinfo.ini │ │ │ └── installer.ini │ │ ├── Other │ │ ├── Help │ │ │ └── Images │ │ │ │ ├── Donation_Button.png │ │ │ │ ├── Favicon.ico │ │ │ │ ├── Help_Background_Footer.png │ │ │ │ ├── Help_Background_Header.png │ │ │ │ └── Help_Logo_Top.png │ │ └── Source │ │ │ ├── AppNamePortable.ini │ │ │ ├── LauncherLicense.txt │ │ │ ├── LauncherReadme.txt │ │ │ └── PortableApps.comInstallerCustom.nsh │ │ └── help.html ├── rpm │ └── firefoxpwa.spec └── wix │ ├── assets │ ├── banner.png │ ├── dialog.png │ ├── exclamation.ico │ ├── information.ico │ ├── license.rtf │ ├── new.ico │ ├── product.ico │ └── up.ico │ └── main.wxs ├── rustfmt.toml ├── scripts ├── manifest.scm └── set-version.ds ├── src ├── bin │ ├── firefoxpwa-connector.rs │ └── firefoxpwa.rs ├── components │ ├── _7zip.rs │ ├── mod.rs │ ├── profile.rs │ ├── runtime.rs │ └── site.rs ├── connector │ ├── mod.rs │ ├── process.rs │ ├── request.rs │ └── response.rs ├── console │ ├── app.rs │ ├── mod.rs │ ├── profile.rs │ ├── runtime.rs │ └── site.rs ├── directories.rs ├── integrations │ ├── categories.rs │ ├── implementation │ │ ├── linux.rs │ │ ├── macos.rs │ │ ├── mod.rs │ │ ├── portableapps.rs │ │ └── windows.rs │ ├── mod.rs │ └── utils.rs ├── lib.rs ├── storage.rs └── utils.rs └── userchrome ├── profile └── chrome │ └── pwa │ ├── boot.sys.mjs │ ├── chrome.manifest │ ├── chrome.sys.mjs │ ├── content │ ├── browser.css │ ├── browser.sys.mjs │ ├── macosHiddenWindow.sys.mjs │ ├── preferences.css │ └── preferences.sys.mjs │ ├── icons │ ├── send-to-device.svg │ └── share.svg │ ├── localization │ ├── en-US │ │ └── pwa │ │ │ ├── appmenu.ftl │ │ │ ├── browser.ftl │ │ │ ├── contextmenu.ftl │ │ │ ├── customizemode.ftl │ │ │ ├── preferences.ftl │ │ │ └── widgets.ftl │ ├── pt-BR │ │ └── pwa │ │ │ ├── appmenu.ftl │ │ │ ├── browser.ftl │ │ │ ├── contextmenu.ftl │ │ │ ├── customizemode.ftl │ │ │ ├── preferences.ftl │ │ │ └── widgets.ftl │ ├── ru │ │ └── pwa │ │ │ ├── appmenu.ftl │ │ │ ├── browser.ftl │ │ │ ├── contextmenu.ftl │ │ │ ├── customizemode.ftl │ │ │ ├── preferences.ftl │ │ │ └── widgets.ftl │ ├── sl │ │ └── pwa │ │ │ ├── appmenu.ftl │ │ │ ├── browser.ftl │ │ │ ├── contextmenu.ftl │ │ │ ├── customizemode.ftl │ │ │ ├── preferences.ftl │ │ │ └── widgets.ftl │ ├── zh-CN │ │ └── pwa │ │ │ ├── browser.ftl │ │ │ ├── contextmenu.ftl │ │ │ ├── customizemode.ftl │ │ │ ├── preferences.ftl │ │ │ └── widgets.ftl │ └── zh-TW │ │ └── pwa │ │ ├── browser.ftl │ │ ├── contextmenu.ftl │ │ └── customizemode.ftl │ └── utils │ ├── common.sys.mjs │ ├── hookFunction.sys.mjs │ ├── nativeMessaging.sys.mjs │ ├── systemIntegration.sys.mjs │ └── xPref.sys.mjs └── runtime ├── _autoconfig.cfg └── defaults └── pref └── autoconfig.js /crowdin.yml: -------------------------------------------------------------------------------- 1 | project_id_env: CROWDIN_PROJECT_ID 2 | api_token_env: CROWDIN_PERSONAL_TOKEN 3 | 4 | preserve_hierarchy: true 5 | 6 | pull_request_title: Translation updates from Crowdin 7 | pull_request_labels: [ crowdin ] 8 | 9 | commit_message: "[skip ci]" 10 | 11 | files: 12 | - source: /extension/src/_locales/en/messages.json 13 | translation: /extension/src/_locales/%two_letters_code%/messages.json 14 | skip_untranslated_strings: true 15 | type: chrome 16 | 17 | - source: /native/userchrome/profile/chrome/pwa/localization/en-US/**/*.ftl 18 | translation: /native/userchrome/profile/chrome/pwa/localization/%two_letters_code%/**/%original_file_name% 19 | skip_untranslated_strings: true 20 | type: ftl 21 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | Progressive Web Apps for Firefox - Documentation 2 | ================================================ 3 | 4 | The documentation for the PWAsForFirefox project. 5 | 6 | > [!NOTE] 7 | > Parts of the website are still work-in-progress. Please use the feedback button and open 8 | > GitHub issues with your feedback and suggestions about potential improvements. You can 9 | > also participate [in a GitHub discussion](https://github.com/filips123/PWAsForFirefox/discussions/335) 10 | > about the documentation website. Thank you! 11 | 12 | ## Description 13 | 14 | > [!NOTE] 15 | > The website can be viewed at [pwasforfirefox.filips.si](https://pwasforfirefox.filips.si/). 16 | 17 | The documentation website for the project is built using [MkDocs](https://www.mkdocs.org/) and [Material for MkDocs](https://squidfunk.github.io/mkdocs-material/) theme. It is built and deployed automatically on new releases, but a deployment can also be triggered manually if needed. 18 | 19 | ## Development 20 | 21 | ### Dependencies 22 | 23 | ```shell 24 | pip install -r requirements.txt 25 | ``` 26 | 27 | ### Commands 28 | 29 | * `mkdocs serve` - Start the live-reloading docs server. 30 | * `mkdocs build` - Build the documentation site. 31 | * `mkdocs -h` - Print help message and exit. 32 | -------------------------------------------------------------------------------- /docs/docs/_headers: -------------------------------------------------------------------------------- 1 | /assets/icons/* 2 | Cache-Control max-age=3600, public, must-revalidate 3 | 4 | /assets/images/* 5 | Cache-Control max-age=3600, public, must-revalidate 6 | 7 | /assets/stylesheets/* 8 | Cache-Control: max-age=31536000, public, immutable 9 | 10 | /assets/javascripts/* 11 | Cache-Control: max-age=31536000, public, immutable 12 | 13 | /assets/javascripts/lunr/* 14 | ! Cache-Control 15 | -------------------------------------------------------------------------------- /docs/docs/about/contributing.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | 13 | 14 | ## Contributions 15 | 16 | You can read [our contributions guide][link-contributing] on GitHub. When dealing with 17 | security issues, please also make sure to read [our security policy][link-security]. 18 | 19 | [link-contributing]: https://github.com/filips123/PWAsForFirefox/blob/main/.github/CONTRIBUTING.md 20 | [link-security]: https://github.com/filips123/PWAsForFirefox/blob/main/.github/SECURITY.md 21 | 22 | ## Donations 23 | 24 | Thanks for your interest in donating to the project! Donations can help cover costs 25 | associated with the project and related services. With your support, I can focus more 26 | on the project to develop new features and fix the bugs quicker. 27 | 28 | The following donation services are available: 29 | 30 |
31 | 32 | * :simple-github:{ .brand .brand-github .invisible } [GitHub Sponsors](https://github.com/sponsors/filips123) 33 | * :simple-patreon:{ .brand .brand-patreon .invisible } [Patreon](https://patreon.com/filips) 34 | * :simple-liberapay:{ .brand .brand-liberapay .invisible } [Liberapay](https://liberapay.com/filips) 35 | * :simple-kofi:{ .brand .brand-kofi .invisible } [Ko-Fi](https://ko-fi.com/filips) 36 | * :simple-buymeacoffee:{ .brand .brand-buymeacoffee .invisible } [Buy Me a Coffee](https://www.buymeacoffee.com/filips) 37 | * :material-heart-box:{ .brand .brand-donorbox .invisible } [Donorbox](https://donorbox.org/filips) 38 | 39 |
40 | -------------------------------------------------------------------------------- /docs/docs/about/current-limitations.md: -------------------------------------------------------------------------------- 1 | # Current Limitations 2 | 3 | 9 | 10 | These are things that I would like to fix eventually, but will currently stay, either 11 | because they are too hard to fix, I don't know how to fix them, or would require modifying 12 | the Firefox source. You can check the full list of issues on [GitHub Projects][link-projects] 13 | page. I will appreciate any help to fix them. 14 | 15 | ## All web apps are merged with the first one that was opened (macOS) 16 | 17 | When some web app is already running, all newly launched web apps will merge with it 18 | and remain merged until all of them are closed. This will cause the app menu to display 19 | all web apps as part of the first web app that has been launched, with its icon and 20 | desktop actions. 21 | 22 | This happens because Apple only allows a process to be associated with a single 23 | application at all times. Perhaps this could be solved by using an IPC link between 24 | a host process and the main Firefox runtime process, the same way the Firefox parent 25 | process handles its content processes. This is just a wild theory though and has to be 26 | investigated further. 27 | 28 | This issue can be prevented by installing each web app into a different profile, 29 | which is the default behavior on macOS. 30 | 31 | Check [this comment][link-merged-comment] and related discussions for ideas 32 | and possible solutions for fixing this. This problem is tracked as issue [#81] 33 | [link-merged-issue]. 34 | 35 | 36 | ## Extension cannot detect the native program when using sandboxed Firefox (Linux Flatpak) 37 | 38 | When using Firefox distributed as a Flatpak package, the extension cannot detect the 39 | native program that is used. This is because Flatpak packages are sandboxed and cannot 40 | access/run other programs which is needed for Native Messaging API. This cannot be fixed 41 | until Native Messaging API gets support to work in sandboxed browser packages. 42 | 43 | The workaround for this is to uninstall Flatpak-based Firefox and install a normal DEB 44 | package instead. See [#76][link-flatpak-issue] for more details. 45 | 46 | Previously, this problem was also present on Snap, but it has been fixed recently. If 47 | you still cannot detect the native program, you can check out [the dedicated FAQ entry](../help/faq.md#why-doesnt-the-extension-find-the-native-connector-on-linux) 48 | about common problems on Linux. 49 | 50 | ## Web apps do not remember previous window positions and restore sessions 51 | 52 | When multiple web apps are installed in the same profile, individual web apps do not 53 | remember their previous window positions and sizes. This makes it hard to automatically 54 | open a web app with s specific window position and size. 55 | 56 | This happens because Firefox tracks window positions globally (per profile), and it 57 | is hard to change that using only UserChrome scripts. See [#256][link-session-issue] 58 | for more details. 59 | 60 | The workaround for this is to install each web app into a different profile. 61 | 62 | [link-projects]: https://github.com/users/filips123/projects/1/views/1?filterQuery=status%3A%22On+Hold%22 63 | [link-merged-comment]: https://github.com/filips123/PWAsForFirefox/issues/33#issuecomment-888511078 64 | [link-merged-issue]: https://github.com/filips123/PWAsForFirefox/issues/81 65 | [link-flatpak-issue]: https://github.com/filips123/PWAsForFirefox/issues/76 66 | [link-session-issue]: https://github.com/filips123/PWAsForFirefox/issues/256 67 | -------------------------------------------------------------------------------- /docs/docs/about/how-it-works.md: -------------------------------------------------------------------------------- 1 | # How It Works 2 | 3 | The project consists of three parts, the browser extension, the native program, and the 4 | UserChrome browser modifications. These parts are seamlessly integrated together to make 5 | the whole project work. The extension provides in-browser instructions for installation 6 | of the native program, which also installs the UserChrome modifications. 7 | 8 | This page can be useful for those who want a bit better understanding of how the project 9 | works and how it is implemented. 10 | 11 | ## Extension 12 | 13 | The browser extension is the main interface users will be facing when using the project. 14 | It provides a convenient way of installing, editing, and removing sites and profiles 15 | directly from their main browser. However, since [browser extensions][link-webextensions] 16 | are limited and cannot directly access the operating system, which is needed for the 17 | installation of web apps, the project uses [Native messaging API][link-native-messaging] 18 | for communication with the native program. 19 | 20 | [link-webextensions]: https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions 21 | [link-native-messaging]: https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Native_messaging 22 | 23 | ## Native Program 24 | 25 | The native program is written in [Rust][link-rust] and handles features that the extension 26 | alone cannot do. It communicates with the extension using a native messaging protocol. 27 | Features of the native program include installing Firefox runtime, patching it with the 28 | UserChrome modifications, installing the sites, applying the system integration, and 29 | launching them. 30 | 31 | The native program also provides a command-line program for users that prefer CLI to GUI. 32 | 33 | [link-rust]: https://www.rust-lang.org/ 34 | 35 | ## UserChrome Modifications 36 | 37 | The remaining part of the project is modifying Firefox UI to make a better app-like feel. 38 | This is done using [UserChromeJS (Autoconfig Startup Scripting)][link-userchromejs] 39 | modifications that can execute *low-level* JavaScript code that can modify Firefox UI. 40 | These modifications hide the address bar and tabs, move some browser buttons, provide 41 | additional useful widgets and settings and handle PWA scope and system integration. 42 | 43 | [link-userchromejs]: https://www.userchrome.org/what-is-userchrome-js.html 44 | -------------------------------------------------------------------------------- /docs/docs/about/privacy-policy.md: -------------------------------------------------------------------------------- 1 | # Privacy Policy 2 | 3 | ## Extension Privacy Policy 4 | 5 | The extension exchanges personal data (current website and manifest URLs, as well as other 6 | PWA details) with the native program when the user initiates the web app installation. 7 | This is necessary in order to correctly obtain and parse the Web Application Manifest and 8 | install the web app. Data are stored on the computer as long as that web app is installed. 9 | No personal data leave the computer or are sent to third parties. 10 | 11 | ## Runtime Privacy Policy 12 | 13 | As installed web apps are running inside a Mozilla Firefox browser, you need to agree 14 | to [the Firefox Privacy Notice](https://www.mozilla.org/privacy/firefox/). 15 | 16 | ## Website Privacy Policy 17 | 18 | This website uses or accesses the following third-party services: 19 | 20 | * [Cloudflare Pages](https://www.cloudflare.com/privacypolicy/) 21 | * [Cloudflare Analytics](https://www.cloudflare.com/privacypolicy/) 22 | * [Google Fonts](https://developers.google.com/fonts/faq/privacy) 23 | * [Contrib.rocks](https://contrib.rocks/) 24 | * [Shields.io](https://shields.io/) 25 | * [GitHub](https://docs.github.com/en/site-policy/privacy-policies) 26 | 27 | When opening a page or interacting with the feedback buttons, a request will be made to 28 | our self-hosted service. This requests only includes a page URL and an action (view or 29 | feedback status), without any personal data. Provided data are stored aggregated per page 30 | and day, also without any personal data, and are used to improve the documentation. 31 | -------------------------------------------------------------------------------- /docs/docs/about/supported-features.md: -------------------------------------------------------------------------------- 1 | # Supported Features 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | ## Available Features 14 | 15 | * Command-line tool to install, manage and run Progressive Web Apps in Firefox. 16 | * Extension to set up native programs, and install, manage and run PWAs and their profiles directly from the main Firefox browser. 17 | * Isolated Firefox installation and profile(s) that store the PWAs. 18 | * Installed PWAs have their own start/app menu entry and taskbar icon, and live in their own window. 19 | * Installed PWAs have no tabs and address bar for a better app-like feel. 20 | * Support for installing all websites as Progressive Web Apps. 21 | * Support for all Firefox addons/extensions and built-in Firefox features. 22 | * Support for automatic (user-triggered) installation and patching of installation and profile(s). 23 | 24 | ## Planned Features 25 | 26 | * Support for more system-related web app manifest features (once they are standardized). 27 | 28 | ## Not Planned Features 29 | 30 | * **Integration into official Firefox code.** This project currently modifies the browser chrome (UI) at runtime using JS and CSS. Although this works, it is officially unsupported by Mozilla and can break with Firefox updates. To contribute features back into the official Firefox code, they would need to be implemented properly with the new chrome page and browser services. Unfortunately, this requires an almost complete rewrite of the project, and I currently don't have enough knowledge and time to do that. 31 | 32 | * **Using the same installation profile for PWAs and normal browsing.** This could make the main browser installation/profile unstable if things break. It would also prevent customizing the PWA profile to work better as a PWA profile, and installing custom addons. If you want to sync data between your main and PWA profile, I recommend using Firefox Account or a third-party sync solution. 33 | 34 | * **Running PWAs installed as Windows APPX/MSIX packages or from Microsoft Store.** They will always use Chromium-based Edge that is installed on Windows 10/11. I'm not sure if it is possible to override this. If it is not too hard and doesn't cause any problems, I may try this in the future. 35 | 36 | * **Support for Chromium-specific APIs (Filesystem, Bluetooth, NFC, USB...).** This would require forking and directly modifying the Firefox source. Also, I'm not sure if giving websites the same privileges as native apps is the best idea... 37 | -------------------------------------------------------------------------------- /docs/docs/assets/icons/favicon-maskable.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /docs/docs/assets/icons/favicon-monochrome.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /docs/docs/assets/icons/favicon.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /docs/docs/assets/icons/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /docs/docs/assets/javascripts/feedback.js: -------------------------------------------------------------------------------- 1 | function sendFeedback (endpoint, page, value) { 2 | fetch(endpoint, { 3 | method: 'POST', 4 | keepalive: true, 5 | headers: { 6 | 'Content-Type': 'application/json' 7 | }, 8 | body: JSON.stringify({ 9 | 'page': page, 10 | 'value': value 11 | }) 12 | }).catch(() => {}) 13 | } 14 | 15 | document.addEventListener('DOMContentLoaded', function () { 16 | // Check if analytics are enabled and get the endpoint 17 | if (!document.getElementById('__analytics')) return 18 | const endpoint = JSON.parse(document.getElementById('__analytics').innerText).feedback 19 | 20 | document$.subscribe(function () { 21 | // Check if the feedback form is displayed 22 | const feedback = document.forms.feedback 23 | if (!feedback) return 24 | 25 | feedback.addEventListener('submit', function (event) { 26 | event.preventDefault() 27 | 28 | // Retrieve page and feedback value 29 | const page = document.location.pathname 30 | const value = parseInt(event.submitter.getAttribute('data-md-value')) 31 | 32 | // Send the feedback value to the server 33 | sendFeedback(endpoint, page, value) 34 | 35 | // Disable form and show note 36 | feedback.firstElementChild.disabled = true 37 | const note = feedback.querySelector(`.md-feedback__note [data-md-value='${value}']`) 38 | if (note) note.hidden = false 39 | }) 40 | 41 | // Show the feedback widget 42 | feedback.hidden = false 43 | }) 44 | }) 45 | -------------------------------------------------------------------------------- /docs/docs/assets/javascripts/statistics.js: -------------------------------------------------------------------------------- 1 | function sendView (endpoint, page) { 2 | fetch(endpoint, { 3 | method: 'POST', 4 | keepalive: true, 5 | headers: { 6 | 'Content-Type': 'application/json' 7 | }, 8 | body: JSON.stringify({ 9 | 'page': page 10 | }) 11 | }).catch(() => {}) 12 | } 13 | 14 | document.addEventListener('DOMContentLoaded', function () { 15 | // Check if analytics are enabled and get the endpoint 16 | if (!document.getElementById('__analytics')) return 17 | const endpoint = JSON.parse(document.getElementById('__analytics').innerText).views 18 | 19 | // Send the initial load view 20 | sendView(endpoint, document.location.pathname) 21 | 22 | // Send instant loading views 23 | location$.subscribe((url) => { 24 | sendView(endpoint, url.pathname) 25 | }) 26 | }) 27 | -------------------------------------------------------------------------------- /docs/docs/assets/site.webmanifest: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Progressive Web Apps for Firefox", 3 | "short_name": "PWAsForFirefox", 4 | "description": "A tool to install, manage and use Progressive Web Apps (PWAs) in Mozilla Firefox", 5 | "categories": ["reference", "documentation", "network", "utilities"], 6 | "keywords": ["pwas for firefox", "firefoxpwa", "webapp"], 7 | "theme_color": "#cc3e46", 8 | "start_url": "/", 9 | "scope": "/", 10 | "icons": [ 11 | { 12 | "src": "./icons/favicon.svg", 13 | "type": "image/svg+xml", 14 | "sizes": "any" 15 | }, 16 | { 17 | "src": "./icons/favicon-maskable.svg", 18 | "type": "image/svg+xml", 19 | "sizes": "any", 20 | "purpose": "maskable" 21 | }, 22 | { 23 | "src": "./icons/favicon-monochrome.svg", 24 | "type": "image/svg+xml", 25 | "sizes": "any", 26 | "purpose": "monochrome" 27 | } 28 | ] 29 | } 30 | -------------------------------------------------------------------------------- /docs/docs/assets/stylesheets/theme.css: -------------------------------------------------------------------------------- 1 | html { 2 | overscroll-behavior-y: none; 3 | } 4 | 5 | .md-header, .md-tabs { 6 | background-color: #cc3e46; 7 | background-image: linear-gradient(90deg, rgb(255, 60, 97) 0%, rgb(254, 124, 56) 100%); 8 | } 9 | 10 | @media screen { 11 | [data-md-color-scheme="slate"] { 12 | --md-hue: 240; 13 | --md-default-fg-color: hsla(var(--md-hue), 25%, 95%, 1); 14 | --md-default-fg-color--light: hsla(var(--md-hue), 25%, 90%, 0.62); 15 | --md-default-fg-color--lighter: hsla(var(--md-hue), 25%, 90%, 0.32); 16 | --md-default-fg-color--lightest: hsla(var(--md-hue), 25%, 90%, 0.12); 17 | --md-default-bg-color: hsla(var(--md-hue), 7%, 13%, 1); 18 | --md-default-bg-color--light: hsla(var(--md-hue), 7%, 13%, 0.54); 19 | --md-default-bg-color--lighter: hsla(var(--md-hue), 7%, 13%, 0.26); 20 | --md-default-bg-color--lightest: hsla(var(--md-hue), 7%, 13%, 0.07); 21 | --md-code-fg-color: hsla(var(--md-hue), 7%, 90%, 1); 22 | --md-code-bg-color: hsla(var(--md-hue), 7%, 17%, 1); 23 | --md-footer-bg-color: hsla(var(--md-hue), 7%, 10%, 0.87); 24 | --md-footer-bg-color--dark: hsla(var(--md-hue), 7%, 8%, 1); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /docs/docs/help/support.md: -------------------------------------------------------------------------------- 1 | # Getting Support 2 | 3 | If you are having problems with the project, please make sure that you have read the 4 | documentation, especially [the FAQ page](faq.md). Before creating bug reports, please 5 | also check for any similar issues or pull requests. If any of them already exists, please 6 | participle in that one. 7 | 8 | If you cannot determine and fix the problem yourself, please read [our guide for reporting 9 | issues](troubleshooting.md#reporting-issues). 10 | -------------------------------------------------------------------------------- /docs/docs/installation/extension.md: -------------------------------------------------------------------------------- 1 | # Extension Installation 2 | 3 | ## From Addon Store 4 | 5 | It is recommended to install the extension from [the Firefox Add-ons website][link-addon-store]. 6 | 7 | ## From Development Artifacts 8 | 9 | You can download and install [the latest build artifact][link-build-artifact] from GitHub Actions builds. 10 | 11 | !!! warning 12 | 13 | These are development versions that may be unstable and are not signed, so you 14 | will need to configure Firefox to accept non-signed extensions or just load it 15 | temporarily. It is generally not recommended to use them, unless you are testing 16 | a specific unreleased feature. 17 | 18 | ## From Source 19 | 20 | You can read the instructions for the installation from source in [the extension README file][link-extension-readme]. 21 | 22 | [link-addon-store]: https://addons.mozilla.org/firefox/addon/pwas-for-firefox/ 23 | [link-build-artifact]: https://github.com/filips123/PWAsForFirefox/actions/workflows/extension.yaml 24 | [link-extension-readme]: https://github.com/filips123/PWAsForFirefox/blob/main/extension/README.md#from-source 25 | -------------------------------------------------------------------------------- /docs/docs/installation/native.md: -------------------------------------------------------------------------------- 1 | # Native Installation 2 | 3 | !!! tip 4 | 5 | The extension should automatically display the installation instructions after 6 | installing it and accepting the licence agreement. If it does not, you can click 7 | the extension widget to display them again. The extension will not work properly 8 | without the native component installed. 9 | 10 | You can read the instructions for the installation from package repositories, release 11 | binaries and source in [the native README file][link-native-readme]. 12 | 13 | [link-native-readme]: https://github.com/filips123/PWAsForFirefox/blob/main/native/README.md#installation 14 | -------------------------------------------------------------------------------- /docs/docs/installation/requirements.md: -------------------------------------------------------------------------------- 1 | # Requirements 2 | 3 | ## Supported Systems 4 | 5 | * Windows (pre-built MSI installer) 6 | * Debian-like Linux (pre-built DEB package) 7 | * Red Hat-like Linux (pre-built RPM package) 8 | * Arch-like Linux (package in `[extra]` repository) 9 | * Gentoo-like Linux (ebuild in GURU overlay) 10 | * NixOS Linux (nixpkgs package) 11 | * Other Linux (source installation only) 12 | * macOS (bottled Homebrew formula) 13 | * BSD (source installation only)[^4] 14 | 15 | ## Extension Requirements 16 | 17 | * Last 2 Firefox versions *OR* Firefox ESR 18 | * Other Firefox-based browsers may also work[^1] 19 | 20 | ## Native Requirements 21 | 22 | * Windows: [Visual C++ Redistributable](https://support.microsoft.com/en-us/help/2977003/the-latest-supported-visual-c-downloads) 23 | * macOS: Xcode Command Line Tools[^2] 24 | * Linux: `glibc` 2.18 or later[^3] 25 | 26 | [^1]: Check [FAQ](../help/faq.md#how-to-use-an-alternative-browser-as-a-main-browser) for additional setup. 27 | [^2]: Automatically installed if using Homebrew. 28 | [^3]: Automatically installed as a package dependency. 29 | [^4]: Support for BSD relies on XDG Desktop Entry Specification and is not regularly tested. 30 | -------------------------------------------------------------------------------- /docs/docs/installation/updates.md: -------------------------------------------------------------------------------- 1 | # Updates 2 | 3 | The extension and native versions should be kept in sync to avoid potential problems and 4 | missing features. It is recommended to keep them updated to the newest available version, 5 | unless there are known big bugs in that release. 6 | 7 | As the native program cannot perform automatic updates, there are two options for keeping 8 | it up to date. The first one is to manually update the native program when the extension 9 | updates. When extension detects an outdated native program, it will display a notification 10 | with update instructions. 11 | 12 | On supported platforms, you can also install the native program with the supported package 13 | managers and manage updates through it. Please keep in mind that some package managers 14 | might have delayed releases. 15 | -------------------------------------------------------------------------------- /docs/docs/resources/profile-properties.md: -------------------------------------------------------------------------------- 1 | # Profile Properties 2 | 3 | ## Name 4 | 5 | A profile name. Displayed in the extension and console. 6 | 7 | ## Description 8 | 9 | A profile description. Displayed in the extension and console. 10 | 11 | ## Template 12 | 13 | A template directory. If specified, all contents of the provided template directory will 14 | be copied to a newly-created or edited profile. This is useful if you want to create a 15 | new profile with the same extensions, settings, etc. as an existing one. 16 | 17 | This is not *a real property*. It only exists when creating a new profile or editing an 18 | existing one, and cannot be changed afterward, as copying from a template is a one-time 19 | operation. 20 | -------------------------------------------------------------------------------- /docs/docs/resources/web-app-properties.md: -------------------------------------------------------------------------------- 1 | # Web App Properties 2 | 3 | ## Name 4 | 5 | A web app name. Used as an application name in the system menus. 6 | 7 | !!! warning 8 | 9 | You cannot re-use the same name for multiple web apps, because newer ones can overwrite 10 | menu entries for existing web apps. You should also be careful not to re-use the name 11 | of an existing native app, because it can also overwrite its menu entry. 12 | 13 | You cannot install multiple instances of the same web app in the same profile, 14 | because they would actually be the same instance. Instead, install each instance 15 | into a separate profile. 16 | 17 | ## Description 18 | 19 | A web app description. Used as an application description in the system menus. 20 | 21 | ## (Menu) Categories 22 | 23 | On Linux, macOS and PortableApps.com, web app categories are mapped to appropriate 24 | categories for that platform. For example, on Linux, the categories determine in which 25 | menu categories the app appears (on DEs that support menu categories). On Windows, 26 | categories are not supported and are ignored, but they can still be used for user 27 | organization. On macOS and PortableApps.com, the application can have only one category, 28 | so only the first one is used. 29 | 30 | ## (Menu) Keywords 31 | 32 | Keywords are used as additional search queries on Linux. On other platforms, keywords 33 | are not supported and are ignored, but they can still be used for user organization. 34 | 35 | ## Start URL 36 | 37 | A URL that is opened when the web app is launched. If not specified, the default start 38 | URL is used. 39 | 40 | ## Icon URL 41 | 42 | A URL of the icon that is used as an application icon. If not specified, the default 43 | icons are used. 44 | 45 | ## Protocol Handlers 46 | 47 | Determine which supported protocol handlers are enabled for that web app. 48 | 49 | By default, all protocol handlers specified in the web app manifest are disabled. Some 50 | websites also dynamically register protocol handlers; those are enabled automatically if 51 | you accept the Firefox prompt. 52 | 53 | ## Auto Launch Settings 54 | 55 | ### Launch this web app on matching website 56 | 57 | If enabled, all URLs that match the scopes of your web app will be automatically 58 | launched as a web app. Specific URLs can also be excluded from being automatically 59 | launched inside web apps with [the exclusion regex option](../user-guide/extension.md#automatic-launching-exclusion) 60 | in the main extension settings. 61 | 62 | This option is only available when [automatic web app launching](../user-guide/extension.md#enable-automatic-web-app-launching) 63 | is enabled in the main extension settings. 64 | 65 | ### Launch this web app on system login 66 | 67 | If enabled, the web app will be automatically launched when you log into the system. 68 | 69 | This option is not available on macOS, as launching on login can easily be enabled directly 70 | from the macOS UI instead. 71 | 72 | ### Launch this web app on browser launch 73 | 74 | If enabled, the web app will be automatically launched when you launch your main browser 75 | (the browser where the extension is installed). 76 | -------------------------------------------------------------------------------- /docs/docs/robots.txt: -------------------------------------------------------------------------------- 1 | Sitemap: https://pwasforfirefox.filips.si/sitemap.xml 2 | Sitemap: https://pwasforfirefox.filips.si/sitemap.xml.gz 3 | -------------------------------------------------------------------------------- /docs/hooks/breadcrumbs.py: -------------------------------------------------------------------------------- 1 | """Generate JSON-LD breadcrumbs for documentation pages.""" 2 | 3 | from __future__ import annotations 4 | 5 | from typing import TYPE_CHECKING 6 | 7 | if TYPE_CHECKING: 8 | from typing import Any 9 | from mkdocs.config.base import MkDocsConfig 10 | from mkdocs.structure.nav import Navigation 11 | from mkdocs.structure.pages import Page 12 | 13 | 14 | def on_page_context(context: dict[str, Any], page: Page, config: MkDocsConfig, nav: Navigation): 15 | if not page.ancestors: 16 | return 17 | 18 | elements = [{ 19 | "@type": "ListItem", 20 | "position": 1, 21 | "name": "Home", 22 | "item": config.site_url, 23 | }] 24 | 25 | position = 2 26 | 27 | for ancestor in page.ancestors[::-1]: 28 | name = ancestor.title 29 | 30 | while ancestor.children: 31 | ancestor = ancestor.children[0] 32 | 33 | url = ancestor.canonical_url 34 | 35 | elements.append({ 36 | "@type": "ListItem", 37 | "position": position, 38 | "name": name, 39 | "item": url, 40 | }) 41 | 42 | position += 1 43 | 44 | elements.append({ 45 | "@type": "ListItem", 46 | "position": position, 47 | "name": page.title, 48 | "item": page.canonical_url, 49 | }) 50 | 51 | if "schema" not in page.meta: 52 | page.meta["schema"] = [] 53 | 54 | if not isinstance(page.meta["schema"], list): 55 | page.meta["schema"] = [page.meta["schema"]] 56 | 57 | page.meta["schema"].append({ 58 | "@context": "https://schema.org", 59 | "@type": "BreadcrumbList", 60 | "itemListElement": elements, 61 | }) 62 | -------------------------------------------------------------------------------- /docs/hooks/faq.py: -------------------------------------------------------------------------------- 1 | """Generate the microdata schema for the FAQ page.""" 2 | 3 | from __future__ import annotations 4 | 5 | from typing import TYPE_CHECKING 6 | 7 | from bs4 import BeautifulSoup 8 | 9 | if TYPE_CHECKING: 10 | from typing import Any 11 | from mkdocs.config.base import MkDocsConfig 12 | from mkdocs.structure.nav import Navigation 13 | from mkdocs.structure.pages import Page 14 | 15 | 16 | def construct(document, container, question, answer): 17 | if not question or not answer: 18 | return 19 | 20 | # Move question text into span, so the paragraph sign is not copied to microdata 21 | name = document.new_tag("span", itemprop="name") 22 | for child in list(question.children): 23 | if child.name == "a" and "headerlink" in child.get("class", []): 24 | continue 25 | name.append(child.extract()) 26 | question.insert(0, name) 27 | 28 | # Construct the accepted answer from the source elements 29 | accepted = document.new_tag("div", itemscope="", itemprop="acceptedAnswer", itemtype="https://schema.org/Answer") 30 | text = document.new_tag("div", itemprop="text") 31 | text.extend(answer) 32 | accepted.append(text) 33 | 34 | # Create an question entry element and append question and accepted answer 35 | entry = document.new_tag("div", itemscope="", itemprop="mainEntity", itemtype="https://schema.org/Question") 36 | entry.append(question) 37 | entry.append(accepted) 38 | container.append(entry) 39 | 40 | 41 | def on_page_context(context: dict[str, Any], page: Page, config: MkDocsConfig, nav: Navigation): 42 | if not page.url == "help/faq/": 43 | return 44 | 45 | original = BeautifulSoup(page.content, features="lxml") 46 | modified = BeautifulSoup("", features="lxml") 47 | 48 | # All questions and answers need to be inside the FAQPage container 49 | container = modified.new_tag("div", itemscope="", itemtype="https://schema.org/FAQPage") 50 | modified.append(container) 51 | 52 | question = None 53 | answer = [] 54 | 55 | for element in list(original.body.children): 56 | # Copy styles and scripts to the target 57 | if element.name in ("style", "script", "h1"): 58 | container.insert_before(element) 59 | 60 | # Parse section and question headings 61 | if element.name in ("h2", "h3"): 62 | # Construct the previous question and reset parser 63 | construct(modified, container, question, answer) 64 | question = None 65 | answer = [] 66 | 67 | if element.name == "h2": 68 | # Copy the section heading to the container 69 | container.append(element) 70 | 71 | elif element.name == "h3": 72 | # Copy the question to the parser 73 | question = element 74 | 75 | continue 76 | 77 | # Copy the answer to the parser 78 | answer.append(element) 79 | 80 | # Construct the final question 81 | construct(modified, container, question, answer) 82 | 83 | # Replace the page content 84 | page.content = str(modified) 85 | -------------------------------------------------------------------------------- /docs/hooks/home.py: -------------------------------------------------------------------------------- 1 | """Generate JSON-LD schema for the homepage.""" 2 | 3 | from __future__ import annotations 4 | 5 | from typing import TYPE_CHECKING 6 | from urllib.parse import urljoin 7 | 8 | if TYPE_CHECKING: 9 | from typing import Any 10 | from mkdocs.config.base import MkDocsConfig 11 | from mkdocs.structure.nav import Navigation 12 | from mkdocs.structure.pages import Page 13 | 14 | 15 | def on_page_context(context: dict[str, Any], page: Page, config: MkDocsConfig, nav: Navigation): 16 | if not page.is_homepage: 17 | return 18 | 19 | common = { 20 | "name": "Progressive Web Apps for Firefox", 21 | "alternateName": ["PWAsForFirefox", "FirefoxPWA"], 22 | "description": config.site_description, 23 | "image": urljoin(config.site_url, "assets/icons/favicon.svg"), 24 | "url": config.site_url, 25 | "sameAs": [ 26 | "https://github.com/filips123/PWAsForFirefox", 27 | "https://addons.mozilla.org/firefox/addon/pwas-for-firefox", 28 | ], 29 | } 30 | 31 | website = { 32 | "@context": "https://schema.org", 33 | "@type": "WebSite", 34 | **common, 35 | "potentialAction": { 36 | "@type": "SearchAction", 37 | "target": { 38 | "@type": "EntryPoint", 39 | "urlTemplate": urljoin(config.site_url, "?q={search_term_string}"), 40 | }, 41 | "query-input": "required name=search_term_string", 42 | }, 43 | } 44 | 45 | application = { 46 | "@context": "https://schema.org", 47 | "@type": "SoftwareApplication", 48 | **common, 49 | "installUrl": "https://addons.mozilla.org/firefox/addon/pwas-for-firefox", 50 | "releaseNotes": "https://github.com/filips123/PWAsForFirefox/releases", 51 | "license": "https://github.com/filips123/PWAsForFirefox#license", 52 | "applicationCategory": ["UtilitiesApplication", "BrowserApplication"], 53 | "operatingSystem": ["Windows", "Linux", "macOS"], 54 | "softwareHelp": [ 55 | { 56 | "@type": "Webpage", 57 | "url": config.site_url, 58 | }, 59 | { 60 | "@type": "Webpage", 61 | "url": "https://github.com/filips123/PWAsForFirefox", 62 | } 63 | ], 64 | "hasPart": [ 65 | { 66 | "@type": "CreativeWork", 67 | "name": "PWAsForFirefox Extension", 68 | "url": [ 69 | "https://addons.mozilla.org/firefox/addon/pwas-for-firefox", 70 | "https://github.com/filips123/PWAsForFirefox/tree/main/extension", 71 | ] 72 | }, 73 | { 74 | "@type": "CreativeWork", 75 | "name": "PWAsForFirefox Native", 76 | "url": [ 77 | "https://repology.org/project/firefoxpwa", 78 | "https://packagecloud.io/filips/FirefoxPWA", 79 | "https://github.com/filips123/PWAsForFirefox/tree/main/native", 80 | ] 81 | } 82 | ], 83 | "offers": { 84 | "@type": "Offer", 85 | "price": "0", 86 | "priceCurrency": "USD", 87 | }, 88 | "subjectOf": { 89 | "@type": "WebSite", 90 | "url": "./", 91 | }, 92 | } 93 | 94 | if "schema" not in page.meta: 95 | page.meta["schema"] = [] 96 | 97 | if not isinstance(page.meta["schema"], list): 98 | page.meta["schema"] = [page.meta["schema"]] 99 | 100 | page.meta["schema"].extend([ 101 | website, 102 | application, 103 | ]) 104 | -------------------------------------------------------------------------------- /docs/overrides/404.html: -------------------------------------------------------------------------------- 1 | {% extends "main.html" %} 2 | 3 | {% block htmltitle %} 4 | Not Found - {{ config.site_name }} 5 | {% endblock %} 6 | 7 | {% block content %} 8 |

404 - Not Found

9 |

The page that you requested was not found. The page might have been moved or deleted. You can try to find it with the search or return to the homepage.

10 | {% endblock %} 11 | -------------------------------------------------------------------------------- /docs/overrides/main.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block extrahead %} 4 | {# Include web app manifest #} 5 | 6 | 7 | {# Include releases and commits feeds #} 8 | 9 | 10 | 11 | {% if page and page.is_homepage %} 12 | {# Prececonnect to the shields.io website for badges #} 13 | 14 | 15 | {# Set custom social title for the homepage #} 16 | 17 | 18 | {% endif %} 19 | 20 | {% if page and page.meta and page.meta.schema %} 21 | {# Support including JSON-LD schema #} 22 | 23 | {% endif %} 24 | 25 | {% if config.extra.analytics.enabled %} 26 | {# Store configuration for page view analytics #} 27 | 28 | {% endif %} 29 | {% endblock %} 30 | -------------------------------------------------------------------------------- /docs/requirements.in: -------------------------------------------------------------------------------- 1 | mkdocs 2 | mkdocs-material 3 | mkdocs-minify-plugin 4 | mkdocs-macros-plugin 5 | beautifulsoup4[lxml] 6 | -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | # 2 | # This file is autogenerated by pip-compile with Python 3.12 3 | # by the following command: 4 | # 5 | # pip-compile 6 | # 7 | babel==2.16.0 8 | # via mkdocs-material 9 | beautifulsoup4[lxml]==4.12.3 10 | # via -r requirements.in 11 | certifi==2024.12.14 12 | # via requests 13 | charset-normalizer==3.4.1 14 | # via requests 15 | click==8.1.8 16 | # via mkdocs 17 | colorama==0.4.6 18 | # via 19 | # click 20 | # mkdocs 21 | # mkdocs-material 22 | csscompressor==0.9.5 23 | # via mkdocs-minify-plugin 24 | ghp-import==2.1.0 25 | # via mkdocs 26 | hjson==3.1.0 27 | # via 28 | # mkdocs-macros-plugin 29 | # super-collections 30 | htmlmin2==0.1.13 31 | # via mkdocs-minify-plugin 32 | idna==3.10 33 | # via requests 34 | jinja2==3.1.5 35 | # via 36 | # mkdocs 37 | # mkdocs-macros-plugin 38 | # mkdocs-material 39 | jsmin==3.0.1 40 | # via mkdocs-minify-plugin 41 | lxml==5.3.0 42 | # via beautifulsoup4 43 | markdown==3.7 44 | # via 45 | # mkdocs 46 | # mkdocs-material 47 | # pymdown-extensions 48 | markupsafe==3.0.2 49 | # via 50 | # jinja2 51 | # mkdocs 52 | mergedeep==1.3.4 53 | # via 54 | # mkdocs 55 | # mkdocs-get-deps 56 | mkdocs==1.6.1 57 | # via 58 | # -r requirements.in 59 | # mkdocs-macros-plugin 60 | # mkdocs-material 61 | # mkdocs-minify-plugin 62 | mkdocs-get-deps==0.2.0 63 | # via mkdocs 64 | mkdocs-macros-plugin==1.3.7 65 | # via -r requirements.in 66 | mkdocs-material==9.5.49 67 | # via -r requirements.in 68 | mkdocs-material-extensions==1.3.1 69 | # via mkdocs-material 70 | mkdocs-minify-plugin==0.8.0 71 | # via -r requirements.in 72 | packaging==24.2 73 | # via 74 | # mkdocs 75 | # mkdocs-macros-plugin 76 | paginate==0.5.7 77 | # via mkdocs-material 78 | pathspec==0.12.1 79 | # via 80 | # mkdocs 81 | # mkdocs-macros-plugin 82 | platformdirs==4.3.6 83 | # via mkdocs-get-deps 84 | pygments==2.18.0 85 | # via mkdocs-material 86 | pymdown-extensions==10.13 87 | # via mkdocs-material 88 | python-dateutil==2.9.0.post0 89 | # via 90 | # ghp-import 91 | # mkdocs-macros-plugin 92 | pyyaml==6.0.2 93 | # via 94 | # mkdocs 95 | # mkdocs-get-deps 96 | # mkdocs-macros-plugin 97 | # pymdown-extensions 98 | # pyyaml-env-tag 99 | pyyaml-env-tag==0.1 100 | # via mkdocs 101 | regex==2024.11.6 102 | # via mkdocs-material 103 | requests==2.32.3 104 | # via mkdocs-material 105 | six==1.17.0 106 | # via python-dateutil 107 | soupsieve==2.6 108 | # via beautifulsoup4 109 | super-collections==0.5.3 110 | # via mkdocs-macros-plugin 111 | termcolor==2.5.0 112 | # via mkdocs-macros-plugin 113 | urllib3==2.3.0 114 | # via requests 115 | watchdog==6.0.0 116 | # via mkdocs 117 | -------------------------------------------------------------------------------- /extension/.browserslistrc: -------------------------------------------------------------------------------- 1 | last 2 Firefox version 2 | Firefox ESR 3 | -------------------------------------------------------------------------------- /extension/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "webextensions": true, 4 | "browser": true, 5 | "es2021": true 6 | }, 7 | "extends": [ 8 | "standard" 9 | ], 10 | "parserOptions": { 11 | "ecmaVersion": 12, 12 | "sourceType": "module" 13 | }, 14 | "plugins": [ 15 | "simple-import-sort" 16 | ], 17 | "rules": { 18 | "simple-import-sort/imports": "error", 19 | "simple-import-sort/exports": "error" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /extension/.parcelrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@parcel/config-webextension", 3 | "resolvers": ["@parcel/resolver-glob", "..."], 4 | "transformers": { 5 | "filename:*": ["./tools/transformers/filename", "..."], 6 | "locale:*": ["./tools/transformers/locale", "@parcel/transformer-json", "@parcel/transformer-js"], 7 | "raw:messages.json": ["./tools/transformers/locale", "./tools/transformers/treeshake"] 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /extension/README.md: -------------------------------------------------------------------------------- 1 | Progressive Web Apps for Firefox - Extension 2 | ============================================ 3 | 4 | The extension part of the PWAsForFirefox project. 5 | 6 | ## Description 7 | 8 | The extension part of the project makes it easier to install and manage Progressive Web Apps directly from the main Firefox browser. It supports installing supported PWAs with just a few clicks, managing and launching them, and creating and managing app profiles directly from the UI. 9 | 10 | Read [the main README file](../README.md) for more details about the project. 11 | 12 | ## Installation 13 | 14 | ### From Addon Store 15 | 16 | It is recommended to install the extension from [the Firefox Add-ons website](https://addons.mozilla.org/firefox/addon/pwas-for-firefox/). 17 | 18 | ### From Development Artifacts 19 | 20 | You can download and install [the latest build artifact](https://github.com/filips123/PWAsForFirefox/actions/workflows/extension.yaml) from GitHub Actions builds. 21 | 22 | Note that these are development versions that may be unstable and are not signed, so you will need to configure Firefox to accept non-signed extensions or just load it temporarily. It is generally not recommended to use them, unless you are testing a specific unreleased feature. 23 | 24 | ### From Source 25 | 26 | 1. Install Node.js and Yarn package manager. 27 | 28 | 2. Clone the repository and cd into the `extension` (this) directory. 29 | 30 | 3. Run `yarn install` to install the dependencies. 31 | 32 | 4. If building a specific version: 33 | 1. Checkout the correct Git tag. 34 | 2. Run `yarn set-version` to add the version information to the configuration files. 35 | 36 | 5. Either: 37 | 38 | a. Run `yarn build` to build the extension in release mode and package it. 39 | 40 | b. Run `yarn watch` to build the extension in development mode and automatically rebuild it on any changes. 41 | 42 | 6. Either: 43 | 44 | a. Install the packaged extension from `dist/firefoxpwa-{version}-compiled.zip` in `about:debugging`. 45 | 46 | b. Install the development extension from `dist/manifest.json` in `about:debugging`. 47 | 48 | ## Usage 49 | 50 | You can read [our documentation website](https://pwasforfirefox.filips.si/user-guide/extension/) for usage instructions. 51 | 52 | ## Contributing 53 | 54 | Please make sure that your JS code is properly linted and formatted using `yarn lint` (to check the code) and `yarn fix` (to automatically apply some fixes). 55 | -------------------------------------------------------------------------------- /extension/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "firefoxpwa", 3 | "description": "The browser extension part of the PWAsForFirefox project", 4 | "license": "MPL-2.0", 5 | "version": "0.0.0", 6 | "homepage": "https://pwasforfirefox.filips.si/", 7 | "repository": "https://github.com/filips123/PWAsForFirefox", 8 | "bugs": "https://github.com/filips123/PWAsForFirefox/issues", 9 | "funding": "https://github.com/filips123/PWAsForFirefox?sponsor=1", 10 | "author": "filips ", 11 | "private": true, 12 | "keywords": [ 13 | "firefox", 14 | "progressive-web-app", 15 | "site-specific-browser", 16 | "pwa" 17 | ], 18 | "scripts": { 19 | "watch": "run-s prepare:* watch:*", 20 | "watch:parcel": "parcel watch src/manifest.json src/{background,content}.js src/**/*.{html,svg} --no-content-hash", 21 | "build": "run-s prepare:* build:*", 22 | "build:parcel": "parcel build src/manifest.json src/{background,content}.js src/**/*.{html,svg} --no-content-hash --no-source-maps", 23 | "build:webext": "web-ext build -s dist -a dist -n firefoxpwa-{version}-compiled.zip", 24 | "lint": "run-s -c lint:*", 25 | "lint:eslint": "eslint src tools", 26 | "lint:webext": "web-ext lint -s src", 27 | "fix": "run-s -c fix:*", 28 | "fix:eslint": "eslint --fix src tools", 29 | "prepare:clean": "rimraf dist .parcel-cache", 30 | "prepare:icons": "node ./tools/icons/generate.js", 31 | "set-version": "node ./tools/set-version.js" 32 | }, 33 | "packageManager": "yarn@1.22.22", 34 | "dependencies": { 35 | "@popperjs/core": "^2.11.8", 36 | "base64-js": "^1.5.1", 37 | "bootstrap": "^5.3.3", 38 | "bootstrap-icons": "^1.11.3", 39 | "bootstrap5-tags": "^1.7.6", 40 | "dompurify": "^3.2.4", 41 | "iframe-resizer": "4.4.2", 42 | "semver": "^7.7.1" 43 | }, 44 | "devDependencies": { 45 | "@parcel/config-webextension": "^2.13.3", 46 | "@parcel/plugin": "^2.13.3", 47 | "@parcel/resolver-glob": "^2.13.3", 48 | "@parcel/transformer-raw": "^2.13.3", 49 | "@parcel/transformer-sass": "^2.13.3", 50 | "@twbs/fantasticon": "^3.0.0", 51 | "eslint": "^8.57.1", 52 | "eslint-config-standard": "^17.1.0", 53 | "eslint-plugin-import": "^2.31.0", 54 | "eslint-plugin-n": "^17.15.1", 55 | "eslint-plugin-promise": "^7.2.1", 56 | "eslint-plugin-simple-import-sort": "^12.1.1", 57 | "npm-run-all": "^4.1.5", 58 | "parcel": "^2.13.3", 59 | "rimraf": "^6.0.1", 60 | "svgo": "^3.3.2", 61 | "web-ext": "^8.4.0" 62 | }, 63 | "resolutions": { 64 | "jackspeak": "2.1.1", 65 | "sass": "1.77.6" 66 | }, 67 | "icons": [ 68 | "box-arrow-up-right", 69 | "check", 70 | "clipboard-check", 71 | "cloud-download", 72 | "download", 73 | "gear-fill", 74 | "grid-3x3-gap-fill", 75 | "pencil-square", 76 | "plus-lg", 77 | "trash" 78 | ], 79 | "messages": [ 80 | "appName", 81 | "appShortName", 82 | "appDescription", 83 | "actionInstallSite", 84 | "actionLaunchSite", 85 | "updateNotificationTitle", 86 | "updateNotificationMessage" 87 | ], 88 | "alias": { 89 | "process": false 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /extension/src/_locales/uk/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "appName": { 3 | "message": "Progressive Web Apps for Firefox", 4 | "description": "The name of the application, should not be translated" 5 | }, 6 | "appShortName": { 7 | "message": "PWAsForFirefox", 8 | "description": "The short name of the application, should not be translated" 9 | }, 10 | "appDescription": { 11 | "message": "Інструмент для встановлення, керування та використання прогресивних вебзастосунків (Progressive Web Apps) у Mozilla Firefox", 12 | "description": "The description of the application" 13 | }, 14 | "actionInstallSite": { 15 | "message": "Встановити цей сайт", 16 | "description": "Instruct users that they can install the current site here" 17 | }, 18 | "actionLaunchSite": { 19 | "message": "Запустити цей сайт", 20 | "description": "Instruct users that they can launch the current site here" 21 | }, 22 | "updateNotificationTitle": { 23 | "message": "Оновлення PWAsForFirefox", 24 | "description": "The title of the update notification, displayed when an update is available" 25 | }, 26 | "updateNotificationMessage": { 27 | "message": "Доступне PWAsForFirefox. Натисніть на сповіщення, щоб показати інструкції оновлення.", 28 | "description": "The message of the update notification, displayed when an update is available" 29 | }, 30 | "commonError": { 31 | "message": "Помилка", 32 | "description": "Displayed before error messages or as a popup title" 33 | }, 34 | "commonWarning": { 35 | "message": "Попередження", 36 | "description": "Displayed before warning messages or as a popup title" 37 | }, 38 | "commonNote": { 39 | "message": "Примітка", 40 | "description": "Displayed before note messages or as a popup title" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /extension/src/content.js: -------------------------------------------------------------------------------- 1 | const isAppleMaskIcon = link => link.getAttribute('rel').toLowerCase().includes('mask-icon') 2 | 3 | function getIconType (link) { 4 | const type = link.getAttribute('type') 5 | if (type) return type.includes('/') ? type : `image/${type}` 6 | else return isAppleMaskIcon(link) ? 'image/svg+xml' : null 7 | } 8 | 9 | function getIconPurpose (link) { 10 | return isAppleMaskIcon(link) ? 'monochrome' : 'any' 11 | } 12 | 13 | // Obtain the initial web app manifest URL 14 | const manifestElement = document.querySelector('link[rel=manifest]') 15 | const manifestUrl = manifestElement ? new URL(manifestElement.getAttribute('href'), document.baseURI) : null 16 | 17 | // Send the secure context state, initial manifest and document URLs on the page load 18 | browser.runtime.sendMessage({ manifestUrl: manifestUrl?.href, documentUrl: document.location.href, isSecureContext }) 19 | 20 | // Send the current manifest and document URLs on request 21 | browser.runtime.onMessage.addListener((message, _, sendResponse) => { 22 | // Ignore invalid messages 23 | if (message !== 'ObtainUrls') return 24 | 25 | // Collect the current web app manifest URL 26 | const manifestElement = document.querySelector('link[rel=manifest]') 27 | const manifestUrl = manifestElement ? new URL(manifestElement.getAttribute('href'), document.baseURI) : null 28 | 29 | // Collect page info that can be used if the manifest does not exist 30 | const pageInfo = { 31 | name: document.querySelector('meta[name=application-name]')?.content || document.title, 32 | description: document.querySelector('meta[name=description]')?.content, 33 | icons: [...document.getElementsByTagName('link')] 34 | .filter(link => link.getAttribute('rel')?.toLowerCase().includes('icon')) 35 | .map(link => ({ 36 | src: new URL(link.getAttribute('href'), document.baseURI).href, 37 | type: getIconType(link), 38 | purpose: getIconPurpose(link), 39 | sizes: link.getAttribute('sizes') || '' 40 | })) 41 | } 42 | 43 | // Send a response with the URLs and page info 44 | sendResponse({ manifestUrl: manifestUrl?.href, documentUrl: document.location.href, pageInfo }) 45 | }) 46 | -------------------------------------------------------------------------------- /extension/src/images/addon-logo.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /extension/src/images/browser-action.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /extension/src/images/page-action-install.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /extension/src/images/page-action-launch.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /extension/src/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 2, 3 | "name": "__MSG_appName__", 4 | "short_name": "__MSG_appShortName__", 5 | "description": "__MSG_appDescription__", 6 | "homepage_url": "https://pwasforfirefox.filips.si/", 7 | "version": "0.0.0", 8 | "default_locale": "en", 9 | "browser_specific_settings": { 10 | "gecko": { 11 | "id": "firefoxpwa@filips.si" 12 | } 13 | }, 14 | "icons": { 15 | "48": "images/addon-logo.svg", 16 | "96": "images/addon-logo.svg", 17 | "192": "images/addon-logo.svg" 18 | }, 19 | "background": { 20 | "scripts": ["background.js"] 21 | }, 22 | "content_scripts": [ 23 | { 24 | "js": ["content.js"], 25 | "matches": ["http://*/*", "https://*/*"], 26 | "run_at": "document_end" 27 | } 28 | ], 29 | "browser_action": { 30 | "default_icon": "images/browser-action.svg", 31 | "default_popup": "sites/manage.html" 32 | }, 33 | "page_action": {}, 34 | "permissions": [ 35 | "http://*/*", 36 | "https://*/*", 37 | "nativeMessaging", 38 | "notifications", 39 | "storage" 40 | ], 41 | "optional_permissions": [ 42 | "webNavigation", 43 | "webRequest", 44 | "webRequestBlocking" 45 | ] 46 | } 47 | -------------------------------------------------------------------------------- /extension/src/setup/install.scss: -------------------------------------------------------------------------------- 1 | ////////////////////////////// 2 | // Setup Styles 3 | ////////////////////////////// 4 | 5 | @import "setup.scss"; 6 | @import "../icons/bootstrap-icons.css"; 7 | 8 | ////////////////////////////// 9 | // Progress Bar 10 | ////////////////////////////// 11 | 12 | #progressbar { 13 | margin-top: 20px; 14 | margin-bottom: 30px; 15 | padding: 0; 16 | overflow: hidden; 17 | color: #495057; 18 | @media (prefers-color-scheme: dark) { color: #e1e1e1; } 19 | } 20 | 21 | #progressbar li { 22 | position: relative; 23 | float: left; 24 | width: 25%; 25 | list-style: none; 26 | font-size: 12px; 27 | } 28 | 29 | #progressbar li:before { 30 | display: block; 31 | width: 50px; 32 | height: 50px; 33 | line-height: 45px; 34 | font-size: 18px; 35 | color: white; 36 | border-radius: 50%; 37 | margin: 0 auto 10px auto; 38 | padding: 2px; 39 | } 40 | 41 | #progressbar li:after { 42 | position: absolute; 43 | left: 0; 44 | top: 25px; 45 | content: ''; 46 | width: 100%; 47 | height: 2px; 48 | z-index: -1; 49 | } 50 | 51 | #progressbar li:before, #progressbar li:after { 52 | background-color: #6c757d; 53 | } 54 | 55 | #progressbar li.active:before, #progressbar li.active:after { 56 | background-color: #0d6efd; 57 | } 58 | 59 | ////////////////////////////// 60 | // Text Utilities 61 | ////////////////////////////// 62 | 63 | .text-justify { 64 | text-align: justify; 65 | text-justify: inter-word; 66 | } 67 | -------------------------------------------------------------------------------- /extension/src/setup/instructions.js: -------------------------------------------------------------------------------- 1 | import 'iframe-resizer/js/iframeResizer.contentWindow' 2 | import '../utils/i18nHtml' 3 | 4 | import Tab from 'bootstrap/js/src/tab' 5 | 6 | import { EVENT_LOCALIZATION_READY } from '../utils' 7 | 8 | async function prepareInstallInstructions () { 9 | const version = browser.runtime.getManifest().version 10 | const { os, arch } = await browser.runtime.getPlatformInfo() 11 | 12 | // Set CRT download URL based on system arch 13 | document.getElementById('connector-download-url-crt').setAttribute('href', `https://aka.ms/vs/16/release/vc_redist.${arch === 'x86-32' ? 'x86' : 'x64'}.exe`) 14 | 15 | // Set MSI download URL based on system arch and extension version 16 | // Currently just relying on x86 emulation for Windows ARM 17 | const msiArch = arch === 'x86-64' ? 'x86_64' : 'x86' 18 | document.getElementById('connector-download-url-msi').setAttribute('href', `https://github.com/filips123/PWAsForFirefox/releases/download/v${version}/firefoxpwa-${version}-${msiArch}.msi`) 19 | 20 | // Set PAF download url based on extension version 21 | document.getElementById('connector-download-url-paf').setAttribute('href', `https://github.com/filips123/PWAsForFirefox/releases/download/v${version}/firefoxpwa_${version}_online.paf.exe`) 22 | 23 | // Set DEB download URL based on system arch and extension version 24 | const debArch = (() => { 25 | switch (arch) { 26 | case 'x86-32': 27 | return 'i386' 28 | case 'x86-64': 29 | return 'amd64' 30 | case 'arm': 31 | return 'armhf' 32 | case 'arm64': 33 | case 'aarch64': 34 | return 'arm64' 35 | default: 36 | return null 37 | } 38 | })() 39 | document.getElementById('connector-download-url-deb').setAttribute('href', `https://github.com/filips123/PWAsForFirefox/releases/download/v${version}/firefoxpwa_${version}_${debArch}.deb`) 40 | 41 | // Set RPM download URL based on system arch and extension version 42 | const rpmArch = (() => { 43 | switch (arch) { 44 | case 'x86-32': 45 | return 'i686' 46 | case 'x86-64': 47 | return 'x86_64' 48 | case 'arm': 49 | return 'armv7hl' 50 | case 'arm64': 51 | case 'aarch64': 52 | return 'aarch64' 53 | default: 54 | return null 55 | } 56 | })() 57 | document.getElementById('connector-download-url-rpm').setAttribute('href', `https://github.com/filips123/PWAsForFirefox/releases/download/v${version}/firefoxpwa-${version}-1.${rpmArch}.rpm`) 58 | 59 | // Set repository info based on extension version 60 | for (const elem of document.getElementsByClassName('connector-repository-tag')) elem.innerText = `v${version}` 61 | for (const elem of document.getElementsByClassName('connector-project-version')) elem.innerText = version 62 | 63 | // Link to the specific version for the install script 64 | const branchName = version === '0.0.0' ? 'main' : `v${version}` 65 | document.getElementById('connector-source-install').setAttribute('href', `https://github.com/filips123/PWAsForFirefox/tree/${branchName}/native#from-source`) 66 | 67 | // Hide DEB and RPM tabs on unsupported platforms 68 | if (debArch === null) document.getElementById('linux-deb-install-tab').classList.add('d-none') 69 | if (rpmArch === null) document.getElementById('linux-rpm-install-tab').classList.add('d-none') 70 | 71 | // Set the default tab to the current OS 72 | let defaultTab 73 | 74 | if (os === 'win') { 75 | defaultTab = 'windows' 76 | } else if (os === 'linux') { 77 | defaultTab = debArch ? 'linux-deb' : 'source' 78 | } else if (os === 'mac') { 79 | defaultTab = 'macos' 80 | } else if (os === 'openbsd') { 81 | defaultTab = 'bsd' 82 | } else { 83 | defaultTab = 'other' 84 | } 85 | 86 | new Tab(document.getElementById(`${defaultTab}-install-tab`)).show() 87 | } 88 | 89 | document.addEventListener(EVENT_LOCALIZATION_READY, prepareInstallInstructions) 90 | -------------------------------------------------------------------------------- /extension/src/setup/instructions.scss: -------------------------------------------------------------------------------- 1 | ////////////////////////////// 2 | // Bootstrap 3 | ////////////////////////////// 4 | 5 | @import "../styles/bootstrap.css"; 6 | 7 | ////////////////////////////// 8 | // Various Modifications 9 | ////////////////////////////// 10 | 11 | .nav-link { 12 | text-align: left; 13 | min-width: 13em; 14 | } 15 | 16 | ol > li { 17 | padding: 3px; 18 | } 19 | 20 | ul { 21 | padding-left: 1rem; 22 | } 23 | 24 | .snippet { 25 | display: block; 26 | background-color: #343a40; 27 | color: #fff; 28 | border-radius: .25rem; 29 | padding: .1875rem .375rem; 30 | margin-top: .4rem; 31 | margin-bottom: .4rem; 32 | } 33 | -------------------------------------------------------------------------------- /extension/src/setup/setup.scss: -------------------------------------------------------------------------------- 1 | ////////////////////////////// 2 | // Bootstrap 3 | ////////////////////////////// 4 | 5 | @import "../styles/bootstrap.css"; 6 | 7 | ////////////////////////////// 8 | // Content Card 9 | ////////////////////////////// 10 | 11 | .wrapper { 12 | max-width: 67rem; 13 | } 14 | 15 | .card { 16 | z-index: 0; 17 | border: none; 18 | border-radius: 0.5rem; 19 | } 20 | 21 | ////////////////////////////// 22 | // Instructions Iframe 23 | ////////////////////////////// 24 | 25 | iframe { 26 | border: 0; 27 | margin: 0; 28 | padding: 0; 29 | width: 100%; 30 | } 31 | -------------------------------------------------------------------------------- /extension/src/setup/update.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |
15 |
16 |

17 |
18 |
19 |

20 | 21 | 22 | 23 | 24 |

25 | 26 |

27 | 28 | 29 | 30 | 31 |

32 | 33 |

34 | 35 |

36 | 37 | 38 |

39 | 40 |

41 | : 42 | 43 |

44 | 45 |
    46 |
  • 47 |
  • 48 |
49 | 50 | 51 |
52 |
53 |
54 |
55 |
56 |
57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /extension/src/setup/update.scss: -------------------------------------------------------------------------------- 1 | ////////////////////////////// 2 | // Setup Styles 3 | ////////////////////////////// 4 | 5 | @import "setup.scss"; 6 | -------------------------------------------------------------------------------- /extension/src/sites/categories.js: -------------------------------------------------------------------------------- 1 | /** 2 | * A list of most categories supported by the system integration. 3 | * 4 | * Some categories are intentionally missing because they are aliases of other categories 5 | * or are unlikely to be used in common web apps. This list should mostly be kept in sync 6 | * with lists in the system integration part of the native program. 7 | * 8 | * @type {string[]} 9 | */ 10 | export const knownCategories = [ 11 | // XDG Main Categories 12 | 'audio-video', 13 | 'audio', 14 | 'music', 15 | 'video', 16 | 'development', 17 | 'education', 18 | 'game', 19 | 'design', 20 | 'graphics', 21 | 'network', 22 | 'office', 23 | 'settings', 24 | 'system', 25 | 'personalization', 26 | 'utility', 27 | 28 | // XDG Additional Categories 29 | 'debugger', 30 | 'ide', 31 | 'revision-control', 32 | 'version-control', 33 | 'translation', 34 | 'calendar', 35 | 'contact-management', 36 | 'database', 37 | 'dictionary', 38 | 'chart', 39 | 'email', 40 | 'finance', 41 | 'flowchart', 42 | 'presentation', 43 | 'spreadsheet', 44 | 'word-processor', 45 | '2d-graphics', 46 | 'vector-graphics', 47 | 'raster-graphics', 48 | '3d-graphics', 49 | 'scanning', 50 | 'ocr', 51 | 'photo', 52 | 'photography', 53 | 'publishing', 54 | 'printing', 55 | 'chat', 56 | 'social', 57 | 'irc-client', 58 | 'file-transfer', 59 | 'ham-radio', 60 | 'magazines', 61 | 'newspapers', 62 | 'news', 63 | 'p2p', 64 | 'remote-access', 65 | 'telephony', 66 | 'video-conference', 67 | 'browser', 68 | 'web-browser', 69 | 'web-development', 70 | 'tv', 71 | 'audio-editing', 72 | 'video-editing', 73 | 'audio-video-editing', 74 | 'player', 75 | 'recorder', 76 | 'disc-burning', 77 | 'action-game', 78 | 'adventure-game', 79 | 'arcade-game', 80 | 'board-game', 81 | 'blocks-game', 82 | 'card-game', 83 | 'kids-game', 84 | 'logic-game', 85 | 'role-playing-game', 86 | 'simulation-game', 87 | 'sports-game', 88 | 'strategy-game', 89 | 'art', 90 | 'science', 91 | 'astronomy', 92 | 'biology', 93 | 'chemistry', 94 | 'computer-science', 95 | 'data-visualization', 96 | 'economy', 97 | 'electricity', 98 | 'geography', 99 | 'geology', 100 | 'geoscience', 101 | 'history', 102 | 'book', 103 | 'literature', 104 | 'math', 105 | 'medical', 106 | 'physics', 107 | 'robotics', 108 | 'fitness', 109 | 'sport', 110 | 'amusement', 111 | 'entertainment', 112 | 'electronics', 113 | 'emulation', 114 | 'engineering', 115 | 'file-tools', 116 | 'security', 117 | 'accessibility', 118 | 'documentation', 119 | 120 | // macOS Categories 121 | 'business', 122 | 'casino-game', 123 | 'dice-game', 124 | 'educational-game', 125 | 'family-game', 126 | 'music-game', 127 | 'puzzle-game', 128 | 'racing-game', 129 | 'trivia-game', 130 | 'word-game', 131 | 'health', 132 | 'lifestyle', 133 | 'productivity', 134 | 'reference', 135 | 'travel', 136 | 'weather' 137 | ] 138 | -------------------------------------------------------------------------------- /extension/src/sites/install.scss: -------------------------------------------------------------------------------- 1 | ////////////////////////////// 2 | // Popup Styles 3 | ////////////////////////////// 4 | 5 | @import "popup.scss"; 6 | -------------------------------------------------------------------------------- /extension/src/sites/launch.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | 21 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /extension/src/sites/launch.js: -------------------------------------------------------------------------------- 1 | import '../utils/errors' 2 | import '../utils/i18nHtml' 3 | 4 | import { launchSite, obtainSiteList, obtainUrls, PREF_LAUNCH_CURRENT_URL, sanitizeString, setPopupSize } from '../utils' 5 | import { getMessage } from '../utils/i18n' 6 | 7 | async function createInstanceList () { 8 | // Obtain a current manifest URL 9 | const { manifestUrl, documentUrl } = await obtainUrls() 10 | 11 | // Obtain a list of existing sites and restrict them to the current manifest URL 12 | const sites = Object.values(await obtainSiteList()) 13 | .filter(site => site.config.manifest_url === manifestUrl) 14 | 15 | // Get the list element 16 | const listElement = document.getElementById('instances-list') 17 | listElement.innerText = '' 18 | 19 | // Launch site with the current URL, if enabled in settings 20 | let settingsLaunchCurrentUrl = (await browser.storage.local.get(PREF_LAUNCH_CURRENT_URL))[PREF_LAUNCH_CURRENT_URL] 21 | settingsLaunchCurrentUrl = settingsLaunchCurrentUrl !== undefined ? settingsLaunchCurrentUrl : true 22 | 23 | // Create a list element for every instance with handler that launches it 24 | for (const site of sites) { 25 | const name = sanitizeString(site.config.name || site.manifest.name || site.manifest.short_name) 26 | const url = settingsLaunchCurrentUrl ? documentUrl : undefined 27 | 28 | const siteElement = document.createElement('button') 29 | siteElement.classList.add(...['list-group-item', 'list-group-item-action']) 30 | siteElement.innerText = name || new URL(site.manifest.scope).host 31 | siteElement.addEventListener('click', () => { launchSite(site, url) }) 32 | 33 | listElement.append(siteElement) 34 | } 35 | 36 | // Create a list element that opens new instance popup 37 | { 38 | const newInstanceElement = document.createElement('button') 39 | newInstanceElement.classList.add(...['list-group-item', 'list-group-item-action']) 40 | 41 | const newInstanceEm = document.createElement('em') 42 | newInstanceEm.innerText = await getMessage('launchPageInstallNewInstance') 43 | newInstanceElement.append(newInstanceEm) 44 | 45 | newInstanceElement.addEventListener('click', async () => { 46 | document.location = '/sites/install.html' 47 | }) 48 | 49 | listElement.append(newInstanceElement) 50 | } 51 | } 52 | 53 | setPopupSize() 54 | createInstanceList() 55 | -------------------------------------------------------------------------------- /extension/src/sites/launch.scss: -------------------------------------------------------------------------------- 1 | ////////////////////////////// 2 | // Popup Styles 3 | ////////////////////////////// 4 | 5 | @import "popup.scss"; 6 | 7 | ////////////////////////////// 8 | // Card Modifications 9 | ////////////////////////////// 10 | 11 | .card-body { 12 | padding: 0; 13 | } 14 | 15 | .list-group-item { 16 | white-space: nowrap; 17 | overflow: hidden; 18 | text-overflow: ellipsis; 19 | } 20 | -------------------------------------------------------------------------------- /extension/src/sites/popup.scss: -------------------------------------------------------------------------------- 1 | ////////////////////////////// 2 | // Bootstrap 3 | ////////////////////////////// 4 | 5 | @import "../styles/bootstrap.css"; 6 | 7 | ////////////////////////////// 8 | // Improve Card & Popup Width 9 | ////////////////////////////// 10 | 11 | html, body { 12 | min-width: 400px; 13 | overflow-x: clip; 14 | scrollbar-width: thin; 15 | } 16 | 17 | .card { 18 | z-index: 0; 19 | border: none; 20 | border-radius: 0; 21 | } 22 | 23 | .card-header { 24 | font-weight: 600 !important; 25 | } 26 | 27 | .card-body { 28 | overflow-y: auto; 29 | max-width: 400px; 30 | } 31 | 32 | .card-body, .offcanvas-body { 33 | scrollbar-width: thin; 34 | } 35 | 36 | ////////////////////////////// 37 | // Form Styles 38 | ////////////////////////////// 39 | 40 | .form-label { 41 | margin-bottom: 0.1rem; 42 | font-weight: 600 !important; 43 | } 44 | 45 | .form-control { 46 | padding-top: 0.25rem; 47 | padding-bottom: 0.25rem; 48 | padding-left: 0.5rem; 49 | font-size: 0.875rem; 50 | } 51 | 52 | ////////////////////////////// 53 | // Error Toast 54 | ////////////////////////////// 55 | 56 | .toast { 57 | position: fixed; 58 | bottom: 1.5rem; 59 | left: 50%; 60 | transform: translate(-50%, 0px); 61 | z-index: 9999; 62 | } 63 | 64 | ////////////////////////////// 65 | // Fix Header Background 66 | ////////////////////////////// 67 | 68 | .card-header, .card-footer { 69 | background-color: #f7f7f7; 70 | } 71 | 72 | @media (prefers-color-scheme: dark) { 73 | .card-header, .card-footer { 74 | background-color: #1a1a1a; 75 | } 76 | 77 | .card-offcanvas-body { 78 | background-color: #1d1d1d; 79 | } 80 | 81 | .modal-backdrop { 82 | --bs-backdrop-opacity: 0.75; 83 | } 84 | } 85 | 86 | ////////////////////////////// 87 | // Fix Dark Theme 88 | ////////////////////////////// 89 | 90 | .bg-warning .btn-close { 91 | filter: unset; 92 | } 93 | -------------------------------------------------------------------------------- /extension/src/styles/bootstrap.css: -------------------------------------------------------------------------------- 1 | /* We need to go through an additional CSS file so Parcel does not duplicate Bootstrap styles */ 2 | @import "bootstrap.scss"; 3 | -------------------------------------------------------------------------------- /extension/src/styles/bootstrap.scss: -------------------------------------------------------------------------------- 1 | @use "sass:map"; 2 | 3 | @import "bootstrap/scss/_functions"; 4 | 5 | $color-mode-type: media-query; 6 | 7 | $gray-100-alt: #e1e1e1; 8 | $gray-200-alt: #cfcfcf; 9 | $gray-300-alt: #b1b1b1; 10 | $gray-400-alt: #9e9e9e; 11 | $gray-500-alt: #7e7e7e; 12 | $gray-600-alt: #626262; 13 | $gray-700-alt: #515151; 14 | $gray-800-alt: #3b3b3b; 15 | $gray-900-alt: #1d1d1d; 16 | $black-alt: #050505; 17 | 18 | $light-bg-subtle-dark: $gray-800-alt; 19 | $dark-bg-subtle-dark: mix($gray-800-alt, $black-alt); 20 | $light-border-subtle-dark: $gray-700-alt; 21 | $dark-border-subtle-dark: $gray-800-alt; 22 | $body-bg-dark: $gray-900-alt; 23 | $body-secondary-bg-dark: $gray-800-alt; 24 | $body-tertiary-bg-dark: mix($gray-800-alt, $gray-900-alt, 50%); 25 | $border-color-dark: $gray-700-alt; 26 | 27 | @import "bootstrap/scss/_variables"; 28 | @import "bootstrap/scss/_variables-dark"; 29 | 30 | $form-validation-states: map.remove($form-validation-states, "valid"); 31 | 32 | $form-validation-states: map.set($form-validation-states, "warning", ( 33 | "color": $yellow-300, 34 | "icon": "", 35 | "tooltip-color": #fff, 36 | "tooltip-bg-color": var(--#{$prefix}warning), 37 | "focus-box-shadow": 0 0 $input-btn-focus-blur $input-focus-width rgba(var(--#{$prefix}warning-rgb), $input-btn-focus-color-opacity), 38 | "border-color": $yellow-300, 39 | )); 40 | 41 | $kbd-color: $white; 42 | $kbd-bg: $gray-800; 43 | 44 | @import "bootstrap/scss/_maps"; 45 | @import "bootstrap/scss/_mixins"; 46 | @import "bootstrap/scss/_utilities"; 47 | @import "bootstrap/scss/_root"; 48 | @import "bootstrap/scss/_reboot"; 49 | 50 | @import "bootstrap/scss/_type"; 51 | //@import "bootstrap/scss/_images"; 52 | @import "bootstrap/scss/_containers"; 53 | @import "bootstrap/scss/_grid"; 54 | //@import "bootstrap/scss/_tables"; 55 | @import "bootstrap/scss/_forms"; 56 | @import "bootstrap/scss/_buttons"; 57 | @import "bootstrap/scss/_transitions"; 58 | @import "bootstrap/scss/_dropdown"; 59 | @import "bootstrap/scss/_button-group"; 60 | @import "bootstrap/scss/_nav"; 61 | //@import "bootstrap/scss/_navbar"; 62 | @import "bootstrap/scss/_card"; 63 | //@import "bootstrap/scss/_accordion"; 64 | //@import "bootstrap/scss/_breadcrumb"; 65 | //@import "bootstrap/scss/_pagination"; 66 | @import "bootstrap/scss/_badge"; 67 | //@import "bootstrap/scss/_alert"; 68 | //@import "bootstrap/scss/_progress"; 69 | @import "bootstrap/scss/_list-group"; 70 | @import "bootstrap/scss/_close"; 71 | @import "bootstrap/scss/_toasts"; 72 | @import "bootstrap/scss/_modal"; 73 | //@import "bootstrap/scss/_tooltip"; 74 | //@import "bootstrap/scss/_popover"; 75 | //@import "bootstrap/scss/_carousel"; 76 | //@import "bootstrap/scss/_spinners"; 77 | @import "bootstrap/scss/_offcanvas"; 78 | //@import "bootstrap/scss/_placeholders"; 79 | 80 | @import "bootstrap/scss/_helpers"; 81 | 82 | @import "bootstrap/scss/utilities/_api"; 83 | -------------------------------------------------------------------------------- /extension/src/utils/errors.js: -------------------------------------------------------------------------------- 1 | import Toast from 'bootstrap/js/src/toast' 2 | 3 | function displayError (error) { 4 | document.getElementById('error-text').innerText = error.message 5 | Toast.getOrCreateInstance(document.getElementById('error-toast')).show() 6 | } 7 | 8 | window.addEventListener('error', function (error) { 9 | displayError(error.error) 10 | return false 11 | }) 12 | 13 | window.addEventListener('unhandledrejection', function (error) { 14 | displayError(error.reason) 15 | return false 16 | }) 17 | -------------------------------------------------------------------------------- /extension/src/utils/i18nHtml.js: -------------------------------------------------------------------------------- 1 | import * as DOMPurify from 'dompurify' 2 | 3 | import { EVENT_LOCALIZATION_READY } from '../utils' 4 | import { getCurrentLocale, getMessage } from './i18n' 5 | 6 | const allowedTags = ['em', 'strong', 'kbd', 'code', 'a'] 7 | 8 | async function applyElementLocalization (element) { 9 | for (const { name: attribute, value: key } of element.attributes) { 10 | // Support for translating the main content 11 | if (attribute === 'data-i18n' && key) { 12 | const message = await getMessage(key) 13 | if (message) applyMessage(element, message) 14 | } 15 | 16 | if (!attribute.startsWith('data-i18n-') || !key) continue 17 | 18 | // Support for translating arbitrary attributes 19 | const target = attribute.replace('data-i18n-', '') 20 | const message = await getMessage(key) 21 | if (message) element.setAttribute(target, message) 22 | } 23 | } 24 | 25 | function applyMessage (element, message) { 26 | // Set the element content for messages without HTML 27 | if (message.indexOf('<') === -1) { 28 | element.textContent = message 29 | return 30 | } 31 | 32 | // Sanitize the HTML message and set it to the element 33 | const sanitized = DOMPurify.sanitize(message, { 34 | RETURN_DOM_FRAGMENT: true, 35 | ALLOWED_TAGS: allowedTags, 36 | ADD_ATTR: ['target'] 37 | }) 38 | element.replaceChildren(sanitized) 39 | } 40 | 41 | ;(async function () { 42 | await Promise.all(Array.prototype.map.call(document.querySelectorAll('[data-i18n]'), element => applyElementLocalization(element))) 43 | document.querySelector('html').setAttribute('lang', await getCurrentLocale()) 44 | document.dispatchEvent(new Event(EVENT_LOCALIZATION_READY)) 45 | })() 46 | -------------------------------------------------------------------------------- /extension/tools/icons/_css.hbs: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: "{{ name }}"; 3 | src: {{{ fontSrc }}}; 4 | } 5 | 6 | .{{prefix}}::before, 7 | [class^="{{prefix}}-"]::before, 8 | [class*=" {{prefix}}-"]::before { 9 | display: inline-block; 10 | font-display: block; 11 | font-family: {{ name }} !important; 12 | font-style: normal; 13 | font-weight: normal !important; 14 | font-variant: normal; 15 | text-transform: none; 16 | line-height: 1; 17 | vertical-align: -.125em; 18 | -webkit-font-smoothing: antialiased; 19 | -moz-osx-font-smoothing: grayscale; 20 | } 21 | 22 | {{# each codepoints }} 23 | .{{ ../prefix }}-{{ @key }}::before { content: "\\{{ codepoint this }}"; } 24 | {{/ each }} 25 | -------------------------------------------------------------------------------- /extension/tools/icons/generate.js: -------------------------------------------------------------------------------- 1 | const { generateFonts } = require('@twbs/fantasticon') 2 | 3 | const path = require('path') 4 | const fs = require('fs') 5 | const os = require('os') 6 | 7 | const packageJson = require('../../package.json') 8 | const targetDir = path.join(__dirname, '../../src/icons/') 9 | 10 | const bootstrapSvgs = path.join(__dirname, '../../node_modules/bootstrap-icons/icons/') 11 | const selectedIcons = fs.mkdtempSync(path.join(os.tmpdir(), 'ffpwa-bootstrap-icons')) 12 | const bootstrapCodepoints = require('bootstrap-icons/font/bootstrap-icons.json') 13 | const selectedCodepoints = {} 14 | 15 | async function generateIcons () { 16 | for (const icon of packageJson.icons) { 17 | await fs.promises.copyFile(path.join(bootstrapSvgs, `${icon}.svg`), path.join(selectedIcons, `${icon}.svg`)) 18 | selectedCodepoints[icon] = bootstrapCodepoints[icon] 19 | } 20 | 21 | if (!fs.existsSync(targetDir)) { 22 | fs.mkdirSync(targetDir, { recursive: true }) 23 | } 24 | 25 | await generateFonts({ 26 | name: 'bootstrap-icons', 27 | fontTypes: ['woff2'], 28 | assetTypes: ['css'], 29 | prefix: 'bi', 30 | selector: '.bi', 31 | codepoints: selectedCodepoints, 32 | inputDir: selectedIcons, 33 | outputDir: targetDir, 34 | templates: { css: path.join(__dirname, './_css.hbs') } 35 | }) 36 | } 37 | 38 | generateIcons().finally(() => { 39 | fs.rmSync(selectedIcons, { recursive: true }) 40 | }) 41 | -------------------------------------------------------------------------------- /extension/tools/set-version.js: -------------------------------------------------------------------------------- 1 | const { execSync } = require('child_process') 2 | const { accessSync, constants: { F_OK, R_OK, W_OK }, readFileSync, writeFileSync } = require('fs') 3 | 4 | const packagePath = './package.json' 5 | const manifestPath = './src/manifest.json' 6 | const neededPermissions = F_OK | R_OK | W_OK 7 | 8 | let pwaVersion 9 | 10 | if (process.argv.length >= 3) { 11 | // Use the first argument as a version 12 | pwaVersion = process.argv[2].toString() 13 | } else { 14 | try { 15 | // No argument, try to find a version from git 16 | pwaVersion = execSync('git describe --tags --abbrev=0').toString() 17 | } catch (err) { 18 | console.error('No version provided and unable to retrieve git tag') 19 | throw err 20 | } 21 | } 22 | 23 | // Remove single leading v if it exists 24 | pwaVersion = pwaVersion.trim().replace(/^v(.+)$/, '$1') 25 | 26 | try { 27 | accessSync(packagePath, neededPermissions) 28 | accessSync(manifestPath, neededPermissions) 29 | } catch (err) { 30 | console.error('Unable to access ./package.json and ./src/manifest.json') 31 | throw err 32 | } 33 | 34 | console.error(`Editing package.json version: '${pwaVersion}'`) 35 | let packageText = readFileSync(packagePath).toString() 36 | const packageJson = JSON.parse(packageText) 37 | packageJson.version = pwaVersion 38 | packageText = JSON.stringify(packageJson, null, 2) 39 | writeFileSync(packagePath, packageText) 40 | 41 | console.error(`Editing manifest.json version: '${pwaVersion}'`) 42 | let manifestText = readFileSync(manifestPath).toString() 43 | const manifestJson = JSON.parse(manifestText) 44 | manifestJson.version = pwaVersion 45 | manifestText = JSON.stringify(manifestJson, null, 2) 46 | writeFileSync(manifestPath, manifestText) 47 | -------------------------------------------------------------------------------- /extension/tools/transformers/filename.js: -------------------------------------------------------------------------------- 1 | const { Transformer } = require('@parcel/plugin') 2 | 3 | module.exports = new Transformer({ 4 | async transform ({ asset }) { 5 | asset.setCode('undefined') 6 | asset.type = 'raw' 7 | asset.sideEffects = false 8 | asset.isBundleSplittable = true 9 | asset.bundleBehavior = 'inline' 10 | return [asset] 11 | } 12 | }) 13 | -------------------------------------------------------------------------------- /extension/tools/transformers/locale.js: -------------------------------------------------------------------------------- 1 | const { Transformer } = require('@parcel/plugin') 2 | const json5 = require('json5') 3 | 4 | module.exports = new Transformer({ 5 | async transform ({ asset }) { 6 | const messages = json5.parse(await asset.getCode()) 7 | 8 | for (const key in messages) { 9 | // Remove unnecessary message descriptions 10 | delete messages[key].description 11 | 12 | // Remove unnecessary placeholder examples 13 | for (const placeholder in messages[key].placeholders) delete messages[key].placeholders[placeholder].example 14 | } 15 | 16 | asset.setCode(JSON.stringify(messages)) 17 | asset.type = 'json' 18 | 19 | return [asset] 20 | } 21 | }) 22 | -------------------------------------------------------------------------------- /extension/tools/transformers/treeshake.js: -------------------------------------------------------------------------------- 1 | const { Transformer } = require('@parcel/plugin') 2 | 3 | const allowed = require('../../package.json').messages 4 | 5 | module.exports = new Transformer({ 6 | async transform ({ asset }) { 7 | const messages = JSON.parse(await asset.getCode()) 8 | 9 | // Remove all messages except ones used by the standard localization system 10 | for (const key in messages) { 11 | if (!allowed.includes(key)) { 12 | delete messages[key] 13 | } 14 | } 15 | 16 | asset.setCode(JSON.stringify(messages)) 17 | asset.type = 'json' 18 | 19 | return [asset] 20 | } 21 | }) 22 | -------------------------------------------------------------------------------- /native/assets/Metropolis-SemiBold.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/filips123/PWAsForFirefox/71826df22c8d6203158181fae60636b68edd40c0/native/assets/Metropolis-SemiBold.otf -------------------------------------------------------------------------------- /native/assets/icon-mask-macos.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/filips123/PWAsForFirefox/71826df22c8d6203158181fae60636b68edd40c0/native/assets/icon-mask-macos.png -------------------------------------------------------------------------------- /native/assets/icon-shadow-macos.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/filips123/PWAsForFirefox/71826df22c8d6203158181fae60636b68edd40c0/native/assets/icon-shadow-macos.png -------------------------------------------------------------------------------- /native/build.rs: -------------------------------------------------------------------------------- 1 | use cfg_aliases::cfg_aliases; 2 | use clap::CommandFactory; 3 | use clap_complete::{generate_to, Shell}; 4 | 5 | #[path = "src/console/app.rs"] 6 | mod app; 7 | 8 | fn main() { 9 | let out = std::path::PathBuf::from(std::env::var("OUT_DIR").unwrap()); 10 | let target = out.ancestors().nth(3).unwrap().to_owned(); 11 | let completions = target.join("completions"); 12 | 13 | std::fs::create_dir_all(&completions).unwrap(); 14 | 15 | let mut app = app::App::command(); 16 | generate_to(Shell::Bash, &mut app, env!("CARGO_PKG_NAME"), &completions).unwrap(); 17 | generate_to(Shell::Elvish, &mut app, env!("CARGO_PKG_NAME"), &completions).unwrap(); 18 | generate_to(Shell::Fish, &mut app, env!("CARGO_PKG_NAME"), &completions).unwrap(); 19 | generate_to(Shell::PowerShell, &mut app, env!("CARGO_PKG_NAME"), &completions).unwrap(); 20 | generate_to(Shell::Zsh, &mut app, env!("CARGO_PKG_NAME"), &completions).unwrap(); 21 | 22 | cfg_aliases! { 23 | platform_windows: { target_os = "windows" }, 24 | platform_linux: { target_os = "linux" }, 25 | platform_macos: { target_os = "macos" }, 26 | platform_bsd: { any(target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd") }, 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /native/manifests/bsd.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "firefoxpwa", 3 | "description": "The native part of the PWAsForFirefox project", 4 | "path": "/usr/local/libexec/firefoxpwa-connector", 5 | "type": "stdio", 6 | "allowed_extensions": [ 7 | "firefoxpwa@filips.si" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /native/manifests/linux.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "firefoxpwa", 3 | "description": "The native part of the PWAsForFirefox project", 4 | "path": "/usr/libexec/firefoxpwa-connector", 5 | "type": "stdio", 6 | "allowed_extensions": [ 7 | "firefoxpwa@filips.si" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /native/manifests/macos.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "firefoxpwa", 3 | "description": "The native part of the PWAsForFirefox project", 4 | "path": "/usr/local/libexec/firefoxpwa-connector", 5 | "type": "stdio", 6 | "allowed_extensions": [ 7 | "firefoxpwa@filips.si" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /native/manifests/windows.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "firefoxpwa", 3 | "description": "The native part of the PWAsForFirefox project", 4 | "path": "firefoxpwa-connector.exe", 5 | "type": "stdio", 6 | "allowed_extensions": [ 7 | "firefoxpwa@filips.si" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /native/packages/appstream/si.filips.FirefoxPWA.metainfo.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | si.filips.FirefoxPWA 4 | 5 | Progressive Web Apps for Firefox 6 | A tool to install, manage and use Progressive Web Apps (PWAs) in Mozilla Firefox (native component) 7 | 8 | https://pwasforfirefox.filips.si/ 9 | https://github.com/filips123/PWAsForFirefox/issues 10 | https://pwasforfirefox.filips.si/help/faq/ 11 | https://pwasforfirefox.filips.si/ 12 | https://github.com/filips123/PWAsForFirefox?sponsor=1 13 | https://crowdin.com/project/firefoxpwa 14 | https://github.com/filips123/PWAsForFirefox 15 | https://github.com/filips123/PWAsForFirefox/blob/main/.github/CONTRIBUTING.md 16 | 17 | filips 18 | 19 | CC0-1.0 20 | MPL-2.0 21 | 22 | si.filips.FirefoxPWA 23 | 24 | 25 |

Progressive Web Apps (PWAs) are web apps that use web APIs and features along with progressive enhancement strategy to bring a native app-like user experience to cross-platform web applications. Although Firefox supports many of Progressive Web App APIs, it does not support functionality to install them as a standalone system app with an app-like experience.

26 |

This project creates a custom modified Firefox runtime to allow websites to be installed as standalone apps and provides a console tool and browser extension to install, manage and use them.

27 |

This package contains only the native part of the PWAsForFirefox project. You should also install the browser extension if you haven't already. You can download it from <https://addons.mozilla.org/firefox/addon/pwas-for-firefox/>.

28 |
29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | Network 44 | Utility 45 | WebBrowser 46 | 47 | 48 | 49 | pwas for firefox 50 | firefoxpwa 51 | firefox 52 | progressive web app 53 | webapp 54 | web 55 | 56 | 57 | 58 | firefoxpwa 59 | 60 | 61 | 62 | org.mozilla.firefox 63 | always 64 | 65 | 66 | 67 | pointing 68 | keyboard 69 | touch 70 | 71 | 72 | 73 | #fe6d45 74 | #cc3e46 75 | 76 | 77 | 78 | [(254, 109, 69), (204, 62, 70)] 79 | 80 |
81 | -------------------------------------------------------------------------------- /native/packages/appstream/si.filips.FirefoxPWA.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /native/packages/brew/configure.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | 4 | # Internal script used by Homebrew formula to prepare project for Homebrew installation 5 | # Needs to be run in the "native" directory of the repository 6 | # Usage: ./configure.sh {VERSION} {BIN} {LIBEXEC} 7 | 8 | if [ "$#" -ne 3 ]; then 9 | echo "Usage: $0 {VERSION} {BIN} {LIBEXEC}" > /dev/stderr 10 | exit 1 11 | fi 12 | 13 | VERSION=$1 14 | BIN=$2 15 | LIBEXEC=$3 16 | 17 | # Set the correct version in the source files 18 | sed -i"" -e "s/version = \"0.0.0\"/version = \"$VERSION\"/g" Cargo.toml 19 | sed -i"" -e "s/DISTRIBUTION_VERSION = '0.0.0'/DISTRIBUTION_VERSION = '$VERSION'/g" userchrome/profile/chrome/pwa/chrome.sys.mjs 20 | 21 | # Set the path in the manifest to the Homebrew libexec directory 22 | cp manifests/macos.json manifests/brew.json 23 | sed -i"" -e "s@/usr/local/libexec/firefoxpwa-connector@$LIBEXEC/firefoxpwa-connector@g" manifests/brew.json 24 | -------------------------------------------------------------------------------- /native/packages/choco/firefoxpwa.nuspec: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | firefoxpwa 6 | Progressive Web Apps for Firefox 7 | {{PackageVersion}} 8 | filips 9 | filips 10 | 11 | https://github.com/filips123/PWAsForFirefox/tree/main/native/packages/choco 12 | https://github.com/filips123/PWAsForFirefox 13 | https://pwasforfirefox.filips.si/ 14 | https://github.com/filips123/PWAsForFirefox/issues 15 | https://pwasforfirefox.filips.si/ 16 | https://github.com/filips123/PWAsForFirefox/blob/main/LICENSE 17 | https://rawcdn.githack.com/wiki/filips123/PWAsForFirefox/images/icon.png 18 | false 19 | 20 | pwas-for-firefox firefoxpwa firefox progressive-web-app webapp web foss open-source cross-platform 21 | You can read release notes on [the repository releases page](https://github.com/filips123/PWAsForFirefox/releases/tag/v{{PackageVersion}}). 22 | A tool to install, manage and use Progressive Web Apps (PWAs) in Mozilla Firefox (native component) 23 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /native/packages/choco/legal/LICENSE.txt: -------------------------------------------------------------------------------- 1 | From: https://github.com/filips123/PWAsForFirefox#license 2 | 3 | LICENSE 4 | 5 | The project is licensed under the Mozilla Public License 2.0 . 6 | By using, redistributing, or modifying it, you must agree to the license, and the additional 7 | clauses provided below. See the LICENSE file 8 | in the project repository for the full MPL 2.0 license text. 9 | 10 | The project uses additional third-party assets and code: 11 | 12 | - The project logo is based on the "Fox SVG Vector" icon 13 | and the community-introduced PWA logo , 14 | both dedicated to the public domain using CC0 . 15 | 16 | - Browser chrome modifications were inspired by and partially derived from the 17 | `xiaoxiaoflood/firefox-scripts` 18 | repository on GitHub, licensed under the Mozilla Public License 2.0. Detailed 19 | information can be found in the respective files. 20 | 21 | - Browser chrome modifications partially use code derived from the `black7375/Firefox-UI-Fix` 22 | repository on GitHub, licensed under the 23 | Mozilla Public License 2.0. Detailed information can be found in the respective files. 24 | 25 | - Browser chrome modifications partially use code and icons derived from the original 26 | Firefox source , licensed under the Mozilla Public 27 | License 2.0. Detailed information can be found in the respective files. 28 | 29 | - Native programs contain the Metropolis Semi Bold typeface 30 | by Chris Simpson, released into the public domain using the Unlicense . 31 | 32 | - Windows installer contains Bootstrap Icons , licensed under the 33 | MIT License . Detailed license information can be found in 34 | the WiX configuration file . 35 | 36 | Additional open source software will be downloaded and installed at runtime when initiated by the user: 37 | 38 | - Installing the runtime on Windows will install 7-Zip if it is not 39 | already installed. The 7-Zip project is made by Igor Pavlov and licensed under the GNU 40 | LGPL license and others . This project is not affiliated 41 | with the 7-Zip project or its developers in any way. 42 | 43 | - Installing the runtime on any system will download the unmodified Mozilla Firefox 44 | , and locally modify it. By using this project you also 45 | agree to the Firefox Privacy Notice . Firefox is 46 | licensed under the Mozilla Public License 2.0. Firefox and the Firefox logo are trademarks 47 | of the Mozilla Foundation in the U.S. and other countries. This project is not affiliated 48 | with the Mozilla Foundation in any way. 49 | -------------------------------------------------------------------------------- /native/packages/choco/legal/VERIFICATION.txt: -------------------------------------------------------------------------------- 1 | VERIFICATION 2 | 3 | I am the author of the PWAsForFirefox project. 4 | 5 | Both Windows installers are built automatically on every release using GitHub Actions from 6 | the project source, published as workflow artifacts, and then added to a new GitHub release. 7 | After the installers are built, another automated script downloads installers from artifacts, 8 | automatically updates the version in the `.nuspec` file, calculates installer checksums and 9 | saves them into this file, and publishes a new version to Chocolatey. 10 | 11 | Installer checksums can be verified by downloading MSI installers from the correct GitHub 12 | release on the repository, generating their SHA-256 checksums (for example, using sha256sum), 13 | and comparing them to the checksums provided below. 14 | 15 | The source for building and publishing process can be found in the GitHub Actions workflow 16 | file for the native component in the GitHub repository. 17 | 18 | INSTALLER CHECKSUMS 19 | 20 | -------------------------------------------------------------------------------- /native/packages/choco/tools/chocolateyinstall.ps1: -------------------------------------------------------------------------------- 1 | $ErrorActionPreference = "Stop"; 2 | 3 | $toolsPath = Split-Path $MyInvocation.MyCommand.Definition 4 | $filePath32 = "$toolsPath\firefoxpwa-$($env:ChocolateyPackageVersion)-x86.msi" 5 | $filePath64 = "$toolsPath\firefoxpwa-$($env:ChocolateyPackageVersion)-x86_64.msi" 6 | 7 | $packageArgs = @{ 8 | PackageName = "$($env:ChocolateyPackageName)" 9 | SoftwareName = "$($env:ChocolateyPackageTitle)" 10 | FileType = "msi" 11 | SilentArgs = "/quiet" 12 | File = $filePath32 13 | File64 = $filePath64 14 | } 15 | 16 | Install-ChocolateyInstallPackage @packageArgs 17 | Remove-Item -Force $filePath32, $filePath64 -ea 0 18 | -------------------------------------------------------------------------------- /native/packages/deb/description: -------------------------------------------------------------------------------- 1 | Progressive Web Apps (PWAs) are web apps that use web APIs and features along 2 | with progressive enhancement strategy to bring a native app-like user 3 | experience to cross-platform web applications. Although Firefox supports many 4 | of Progressive Web App APIs, it does not support functionality to install them 5 | as a standalone system app with an app-like experience. 6 | 7 | This project creates a custom modified Firefox runtime to allow websites to be 8 | installed as standalone apps and provides a console tool and browser extension 9 | to install, manage and use them. 10 | 11 | This package contains only the native part of the PWAsForFirefox project. You 12 | should also install the browser extension if you haven't already. You can 13 | download it from . 14 | -------------------------------------------------------------------------------- /native/packages/deb/postinst: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | # Duplicate the manifest file over lib and lib64 directories 5 | # Needed because otherwise the manifest may not be detected on some platforms 6 | # Needed here because packaging it normally breaks when lib64 is a symlink to lib 7 | mkdir -p /usr/lib64/mozilla/native-messaging-hosts || true 8 | cp -f /usr/lib/mozilla/native-messaging-hosts/firefoxpwa.json /usr/lib64/mozilla/native-messaging-hosts/firefoxpwa.json || true 9 | 10 | # Make shell completions executable 11 | # We cannot do this in Cargo.toml because of cargo-deb bug 12 | # See: https://github.com/mmstick/cargo-deb/issues/67 13 | chmod 755 /usr/share/bash-completion/completions/firefoxpwa 14 | chmod 755 /usr/share/fish/vendor_completions.d/firefoxpwa.fish 15 | chmod 755 /usr/share/zsh/vendor-completions/_firefoxpwa 16 | 17 | # Add notice that it is recommended to also install the extension 18 | if [ -z "$2" ] 19 | then 20 | echo "You have successfully installed the native part of the PWAsForFirefox project" 21 | echo "You should also install the Firefox extension if you haven't already" 22 | echo "Download: https://addons.mozilla.org/firefox/addon/pwas-for-firefox/" 23 | fi 24 | -------------------------------------------------------------------------------- /native/packages/deb/postrm: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | # Remove the duplicated manifest files 5 | if [ "$1" = "remove" ] 6 | then 7 | rm /usr/lib/mozilla/native-messaging-hosts/firefoxpwa.json || true 8 | rm /usr/lib64/mozilla/native-messaging-hosts/firefoxpwa.json || true 9 | fi 10 | 11 | # Add warning that runtime, profiles and web apps are still installed 12 | if [ "$1" = "remove" ] 13 | then 14 | echo "Runtime, profiles and web apps are still installed in user directories" 15 | echo "You can remove them manually after this package is uninstalled" 16 | echo "Doing that will remove all installed web apps and their data" 17 | fi 18 | -------------------------------------------------------------------------------- /native/packages/paf/PWAsForFirefoxHelpers/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "firefoxpwa-background" 3 | version = "0.0.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [profile.release] 8 | codegen-units = 1 9 | lto = true 10 | 11 | [dependencies] 12 | tauri-winrt-notification = { version = "0.6.0", default-features = false } 13 | trayicon = { version = "0.2.0", default-features = false } 14 | winit = { version = "0.30.5", default-features = false } 15 | -------------------------------------------------------------------------------- /native/packages/paf/PWAsForFirefoxHelpers/src/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/filips123/PWAsForFirefox/71826df22c8d6203158181fae60636b68edd40c0/native/packages/paf/PWAsForFirefoxHelpers/src/icon.ico -------------------------------------------------------------------------------- /native/packages/paf/PWAsForFirefoxHelpers/src/main.rs: -------------------------------------------------------------------------------- 1 | #![windows_subsystem = "windows"] 2 | 3 | use tauri_winrt_notification::Toast; 4 | use trayicon::{MenuBuilder, TrayIconBuilder}; 5 | use winit::event::Event; 6 | use winit::event_loop::{ControlFlow, EventLoop}; 7 | 8 | #[derive(Clone, Eq, PartialEq)] 9 | enum Events { 10 | Exit, 11 | ShowMenu, 12 | } 13 | 14 | #[allow(deprecated)] 15 | fn main() { 16 | let events = EventLoop::::with_user_event().build().unwrap(); 17 | let proxy = events.create_proxy(); 18 | let icon = include_bytes!("icon.ico"); 19 | 20 | let sender = move |event: &Events| { 21 | let _ = proxy.send_event(event.clone()); 22 | }; 23 | 24 | Toast::new(Toast::POWERSHELL_APP_ID) 25 | .title("PWAsForFirefox Portable is running") 26 | .text1("It is now possible to use it from the extension") 27 | .text2("You can stop the program from the tray menu") 28 | .show() 29 | .unwrap_or_default(); 30 | 31 | let mut tray = TrayIconBuilder::new() 32 | .sender(sender) 33 | .icon_from_buffer(icon) 34 | .tooltip("PWAsForFirefox") 35 | .menu(MenuBuilder::new().item("E&xit", Events::Exit)) 36 | .on_click(Events::ShowMenu) 37 | .on_right_click(Events::ShowMenu) 38 | .build() 39 | .unwrap(); 40 | 41 | events 42 | .run(move |event, target| { 43 | target.set_control_flow(ControlFlow::Wait); 44 | let _ = tray; 45 | 46 | if let Event::UserEvent(event) = event { 47 | match event { 48 | Events::Exit => target.exit(), 49 | Events::ShowMenu => tray.show_menu().unwrap(), 50 | } 51 | } 52 | }) 53 | .unwrap(); 54 | } 55 | -------------------------------------------------------------------------------- /native/packages/paf/PWAsForFirefoxPortable/App/AppInfo/EULA.txt: -------------------------------------------------------------------------------- 1 | The project is licensed under the Mozilla Public License 2.0 . By using, redistributing, or modifying it, you must agree to the license, and the additional clauses provided below. See the LICENSE file in the project repository for the full MPL 2.0 license text. 2 | 3 | The project uses additional third-party assets and code: 4 | 5 | - The project logo is based on the "Fox SVG Vector" icon and the community-introduced PWA logo , both dedicated to the public domain using CC0 . 6 | 7 | - Browser chrome modifications were inspired by and partially derived from the `xiaoxiaoflood/firefox-scripts` repository on GitHub, licensed under the Mozilla Public License 2.0. Detailed information can be found in the respective files. 8 | 9 | - Browser chrome modifications partially use code derived from the `black7375/Firefox-UI-Fix` repository on GitHub, licensed under the Mozilla Public License 2.0. Detailed information can be found in the respective files. 10 | 11 | - Browser chrome modifications partially use code and icons derived from the original Firefox source , licensed under the Mozilla Public License 2.0. Detailed information can be found in the respective files. 12 | 13 | - Native programs contain the Metropolis Semi Bold typeface by Chris Simpson, released into the public domain using the Unlicense . 14 | 15 | - Windows installer contains Bootstrap Icons , licensed under the MIT License . Detailed license information can be found in the WiX configuration file . 16 | 17 | Additional open source software will be downloaded if you continue with the installation: 18 | 19 | - Proceeding with the installation will automatically download the unmodified Mozilla Firefox , and locally modify it. By using this project you also agree to the Firefox Privacy Notice . Firefox is licensed under the Mozilla Public License 2.0. Firefox and the Firefox logo are trademarks of the Mozilla Foundation in the U.S. and other countries. This project is not affiliated with the Mozilla Foundation in any way. 20 | -------------------------------------------------------------------------------- /native/packages/paf/PWAsForFirefoxPortable/App/AppInfo/Launcher/PWAsForFirefoxPortable.ini: -------------------------------------------------------------------------------- 1 | [Launch] 2 | ProgramExecutable=PWAsForFirefox/firefoxpwa-background.exe 3 | SinglePortableAppInstance=true 4 | SingleAppInstance=true 5 | 6 | [Activate] 7 | Registry=true 8 | 9 | [RegistryKeys] 10 | -=HKCU\Software\Mozilla\NativeMessagingHosts\firefoxpwa 11 | 12 | [RegistryValueWrite] 13 | HKCU\Software\Mozilla\NativeMessagingHosts\firefoxpwa\=REG_SZ:%PAL:AppDir%\PWAsForFirefox\firefoxpwa.json 14 | 15 | [RegistryCleanupIfEmpty] 16 | 1=HKCU\Software\filips\FirefoxPWA 17 | 2=HKCU\Software\filips 18 | -------------------------------------------------------------------------------- /native/packages/paf/PWAsForFirefoxPortable/App/AppInfo/appicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/filips123/PWAsForFirefox/71826df22c8d6203158181fae60636b68edd40c0/native/packages/paf/PWAsForFirefoxPortable/App/AppInfo/appicon.ico -------------------------------------------------------------------------------- /native/packages/paf/PWAsForFirefoxPortable/App/AppInfo/appicon_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/filips123/PWAsForFirefox/71826df22c8d6203158181fae60636b68edd40c0/native/packages/paf/PWAsForFirefoxPortable/App/AppInfo/appicon_128.png -------------------------------------------------------------------------------- /native/packages/paf/PWAsForFirefoxPortable/App/AppInfo/appicon_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/filips123/PWAsForFirefox/71826df22c8d6203158181fae60636b68edd40c0/native/packages/paf/PWAsForFirefoxPortable/App/AppInfo/appicon_16.png -------------------------------------------------------------------------------- /native/packages/paf/PWAsForFirefoxPortable/App/AppInfo/appicon_256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/filips123/PWAsForFirefox/71826df22c8d6203158181fae60636b68edd40c0/native/packages/paf/PWAsForFirefoxPortable/App/AppInfo/appicon_256.png -------------------------------------------------------------------------------- /native/packages/paf/PWAsForFirefoxPortable/App/AppInfo/appicon_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/filips123/PWAsForFirefox/71826df22c8d6203158181fae60636b68edd40c0/native/packages/paf/PWAsForFirefoxPortable/App/AppInfo/appicon_32.png -------------------------------------------------------------------------------- /native/packages/paf/PWAsForFirefoxPortable/App/AppInfo/appicon_75.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/filips123/PWAsForFirefox/71826df22c8d6203158181fae60636b68edd40c0/native/packages/paf/PWAsForFirefoxPortable/App/AppInfo/appicon_75.png -------------------------------------------------------------------------------- /native/packages/paf/PWAsForFirefoxPortable/App/AppInfo/appinfo.ini: -------------------------------------------------------------------------------- 1 | [Format] 2 | Type=PortableAppsFormat 3 | Version=3.7 4 | 5 | [Details] 6 | Name=PWAsForFirefox Portable 7 | AppId=PWAsForFirefoxPortable 8 | Publisher=filips 9 | Homepage=https://pwasforfirefox.filips.si/ 10 | Donate=https://github.com/filips123/PWAsForFirefox?sponsor=1 11 | Category=Internet 12 | Description=A tool to install, manage and use Progressive Web Apps (PWAs) in Mozilla Firefox (native component) 13 | Trademarks=Firefox and the Firefox logo are trademarks of the Mozilla Foundation in the U.S. and other countries. This project is not affiliated with the Mozilla Foundation in any way. 14 | Language=Multilingual 15 | 16 | [License] 17 | Shareable=true 18 | OpenSource=true 19 | Freeware=true 20 | CommercialUse=true 21 | 22 | [Version] 23 | PackageVersion=0.0.0.0 24 | DisplayVersion=0.0.0 25 | 26 | [Dependencies] 27 | Requires64bitOS=yes 28 | 29 | [Control] 30 | Icons=1 31 | Start=PWAsForFirefoxPortable.exe 32 | -------------------------------------------------------------------------------- /native/packages/paf/PWAsForFirefoxPortable/App/AppInfo/installer.ini: -------------------------------------------------------------------------------- 1 | [DirectoriesToPreserve] 2 | PreserveDirectory1=App\PWAsForFirefox\runtime 3 | 4 | [DownloadFiles] 5 | DownloadURL=https://download.mozilla.org/?product=firefox-latest-ssl&os=win64 6 | DownloadName=Mozilla Firefox 7 | DownloadFilename=firefox-setup.exe 8 | AdvancedExtract1To=App\PWAsForFirefox\extracted 9 | AdvancedExtract1Filter=core\** 10 | AdditionalInstallSize=60000 11 | -------------------------------------------------------------------------------- /native/packages/paf/PWAsForFirefoxPortable/Other/Help/Images/Donation_Button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/filips123/PWAsForFirefox/71826df22c8d6203158181fae60636b68edd40c0/native/packages/paf/PWAsForFirefoxPortable/Other/Help/Images/Donation_Button.png -------------------------------------------------------------------------------- /native/packages/paf/PWAsForFirefoxPortable/Other/Help/Images/Favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/filips123/PWAsForFirefox/71826df22c8d6203158181fae60636b68edd40c0/native/packages/paf/PWAsForFirefoxPortable/Other/Help/Images/Favicon.ico -------------------------------------------------------------------------------- /native/packages/paf/PWAsForFirefoxPortable/Other/Help/Images/Help_Background_Footer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/filips123/PWAsForFirefox/71826df22c8d6203158181fae60636b68edd40c0/native/packages/paf/PWAsForFirefoxPortable/Other/Help/Images/Help_Background_Footer.png -------------------------------------------------------------------------------- /native/packages/paf/PWAsForFirefoxPortable/Other/Help/Images/Help_Background_Header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/filips123/PWAsForFirefox/71826df22c8d6203158181fae60636b68edd40c0/native/packages/paf/PWAsForFirefoxPortable/Other/Help/Images/Help_Background_Header.png -------------------------------------------------------------------------------- /native/packages/paf/PWAsForFirefoxPortable/Other/Help/Images/Help_Logo_Top.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/filips123/PWAsForFirefox/71826df22c8d6203158181fae60636b68edd40c0/native/packages/paf/PWAsForFirefoxPortable/Other/Help/Images/Help_Logo_Top.png -------------------------------------------------------------------------------- /native/packages/paf/PWAsForFirefoxPortable/Other/Source/AppNamePortable.ini: -------------------------------------------------------------------------------- 1 | AdditionalParameters= 2 | DisableSplashScreen=false 3 | RunLocally=false 4 | 5 | # The above options are explained in the included launcher readme 6 | # This INI file is an example only and is not used unless it is placed as described in the included readme 7 | -------------------------------------------------------------------------------- /native/packages/paf/PWAsForFirefoxPortable/Other/Source/LauncherReadme.txt: -------------------------------------------------------------------------------- 1 | The base application's source code is available from the portable app's 2 | repository listed in the help.html file (if applicable). 3 | 4 | Details of most other things are available there as well. 5 | 6 | LICENSE 7 | ======= 8 | 9 | This package's installer and launcher are released under the GPL. The launcher 10 | is the PortableApps.com Launcher, available with full source and documentation 11 | from https://portableapps.com/development. We request that developers using the 12 | PortableApps.com Launcher please leave this directory intact and unchanged. 13 | 14 | USER CONFIGURATION 15 | ================== 16 | 17 | Some configuration in the PortableApps.com Launcher can be overridden by the 18 | user in an INI file next to PWAsForFirefoxPortable.exe called PWAsForFirefoxPortable.ini. 19 | If you are happy with the default options, it is not necessary, though. There 20 | is an example INI included with this package to get you started. To use it, 21 | copy AppNamePortable.ini from this directory to PWAsForFirefoxPortable.ini next 22 | to PWAsForFirefoxPortable.exe. The options in the INI file are as follows: 23 | 24 | AdditionalParameters= 25 | DisableSplashScreen=false 26 | RunLocally=false 27 | 28 | (There is no need for an INI header in this file; if you have one, though, it 29 | won't damage anything.) 30 | 31 | The AdditionalParameters entry allows you to pass additional command-line 32 | parameters to the application. 33 | 34 | The DisableSplashScreen entry allows you to run the launcher without the splash 35 | screen showing up. The default is false. 36 | 37 | The RunLocally entry allows you to run the portable application from a read- 38 | only medium. This is known as Live mode. It copies what it needs to a 39 | temporary directory on the host computer, runs the application, and then 40 | deletes it afterwards, leaving nothing behind. This can be useful for running 41 | the application from a CD or if you work on a computer that may have spyware or 42 | viruses and you'd like to keep your device set to read-only. As a consequence 43 | of this technique, any changes you make during the Live mode session aren't 44 | saved back to your device. The default is false. 45 | 46 | There may be other values also permitted in the user configuration file by the 47 | portable application; refer to help.html for any details of them. 48 | -------------------------------------------------------------------------------- /native/packages/paf/PWAsForFirefoxPortable/Other/Source/PortableApps.comInstallerCustom.nsh: -------------------------------------------------------------------------------- 1 | !include PortableApps.comInstallerMoveFiles.nsh 2 | 3 | !macro CustomCodePostInstall 4 | IfFileExists "$INSTDIR\App\PWAsForFirefox\runtime\firefox.exe" remove copy 5 | 6 | copy: 7 | ; Copy only if runtime does not exist yet 8 | CreateDirectory "$INSTDIR\App\PWAsForFirefox\runtime" 9 | ${MoveFiles} DIR+FORCE "*" "$INSTDIR\App\PWAsForFirefox\extracted\core" "$INSTDIR\App\PWAsForFirefox\runtime" 10 | 11 | remove: 12 | ; Then remove the temporary extracted directory 13 | RMDir /r "$INSTDIR\App\PWAsForFirefox\extracted" 14 | !macroend 15 | -------------------------------------------------------------------------------- /native/packages/wix/assets/banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/filips123/PWAsForFirefox/71826df22c8d6203158181fae60636b68edd40c0/native/packages/wix/assets/banner.png -------------------------------------------------------------------------------- /native/packages/wix/assets/dialog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/filips123/PWAsForFirefox/71826df22c8d6203158181fae60636b68edd40c0/native/packages/wix/assets/dialog.png -------------------------------------------------------------------------------- /native/packages/wix/assets/exclamation.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/filips123/PWAsForFirefox/71826df22c8d6203158181fae60636b68edd40c0/native/packages/wix/assets/exclamation.ico -------------------------------------------------------------------------------- /native/packages/wix/assets/information.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/filips123/PWAsForFirefox/71826df22c8d6203158181fae60636b68edd40c0/native/packages/wix/assets/information.ico -------------------------------------------------------------------------------- /native/packages/wix/assets/new.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/filips123/PWAsForFirefox/71826df22c8d6203158181fae60636b68edd40c0/native/packages/wix/assets/new.ico -------------------------------------------------------------------------------- /native/packages/wix/assets/product.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/filips123/PWAsForFirefox/71826df22c8d6203158181fae60636b68edd40c0/native/packages/wix/assets/product.ico -------------------------------------------------------------------------------- /native/packages/wix/assets/up.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/filips123/PWAsForFirefox/71826df22c8d6203158181fae60636b68edd40c0/native/packages/wix/assets/up.ico -------------------------------------------------------------------------------- /native/rustfmt.toml: -------------------------------------------------------------------------------- 1 | group_imports = "StdExternalCrate" 2 | imports_granularity = "Module" 3 | imports_layout = "HorizontalVertical" 4 | 5 | newline_style = "Unix" 6 | 7 | overflow_delimited_expr = true 8 | use_field_init_shorthand = true 9 | use_try_shorthand = true 10 | use_small_heuristics = "Max" 11 | -------------------------------------------------------------------------------- /native/scripts/manifest.scm: -------------------------------------------------------------------------------- 1 | !/usr/bin/env -S guix shell -m 2 | !# 3 | 4 | ;;; This is a manifest file for GNU Guix to provide all dependencies needed to build the project 5 | 6 | (use-modules (guix channels)) 7 | 8 | ;; NOTE(Krey): This is used to establish reproducibility, but I decided to not use it in this repo as it would require additional maintenance to update the commit over time. Kept in case it's needed in the future. 9 | ;; (list (channel 10 | ;; (name 'guix) 11 | ;; (url "https://git.savannah.gnu.org/git/guix.git") 12 | ;; (commit 13 | ;; "f1bfd9f1948a5ff336d737c0614b9a30c2bb3097") 14 | ;; (introduction 15 | ;; (make-channel-introduction 16 | ;; "9edb3f66fd807b096b48283debdcddccfea34bad" 17 | ;; (openpgp-fingerprint 18 | ;; "BBB0 2DDF 2CEA F6A8 0D1D E643 A2A0 6DF2 A33A 54FA"))))) 19 | 20 | (specifications->manifest (list 21 | "rust" 22 | "rust-cargo" 23 | ;; NOTE(Krey): clang can also be used if needed 24 | "gcc" 25 | "openssl" 26 | "rust-pkg-config" 27 | "pkg-config")) 28 | -------------------------------------------------------------------------------- /native/scripts/set-version.ds: -------------------------------------------------------------------------------- 1 | # Called from Makefile.toml, but could be run on its own 2 | # Sets version strings in configuration files to first argument or git tag 3 | 4 | fn replace_version 5 | content = set ${1} 6 | version_find = set ${2} 7 | pwa_ver = set ${3} 8 | quote_find = set ${4} 9 | version_find_length = strlen ${version_find} 10 | back_index = indexof ${content} ${version_find} 11 | back_index = calc ${back_index} + ${version_find_length} 12 | back = substring ${content} ${back_index} 13 | back_length = strlen ${back} 14 | front = substring ${content} -${back_length} 15 | quote_find_index = indexof ${back} ${quote_find} 16 | back = substring ${back} ${quote_find_index} 17 | content = concat ${front} ${pwa_ver} ${back} 18 | return ${content} 19 | end 20 | 21 | if ${1} 22 | # Use first argument 23 | pwa_ver = set ${1} 24 | else 25 | # No argument, find tag from git 26 | git_tag = exec git describe --tags --abbrev=0 27 | assert_eq ${git_tag.code} 0 "No version provided to task and unable to retrieve git tag" 28 | pwa_ver = trim ${git_tag.stdout} 29 | release git_tag 30 | unset git_tag 31 | end 32 | 33 | # Remove single leading v if it exists 34 | if starts_with ${pwa_ver} "v" 35 | pwa_ver = substring ${pwa_ver} 1 36 | end 37 | 38 | # Replace versions in files 39 | cargo_toml_path = set "./Cargo.toml" 40 | cargo_lock_path = set "./Cargo.lock" 41 | chrome_mjs_path = set "./userchrome/profile/chrome/pwa/chrome.sys.mjs" 42 | cargo_toml_exists = is_file ${cargo_toml_path} 43 | cargo_lock_exists = is_file ${cargo_lock_path} 44 | chrome_mjs_exists = is_file ${chrome_mjs_path} 45 | if ${cargo_toml_exists} and ${cargo_lock_exists} and ${chrome_mjs_exists} 46 | echo "Setting version in Cargo.toml to \"${pwa_ver}\"" 47 | cargo_toml = readfile ${cargo_toml_path} 48 | cargo_toml = replace_version ${cargo_toml} "# Version will be set by CI from the Git tag when building and releasing\nversion = \"" ${pwa_ver} "\"" 49 | writefile ${cargo_toml_path} ${cargo_toml} 50 | echo "Setting version in Cargo.lock to \"${pwa_ver}\"" 51 | cargo_lock = readfile ${cargo_lock_path} 52 | cargo_lock = replace_version ${cargo_lock} "name = \"firefoxpwa\"\nversion = \"" ${pwa_ver} "\"" 53 | writefile ${cargo_lock_path} ${cargo_lock} 54 | echo "Setting version in chrome.sys.mjs to \"${pwa_ver}\"" 55 | chrome_mjs = readfile ${chrome_mjs_path} 56 | chrome_mjs = replace_version ${chrome_mjs} "DISTRIBUTION_VERSION = '" ${pwa_ver} "'" 57 | writefile ${chrome_mjs_path} ${chrome_mjs} 58 | else 59 | assert_fail "Unable to locate ./Cargo.toml, ./Cargo.lock and ./userchrome/profile/chrome/pwa/chrome.sys.mjs" 60 | end 61 | -------------------------------------------------------------------------------- /native/src/bin/firefoxpwa-connector.rs: -------------------------------------------------------------------------------- 1 | use std::fs::OpenOptions; 2 | use std::process::exit; 3 | 4 | use anyhow::Result; 5 | use log::{error, LevelFilter}; 6 | use simplelog::{ColorChoice, CombinedLogger, Config, TermLogger, TerminalMode, WriteLogger}; 7 | 8 | #[rustfmt::skip] 9 | use firefoxpwa::{connector::Connection, directories::ProjectDirs}; 10 | 11 | fn main() -> Result<()> { 12 | let dirs = ProjectDirs::new()?; 13 | 14 | let debugmode = dirs.userdata.join("DEBUG").exists(); 15 | let loglevel = if debugmode { LevelFilter::Debug } else { LevelFilter::Warn }; 16 | 17 | let logfile = dirs.userdata.join("firefoxpwa.log"); 18 | let logfile = OpenOptions::new().create(true).append(true).open(logfile)?; 19 | 20 | CombinedLogger::init(vec![ 21 | TermLogger::new(loglevel, Config::default(), TerminalMode::Stderr, ColorChoice::Auto), 22 | WriteLogger::new(loglevel, Config::default(), logfile), 23 | ])?; 24 | 25 | if let Err(error) = Connection::start(&dirs, debugmode) { 26 | error!("{:?}", error); 27 | exit(1); 28 | } 29 | 30 | Ok(()) 31 | } 32 | -------------------------------------------------------------------------------- /native/src/bin/firefoxpwa.rs: -------------------------------------------------------------------------------- 1 | use std::process::exit; 2 | 3 | use anyhow::Result; 4 | use clap::Parser; 5 | use log::{error, LevelFilter}; 6 | use simplelog::{ColorChoice, Config, TermLogger, TerminalMode}; 7 | 8 | #[rustfmt::skip] 9 | use firefoxpwa::console::{App, Run}; 10 | 11 | fn main() -> Result<()> { 12 | TermLogger::init(LevelFilter::Info, Config::default(), TerminalMode::Mixed, ColorChoice::Auto)?; 13 | 14 | let app = App::parse(); 15 | if let Err(error) = app.run() { 16 | error!("{:?}", error); 17 | exit(1); 18 | } 19 | 20 | Ok(()) 21 | } 22 | -------------------------------------------------------------------------------- /native/src/components/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(platform_windows)] 2 | pub mod _7zip; 3 | 4 | pub mod profile; 5 | pub mod runtime; 6 | pub mod site; 7 | -------------------------------------------------------------------------------- /native/src/components/profile.rs: -------------------------------------------------------------------------------- 1 | use std::fs::{create_dir_all, remove_dir_all}; 2 | 3 | use anyhow::{Context, Result}; 4 | use fs_extra::dir::{copy, CopyOptions}; 5 | use log::info; 6 | use serde::{Deserialize, Serialize}; 7 | use ulid::Ulid; 8 | 9 | use crate::directories::ProjectDirs; 10 | 11 | #[non_exhaustive] 12 | #[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)] 13 | pub struct Profile { 14 | /// A profile ID. 15 | /// 16 | /// Stored as the ULID format. Unique for each profile 17 | /// and auto-generated when a profile is created. 18 | /// 19 | /// One profile with zero/nil ULID always exists and is treated 20 | /// as a default profile for all web apps. This profile cannot 21 | /// be completely removed. When trying to remove it, web apps 22 | /// and data will be cleared, but the profile will stay. 23 | pub ulid: Ulid, 24 | 25 | /// A profile name. 26 | pub name: Option, 27 | 28 | /// A profile description. 29 | pub description: Option, 30 | 31 | /// A list of web app IDs installed within this profile. 32 | #[serde(default)] 33 | pub sites: Vec, 34 | } 35 | 36 | impl Default for Profile { 37 | #[inline] 38 | fn default() -> Self { 39 | Self { 40 | ulid: Ulid::nil(), 41 | name: Some("Default".into()), 42 | description: Some("Default profile for all web apps".into()), 43 | sites: vec![], 44 | } 45 | } 46 | } 47 | 48 | impl Profile { 49 | #[inline] 50 | pub fn new(name: Option, description: Option) -> Self { 51 | Self { ulid: Ulid::new(), name, description, sites: vec![] } 52 | } 53 | 54 | pub fn patch(&self, dirs: &ProjectDirs) -> Result<()> { 55 | let source = dirs.sysdata.join("userchrome/profile"); 56 | let profile = dirs.userdata.join("profiles").join(self.ulid.to_string()); 57 | 58 | let mut options = CopyOptions::new(); 59 | options.content_only = true; 60 | options.overwrite = true; 61 | 62 | if !profile.exists() { 63 | info!("Creating a profile directory"); 64 | create_dir_all(&profile).context("Failed to create a profile directory")?; 65 | } 66 | 67 | info!("Patching the profile"); 68 | let _ = remove_dir_all(profile.join("startupCache")); 69 | let _ = remove_dir_all(profile.join("chrome/pwa")); 70 | copy(source, profile, &options).context("Failed to patch the profile")?; 71 | 72 | info!("Profile patched!"); 73 | Ok(()) 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /native/src/connector/response.rs: -------------------------------------------------------------------------------- 1 | use std::collections::BTreeMap; 2 | 3 | use serde::Serialize; 4 | use ulid::Ulid; 5 | 6 | use crate::components::profile::Profile; 7 | use crate::components::site::Site; 8 | use crate::storage::Config; 9 | 10 | /// TODO: Docs 11 | #[derive(Serialize, Debug, PartialEq, Clone)] 12 | #[serde(tag = "type", content = "data")] 13 | pub enum ConnectorResponse { 14 | /// Versions of the installed system components. 15 | SystemVersions { 16 | /// Version of the PWAsForFirefox native program. 17 | /// 18 | /// Always set. When using a development version, 19 | /// commonly set to `0.0.0`. 20 | firefoxpwa: Option, 21 | 22 | /// Version of the Firefox runtime. 23 | /// 24 | /// Only set if the runtime is installed. 25 | firefox: Option, 26 | 27 | /// Version of the 7-Zip program. 28 | /// 29 | /// Only set on Windows, and if 7-Zip is installed. 30 | /// May also be `0.0.0` if 7-Zip was located through 31 | /// the `PATH` environment variable. 32 | _7zip: Option, 33 | }, 34 | 35 | /// Config of the native program. 36 | Config(Config), 37 | 38 | /// Config of the native program has been set. 39 | ConfigSet, 40 | 41 | /// Runtime has been installed. 42 | RuntimeInstalled, 43 | 44 | /// Runtime has been uninstalled. 45 | RuntimeUninstalled, 46 | 47 | /// List of all installed web apps. 48 | SiteList(BTreeMap), 49 | 50 | /// Web app has been launched. 51 | SiteLaunched, 52 | 53 | /// Web app has been installed. 54 | SiteInstalled(Ulid), 55 | 56 | /// Web app has been uninstalled. 57 | SiteUninstalled, 58 | 59 | /// Web app has been updated. 60 | SiteUpdated, 61 | 62 | /// All web apps have been updated. 63 | AllSitesUpdated, 64 | 65 | /// List of all available profiles. 66 | ProfileList(BTreeMap), 67 | 68 | /// Profile has been created. 69 | ProfileCreated(Ulid), 70 | 71 | /// Profile has been removed. 72 | ProfileRemoved, 73 | 74 | /// Profile has been updated. 75 | ProfileUpdated, 76 | 77 | /// All profiles and runtime have been patched. 78 | AllProfilesPatched, 79 | 80 | /// Protocol handler has been registered. 81 | ProtocolHandlerRegistered, 82 | 83 | /// Protocol handler has been unregistered. 84 | ProtocolHandlerUnregistered, 85 | 86 | /// Something went wrong... 87 | Error(String), 88 | } 89 | -------------------------------------------------------------------------------- /native/src/console/mod.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | 3 | pub use crate::console::app::App; 4 | use crate::console::app::{ProfileCommand, RuntimeCommand, SiteCommand}; 5 | 6 | pub mod app; 7 | pub mod profile; 8 | pub mod runtime; 9 | pub mod site; 10 | 11 | /// Parses and stores `Option>` parameters. 12 | /// 13 | /// Rules: 14 | /// - `None` - Ignores the parameter and keeps its previous value. 15 | /// - `Some(None)` - Stores `None` and uses a default/manifest value. 16 | /// - `Some(value)` - Stores `Some(value)`. 17 | /// 18 | macro_rules! store_value { 19 | ($target:expr, $source:expr) => { 20 | match &$source { 21 | Some(value) => $target = value.to_owned(), 22 | None => {} 23 | }; 24 | }; 25 | } 26 | 27 | /// Parses and stores `Option>` parameters. 28 | /// 29 | /// This needs some weird parsing hacks to be compatible both with the 30 | /// Serve-based API and Clap CLI: Array with only an empty string is 31 | /// treated as `None`. 32 | /// 33 | /// Rules: 34 | /// - `None` - Ignores the parameter and keeps its previous value. 35 | /// - `Some(vec![""])` -> Stores `None` and uses a default/manifest value. 36 | /// - `Some(vec![a, b, c])` - Stores `Some(vec![a, b, c])`. 37 | /// 38 | macro_rules! store_value_vec { 39 | ($target:expr, $source:expr) => { 40 | if let Some(source) = &$source { 41 | if source.len() == 1 && source.first() == Some(&"".into()) { 42 | $target = None; 43 | } else { 44 | $target = Some(source.to_vec()); 45 | } 46 | } 47 | }; 48 | } 49 | 50 | pub(in crate::console) use {store_value, store_value_vec}; 51 | 52 | pub trait Run { 53 | fn run(&self) -> Result<()>; 54 | } 55 | 56 | impl Run for App { 57 | #[inline] 58 | fn run(&self) -> Result<()> { 59 | match self { 60 | App::Site(cmd) => cmd.run(), 61 | App::Profile(cmd) => cmd.run(), 62 | App::Runtime(cmd) => cmd.run(), 63 | } 64 | } 65 | } 66 | 67 | impl Run for SiteCommand { 68 | #[inline] 69 | fn run(&self) -> Result<()> { 70 | match self { 71 | SiteCommand::Launch(cmd) => cmd.run(), 72 | SiteCommand::Install(cmd) => cmd.run(), 73 | SiteCommand::Uninstall(cmd) => cmd.run(), 74 | SiteCommand::Update(cmd) => cmd.run(), 75 | } 76 | } 77 | } 78 | 79 | impl Run for ProfileCommand { 80 | #[inline] 81 | fn run(&self) -> Result<()> { 82 | match self { 83 | ProfileCommand::List(cmd) => cmd.run(), 84 | ProfileCommand::Create(cmd) => cmd.run(), 85 | ProfileCommand::Remove(cmd) => cmd.run(), 86 | ProfileCommand::Update(cmd) => cmd.run(), 87 | } 88 | } 89 | } 90 | 91 | impl Run for RuntimeCommand { 92 | #[inline] 93 | fn run(&self) -> Result<()> { 94 | match self { 95 | RuntimeCommand::Install(cmd) => cmd.run(), 96 | RuntimeCommand::Uninstall(cmd) => cmd.run(), 97 | RuntimeCommand::Patch(cmd) => cmd.run(), 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /native/src/console/runtime.rs: -------------------------------------------------------------------------------- 1 | use anyhow::{Context, Result}; 2 | use cfg_if::cfg_if; 3 | 4 | use crate::components::runtime::Runtime; 5 | use crate::console::app::{RuntimeInstallCommand, RuntimePatchCommand, RuntimeUninstallCommand}; 6 | use crate::console::Run; 7 | use crate::directories::ProjectDirs; 8 | 9 | impl Run for RuntimeInstallCommand { 10 | #[cfg(not(feature = "immutable-runtime"))] 11 | fn run(&self) -> Result<()> { 12 | cfg_if! { 13 | if #[cfg(platform_windows)] { 14 | use log::warn; 15 | use crate::components::_7zip::_7Zip; 16 | 17 | let _7zip = _7Zip::new()?; 18 | if _7zip.version.is_none() { 19 | warn!("7-Zip is currently not installed and will be installed automatically"); 20 | warn!("You can remove it manually after the runtime is installed"); 21 | _7zip.install().context("Failed to install 7-Zip")?; 22 | } 23 | } 24 | } 25 | 26 | let dirs = ProjectDirs::new()?; 27 | let runtime = Runtime::new(&dirs)?; 28 | 29 | #[cfg(platform_linux)] 30 | if self.link { 31 | runtime.link().context("Failed to link runtime")? 32 | } else { 33 | runtime.install().context("Failed to install runtime")?; 34 | } 35 | 36 | #[cfg(not(platform_linux))] 37 | runtime.install().context("Failed to install runtime")?; 38 | 39 | let runtime = Runtime::new(&dirs)?; 40 | runtime.patch(&dirs, None)?; 41 | 42 | Ok(()) 43 | } 44 | 45 | #[cfg(feature = "immutable-runtime")] 46 | fn run(&self) -> Result<()> { 47 | anyhow::bail!("Cannot install runtime when the immutable runtime feature is enabled") 48 | } 49 | } 50 | 51 | impl Run for RuntimeUninstallCommand { 52 | #[cfg(not(feature = "immutable-runtime"))] 53 | fn run(&self) -> Result<()> { 54 | let dirs = ProjectDirs::new()?; 55 | let runtime = Runtime::new(&dirs)?; 56 | 57 | runtime.uninstall().context("Failed to uninstall runtime") 58 | } 59 | 60 | #[cfg(feature = "immutable-runtime")] 61 | fn run(&self) -> Result<()> { 62 | anyhow::bail!("Cannot uninstall runtime when the immutable runtime feature is enabled") 63 | } 64 | } 65 | 66 | impl Run for RuntimePatchCommand { 67 | fn run(&self) -> Result<()> { 68 | let dirs = ProjectDirs::new()?; 69 | let runtime = Runtime::new(&dirs)?; 70 | runtime.patch(&dirs, None) 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /native/src/integrations/implementation/mod.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | use cfg_if::cfg_if; 3 | 4 | #[rustfmt::skip] 5 | #[cfg(platform_macos)] 6 | use {crate::components::site::Site, std::process::Child, url::Url}; 7 | 8 | use crate::integrations::{IntegrationInstallArgs, IntegrationUninstallArgs}; 9 | 10 | #[cfg(all(platform_windows, not(feature = "portable")))] 11 | mod windows; 12 | 13 | #[cfg(any(platform_linux, platform_bsd))] 14 | mod linux; 15 | 16 | #[cfg(platform_macos)] 17 | mod macos; 18 | 19 | #[cfg(all(platform_windows, feature = "portable"))] 20 | mod portableapps; 21 | 22 | #[inline] 23 | pub fn install(args: &IntegrationInstallArgs) -> Result<()> { 24 | cfg_if! { 25 | if #[cfg(all(platform_windows, not(feature = "portable")))] { 26 | windows::install(args) 27 | } else if #[cfg(all(platform_windows, feature = "portable"))] { 28 | portableapps::install(args) 29 | } else if #[cfg(any(platform_linux, platform_bsd))] { 30 | linux::install(args) 31 | } else if #[cfg(platform_macos)] { 32 | macos::install(args) 33 | } else { 34 | compile_error!("Unknown operating system"); 35 | } 36 | } 37 | } 38 | 39 | #[inline] 40 | pub fn uninstall(args: &IntegrationUninstallArgs) -> Result<()> { 41 | cfg_if! { 42 | if #[cfg(all(platform_windows, not(feature = "portable")))] { 43 | windows::uninstall(args) 44 | } else if #[cfg(all(platform_windows, feature = "portable"))] { 45 | portableapps::uninstall(args) 46 | } else if #[cfg(any(platform_linux, platform_bsd))] { 47 | linux::uninstall(args) 48 | } else if #[cfg(platform_macos)] { 49 | macos::uninstall(args) 50 | } else { 51 | compile_error!("Unknown operating system"); 52 | } 53 | } 54 | } 55 | 56 | #[cfg(platform_macos)] 57 | #[inline] 58 | pub fn launch(site: &Site, urls: &[Url], arguments: &[String]) -> Result { 59 | macos::launch(site, urls, arguments) 60 | } 61 | -------------------------------------------------------------------------------- /native/src/integrations/mod.rs: -------------------------------------------------------------------------------- 1 | use reqwest::blocking::Client; 2 | 3 | use crate::components::site::Site; 4 | use crate::directories::ProjectDirs; 5 | 6 | mod categories; 7 | mod implementation; 8 | mod utils; 9 | 10 | #[cfg(platform_macos)] 11 | pub use implementation::launch; 12 | pub use implementation::{install, uninstall}; 13 | 14 | #[derive(Debug, Clone)] 15 | pub struct IntegrationInstallArgs<'a> { 16 | pub site: &'a Site, 17 | pub dirs: &'a ProjectDirs, 18 | pub client: Option<&'a Client>, 19 | pub update_manifest: bool, 20 | pub update_icons: bool, 21 | pub old_name: Option<&'a str>, 22 | } 23 | 24 | #[derive(Debug, Clone)] 25 | pub struct IntegrationUninstallArgs<'a> { 26 | pub site: &'a Site, 27 | pub dirs: &'a ProjectDirs, 28 | } 29 | -------------------------------------------------------------------------------- /native/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod components; 2 | pub mod connector; 3 | pub mod console; 4 | pub mod directories; 5 | pub mod integrations; 6 | pub mod storage; 7 | pub mod utils; 8 | -------------------------------------------------------------------------------- /native/userchrome/profile/chrome/pwa/chrome.manifest: -------------------------------------------------------------------------------- 1 | resource pwa ./ 2 | resource user ../user/ 3 | -------------------------------------------------------------------------------- /native/userchrome/profile/chrome/pwa/content/macosHiddenWindow.sys.mjs: -------------------------------------------------------------------------------- 1 | import { sanitizeString } from 'resource://pwa/utils/common.sys.mjs'; 2 | 3 | function OpenPwaShortcut(url) { 4 | switchToTabHavingURI(url, true); 5 | } 6 | 7 | class MacOSHiddenWindow { 8 | constructor() { 9 | this.setupDock(); 10 | } 11 | 12 | setupDock() { 13 | const mainWindow = Services.wm.getMostRecentWindow('navigator:browser'); 14 | const dockMenu = document.getElementById('menu_mac_dockmenu'); 15 | const { shortcuts = [] } = mainWindow.gFFPWASiteConfig.manifest; 16 | 17 | Array.from(dockMenu.children).forEach((child) => { 18 | dockMenu.removeChild(child); 19 | }); 20 | 21 | shortcuts.forEach((item) => { 22 | const menuItem = document.createXULElement('menuitem'); 23 | 24 | menuItem.setAttribute('label', sanitizeString(item.name)); 25 | 26 | menuItem.addEventListener('command', () => { 27 | OpenPwaShortcut(sanitizeString(item.url)); 28 | }); 29 | 30 | dockMenu.appendChild(menuItem); 31 | }); 32 | } 33 | } 34 | 35 | new MacOSHiddenWindow(); 36 | -------------------------------------------------------------------------------- /native/userchrome/profile/chrome/pwa/content/preferences.css: -------------------------------------------------------------------------------- 1 | @-moz-document url-prefix('about:preferences') { 2 | /* Hide the home category */ 3 | /* Not needed because it only configures the home and new tab page, which are not accessible */ 4 | #category-home { 5 | display: none; 6 | } 7 | 8 | /* Hide the default browser box */ 9 | /* Not needed because PWAsForFirefox browser installation is not supposed to be made default */ 10 | #defaultBrowserBox { 11 | display: none; 12 | } 13 | 14 | /* Hide native window controls preference when CSD is not available */ 15 | /* Because it doesn't have effect on those platforms */ 16 | @media not (-moz-gtk-csd-available) { 17 | .pref-csd-only { 18 | display: none; 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /native/userchrome/profile/chrome/pwa/icons/send-to-device.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /native/userchrome/profile/chrome/pwa/icons/share.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /native/userchrome/profile/chrome/pwa/localization/en-US/pwa/appmenu.ftl: -------------------------------------------------------------------------------- 1 | ## App menu items should be translated in the same style and capitalization as existing Firefox messages in your language 2 | ## Example: https://pontoon.mozilla.org/sl/firefox/browser/browser/appmenu.ftl/?search=appmenuitem-new&search_identifiers=true 3 | 4 | app-menu-new-default-browser = 5 | .label = New default browser 6 | -------------------------------------------------------------------------------- /native/userchrome/profile/chrome/pwa/localization/en-US/pwa/browser.ftl: -------------------------------------------------------------------------------- 1 | ## Toolbars should be translated in the same style and capitalization as existing Firefox messages in your language 2 | ## Example: https://pontoon.mozilla.org/sl/firefox/browser/browser/toolbarContextMenu.ftl/?string=231726 3 | ## Example: https://pontoon.mozilla.org/sl/firefox/browser/browser/browser.ftl/?string=217992 4 | 5 | # The label for the tabs/icon bar, when the tabs mode is enabled 6 | toolbar-tabs-ffpwa = 7 | .toolbarname = Tabs Bar 8 | .aria-label = Tabs 9 | .accesskey = T 10 | 11 | # The label for the tabs/icon bar, when the tabs mode is disabled 12 | toolbar-icon-ffpwa = 13 | .toolbarname = Icon Bar 14 | .aria-label = Icon 15 | .accesskey = I 16 | 17 | ## Popup inputs should be translated in imperative mood, without any end punctuation 18 | 19 | # The prompt for the address input popup 20 | popup-address-input = Enter site address 21 | -------------------------------------------------------------------------------- /native/userchrome/profile/chrome/pwa/localization/en-US/pwa/contextmenu.ftl: -------------------------------------------------------------------------------- 1 | ## Context menu items should be translated in the same style and capitalization as existing Firefox messages in your language 2 | ## Example: https://pontoon.mozilla.org/sl/firefox/browser/browser/browserContext.ftl/?search=view-new-tab&search_identifiers=true 3 | ## Example: https://pontoon.mozilla.org/sl/firefox/browser/browser/browserContext.ftl/?search=open-link&search_identifiers=true 4 | 5 | context-menu-image-view-current-tab = 6 | .label = Open Image 7 | .accesskey = { main-context-menu-image-view-new-tab.accesskey } 8 | 9 | context-menu-image-view-new-tab = 10 | .label = { main-context-menu-image-view-new-tab.label } 11 | .accesskey = { main-context-menu-image-view-new-tab.accesskey } 12 | 13 | context-menu-image-view-new-window = 14 | .label = Open Image in New Window 15 | .accesskey = { main-context-menu-image-view-new-tab.accesskey } 16 | 17 | context-menu-video-view-current-tab = 18 | .label = Open Video 19 | .accesskey = { main-context-menu-video-view-new-tab.accesskey } 20 | 21 | context-menu-video-view-new-tab = 22 | .label = { main-context-menu-video-view-new-tab.label } 23 | .accesskey = { main-context-menu-video-view-new-tab.accesskey } 24 | 25 | context-menu-video-view-new-window = 26 | .label = Open Video in New Window 27 | .accesskey = { main-context-menu-video-view-new-tab.accesskey } 28 | 29 | context-menu-open-link-default-browser = 30 | .label = Open Link in Default Browser 31 | .accesskey = D 32 | -------------------------------------------------------------------------------- /native/userchrome/profile/chrome/pwa/localization/en-US/pwa/customizemode.ftl: -------------------------------------------------------------------------------- 1 | ## Autohide messages should be translated in the same style and capitalization as existing Firefox messages in your language 2 | ## Example: https://pontoon.mozilla.org/sl/firefox/browser/browser/customizeMode.ftl/?search=autohide&search_identifiers=true 3 | 4 | customize-mode-mute-button-autohide = 5 | .label = Hide button when not playing 6 | -------------------------------------------------------------------------------- /native/userchrome/profile/chrome/pwa/localization/en-US/pwa/preferences.ftl: -------------------------------------------------------------------------------- 1 | ## Progressive Web Apps Group Details 2 | 3 | firefoxpwa-group-header = Progressive Web Apps 4 | firefoxpwa-group-note = You may need to restart the browser to apply these settings 5 | 6 | ## Colors Preferences 7 | 8 | sites-set-theme-color = 9 | .label = Allow web apps to override a theme (titlebar) color 10 | 11 | sites-set-background-color = 12 | .label = Allow web apps to override a background (window) color 13 | 14 | dynamic-theme-color = 15 | .label = Allow web apps to dynamically change a theme color 16 | 17 | ## Titlebar Preferences 18 | 19 | dynamic-window-title = 20 | .label = Change the window title based on the web app's title 21 | 22 | dynamic-window-icon = 23 | .label = Change the window icon based on the web app's icon 24 | 25 | native-window-controls = 26 | .label = Always use native window controls 27 | 28 | ## User Experience Preferences 29 | 30 | open-out-of-scope-in-default-browser = 31 | .label = Open out-of-scope URLs in a default browser (can break some web apps) 32 | 33 | enable-tabs-mode = 34 | .label = Show browser tabs and enable using multi-tabbed web apps 35 | 36 | ## Links Target Preference 37 | 38 | links-target-description = When opening a link that should normally open in a new window or tab 39 | 40 | links-target-choice-current-tab = 41 | .label = Force links into the current tab 42 | 43 | links-target-choice-new-tab = 44 | .label = Force links into a new tab 45 | 46 | links-target-choice-new-window = 47 | .label = Force links into a new window 48 | 49 | links-target-choice-keep = 50 | .label = Do not change link behavior 51 | 52 | ## Launch Type Preference 53 | 54 | launch-type-description = When launching a web app that is already opened 55 | 56 | launch-type-choice-new-window = 57 | .label = Open web app in a new window 58 | 59 | launch-type-choice-new-tab = 60 | .label = Open web app in a new tab 61 | 62 | launch-type-choice-replace = 63 | .label = Replace the existing tab 64 | 65 | launch-type-choice-focus = 66 | .label = Focus the existing window 67 | 68 | ## Address Bar Preference 69 | 70 | display-address-bar-description = Display the address bar 71 | 72 | display-address-bar-choice-out-of-scope = 73 | .label = When the URL is out-of-scope 74 | 75 | display-address-bar-choice-always = 76 | .label = Always 77 | 78 | display-address-bar-choice-never = 79 | .label = Never 80 | 81 | ## Allowed Domains Preference 82 | 83 | allowed-domains-description = Domains always allowed to be opened in the app browser 84 | 85 | allowed-domains-input = 86 | .placeholder = Enter a comma-separated list of domains... 87 | 88 | ## Keyboard Shortcuts Group Details 89 | 90 | shortcuts-group-header = Keyboard Shortcuts 91 | shortcuts-group-note = You may need to restart the browser to apply these settings 92 | 93 | ## Keyboard Shortcuts Preferences 94 | 95 | shortcuts-close-tab = 96 | .label = Close tab ({ $shortcut }) 97 | 98 | shortcuts-close-window = 99 | .label = Close window ({ $shortcut }) 100 | 101 | shortcuts-quit-application = 102 | .label = Quit application ({ $shortcut }) 103 | 104 | shortcuts-private-browsing = 105 | .label = Private browsing ({ $shortcut }) 106 | -------------------------------------------------------------------------------- /native/userchrome/profile/chrome/pwa/localization/en-US/pwa/widgets.ftl: -------------------------------------------------------------------------------- 1 | ## Widget names and tooltips should be translated in the same style and capitalization as existing Firefox messages in your language 2 | ## Example: https://pontoon.mozilla.org/sl/firefox/browser/browser/browser.ftl/?search=toolbar-button&search_identifiers=true 3 | ## Example: https://pontoon.mozilla.org/sl/firefox/browser/browser/browser.ftl/?search=navbar&search_identifiers=true 4 | 5 | toolbar-button-mute = 6 | .label = Toggle Sound 7 | .tooltiptext = Toggle the page sound ({ $shortcut }) 8 | 9 | toolbar-button-reader-view = 10 | .label = Reader View 11 | .tooltiptext = Toggle the reader view ({ $shortcut }) 12 | 13 | toolbar-button-copy-link = 14 | .label = Copy link 15 | .tooltiptext = Copy a link to this page 16 | 17 | toolbar-button-share-link = 18 | .label = Share link 19 | .tooltiptext = Share a link to this page 20 | 21 | toolbar-button-send-to-device = 22 | .label = Send to Device 23 | .tooltiptext = Send this page to another device 24 | 25 | toolbar-button-open-in-browser = 26 | .label = Open in Browser 27 | .tooltiptext = Open this page in browser 28 | 29 | toolbar-button-tracking-protection = 30 | .label = Tracking Protection 31 | .tooltiptext = View information about tracking protection on this site 32 | 33 | toolbar-button-identity = 34 | .label = Site Information 35 | .tooltiptext = View information about this site 36 | 37 | toolbar-button-permissions = 38 | .label = Site Permissions 39 | .tooltiptext = View permissions granted to this site 40 | 41 | toolbar-button-notifications = 42 | .label = Site Notifications 43 | .tooltiptext = Popup notifications for this site 44 | 45 | toolbar-button-close = 46 | .label = Close 47 | .tooltiptext = Close the current page 48 | 49 | toolbar-button-home-ffpwa = 50 | .label = { navbar-home.label } 51 | .tooltiptext = App Start Page 52 | 53 | ## Internal messages that should not be translated 54 | 55 | toolbar-button-back-ffpwa = 56 | .label = { main-context-menu-back-2.aria-label } 57 | .tooltiptext = { main-context-menu-back-2.tooltiptext } 58 | 59 | toolbar-button-forward-ffpwa = 60 | .label = { main-context-menu-forward-2.aria-label } 61 | .tooltiptext = { main-context-menu-forward-2.tooltiptext } 62 | -------------------------------------------------------------------------------- /native/userchrome/profile/chrome/pwa/localization/pt-BR/pwa/appmenu.ftl: -------------------------------------------------------------------------------- 1 | ## App menu items should be translated in the same style and capitalization as existing Firefox messages in your language 2 | ## Example: https://pontoon.mozilla.org/sl/firefox/browser/browser/appmenu.ftl/?search=appmenuitem-new&search_identifiers=true 3 | 4 | app-menu-new-default-browser = 5 | .label = Novo navegador padrão 6 | -------------------------------------------------------------------------------- /native/userchrome/profile/chrome/pwa/localization/pt-BR/pwa/browser.ftl: -------------------------------------------------------------------------------- 1 | ## Toolbars should be translated in the same style and capitalization as existing Firefox messages in your language 2 | ## Example: https://pontoon.mozilla.org/sl/firefox/browser/browser/toolbarContextMenu.ftl/?string=231726 3 | ## Example: https://pontoon.mozilla.org/sl/firefox/browser/browser/browser.ftl/?string=217992 4 | 5 | # The label for the tabs/icon bar, when the tabs mode is enabled 6 | toolbar-tabs-ffpwa = 7 | .toolbarname = Barra de abas 8 | .aria-label = Abas 9 | .accesskey = A 10 | # The label for the tabs/icon bar, when the tabs mode is disabled 11 | toolbar-icon-ffpwa = 12 | .toolbarname = Barra de ícones 13 | .aria-label = Ícones 14 | .accesskey = Í 15 | 16 | ## Popup inputs should be translated in imperative mood, without any end punctuation 17 | 18 | # The prompt for the address input popup 19 | popup-address-input = Insira o endereço do site 20 | -------------------------------------------------------------------------------- /native/userchrome/profile/chrome/pwa/localization/pt-BR/pwa/contextmenu.ftl: -------------------------------------------------------------------------------- 1 | ## Context menu items should be translated in the same style and capitalization as existing Firefox messages in your language 2 | ## Example: https://pontoon.mozilla.org/sl/firefox/browser/browser/browserContext.ftl/?search=view-new-tab&search_identifiers=true 3 | ## Example: https://pontoon.mozilla.org/sl/firefox/browser/browser/browserContext.ftl/?search=open-link&search_identifiers=true 4 | 5 | context-menu-image-view-current-tab = 6 | .label = Abrir Imagem 7 | .accesskey = { main-context-menu-image-view-new-tab.accesskey } 8 | context-menu-image-view-new-tab = 9 | .label = { main-context-menu-image-view-new-tab.label } 10 | .accesskey = { main-context-menu-image-view-new-tab.accesskey } 11 | context-menu-image-view-new-window = 12 | .label = Abrir imagem em nova janela 13 | .accesskey = { main-context-menu-image-view-new-tab.accesskey } 14 | context-menu-video-view-current-tab = 15 | .label = Abrir vídeo 16 | .accesskey = { main-context-menu-video-view-new-tab.accesskey } 17 | context-menu-video-view-new-tab = 18 | .label = { main-context-menu-video-view-new-tab.label } 19 | .accesskey = { main-context-menu-video-view-new-tab.accesskey } 20 | context-menu-video-view-new-window = 21 | .label = Abrir vídeo em uma nova janela 22 | .accesskey = { main-context-menu-video-view-new-tab.accesskey } 23 | context-menu-open-link-default-browser = 24 | .label = Abrir link no navegador padrão 25 | .accesskey = P 26 | -------------------------------------------------------------------------------- /native/userchrome/profile/chrome/pwa/localization/pt-BR/pwa/customizemode.ftl: -------------------------------------------------------------------------------- 1 | ## Autohide messages should be translated in the same style and capitalization as existing Firefox messages in your language 2 | ## Example: https://pontoon.mozilla.org/sl/firefox/browser/browser/customizeMode.ftl/?search=autohide&search_identifiers=true 3 | 4 | customize-mode-mute-button-autohide = 5 | .label = Ocultar botão quando não estiver jogando 6 | -------------------------------------------------------------------------------- /native/userchrome/profile/chrome/pwa/localization/pt-BR/pwa/preferences.ftl: -------------------------------------------------------------------------------- 1 | ## Progressive Web Apps Group Details 2 | 3 | firefoxpwa-group-header = Aplicativos Web Progressivos 4 | firefoxpwa-group-note = Pode ser necessário reiniciar o navegador para aplicar essas configurações 5 | 6 | ## Colors Preferences 7 | 8 | sites-set-theme-color = 9 | .label = Permitir que aplicativos da web substituam uma cor de tema (barra de título) 10 | sites-set-background-color = 11 | .label = Permitir que aplicativos da web substituam uma cor de fundo (janela) 12 | dynamic-theme-color = 13 | .label = Permitir que aplicativos da web alterem dinamicamente a cor de um tema 14 | 15 | ## Titlebar Preferences 16 | 17 | dynamic-window-title = 18 | .label = Alterar o título da janela com base no título do aplicativo da web 19 | dynamic-window-icon = 20 | .label = Alterar o ícone da janela com base no ícone do aplicativo da web 21 | native-window-controls = 22 | .label = Sempre use controles de janela nativos 23 | 24 | ## User Experience Preferences 25 | 26 | open-out-of-scope-in-default-browser = 27 | .label = Abra URLs fora do escopo em um navegador padrão (pode quebrar alguns aplicativos da web) 28 | enable-tabs-mode = 29 | .label = Mostrar abas do navegador e habilitar o uso de aplicativos da web com várias abas 30 | 31 | ## Links Target Preference 32 | 33 | links-target-description = Ao abrir um link que normalmente deveria abrir em uma nova janela ou aba 34 | links-target-choice-current-tab = 35 | .label = Forçar links na aba atual 36 | links-target-choice-new-tab = 37 | .label = Forçar links em uma nova aba 38 | links-target-choice-new-window = 39 | .label = Forçar links para uma nova janela 40 | links-target-choice-keep = 41 | .label = Não altere o comportamento do link 42 | 43 | ## Launch Type Preference 44 | 45 | launch-type-description = Ao iniciar um aplicativo da web que já está aberto 46 | launch-type-choice-new-window = 47 | .label = Abra o aplicativo da web em uma nova janela 48 | launch-type-choice-new-tab = 49 | .label = Abra o aplicativo da web em uma nova guia 50 | launch-type-choice-replace = 51 | .label = Substituir a aba existente 52 | launch-type-choice-focus = 53 | .label = Focar a janela existente 54 | 55 | ## Address Bar Preference 56 | 57 | display-address-bar-description = Exibir a barra de endereço 58 | display-address-bar-choice-out-of-scope = 59 | .label = Quando o URL está fora do escopo 60 | display-address-bar-choice-always = 61 | .label = Sempre 62 | display-address-bar-choice-never = 63 | .label = Nunca 64 | 65 | ## Allowed Domains Preference 66 | 67 | allowed-domains-description = Domínios sempre podem ser abertos no navegador do aplicativo 68 | allowed-domains-input = 69 | .placeholder = Insira uma lista de domínios separados por vírgulas... 70 | 71 | ## Keyboard Shortcuts Group Details 72 | 73 | shortcuts-group-header = Atalhos de teclado 74 | shortcuts-group-note = Pode ser necessário reiniciar o navegador para aplicar essas configurações 75 | 76 | ## Keyboard Shortcuts Preferences 77 | 78 | shortcuts-close-tab = 79 | .label = Fechar aba ({ $shortcut }) 80 | shortcuts-close-window = 81 | .label = Fechar janela ({ $shortcut }) 82 | shortcuts-quit-application = 83 | .label = Sair do aplicativo ({ $shortcut }) 84 | shortcuts-private-browsing = 85 | .label = Navegação privada ({ $shortcut }) 86 | -------------------------------------------------------------------------------- /native/userchrome/profile/chrome/pwa/localization/pt-BR/pwa/widgets.ftl: -------------------------------------------------------------------------------- 1 | ## Widget names and tooltips should be translated in the same style and capitalization as existing Firefox messages in your language 2 | ## Example: https://pontoon.mozilla.org/sl/firefox/browser/browser/browser.ftl/?search=toolbar-button&search_identifiers=true 3 | ## Example: https://pontoon.mozilla.org/sl/firefox/browser/browser/browser.ftl/?search=navbar&search_identifiers=true 4 | 5 | toolbar-button-mute = 6 | .label = Alternar som 7 | .tooltiptext = Alternar o som da página ({ $shortcut }) 8 | toolbar-button-reader-view = 9 | .label = Visualização do leitor 10 | .tooltiptext = Alternar a visualização do leitor ({ $shortcut }) 11 | toolbar-button-copy-link = 12 | .label = Copiar link 13 | .tooltiptext = Copie um link para esta página 14 | toolbar-button-share-link = 15 | .label = Compartilhar link 16 | .tooltiptext = Compartilhe um link para esta página 17 | toolbar-button-send-to-device = 18 | .label = Enviar para o dispositivo 19 | .tooltiptext = Enviar esta página para outro dispositivo 20 | toolbar-button-open-in-browser = 21 | .label = Abrir no navegador 22 | .tooltiptext = Abra esta página no navegador 23 | toolbar-button-tracking-protection = 24 | .label = Proteção de rastreamento 25 | .tooltiptext = Veja informações sobre proteção de rastreamento neste site 26 | toolbar-button-identity = 27 | .label = Informações do site 28 | .tooltiptext = Ver informações sobre este site 29 | toolbar-button-permissions = 30 | .label = Permissões do site 31 | .tooltiptext = Ver permissões concedidas a este site 32 | toolbar-button-notifications = 33 | .label = Notificações do site 34 | .tooltiptext = Notificações pop-up para este site 35 | toolbar-button-close = 36 | .label = Fechar 37 | .tooltiptext = Feche a página atual 38 | toolbar-button-home-ffpwa = 39 | .label = { navbar-home.label } 40 | .tooltiptext = Página inicial do aplicativo 41 | 42 | ## Internal messages that should not be translated 43 | 44 | toolbar-button-back-ffpwa = 45 | .label = { main-context-menu-back-2.aria-label } 46 | .tooltiptext = { main-context-menu-back-2.tooltiptext } 47 | toolbar-button-forward-ffpwa = 48 | .label = { main-context-menu-forward-2.aria-label } 49 | .tooltiptext = { main-context-menu-forward-2.tooltiptext } 50 | -------------------------------------------------------------------------------- /native/userchrome/profile/chrome/pwa/localization/ru/pwa/appmenu.ftl: -------------------------------------------------------------------------------- 1 | ## App menu items should be translated in the same style and capitalization as existing Firefox messages in your language 2 | ## Example: https://pontoon.mozilla.org/sl/firefox/browser/browser/appmenu.ftl/?search=appmenuitem-new&search_identifiers=true 3 | 4 | app-menu-new-default-browser = 5 | .label = Новый браузер по умолчанию 6 | -------------------------------------------------------------------------------- /native/userchrome/profile/chrome/pwa/localization/ru/pwa/browser.ftl: -------------------------------------------------------------------------------- 1 | ## Toolbars should be translated in the same style and capitalization as existing Firefox messages in your language 2 | ## Example: https://pontoon.mozilla.org/sl/firefox/browser/browser/toolbarContextMenu.ftl/?string=231726 3 | ## Example: https://pontoon.mozilla.org/sl/firefox/browser/browser/browser.ftl/?string=217992 4 | 5 | # The label for the tabs/icon bar, when the tabs mode is enabled 6 | toolbar-tabs-ffpwa = 7 | .toolbarname = Панель вкладок 8 | .aria-label = Вкладки 9 | .accesskey = T 10 | # The label for the tabs/icon bar, when the tabs mode is disabled 11 | toolbar-icon-ffpwa = 12 | .toolbarname = Панель иконки 13 | .aria-label = Иконка 14 | .accesskey = I 15 | 16 | ## Popup inputs should be translated in imperative mood, without any end punctuation 17 | 18 | # The prompt for the address input popup 19 | popup-address-input = Вставьте адрес сайта 20 | -------------------------------------------------------------------------------- /native/userchrome/profile/chrome/pwa/localization/ru/pwa/contextmenu.ftl: -------------------------------------------------------------------------------- 1 | ## Context menu items should be translated in the same style and capitalization as existing Firefox messages in your language 2 | ## Example: https://pontoon.mozilla.org/sl/firefox/browser/browser/browserContext.ftl/?search=view-new-tab&search_identifiers=true 3 | ## Example: https://pontoon.mozilla.org/sl/firefox/browser/browser/browserContext.ftl/?search=open-link&search_identifiers=true 4 | 5 | context-menu-image-view-current-tab = 6 | .label = Открыть изображение 7 | .accesskey = { main-context-menu-image-view-new-tab.accesskey } 8 | context-menu-image-view-new-tab = 9 | .label = { main-context-menu-image-view-new-tab.label } 10 | .accesskey = { main-context-menu-image-view-new-tab.accesskey } 11 | context-menu-image-view-new-window = 12 | .label = Открыть изображение в новом окне 13 | .accesskey = { main-context-menu-image-view-new-tab.accesskey } 14 | context-menu-video-view-current-tab = 15 | .label = Открыть видео 16 | .accesskey = { main-context-menu-video-view-new-tab.accesskey } 17 | context-menu-video-view-new-tab = 18 | .label = { main-context-menu-video-view-new-tab.label } 19 | .accesskey = { main-context-menu-video-view-new-tab.accesskey } 20 | context-menu-video-view-new-window = 21 | .label = Открыть видео в новом окне 22 | .accesskey = { main-context-menu-video-view-new-tab.accesskey } 23 | context-menu-open-link-default-browser = 24 | .label = Открыть ссылку в браузере по умолчанию 25 | .accesskey = D 26 | -------------------------------------------------------------------------------- /native/userchrome/profile/chrome/pwa/localization/ru/pwa/customizemode.ftl: -------------------------------------------------------------------------------- 1 | ## Autohide messages should be translated in the same style and capitalization as existing Firefox messages in your language 2 | ## Example: https://pontoon.mozilla.org/sl/firefox/browser/browser/customizeMode.ftl/?search=autohide&search_identifiers=true 3 | 4 | customize-mode-mute-button-autohide = 5 | .label = Скрывать кнопку, когда не проигрывается 6 | -------------------------------------------------------------------------------- /native/userchrome/profile/chrome/pwa/localization/ru/pwa/preferences.ftl: -------------------------------------------------------------------------------- 1 | ## Progressive Web Apps Group Details 2 | 3 | firefoxpwa-group-header = Progressive Web Apps 4 | firefoxpwa-group-note = Необходимо перезапустить браузер для применения этих настроек 5 | 6 | ## Colors Preferences 7 | 8 | sites-set-theme-color = 9 | .label = Разрешить веб-приложениям переопределять цвет темы (панели закладок) 10 | sites-set-background-color = 11 | .label = Разрешить веб-приложениям переопределять цвет фона (окна) 12 | dynamic-theme-color = 13 | .label = Разрешить веб-приложениям динамически менять цвет темы 14 | 15 | ## Titlebar Preferences 16 | 17 | dynamic-window-title = 18 | .label = Изменить заголовок окна на основе заголовка веб-приложения 19 | dynamic-window-icon = 20 | .label = Изменить иконку окна на основе иконки веб-приложения 21 | native-window-controls = 22 | .label = Всегда использовать родные элементы управления окнами 23 | 24 | ## User Experience Preferences 25 | 26 | open-out-of-scope-in-default-browser = 27 | .label = Открывать URL-адреса, выходящие за пределы области действия, в браузере по умолчанию (может нарушить работу некоторых веб-приложений) 28 | enable-tabs-mode = 29 | .label = Отображать вкладки браузера и возможность использования веб-приложений с несколькими вкладками 30 | 31 | ## Links Target Preference 32 | 33 | links-target-description = При открытии ссылки, которая обычно должна открываться в новом окне или вкладке 34 | links-target-choice-current-tab = 35 | .label = Принудительное перемещение ссылок на текущую вкладку 36 | links-target-choice-new-tab = 37 | .label = Принудительное перемещение ссылок на новую вкладку 38 | links-target-choice-new-window = 39 | .label = Принудительное перемещение ссылок на новое окно 40 | links-target-choice-keep = 41 | .label = Не изменять поведение ссылок 42 | 43 | ## Launch Type Preference 44 | 45 | launch-type-description = При запуске веб-приложения, которое уже открыто 46 | launch-type-choice-new-window = 47 | .label = Открыть веб-приложение в новом окне 48 | launch-type-choice-new-tab = 49 | .label = Открыть веб-приложение в новой вкладке 50 | launch-type-choice-replace = 51 | .label = Заменить текущую вкладку 52 | launch-type-choice-focus = 53 | .label = Сфокусироваться в текущей вкладке 54 | 55 | ## Address Bar Preference 56 | 57 | display-address-bar-description = Отображать адресную строку 58 | display-address-bar-choice-out-of-scope = 59 | .label = Когда URL-адрес выходит за пределы области действия 60 | display-address-bar-choice-always = 61 | .label = Всегда 62 | display-address-bar-choice-never = 63 | .label = Никогда 64 | 65 | ## Allowed Domains Preference 66 | 67 | allowed-domains-description = Домены, которые всегда разрешено открывать в браузере 68 | allowed-domains-input = 69 | .placeholder = Введите список доменов, разделённых запятыми… 70 | 71 | ## Keyboard Shortcuts Group Details 72 | 73 | shortcuts-group-header = Горячие клавиши 74 | shortcuts-group-note = Необходимо перезапустить браузер для применения этих настроек 75 | 76 | ## Keyboard Shortcuts Preferences 77 | 78 | shortcuts-close-tab = 79 | .label = Закрыть вкладку ({ $shortcut }) 80 | shortcuts-close-window = 81 | .label = Закрыть окно ({ $shortcut }) 82 | shortcuts-quit-application = 83 | .label = Выйти из приложения ({ $shortcut }) 84 | shortcuts-private-browsing = 85 | .label = Приватный просмотр ({ $shortcut }) 86 | -------------------------------------------------------------------------------- /native/userchrome/profile/chrome/pwa/localization/ru/pwa/widgets.ftl: -------------------------------------------------------------------------------- 1 | ## Widget names and tooltips should be translated in the same style and capitalization as existing Firefox messages in your language 2 | ## Example: https://pontoon.mozilla.org/sl/firefox/browser/browser/browser.ftl/?search=toolbar-button&search_identifiers=true 3 | ## Example: https://pontoon.mozilla.org/sl/firefox/browser/browser/browser.ftl/?search=navbar&search_identifiers=true 4 | 5 | toolbar-button-mute = 6 | .label = Переключатель звука 7 | .tooltiptext = Переключить звук страницы ({ $shortcut }) 8 | toolbar-button-reader-view = 9 | .label = Режим чтения 10 | .tooltiptext = Переключить режим чтения ({ $shortcut }) 11 | toolbar-button-copy-link = 12 | .label = Скопировать ссылку 13 | .tooltiptext = Скопировать ссылку на эту страницу 14 | toolbar-button-share-link = 15 | .label = Поделиться ссылкой 16 | .tooltiptext = Поделиться ссылкой на эту страницу 17 | toolbar-button-send-to-device = 18 | .label = Отправить на устройство 19 | .tooltiptext = Отправить эту страницу на другое устройство 20 | toolbar-button-open-in-browser = 21 | .label = Открыть в браузере 22 | .tooltiptext = Открыть эту страницу в браузере 23 | toolbar-button-tracking-protection = 24 | .label = Защита от слежения 25 | .tooltiptext = Просмотр информации о защите от слежения на этом сайте 26 | toolbar-button-identity = 27 | .label = Информация сайта 28 | .tooltiptext = Просмотр информации об этом сайте 29 | toolbar-button-permissions = 30 | .label = Разрешения сайта 31 | .tooltiptext = Просмотр разрешений, предоставленных этому сайту 32 | toolbar-button-notifications = 33 | .label = Уведомления сайта 34 | .tooltiptext = Всплывающие уведомления для этого сайта 35 | toolbar-button-close = 36 | .label = Закрыть 37 | .tooltiptext = Закрыть текущую страницу 38 | toolbar-button-home-ffpwa = 39 | .label = { navbar-home.label } 40 | .tooltiptext = Стартовая страница приложения 41 | 42 | ## Internal messages that should not be translated 43 | 44 | toolbar-button-back-ffpwa = 45 | .label = { main-context-menu-back-2.aria-label } 46 | .tooltiptext = { main-context-menu-back-2.tooltiptext } 47 | toolbar-button-forward-ffpwa = 48 | .label = { main-context-menu-forward-2.aria-label } 49 | .tooltiptext = { main-context-menu-forward-2.tooltiptext } 50 | -------------------------------------------------------------------------------- /native/userchrome/profile/chrome/pwa/localization/sl/pwa/appmenu.ftl: -------------------------------------------------------------------------------- 1 | ## App menu items should be translated in the same style and capitalization as existing Firefox messages in your language 2 | ## Example: https://pontoon.mozilla.org/sl/firefox/browser/browser/appmenu.ftl/?search=appmenuitem-new&search_identifiers=true 3 | 4 | app-menu-new-default-browser = 5 | .label = Nov privzet brskalnik 6 | -------------------------------------------------------------------------------- /native/userchrome/profile/chrome/pwa/localization/sl/pwa/browser.ftl: -------------------------------------------------------------------------------- 1 | ## Toolbars should be translated in the same style and capitalization as existing Firefox messages in your language 2 | ## Example: https://pontoon.mozilla.org/sl/firefox/browser/browser/toolbarContextMenu.ftl/?string=231726 3 | ## Example: https://pontoon.mozilla.org/sl/firefox/browser/browser/browser.ftl/?string=217992 4 | 5 | # The label for the tabs/icon bar, when the tabs mode is enabled 6 | toolbar-tabs-ffpwa = 7 | .toolbarname = Vrstica z zavihki 8 | .aria-label = Zavihki 9 | .accesskey = K 10 | # The label for the tabs/icon bar, when the tabs mode is disabled 11 | toolbar-icon-ffpwa = 12 | .toolbarname = Vrstica z ikonami 13 | .aria-label = Ikone 14 | .accesskey = K 15 | 16 | ## Popup inputs should be translated in imperative mood, without any end punctuation 17 | 18 | # The prompt for the address input popup 19 | popup-address-input = Vnesite naslov strani 20 | -------------------------------------------------------------------------------- /native/userchrome/profile/chrome/pwa/localization/sl/pwa/contextmenu.ftl: -------------------------------------------------------------------------------- 1 | ## Context menu items should be translated in the same style and capitalization as existing Firefox messages in your language 2 | ## Example: https://pontoon.mozilla.org/sl/firefox/browser/browser/browserContext.ftl/?search=view-new-tab&search_identifiers=true 3 | ## Example: https://pontoon.mozilla.org/sl/firefox/browser/browser/browserContext.ftl/?search=open-link&search_identifiers=true 4 | 5 | context-menu-image-view-current-tab = 6 | .label = Odpri sliko 7 | .accesskey = { main-context-menu-image-view-new-tab.accesskey } 8 | context-menu-image-view-new-tab = 9 | .label = { main-context-menu-image-view-new-tab.label } 10 | .accesskey = { main-context-menu-image-view-new-tab.accesskey } 11 | context-menu-image-view-new-window = 12 | .label = Odpri sliko v novem oknu 13 | .accesskey = { main-context-menu-image-view-new-tab.accesskey } 14 | context-menu-video-view-current-tab = 15 | .label = Odpri videoposnetek 16 | .accesskey = { main-context-menu-video-view-new-tab.accesskey } 17 | context-menu-video-view-new-tab = 18 | .label = { main-context-menu-video-view-new-tab.label } 19 | .accesskey = { main-context-menu-video-view-new-tab.accesskey } 20 | context-menu-video-view-new-window = 21 | .label = Odpri videoposnetek v novem oknu 22 | .accesskey = { main-context-menu-video-view-new-tab.accesskey } 23 | context-menu-open-link-default-browser = 24 | .label = Odpri povezavo v privzetem brskalniku 25 | .accesskey = D 26 | -------------------------------------------------------------------------------- /native/userchrome/profile/chrome/pwa/localization/sl/pwa/customizemode.ftl: -------------------------------------------------------------------------------- 1 | ## Autohide messages should be translated in the same style and capitalization as existing Firefox messages in your language 2 | ## Example: https://pontoon.mozilla.org/sl/firefox/browser/browser/customizeMode.ftl/?search=autohide&search_identifiers=true 3 | 4 | customize-mode-mute-button-autohide = 5 | .label = Skrij gumb, ko se nič ne predvaja 6 | -------------------------------------------------------------------------------- /native/userchrome/profile/chrome/pwa/localization/sl/pwa/preferences.ftl: -------------------------------------------------------------------------------- 1 | ## Progressive Web Apps Group Details 2 | 3 | firefoxpwa-group-header = Progresivne spletne aplikacije 4 | firefoxpwa-group-note = Za uveljavitev teh nastavitev boste morda morali ponovno zagnati brskalnik 5 | 6 | ## Colors Preferences 7 | 8 | sites-set-theme-color = 9 | .label = Dovoli aplikacijam spreminjati barvo teme 10 | sites-set-background-color = 11 | .label = Dovoli aplikacijam spreminjati barvo ozadja 12 | dynamic-theme-color = 13 | .label = Dovoli aplikacijam dinamično spreminjati barvo teme 14 | 15 | ## Titlebar Preferences 16 | 17 | dynamic-window-title = 18 | .label = Spreminjaj ime okna glede na ime aplikacije 19 | dynamic-window-icon = 20 | .label = Spreminjaj ikono okna glede na ikono aplikacije 21 | native-window-controls = 22 | .label = Vedno uporabljaj sistemske gumbe za upravljanje oken 23 | 24 | ## User Experience Preferences 25 | 26 | open-out-of-scope-in-default-browser = 27 | .label = Odpri URL-je izven aplikacije v privzetem brskalniku (lahko pokvari nekatere aplikacije) 28 | enable-tabs-mode = 29 | .label = Prikaži zavihke brskalnika in omogoči uporabo aplikacij z več zavihki 30 | 31 | ## Links Target Preference 32 | 33 | links-target-description = Pri odpiranju povezave, ki bi se običajno odprla v novem oknu ali zavihku 34 | links-target-choice-current-tab = 35 | .label = Odpri povezavo v trenutnem zavihku 36 | links-target-choice-new-tab = 37 | .label = Odpri povezavo v novem zavihku 38 | links-target-choice-new-window = 39 | .label = Odpri povezavo v novem oknu 40 | links-target-choice-keep = 41 | .label = Ne spremeni vedenja povezave 42 | 43 | ## Launch Type Preference 44 | 45 | launch-type-description = Pri odpiranju aplikacije, ki je že odprta 46 | launch-type-choice-new-window = 47 | .label = Odpri aplikacijo v novem oknu 48 | launch-type-choice-new-tab = 49 | .label = Odpri aplikacijo v novem zavihku 50 | launch-type-choice-replace = 51 | .label = Zamenjaj obstoječi zavihek 52 | launch-type-choice-focus = 53 | .label = Prikaži obstoječe okno 54 | 55 | ## Address Bar Preference 56 | 57 | display-address-bar-description = Prikaži naslovno vrstico 58 | display-address-bar-choice-out-of-scope = 59 | .label = Ko je URL izven aplikacije 60 | display-address-bar-choice-always = 61 | .label = Vedno 62 | display-address-bar-choice-never = 63 | .label = Nikoli 64 | 65 | ## Allowed Domains Preference 66 | 67 | allowed-domains-description = Domene, ki so vedno lahko odprte v aplikaciji 68 | allowed-domains-input = 69 | .placeholder = Vnesite seznam domen, ločenih z vejico ... 70 | 71 | ## Keyboard Shortcuts Group Details 72 | 73 | shortcuts-group-header = Bližnjice na tipkovnici 74 | shortcuts-group-note = Za uveljavitev teh nastavitev boste morda morali ponovno zagnati brskalnik 75 | 76 | ## Keyboard Shortcuts Preferences 77 | 78 | shortcuts-close-tab = 79 | .label = Zapri zavihek ({ $shortcut }) 80 | shortcuts-close-window = 81 | .label = Zapri okno ({ $shortcut }) 82 | shortcuts-quit-application = 83 | .label = Zapri aplikacijo ({ $shortcut }) 84 | shortcuts-private-browsing = 85 | .label = Zasebno brskanje ({ $shortcut }) 86 | -------------------------------------------------------------------------------- /native/userchrome/profile/chrome/pwa/localization/sl/pwa/widgets.ftl: -------------------------------------------------------------------------------- 1 | ## Widget names and tooltips should be translated in the same style and capitalization as existing Firefox messages in your language 2 | ## Example: https://pontoon.mozilla.org/sl/firefox/browser/browser/browser.ftl/?search=toolbar-button&search_identifiers=true 3 | ## Example: https://pontoon.mozilla.org/sl/firefox/browser/browser/browser.ftl/?search=navbar&search_identifiers=true 4 | 5 | toolbar-button-mute = 6 | .label = Preklopi zvok 7 | .tooltiptext = Preklopi zvok strani ({ $shortcut }) 8 | toolbar-button-reader-view = 9 | .label = Bralni pogled 10 | .tooltiptext = Preklopi bralni pogled ({ $shortcut }) 11 | toolbar-button-copy-link = 12 | .label = Kopiraj povezavo 13 | .tooltiptext = Kopiraj povezavo na to stran 14 | toolbar-button-share-link = 15 | .label = Deli povezavo 16 | .tooltiptext = Deli povezavo na to stran 17 | toolbar-button-send-to-device = 18 | .label = Pošlji na napravo 19 | .tooltiptext = Pošlji to stran na drugo napravo 20 | toolbar-button-open-in-browser = 21 | .label = Odpri v brskalniku 22 | .tooltiptext = Odpri to stran v brskalniku 23 | toolbar-button-tracking-protection = 24 | .label = Zaščita pred sledenjem 25 | .tooltiptext = Prikaži informacije o zaščiti pred sledenjem za to stran 26 | toolbar-button-identity = 27 | .label = Informacije o strani 28 | .tooltiptext = Prikaži informacije o tej strani 29 | toolbar-button-permissions = 30 | .label = Dovoljenja strani 31 | .tooltiptext = Prikaži dovoljenja, dodeljena tej strani 32 | toolbar-button-notifications = 33 | .label = Obvestila strani 34 | .tooltiptext = Pojavna obvestila za to stran 35 | toolbar-button-close = 36 | .label = Zapri 37 | .tooltiptext = Zapri trenutno stran 38 | toolbar-button-home-ffpwa = 39 | .label = { navbar-home.label } 40 | .tooltiptext = Začetna stran aplikacije 41 | 42 | ## Internal messages that should not be translated 43 | 44 | toolbar-button-back-ffpwa = 45 | .label = { main-context-menu-back-2.aria-label } 46 | .tooltiptext = { main-context-menu-back-2.tooltiptext } 47 | toolbar-button-forward-ffpwa = 48 | .label = { main-context-menu-forward-2.aria-label } 49 | .tooltiptext = { main-context-menu-forward-2.tooltiptext } 50 | -------------------------------------------------------------------------------- /native/userchrome/profile/chrome/pwa/localization/zh-CN/pwa/browser.ftl: -------------------------------------------------------------------------------- 1 | ## Toolbars should be translated in the same style and capitalization as existing Firefox messages in your language 2 | ## Example: https://pontoon.mozilla.org/sl/firefox/browser/browser/toolbarContextMenu.ftl/?string=231726 3 | ## Example: https://pontoon.mozilla.org/sl/firefox/browser/browser/browser.ftl/?string=217992 4 | 5 | # The label for the tabs/icon bar, when the tabs mode is enabled 6 | toolbar-tabs-ffpwa = 7 | .toolbarname = 标签栏 8 | .aria-label = 标签页 9 | .accesskey = T 10 | # The label for the tabs/icon bar, when the tabs mode is disabled 11 | toolbar-icon-ffpwa = 12 | .toolbarname = 图标栏 13 | .aria-label = 图标 14 | .accesskey = I 15 | 16 | ## Popup inputs should be translated in imperative mood, without any end punctuation 17 | 18 | # The prompt for the address input popup 19 | popup-address-input = 输入网站地址 20 | -------------------------------------------------------------------------------- /native/userchrome/profile/chrome/pwa/localization/zh-CN/pwa/contextmenu.ftl: -------------------------------------------------------------------------------- 1 | ## Context menu items should be translated in the same style and capitalization as existing Firefox messages in your language 2 | ## Example: https://pontoon.mozilla.org/sl/firefox/browser/browser/browserContext.ftl/?search=view-new-tab&search_identifiers=true 3 | ## Example: https://pontoon.mozilla.org/sl/firefox/browser/browser/browserContext.ftl/?search=open-link&search_identifiers=true 4 | 5 | context-menu-image-view-current-tab = 6 | .label = 打开图像 7 | .accesskey = { main-context-menu-image-view-new-tab.accesskey } 8 | context-menu-image-view-new-window = 9 | .label = 在新窗口中打开图像 10 | .accesskey = { main-context-menu-image-view-new-tab.accesskey } 11 | context-menu-video-view-current-tab = 12 | .label = 打开视频 13 | .accesskey = { main-context-menu-video-view-new-tab.accesskey } 14 | context-menu-video-view-new-window = 15 | .label = 在新窗口中打开视频 16 | .accesskey = { main-context-menu-video-view-new-tab.accesskey } 17 | context-menu-open-link-default-browser = 18 | .label = 在默认浏览器中打开链接 19 | .accesskey = D 20 | -------------------------------------------------------------------------------- /native/userchrome/profile/chrome/pwa/localization/zh-CN/pwa/customizemode.ftl: -------------------------------------------------------------------------------- 1 | ## Autohide messages should be translated in the same style and capitalization as existing Firefox messages in your language 2 | ## Example: https://pontoon.mozilla.org/sl/firefox/browser/browser/customizeMode.ftl/?search=autohide&search_identifiers=true 3 | 4 | customize-mode-mute-button-autohide = 5 | .label = 当不播放时隐藏按钮 6 | -------------------------------------------------------------------------------- /native/userchrome/profile/chrome/pwa/localization/zh-CN/pwa/preferences.ftl: -------------------------------------------------------------------------------- 1 | ## Progressive Web Apps Group Details 2 | 3 | firefoxpwa-group-header = 渐进式网页应用 4 | firefoxpwa-group-note = 您可能需要重新启动浏览器以应用这些设置 5 | 6 | ## Colors Preferences 7 | 8 | 9 | ## Titlebar Preferences 10 | 11 | 12 | ## User Experience Preferences 13 | 14 | 15 | ## Links Target Preference 16 | 17 | 18 | ## Launch Type Preference 19 | 20 | launch-type-choice-new-window = 21 | .label = 在新窗口中打开网页应用 22 | launch-type-choice-new-tab = 23 | .label = 在新标签页中打开网页应用 24 | launch-type-choice-replace = 25 | .label = 替换现有标签页 26 | 27 | ## Address Bar Preference 28 | 29 | display-address-bar-description = 显示地址栏 30 | display-address-bar-choice-always = 31 | .label = 总是 32 | display-address-bar-choice-never = 33 | .label = 从不 34 | 35 | ## Allowed Domains Preference 36 | 37 | 38 | ## Keyboard Shortcuts Group Details 39 | 40 | shortcuts-group-header = 键盘快捷键 41 | shortcuts-group-note = 您可能需要重新启动浏览器以应用这些设置 42 | 43 | ## Keyboard Shortcuts Preferences 44 | 45 | -------------------------------------------------------------------------------- /native/userchrome/profile/chrome/pwa/localization/zh-CN/pwa/widgets.ftl: -------------------------------------------------------------------------------- 1 | ## Widget names and tooltips should be translated in the same style and capitalization as existing Firefox messages in your language 2 | ## Example: https://pontoon.mozilla.org/sl/firefox/browser/browser/browser.ftl/?search=toolbar-button&search_identifiers=true 3 | ## Example: https://pontoon.mozilla.org/sl/firefox/browser/browser/browser.ftl/?search=navbar&search_identifiers=true 4 | 5 | toolbar-button-mute = 6 | .label = 声音开关 7 | .tooltiptext = Toggle the page sound ({ $shortcut }) 8 | toolbar-button-reader-view = 9 | .label = 阅读视图 10 | .tooltiptext = Toggle the reader view ({ $shortcut }) 11 | toolbar-button-copy-link = 12 | .label = 复制链接 13 | .tooltiptext = 复制到此页面的链接 14 | toolbar-button-share-link = 15 | .label = 分享链接 16 | .tooltiptext = 分享到此页面的链接 17 | toolbar-button-send-to-device = 18 | .label = 发送到设备 19 | .tooltiptext = 将此页面发送到另一台设备 20 | toolbar-button-open-in-browser = 21 | .label = 在浏览器中打开 22 | .tooltiptext = 在浏览器中打开此页面 23 | toolbar-button-tracking-protection = 24 | .label = 跟踪保护 25 | .tooltiptext = 查看此网站的跟踪保护信息 26 | toolbar-button-identity = 27 | .label = 站点信息 28 | .tooltiptext = 查看关于此网站的信息 29 | toolbar-button-permissions = 30 | .label = 网站权限 31 | .tooltiptext = 查看授予此网站的权限 32 | toolbar-button-notifications = 33 | .label = 网站通知 34 | .tooltiptext = 此网站的弹窗通知 35 | toolbar-button-close = 36 | .label = 关闭 37 | .tooltiptext = 关闭当前页面 38 | toolbar-button-home-ffpwa = 39 | .label = { navbar-home.label } 40 | .tooltiptext = 应用启动页面 41 | 42 | ## Internal messages that should not be translated 43 | 44 | -------------------------------------------------------------------------------- /native/userchrome/profile/chrome/pwa/localization/zh-TW/pwa/browser.ftl: -------------------------------------------------------------------------------- 1 | ## Toolbars should be translated in the same style and capitalization as existing Firefox messages in your language 2 | ## Example: https://pontoon.mozilla.org/sl/firefox/browser/browser/toolbarContextMenu.ftl/?string=231726 3 | ## Example: https://pontoon.mozilla.org/sl/firefox/browser/browser/browser.ftl/?string=217992 4 | 5 | # The label for the tabs/icon bar, when the tabs mode is enabled 6 | toolbar-tabs-ffpwa = 7 | .toolbarname = 標簽欄 8 | .aria-label = 標簽頁 9 | .accesskey = T 10 | # The label for the tabs/icon bar, when the tabs mode is disabled 11 | toolbar-icon-ffpwa = 12 | .toolbarname = 圖標欄 13 | .aria-label = 圖標 14 | .accesskey = I 15 | 16 | ## Popup inputs should be translated in imperative mood, without any end punctuation 17 | 18 | # The prompt for the address input popup 19 | popup-address-input = 輸入網站地址 20 | -------------------------------------------------------------------------------- /native/userchrome/profile/chrome/pwa/localization/zh-TW/pwa/contextmenu.ftl: -------------------------------------------------------------------------------- 1 | ## Context menu items should be translated in the same style and capitalization as existing Firefox messages in your language 2 | ## Example: https://pontoon.mozilla.org/sl/firefox/browser/browser/browserContext.ftl/?search=view-new-tab&search_identifiers=true 3 | ## Example: https://pontoon.mozilla.org/sl/firefox/browser/browser/browserContext.ftl/?search=open-link&search_identifiers=true 4 | 5 | context-menu-image-view-current-tab = 6 | .label = 打開圖像 7 | .accesskey = { main-context-menu-image-view-new-tab.accesskey } 8 | context-menu-image-view-new-window = 9 | .label = 在新窗口打開圖像 10 | .accesskey = { main-context-menu-image-view-new-tab.accesskey } 11 | context-menu-video-view-current-tab = 12 | .label = 打開影片 13 | .accesskey = { main-context-menu-video-view-new-tab.accesskey } 14 | context-menu-video-view-new-window = 15 | .label = 在新窗口中打開影片 16 | .accesskey = { main-context-menu-video-view-new-tab.accesskey } 17 | context-menu-open-link-default-browser = 18 | .label = 在默認瀏覽器中打開連結 19 | .accesskey = D 20 | -------------------------------------------------------------------------------- /native/userchrome/profile/chrome/pwa/localization/zh-TW/pwa/customizemode.ftl: -------------------------------------------------------------------------------- 1 | ## Autohide messages should be translated in the same style and capitalization as existing Firefox messages in your language 2 | ## Example: https://pontoon.mozilla.org/sl/firefox/browser/browser/customizeMode.ftl/?search=autohide&search_identifiers=true 3 | 4 | customize-mode-mute-button-autohide = 5 | .label = 在不播放時隱藏按鈕 6 | -------------------------------------------------------------------------------- /native/userchrome/profile/chrome/pwa/utils/common.sys.mjs: -------------------------------------------------------------------------------- 1 | /** 2 | * Removes all control characters from the string. 3 | * 4 | * @param {string?} string 5 | * 6 | * @returns {string|undefined} 7 | */ 8 | export function sanitizeString (string) { 9 | return string?.replace(/[\u0000-\u001F\u007F-\u009F]/g, ''); 10 | } 11 | -------------------------------------------------------------------------------- /native/userchrome/profile/chrome/pwa/utils/hookFunction.sys.mjs: -------------------------------------------------------------------------------- 1 | // File is mostly copied from xiaoxiaoflood/firefox-scripts repository on GitHub, licensed under MPL 2.0 2 | // Original source: https://github.com/xiaoxiaoflood/firefox-scripts/blob/69675c7f09e9009b63b1cc239b94c03c5962a9d7/chrome/utils/hookFunction.jsm 3 | 4 | /** 5 | * Add hooks to a function to execute before and after it. 6 | * 7 | * The function to modify is `functionContext[functionName]`. Call only once per function, 8 | * modification is not supported. Other addons wishing to access the original function may 9 | * do so using the `.originalFunction` member of the replacement function. This member can also 10 | * be set if required, to insert a new function replacement into the chain rather than appending. 11 | * 12 | * @param {object} functionContext - The object on which the function is a property 13 | * @param {string} functionName - The name of the property containing the function (on functionContext) 14 | * @param {function} [onBeforeFunction] - A function to be called before the hooked function is executed. It will be passed the same parameters as the hooked function. It's return value will be passed on to onAfterFunction. 15 | * @param {function} [onAfterFunction] - A function to be called after the hooked function is executed. The parameters passed to it are: onBeforeFunction return value, arguments object from original hooked function, return value from original hooked function. It's return value will be returned in place of that of the original function. 16 | * 17 | * @returns {function} A function which can be called to safely un-hook the hook 18 | * @throws {Error} If the function is not found in context 19 | */ 20 | export function hookFunction(functionContext, functionName, onBeforeFunction, onAfterFunction) { 21 | let originalFunction = functionContext[functionName]; 22 | 23 | if (!originalFunction) { 24 | throw new Error(`Could not find function ${functionName}`); 25 | } 26 | 27 | let replacementFunction = function() { 28 | let onBeforeResult = null; 29 | if (onBeforeFunction) { 30 | onBeforeResult = onBeforeFunction.apply(this, arguments); 31 | } 32 | let originalResult = replacementFunction.originalFunction.apply(this, arguments); 33 | if (onAfterFunction) { 34 | return onAfterFunction.call(this, onBeforeResult, arguments, originalResult); 35 | } else { 36 | return originalResult; 37 | } 38 | } 39 | 40 | replacementFunction.originalFunction = originalFunction; 41 | functionContext[functionName] = replacementFunction; 42 | 43 | return function () { 44 | // Not safe to simply assign originalFunction back again, as something else might have chained onto this function, which would then break the chain 45 | // Unassigning these variables prevent any effects of the hook, though the function itself remains in place 46 | onBeforeFunction = null; 47 | onAfterFunction = null; 48 | }; 49 | } 50 | -------------------------------------------------------------------------------- /native/userchrome/profile/chrome/pwa/utils/nativeMessaging.sys.mjs: -------------------------------------------------------------------------------- 1 | import { ExtensionCommon } from 'resource://gre/modules/ExtensionCommon.sys.mjs'; 2 | import { NativeApp } from 'resource://gre/modules/NativeMessaging.sys.mjs'; 3 | 4 | const extensionId = 'firefoxpwa@filips.si'; 5 | const nativeAppId = 'firefoxpwa'; 6 | const contextName = `UserChrome/${extensionId}/sendNativeMessage/${nativeAppId}`; 7 | 8 | /** 9 | * A custom extension context that "emulates" our main extension. 10 | */ 11 | class UserChromeContext extends ExtensionCommon.BaseContext { 12 | constructor () { 13 | super('userChromeEnv', { id: extensionId, manifestVersion: 2 }); 14 | this.sandbox = Cu.Sandbox(globalThis); 15 | } 16 | 17 | logActivity (type, name, data) { 18 | console.log('[UserChromeContext]', type, name, data); 19 | } 20 | 21 | get cloneScope () { 22 | return this.sandbox; 23 | } 24 | 25 | get principal () { 26 | return Cu.getObjectPrincipal(this.sandbox); 27 | } 28 | } 29 | /** 30 | * Send a message to the native program and return response. 31 | * 32 | * @param {Object} message - The message to send 33 | * 34 | * @returns {Promise} The response from the native program 35 | * @throws {Error} If sending the message failed 36 | */ 37 | export function sendNativeMessage(message) { 38 | const userChromeContext = new UserChromeContext(); 39 | const nativeMessage = NativeApp.encodeMessage(userChromeContext, message); 40 | const nativeApp = new NativeApp(userChromeContext, nativeAppId); 41 | return nativeApp.sendMessage(new StructuredCloneHolder(contextName, null, nativeMessage)); 42 | } 43 | -------------------------------------------------------------------------------- /native/userchrome/profile/chrome/pwa/utils/xPref.sys.mjs: -------------------------------------------------------------------------------- 1 | // File is mostly copied from xiaoxiaoflood/firefox-scripts repository on GitHub, licensed under MPL 2.0 2 | // Original source: https://github.com/xiaoxiaoflood/firefox-scripts/blob/69675c7f09e9009b63b1cc239b94c03c5962a9d7/chrome/utils/xPref.jsm 3 | 4 | export const xPref = { 5 | get: function (prefPath, def = false, valueIfUndefined, setDefault = true) { 6 | let sPrefs = def ? Services.prefs.getDefaultBranch(null) : Services.prefs; 7 | 8 | try { 9 | switch (sPrefs.getPrefType(prefPath)) { 10 | case 0: 11 | if (valueIfUndefined !== undefined) { 12 | return this.set(prefPath, valueIfUndefined, setDefault); 13 | } else { 14 | return undefined; 15 | } 16 | case 32: 17 | return sPrefs.getStringPref(prefPath); 18 | case 64: 19 | return sPrefs.getIntPref(prefPath); 20 | case 128: 21 | return sPrefs.getBoolPref(prefPath); 22 | } 23 | } catch (_) { 24 | return undefined; 25 | } 26 | }, 27 | 28 | set: function (prefPath, value, def = false) { 29 | let sPrefs = def ? Services.prefs.getDefaultBranch(null) : Services.prefs; 30 | 31 | switch (typeof value) { 32 | case 'string': 33 | return sPrefs.setCharPref(prefPath, value) || value; 34 | case 'number': 35 | return sPrefs.setIntPref(prefPath, value) || value; 36 | case 'boolean': 37 | return sPrefs.setBoolPref(prefPath, value) || value; 38 | } 39 | }, 40 | 41 | lockedBackupDef: {}, 42 | 43 | lock: function (prefPath, value) { 44 | let sPrefs = Services.prefs; 45 | this.lockedBackupDef[prefPath] = this.get(prefPath, true); 46 | 47 | if (sPrefs.prefIsLocked(prefPath)) { 48 | sPrefs.unlockPref(prefPath); 49 | } 50 | 51 | this.set(prefPath, value, true); 52 | sPrefs.lockPref(prefPath); 53 | }, 54 | 55 | unlock: function (prefPath) { 56 | Services.prefs.unlockPref(prefPath); 57 | 58 | let bkp = this.lockedBackupDef[prefPath]; 59 | if (bkp === undefined) { 60 | Services.prefs.deleteBranch(prefPath); 61 | } else { 62 | this.set(prefPath, bkp, true); 63 | } 64 | }, 65 | 66 | clear: Services.prefs.clearUserPref, 67 | 68 | addListener: function (prefPath, trat) { 69 | this.observer = function (aSubject, aTopic, prefPath) { 70 | return trat(xPref.get(prefPath), prefPath); 71 | } 72 | 73 | Services.prefs.addObserver(prefPath, this.observer); 74 | 75 | return { 76 | prefPath: prefPath, 77 | observer: this.observer 78 | }; 79 | }, 80 | 81 | removeListener: function (obs) { 82 | Services.prefs.removeObserver(obs.prefPath, obs.observer); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /native/userchrome/runtime/_autoconfig.cfg: -------------------------------------------------------------------------------- 1 | // Any comment. You must start the file with a single-line comment! 2 | 3 | // Register chrome manifest to load chrome modifications 4 | const cmanifest = Services.dirsvc.get('UChrm', Ci.nsIFile); 5 | cmanifest.append('pwa'); 6 | cmanifest.append('chrome.manifest'); 7 | Components.manager.QueryInterface(Ci.nsIComponentRegistrar).autoRegister(cmanifest); 8 | 9 | // Import the main boot file 10 | try { ChromeUtils.importESModule('resource://pwa/boot.sys.mjs'); } catch (error) { Cu.reportError(error); } 11 | 12 | // Import the optional user boot files 13 | try { ChromeUtils.importESModule('resource://user/boot.sys.mjs'); } catch (_) {} 14 | -------------------------------------------------------------------------------- /native/userchrome/runtime/defaults/pref/autoconfig.js: -------------------------------------------------------------------------------- 1 | // Any comment. You must start the file with a single-line comment! 2 | 3 | pref('general.config.filename', '_autoconfig.cfg'); 4 | pref('general.config.obscure_value', 0); 5 | pref('general.config.sandbox_enabled', false); 6 | --------------------------------------------------------------------------------