Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Fix dictionaries being downloaded too late #6886

Merged
merged 1 commit into from
Apr 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
32 changes: 8 additions & 24 deletions src/desktop/ApplicationWindow.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { BrowserWindow, ContextMenuParams, NativeImage, Result, Session } from "electron"
import type { BrowserWindow, ContextMenuParams, NativeImage, Result } from "electron"
import type { WindowBounds, WindowManager } from "./DesktopWindowManager"
import url from "node:url"
import type { lazy } from "@tutao/tutanota-utils"
Expand Down Expand Up @@ -73,7 +73,6 @@ export class ApplicationWindow {
private readonly localShortcut: LocalShortcutManager,
private readonly themeFacade: DesktopThemeFacade,
private readonly remoteBridge: RemoteBridge,
dictUrl: string,
noAutoLogin?: boolean | null,
preloadOverridePath?: string,
) {
Expand Down Expand Up @@ -169,7 +168,6 @@ export class ApplicationWindow {
this.createBrowserWindow(wm, {
preloadPath,
icon,
dictUrl,
})
this.initFacades()

Expand Down Expand Up @@ -268,10 +266,10 @@ export class ApplicationWindow {
opts: {
preloadPath: string
icon: NativeImage
dictUrl: string
},
) {
const { preloadPath, dictUrl, icon } = opts
const { preloadPath, icon } = opts

this._browserWindow = new this.electron.BrowserWindow({
icon,
show: false,
Expand Down Expand Up @@ -299,6 +297,11 @@ export class ApplicationWindow {
},
})

const session = this._browserWindow.webContents.session
session.setPermissionRequestHandler((webContents, permission, callback: (_: boolean) => void) => callback(false))

handleProtocols(session, this.absoluteAssetsPath)

this._browserWindow.setMenuBarVisibility(false)

this._browserWindow.removeMenu()
Expand All @@ -307,13 +310,6 @@ export class ApplicationWindow {

this.id = this._browserWindow.id

const session = this._browserWindow.webContents.session
session.setPermissionRequestHandler((webContents, permission, callback: (_: boolean) => void) => callback(false))

handleProtocols(session, this.absoluteAssetsPath)

this.manageDownloadsForSession(session, dictUrl)

this._browserWindow
.on("closed", async () => {
await this.closeDb()
Expand Down Expand Up @@ -484,18 +480,6 @@ export class ApplicationWindow {
this._desktopFacade.addShortcuts(webShortcuts)
}

private manageDownloadsForSession(session: Session, dictUrl: string) {
dictUrl = dictUrl + "/dictionaries/"
log.debug(TAG, "getting dictionaries from:", dictUrl)
session.setSpellCheckerDictionaryDownloadURL(dictUrl)
session
.removeAllListeners("spellcheck-dictionary-download-failure")
.on("spellcheck-dictionary-initialized", (ev, lcode) => log.debug(TAG, "spellcheck-dictionary-initialized", lcode))
.on("spellcheck-dictionary-download-begin", (ev, lcode) => log.debug(TAG, "spellcheck-dictionary-download-begin", lcode))
.on("spellcheck-dictionary-download-success", (ev, lcode) => log.debug(TAG, "spellcheck-dictionary-download-success", lcode))
.on("spellcheck-dictionary-download-failure", (ev, lcode) => log.debug(TAG, "spellcheck-dictionary-download-failure", lcode))
}

private tryGoBack(): void {
const parsedUrl = url.parse(this._browserWindow.webContents.getURL())

Expand Down
22 changes: 21 additions & 1 deletion src/desktop/DesktopMain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { mp } from "./DesktopMonkeyPatch"
import { err } from "./DesktopErrorHandler"
import { DesktopConfig } from "./config/DesktopConfig"
import * as electron from "electron"
import { app } from "electron"
import { app, type Session } from "electron"
import { DesktopUtils } from "./DesktopUtils"
import { setupAssetProtocol, WindowManager } from "./DesktopWindowManager"
import { DesktopNotifier } from "./DesktopNotifier"
Expand Down Expand Up @@ -73,6 +73,8 @@ dns.setDefaultResultOrder("ipv4first")

setupAssetProtocol(electron)

const TAG = "[DesktopMain]"

mp()
type Components = {
readonly wm: WindowManager
Expand Down Expand Up @@ -189,6 +191,12 @@ async function createComponents(): Promise<Components> {
}

const offlineDbRefCounter = new OfflineDbRefCounter(offlineDbFactory)
const updateUrl = await conf.getConst(BuildConfigKey.updateUrl)
const dictUrl = updateUrl ? updateUrl : "https://app.tuta.com/desktop/"

electron.app.on("session-created", async (session) => {
manageDownloadsForSession(session, dictUrl)
})

const wm = new WindowManager(conf, tray, notifier, electron, shortcutManager, appIcon)
const themeFacade = new DesktopThemeFacade(conf, wm, electron.nativeTheme)
Expand Down Expand Up @@ -339,3 +347,15 @@ async function main(components: Components) {
integrator.runIntegration(wm)
await desktopUtils.handleMailto(components.wm)
}

function manageDownloadsForSession(session: Session, dictUrl: string) {
dictUrl = dictUrl + "/dictionaries/"
log.debug(TAG, "getting dictionaries from:", dictUrl)
session.setSpellCheckerDictionaryDownloadURL(dictUrl)
session
.removeAllListeners("spellcheck-dictionary-download-failure")
.on("spellcheck-dictionary-initialized", (ev, lcode) => log.debug(TAG, "spellcheck-dictionary-initialized", lcode))
.on("spellcheck-dictionary-download-begin", (ev, lcode) => log.debug(TAG, "spellcheck-dictionary-download-begin", lcode))
.on("spellcheck-dictionary-download-success", (ev, lcode) => log.debug(TAG, "spellcheck-dictionary-download-success", lcode))
.on("spellcheck-dictionary-download-failure", (ev, lcode) => log.debug(TAG, "spellcheck-dictionary-download-failure", lcode))
}
3 changes: 0 additions & 3 deletions src/desktop/DesktopWindowManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -283,8 +283,6 @@ export class WindowManager {

private async _newWindow(electron: ElectronExports, localShortcut: LocalShortcutManager, noAutoLogin: boolean | null): Promise<ApplicationWindow> {
const absoluteWebAssetsPath = this._electron.app.getAppPath()
const updateUrl = await this._conf.getConst(BuildConfigKey.updateUrl)
const dictUrl = updateUrl && updateUrl !== "" ? updateUrl : "https://app.tuta.com/desktop/"
// custom builds get the dicts from us as well
return new ApplicationWindow(
this,
Expand All @@ -294,7 +292,6 @@ export class WindowManager {
localShortcut,
this.themeFacade,
this.remoteBridge,
dictUrl,
noAutoLogin,
this.preloadOverride,
)
Expand Down
41 changes: 19 additions & 22 deletions test/tests/desktop/ApplicationWindowTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import BrowserWindow = Electron.BrowserWindow

const { anything } = matchers

const dictUrl = "dictUrl"
o.spec("ApplicationWindow Test", function () {
const electronLocalshortcut = {
callbacks: Object.create(null),
Expand Down Expand Up @@ -304,7 +303,7 @@ o.spec("ApplicationWindow Test", function () {

o("construction", async function () {
const { electronMock, wmMock, electronLocalshortcutMock, themeFacade, remoteBridge } = standardMocks()
const w = new ApplicationWindow(wmMock, desktopHtml, icon, electronMock, electronLocalshortcutMock, themeFacade, remoteBridge, dictUrl)
const w = new ApplicationWindow(wmMock, desktopHtml, icon, electronMock, electronLocalshortcutMock, themeFacade, remoteBridge)
o(electronMock.BrowserWindow.mockedInstances.length).equals(1)
const bwInstance: BrowserWindow = electronMock.BrowserWindow.mockedInstances[0]
// We load some things async before loading URL so we wait for it. __loadedUrl comes from our mock
Expand Down Expand Up @@ -346,8 +345,6 @@ o.spec("ApplicationWindow Test", function () {
o(bwInstance.setMenuBarVisibility.callCount).equals(1)
o(bwInstance.setMenuBarVisibility.args[0]).equals(false)
o(bwInstance.removeMenu.callCount).equals(1)
o(bwInstance.webContents.session.setSpellCheckerDictionaryDownloadURL.callCount).equals(1)
o(bwInstance.webContents.session.setSpellCheckerDictionaryDownloadURL.args[0]).equals(dictUrl + "/dictionaries/")
o(Object.keys((bwInstance.webContents as any).callbacks)).deepEquals([
"will-attach-webview",
"will-navigate",
Expand All @@ -369,7 +366,7 @@ o.spec("ApplicationWindow Test", function () {
o("construction, noAutoLogin", async function () {
const { electronMock, wmMock, electronLocalshortcutMock, themeFacade, remoteBridge } = standardMocks()
// noAutoLogin=true
const w2 = new ApplicationWindow(wmMock, desktopHtml, icon, electronMock, electronLocalshortcutMock, themeFacade, remoteBridge, dictUrl, true)
const w2 = new ApplicationWindow(wmMock, desktopHtml, icon, electronMock, electronLocalshortcutMock, themeFacade, remoteBridge, true)
const bwInstance2 = electronMock.BrowserWindow.mockedInstances[0]
await bwInstance2.__loadedUrl.promise
o(bwInstance2.loadURL.callCount).equals(1)
Expand All @@ -382,7 +379,7 @@ o.spec("ApplicationWindow Test", function () {

o("redirect to start page after failing to load a page due to 404", async function () {
const { wmMock, electronMock, electronLocalshortcutMock, themeFacade, remoteBridge } = standardMocks()
const w = new ApplicationWindow(wmMock, desktopHtml, icon, electronMock, electronLocalshortcutMock, themeFacade, remoteBridge, dictUrl)
const w = new ApplicationWindow(wmMock, desktopHtml, icon, electronMock, electronLocalshortcutMock, themeFacade, remoteBridge)
const bwInstance = electronMock.BrowserWindow.mockedInstances[0]
await bwInstance.__loadedUrl.promise
bwInstance.__loadedUrl = defer()
Expand All @@ -402,7 +399,7 @@ o.spec("ApplicationWindow Test", function () {
n.setPlatform("linux")
const { electronLocalshortcutMock, wmMock, electronMock, themeFacade, remoteBridge } = standardMocks()

const w = new ApplicationWindow(wmMock, desktopHtml, icon, electronMock, electronLocalshortcutMock, themeFacade, remoteBridge, dictUrl)
const w = new ApplicationWindow(wmMock, desktopHtml, icon, electronMock, electronLocalshortcutMock, themeFacade, remoteBridge)
downcast(w._browserWindow.webContents).callbacks["did-finish-load"]()
o(Object.keys(electronLocalshortcutMock.callbacks)).deepEquals([
"Control+F",
Expand All @@ -420,7 +417,7 @@ o.spec("ApplicationWindow Test", function () {
o("shortcut creation, windows", function () {
n.setPlatform("win32")
const { electronLocalshortcutMock, wmMock, electronMock, themeFacade, remoteBridge } = standardMocks()
const w = new ApplicationWindow(wmMock, desktopHtml, icon, electronMock, electronLocalshortcutMock, themeFacade, remoteBridge, dictUrl)
const w = new ApplicationWindow(wmMock, desktopHtml, icon, electronMock, electronLocalshortcutMock, themeFacade, remoteBridge)
downcast(w._browserWindow.webContents).callbacks["did-finish-load"]()
o(Object.keys(electronLocalshortcutMock.callbacks)).deepEquals([
"Control+F",
Expand All @@ -439,7 +436,7 @@ o.spec("ApplicationWindow Test", function () {
n.setPlatform("darwin")
const { electronLocalshortcutMock, wmMock, electronMock, themeFacade, remoteBridge } = standardMocks()

const w = new ApplicationWindow(wmMock, desktopHtml, icon, electronMock, electronLocalshortcutMock, themeFacade, remoteBridge, dictUrl)
const w = new ApplicationWindow(wmMock, desktopHtml, icon, electronMock, electronLocalshortcutMock, themeFacade, remoteBridge)
downcast(w._browserWindow.webContents).callbacks["did-finish-load"]()
o(Object.keys(electronLocalshortcutMock.callbacks)).deepEquals(["Command+F", "Command+P", "F12", "Command+0", "Command+Q", "Command+Control+F"])
})
Expand All @@ -449,7 +446,7 @@ o.spec("ApplicationWindow Test", function () {
const sm = standardMocks()
const { electronMock, electronLocalshortcutMock, wmMock, themeFacade, remoteBridge } = sm

const w = new ApplicationWindow(wmMock, desktopHtml, icon, electronMock, electronLocalshortcutMock, themeFacade, remoteBridge, dictUrl)
const w = new ApplicationWindow(wmMock, desktopHtml, icon, electronMock, electronLocalshortcutMock, themeFacade, remoteBridge)
const bwInstance = electronMock.BrowserWindow.mockedInstances[0]
;(bwInstance.webContents as any).callbacks["did-finish-load"]()
// ApplicationWindow waits for IPC and this is a reliable way to also wait for it
Expand Down Expand Up @@ -536,7 +533,7 @@ o.spec("ApplicationWindow Test", function () {
n.setPlatform("linux")
const { electronMock, electronLocalshortcutMock, wmMock, themeFacade, remoteBridge, desktopFacade } = standardMocks()

const w = new ApplicationWindow(wmMock, desktopHtml, icon, electronMock, electronLocalshortcutMock, themeFacade, remoteBridge, dictUrl)
const w = new ApplicationWindow(wmMock, desktopHtml, icon, electronMock, electronLocalshortcutMock, themeFacade, remoteBridge)
const bwInstance = electronMock.BrowserWindow.mockedInstances[0]
bwInstance.webContents.callbacks["did-finish-load"]()
verify(desktopFacade.addShortcuts(anything()))
Expand All @@ -558,7 +555,7 @@ o.spec("ApplicationWindow Test", function () {
const e = {
preventDefault: spy(),
}
const w = new ApplicationWindow(wmMock, desktopHtml, icon, electronMock, electronLocalshortcutMock, themeFacade, remoteBridge, dictUrl)
const w = new ApplicationWindow(wmMock, desktopHtml, icon, electronMock, electronLocalshortcutMock, themeFacade, remoteBridge)
const bwInstance = electronMock.BrowserWindow.mockedInstances[0]
bwInstance.webContents.callbacks["will-navigate"](e, "http://test.com")
o(e.preventDefault.callCount).equals(1)("Prevent default is called")
Expand All @@ -567,7 +564,7 @@ o.spec("ApplicationWindow Test", function () {
o("attaching webView is denied", function () {
const { electronMock, wmMock, electronLocalshortcutMock, themeFacade, remoteBridge } = standardMocks()

const w = new ApplicationWindow(wmMock, desktopHtml, icon, electronMock, electronLocalshortcutMock, themeFacade, remoteBridge, dictUrl)
const w = new ApplicationWindow(wmMock, desktopHtml, icon, electronMock, electronLocalshortcutMock, themeFacade, remoteBridge)
const bwInstance = electronMock.BrowserWindow.mockedInstances[0]
const e = {
preventDefault: spy(),
Expand All @@ -593,7 +590,7 @@ o.spec("ApplicationWindow Test", function () {
electronMock = sm.electronMock
let { wmMock, electronLocalshortcutMock, themeFacade, remoteBridge } = sm

new ApplicationWindow(wmMock, desktopHtml, icon, electronMock, electronLocalshortcutMock, themeFacade, remoteBridge, dictUrl)
new ApplicationWindow(wmMock, desktopHtml, icon, electronMock, electronLocalshortcutMock, themeFacade, remoteBridge)
bwInstance = electronMock.BrowserWindow.mockedInstances[0]
})
o("not url is not redirected", function () {
Expand Down Expand Up @@ -658,7 +655,7 @@ o.spec("ApplicationWindow Test", function () {
o("context-menu is passed to handler", function () {
const { electronMock, wmMock, electronLocalshortcutMock, themeFacade, remoteBridge } = standardMocks()

const w = new ApplicationWindow(wmMock, desktopHtml, icon, electronMock, electronLocalshortcutMock, themeFacade, remoteBridge, dictUrl)
const w = new ApplicationWindow(wmMock, desktopHtml, icon, electronMock, electronLocalshortcutMock, themeFacade, remoteBridge)
const handlerMock = n.spyify(() => {})
w.setContextMenuHandler(handlerMock)
const bwInstance = electronMock.BrowserWindow.mockedInstances[0]
Expand All @@ -682,7 +679,7 @@ o.spec("ApplicationWindow Test", function () {
o("openMailbox sends mailbox info and shows window", async function () {
const { electronMock, wmMock, electronLocalshortcutMock, themeFacade, remoteBridge, commonNativeFacade } = standardMocks()

const w = new ApplicationWindow(wmMock, desktopHtml, icon, electronMock, electronLocalshortcutMock, themeFacade, remoteBridge, dictUrl)
const w = new ApplicationWindow(wmMock, desktopHtml, icon, electronMock, electronLocalshortcutMock, themeFacade, remoteBridge)
w.openMailBox(
{
userId: "userId",
Expand All @@ -699,7 +696,7 @@ o.spec("ApplicationWindow Test", function () {
n.setPlatform("linux")
const { electronMock, wmMock, electronLocalshortcutMock, themeFacade, remoteBridge } = standardMocks()

const w = new ApplicationWindow(wmMock, desktopHtml, icon, electronMock, electronLocalshortcutMock, themeFacade, remoteBridge, dictUrl)
const w = new ApplicationWindow(wmMock, desktopHtml, icon, electronMock, electronLocalshortcutMock, themeFacade, remoteBridge)
o(w.getBounds()).deepEquals({
rect: {
height: 0,
Expand Down Expand Up @@ -784,7 +781,7 @@ o.spec("ApplicationWindow Test", function () {
o("findInPage, setSearchOverlayState & stopFindInPage", function () {
const { electronMock, wmMock, electronLocalshortcutMock, themeFacade, remoteBridge } = standardMocks()

const w = new ApplicationWindow(wmMock, desktopHtml, icon, electronMock, electronLocalshortcutMock, themeFacade, remoteBridge, dictUrl)
const w = new ApplicationWindow(wmMock, desktopHtml, icon, electronMock, electronLocalshortcutMock, themeFacade, remoteBridge)
const wcMock = electronMock.BrowserWindow.mockedInstances[0].webContents
w.stopFindInPage()
o(wcMock.stopFindInPage.callCount).equals(1)
Expand Down Expand Up @@ -839,7 +836,7 @@ o.spec("ApplicationWindow Test", function () {
o("show", function () {
const { electronMock, wmMock, electronLocalshortcutMock, themeFacade, remoteBridge } = standardMocks()

const w = new ApplicationWindow(wmMock, desktopHtml, icon, electronMock, electronLocalshortcutMock, themeFacade, remoteBridge, dictUrl)
const w = new ApplicationWindow(wmMock, desktopHtml, icon, electronMock, electronLocalshortcutMock, themeFacade, remoteBridge)
const bwMock = electronMock.BrowserWindow.mockedInstances[0]
o(bwMock.devToolsOpened).equals(false)
w.show()
Expand Down Expand Up @@ -870,7 +867,7 @@ o.spec("ApplicationWindow Test", function () {
o("on, once, getTitle, setZoomFactor, isFullScreen, isMinimized, minimize, hide, center, showInactive, isFocused", function () {
const { electronMock, wmMock, electronLocalshortcutMock, themeFacade, remoteBridge } = standardMocks()

const w = new ApplicationWindow(wmMock, desktopHtml, icon, electronMock, electronLocalshortcutMock, themeFacade, remoteBridge, dictUrl)
const w = new ApplicationWindow(wmMock, desktopHtml, icon, electronMock, electronLocalshortcutMock, themeFacade, remoteBridge)
const bwInstance = electronMock.BrowserWindow.mockedInstances[0]

let f = () => {}
Expand Down Expand Up @@ -908,7 +905,7 @@ o.spec("ApplicationWindow Test", function () {
o("when closing, database is closed", function () {
const { electronMock, wmMock, electronLocalshortcutMock, themeFacade, remoteBridge, sqlCipherFacade } = standardMocks()

const w = new ApplicationWindow(wmMock, desktopHtml, icon, electronMock, electronLocalshortcutMock, themeFacade, remoteBridge, dictUrl)
const w = new ApplicationWindow(wmMock, desktopHtml, icon, electronMock, electronLocalshortcutMock, themeFacade, remoteBridge)
const userId = "123"
w.setUserId(userId)
const bwInstance = electronMock.BrowserWindow.mockedInstances[0]
Expand All @@ -920,7 +917,7 @@ o.spec("ApplicationWindow Test", function () {
o("when reloading, database is closed", async function () {
const { electronMock, wmMock, electronLocalshortcutMock, themeFacade, remoteBridge, sqlCipherFacade } = standardMocks()

const w = new ApplicationWindow(wmMock, desktopHtml, icon, electronMock, electronLocalshortcutMock, themeFacade, remoteBridge, dictUrl)
const w = new ApplicationWindow(wmMock, desktopHtml, icon, electronMock, electronLocalshortcutMock, themeFacade, remoteBridge)
const userId = "123"
w.setUserId(userId)

Expand Down