Skip to content
PalmerAL edited this page Apr 12, 2020 · 5 revisions

Min generally follows the standard architecture of an Electron application. However, in addition to having a single renderer process that runs the app's UI (the "UI process"), Min also has a separate renderer process for each tab. This results in the following architecture:

  • The main process is responsible for opening windows, creating the app menu, and handling communication between the other processes. It is also responsible for filtering network requests (if content blocking is enabled), and forwarding notifications for downloaded files and permission prompts to the UI process.
    • Code that runs in this process is mostly located in the main directory.
  • The UI process is the renderer process that displays the app UI - including the tab bar, task overlay, download manager, etc.
    • Like the main process, the UI process has full node access. This means that it can launch subprocesses, read local files, etc, but it shouldn't be used to display untrusted content.
    • This process loads the 'index.html' file located at the root of the repository, which in turn loads runs most of the code in the js/ directory.
  • The places worker is a separate thread that runs within the UI process. It handles tasks that could potentially take a long time (such as history search and indexing) to avoid slowing down the UI.
  • Each tab runs in it's own process. These processes are sandboxed, so they don't have access to the Node API.
    • Each time a new page is loaded in one of these processes, a preload script is also loaded. This script also runs within the sandbox, but it has access to Electron's ipc module, allowing it to send data back to the main process. Examples of what the preload script does include:
      • Extracting the text content of the page to use for full-text search
      • Analyzing the page content to determine if it can be opened in reader mode
      • Detecting password inputs on the page that can be autofilled.
      • The code for this script is located in the js/preload/ directory.

In many cases, the tab and UI processes need to send data to each other - for example, typing a new URL in the searchbar (of the UI process) should load that page in the tab process, and when the page loads, it should send its title to the UI process to be displayed. However, the UI process and tabs don't communicate directly with each other - instead, they send messages to the main process, which then forwards them to their destination.

The main process, places worker, and preload script are all made up of a series of scripts that are concatenated together, so you can reference the contents of any script from any other. The UI process previously used this approach as well, but is gradually being refactored into modules that can be imported. This means that in some cases, you can reference a function that you wish to use directly, but in other cases, it will need to be imported.

Useful modules

Localization system (Main, UI, content)

  • The strings for each language are located in the localization/languages/ directory. If you add a new string, you'll need to add it to each language file (find and replace is useful for this).
  • Running npm run buildLocalization generates a dist/localization.build.js file that can be loaded in any page.
  • Once this file is loaded, you can call l(stringName) anywhere to get a localized string in the user's current language.
  • There are also some special attributes you can use to automatically load strings inside HTML files (however, these will only evaluated once when the document is loaded);
    • data-string sets the element's text content
    • data-label sets the element's title attribute
    • data-value sets the element's value attribute

Settings (Main, UI, Content)

  • settings.get(name) returns the setting's current value.
  • settings.set(name, value) updates the setting.
  • Calling settings.listen(name, cb) will call cb(value) once after the settings are loaded, and again whenever the value of the setting is changed. In most cases, this should be used instead of settings.get so that setting changes can take effect without requiring a restart.

In-content pages (such as the settings page itself) use a different implementation of this (settingsContent.js) that transfers settings data over IPC, but the API is the same.

Webviews (UI)

  • Responsible for managing communication between the main process (viewManager.js) and currently-open BrowserViews (one per tab).
  • Each view is just a webContents, so you can use all the methods and events described in the Electron documentation.
    • To call a method on a tab, use webviews.callAsync(tabId, methodName, arguments, callback)
    • To attach an event listener to all views, use webviews.bindEvent(event, callback, options)
    • To listen for an IPC message from a view, use webviews.bindIPC(name, callback)
  • BrowserViews appear on top of all other UI elements, so if you want to display content on top of a tab, you'll need to either create a popup window or call webviews.requestPlaceholder(name). Doing this will hide the current tab and replace it with a blurred preview image that other content can appear on top of. Calling webviews.hidePlaceholder(name) will show the tab again as soon as long as no other code is still requesting a placeholder.

BrowserUI (UI)

  • Handles common UI actions, such as creating or closing tabs, and calls the appropriate functions in webviews, tabBar, and tabState.
Clone this wiki locally