diff --git a/Base/res/ladybird/about-pages/settings.html b/Base/res/ladybird/about-pages/settings.html index 0477a65fcfb..cfb223a7c2e 100644 --- a/Base/res/ladybird/about-pages/settings.html +++ b/Base/res/ladybird/about-pages/settings.html @@ -500,45 +500,7 @@ - - - newTabPageURL.addEventListener("change", () => { - newTabPageURL.classList.remove("success"); - newTabPageURL.classList.remove("error"); + + + + + + - if (!containsValidURL(newTabPageURL)) { - newTabPageURL.classList.add("error"); - return; - } - - ladybird.sendMessage("setNewTabPageURL", newTabPageURL.value); - newTabPageURL.classList.add("success"); - - setTimeout(() => { - newTabPageURL.classList.remove("success"); - }, 1000); - }); - - const languageDisplayName = language => { - const item = window.languages.find(item => item.language === language); - return item.displayName; - }; - - const saveLanguages = () => { - ladybird.sendMessage("setLanguages", window.settings.languages); - }; - - const moveLanguage = (from, to) => { - [window.settings.languages[from], window.settings.languages[to]] = [ - window.settings.languages[to], - window.settings.languages[from], - ]; - - saveLanguages(); - }; - - const removeLanguage = index => { - window.settings.languages.splice(index, 1); - saveLanguages(); - }; - - const loadLanguages = () => { - for (const language of window.languages) { - const option = document.createElement("option"); - option.text = language.displayName; - option.value = language.language; - - languagesSelect.add(option); - } - }; - - const showLanguages = () => { - languagesList.innerHTML = ""; - - window.settings.languages.forEach((language, index) => { - const name = document.createElement("span"); - name.className = "dialog-list-item-label"; - name.textContent = languageDisplayName(language); - - const moveUp = document.createElement("button"); - moveUp.className = "dialog-button"; - moveUp.innerHTML = upwardArrowSVG; - moveUp.title = "Move up"; - - if (index === 0) { - moveUp.disabled = true; - } else { - moveUp.addEventListener("click", () => { - moveLanguage(index, index - 1); - }); - } - - const moveDown = document.createElement("button"); - moveDown.className = "dialog-button"; - moveDown.innerHTML = downwardArrowSVG; - moveDown.title = "Move down"; - - if (index === window.settings.languages.length - 1) { - moveDown.disabled = true; - } else { - moveDown.addEventListener("click", () => { - moveLanguage(index, index + 1); - }); - } - - const remove = document.createElement("button"); - remove.className = "dialog-button"; - remove.innerHTML = "×"; - remove.title = "Remove"; - - if (window.settings.languages.length <= 1) { - remove.disabled = true; - } else { - remove.addEventListener("click", () => { - removeLanguage(index); - }); - } - - const controls = document.createElement("div"); - controls.className = "dialog-controls"; - controls.appendChild(moveUp); - controls.appendChild(moveDown); - controls.appendChild(remove); - - const item = document.createElement("div"); - item.className = "dialog-list-item"; - item.appendChild(name); - item.appendChild(controls); - - languagesList.appendChild(item); - }); - - for (const language of languagesSelect.options) { - language.disabled = window.settings.languages.includes(language.value); - } - - if (!languagesDialog.open) { - setTimeout(() => languagesSelect.focus()); - languagesDialog.showModal(); - } - }; - - languagesAdd.addEventListener("click", () => { - const language = languagesSelect.value; - - languagesAdd.disabled = true; - languagesSelect.selectedIndex = 0; - - if (!language || window.settings.languages.includes(language)) { - return; - } - - window.settings.languages.push(language); - saveLanguages(); - }); - - languagesClose.addEventListener("click", () => { - languagesDialog.close(); - }); - - languagesSelect.addEventListener("change", () => { - languagesAdd.disabled = !languagesSelect.value; - }); - - languagesSettings.addEventListener("click", event => { - showLanguages(); - event.stopPropagation(); - }); - - const Engine = Object.freeze({ - search: 1, - autocomplete: 2, - }); - - const engineForType = engine => { - if (engine === Engine.search) { - return ["Search", searchToggle, searchEngine]; - } - if (engine === Engine.autocomplete) { - return ["Autocomplete", autocompleteToggle, autocompleteEngine]; - } - throw Error(`Unrecognized engine type ${engine}`); - }; - - const loadEngines = (type, engines) => { - const [name, toggle, engine] = engineForType(type); - - for (const engineName of engines) { - const option = document.createElement("option"); - option.text = engineName; - option.value = engineName; - - engine.add(option); - } - - if (type === Engine.search) { - window.nativeSearchEngineCount = engine.length; - engine.appendChild(document.createElement("hr")); - } - }; - - const renderEngine = type => { - const [name, toggle, engine] = engineForType(type); - - if (toggle.checked) { - engine.closest(".card-group").classList.remove("hidden"); - } else { - engine.closest(".card-group").classList.add("hidden"); - } - - if (toggle.checked && engine.selectedIndex !== 0) { - engine.item(0).disabled = true; - } else if (!toggle.checked) { - engine.item(0).disabled = false; - engine.selectedIndex = 0; - } - }; - - const saveEngine = type => { - const [name, toggle, engine] = engineForType(type); - - if (toggle.checked && engine.selectedIndex !== 0) { - ladybird.sendMessage(`set${name}Engine`, engine.value); - } else if (!toggle.checked) { - ladybird.sendMessage(`set${name}Engine`, null); - } - - renderEngine(type); - }; - - const setSaveEngineListeners = type => { - const [name, toggle, engine] = engineForType(type); - - toggle.addEventListener("change", () => { - saveEngine(type); - }); - engine.addEventListener("change", () => { - saveEngine(type); - }); - }; - - setSaveEngineListeners(Engine.search); - setSaveEngineListeners(Engine.autocomplete); - - const loadCustomSearchEngines = () => { - while (searchEngine.length > window.nativeSearchEngineCount) { - searchEngine.remove(window.nativeSearchEngineCount); - } - - const custom = window.settings.searchEngine?.custom || []; - - custom.forEach(custom => { - const option = document.createElement("option"); - option.text = custom.name; - option.value = custom.name; - - searchEngine.add(option); - }); - }; - - const showSearchEngineSettings = () => { - searchCustomName.classList.remove("error"); - searchCustomURL.classList.remove("error"); - searchList.innerHTML = ""; - - const custom = window.settings.searchEngine?.custom || []; - - if (custom.length === 0) { - const placeholder = document.createElement("div"); - placeholder.className = "dialog-list-item-placeholder"; - placeholder.textContent = "No custom search engines added"; - - searchList.appendChild(placeholder); - } - - custom.forEach(custom => { - const name = document.createElement("span"); - name.textContent = custom.name; - - const url = document.createElement("span"); - url.className = "dialog-list-item-placeholder"; - url.style = "padding-left: 0"; - url.textContent = ` — ${custom.url}`; - - const engine = document.createElement("span"); - engine.className = "dialog-list-item-label"; - engine.appendChild(name); - engine.appendChild(url); - - const remove = document.createElement("button"); - remove.className = "dialog-button"; - remove.innerHTML = "×"; - remove.title = `Remove ${custom.name}`; - - remove.addEventListener("click", () => { - ladybird.sendMessage("removeCustomSearchEngine", custom); - }); - - const item = document.createElement("div"); - item.className = "dialog-list-item"; - item.appendChild(engine); - item.appendChild(remove); - - searchList.appendChild(item); - }); - - if (!searchDialog.open) { - setTimeout(() => searchCustomName.focus()); - searchDialog.showModal(); - } - }; - - const addCustomSearchEngine = () => { - searchCustomName.classList.remove("error"); - searchCustomURL.classList.remove("error"); - - for (const i = 0; i < searchEngine.length; ++i) { - if (searchCustomName.value === searchEngine.item(i).value) { - searchCustomName.classList.add("error"); - return; - } - } - - if (!containsValidURL(searchCustomURL)) { - searchCustomURL.classList.add("error"); - return; - } - - ladybird.sendMessage("addCustomSearchEngine", { - name: searchCustomName.value, - url: searchCustomURL.value, - }); - - searchCustomName.value = ""; - searchCustomURL.value = ""; - - setTimeout(() => searchCustomName.focus()); - }; - - searchCustomAdd.addEventListener("click", addCustomSearchEngine); - - searchCustomName.addEventListener("keydown", event => { - if (event.key === "Enter") { - addCustomSearchEngine(); - } - }); - - searchCustomURL.addEventListener("keydown", event => { - if (event.key === "Enter") { - addCustomSearchEngine(); - } - }); - - searchClose.addEventListener("click", () => { - searchDialog.close(); - }); - - searchSettings.addEventListener("click", event => { - showSearchEngineSettings(); - event.stopPropagation(); - }); - - const forciblyEnableSiteSettings = settings => { - settings.forEach(setting => { - const label = document.querySelector(`#${setting}-forcibly-enabled`); - label.classList.remove("hidden"); - - const button = document.querySelector(`#${setting}-settings`); - button.classList.add("hidden"); - }); - }; - - const currentSiteSetting = () => { - if (!siteSettings.open) { - return null; - } - - return siteSettingsTitle.innerText.toLowerCase(); - }; - - const showSiteSettings = (title, settings) => { - siteSettingsTitle.innerText = title; - siteSettingsGlobal.checked = settings.enabledGlobally; - siteSettingsList.innerHTML = ""; - - siteSettingsGlobal.onchange = () => { - ladybird.sendMessage("setSiteSettingEnabledGlobally", { - setting: currentSiteSetting(), - enabled: siteSettingsGlobal.checked, - }); - }; - - if (settings.siteFilters.length === 0) { - const placeholder = document.createElement("div"); - placeholder.className = "dialog-list-item-placeholder"; - placeholder.textContent = "No sites added"; - - siteSettingsList.appendChild(placeholder); - } - - settings.siteFilters.forEach(site => { - const filter = document.createElement("span"); - filter.className = "dialog-list-item-label"; - filter.textContent = site; - - const remove = document.createElement("button"); - remove.className = "dialog-button"; - remove.innerHTML = "×"; - remove.title = `Remove ${site}`; - - remove.addEventListener("click", () => { - ladybird.sendMessage("removeSiteSettingFilter", { - setting: currentSiteSetting(), - filter: site, - }); - }); - - const item = document.createElement("div"); - item.className = "dialog-list-item"; - item.appendChild(filter); - item.appendChild(remove); - - siteSettingsList.appendChild(item); - }); - - if (!siteSettings.open) { - setTimeout(() => siteSettingsInput.focus()); - siteSettings.showModal(); - } - }; - - const addSiteSettingFilter = () => { - ladybird.sendMessage("addSiteSettingFilter", { - setting: currentSiteSetting(), - filter: siteSettingsInput.value, - }); - - siteSettingsInput.classList.add("success"); - siteSettingsInput.value = ""; - - setTimeout(() => { - siteSettingsInput.classList.remove("success"); - }, 1000); - - setTimeout(() => siteSettingsInput.focus()); - }; - - siteSettingsAdd.addEventListener("click", addSiteSettingFilter); - - siteSettingsInput.addEventListener("keydown", event => { - if (event.key === "Enter") { - addSiteSettingFilter(); - } - }); - - siteSettingsClose.addEventListener("click", () => { - siteSettings.close(); - }); - - siteSettingsRemoveAll.addEventListener("click", () => { - ladybird.sendMessage("removeAllSiteSettingFilters", { - setting: currentSiteSetting(), - }); - }); - - // DNS settings - const loadDnsSettings = settings => { - dnsUpstream.value = settings.mode; - - if (settings.mode === "custom") { - dnsServer.value = settings.server; - dnsPort.value = settings.port; - dnsType.value = settings.type; - customDnsSettings.classList.remove("hidden"); - } else { - dnsServer.value = ""; - dnsType.value = "udp"; - dnsPort.value = "53"; - customDnsSettings.classList.add("hidden"); - } - - if (settings.forciblyEnabled) { - dnsForciblyEnabled.classList.remove("hidden"); - dnsUpstream.setAttribute("disabled", ""); - dnsServer.setAttribute("disabled", ""); - dnsPort.setAttribute("disabled", ""); - dnsType.setAttribute("disabled", ""); - } else { - dnsForciblyEnabled.classList.add("hidden"); - dnsUpstream.removeAttribute("disabled"); - dnsServer.removeAttribute("disabled"); - dnsPort.removeAttribute("disabled"); - dnsType.removeAttribute("disabled"); - } - }; - - dnsUpstream.addEventListener("change", () => { - if (dnsUpstream.value === "custom") { - customDnsSettings.classList.remove("hidden"); - if (dnsServer.value !== "" && dnsPort.value !== "") { - updateDnsSettings(); - } - } else { - customDnsSettings.classList.add("hidden"); - ladybird.sendMessage("setDNSSettings", { mode: "system" }); - } - }); - - function updateDnsSettings() { - if (dnsUpstream.value === "custom") { - dnsPort.placeholder = dnsType.value === "tls" ? "853" : "53"; - if ((dnsPort.value | 0) === 0) dnsPort.value = dnsPort.placeholder; - - const settings = { - mode: "custom", - server: dnsServer.value, - port: dnsPort.value | 0, - type: dnsType.value, - }; - ladybird.sendMessage("setDNSSettings", settings); - } - } - - dnsServer.addEventListener("change", updateDnsSettings); - dnsPort.addEventListener("change", updateDnsSettings); - dnsType.addEventListener("change", updateDnsSettings); - - autoplaySettings.addEventListener("click", event => { - showSiteSettings("Autoplay", window.settings.autoplay); - event.stopPropagation(); - }); - - doNotTrackToggle.addEventListener("change", () => { - ladybird.sendMessage("setDoNotTrack", doNotTrackToggle.checked); - }); + diff --git a/Base/res/ladybird/about-pages/settings/languages.js b/Base/res/ladybird/about-pages/settings/languages.js index a3dde2b7ea9..644406cb97b 100644 --- a/Base/res/ladybird/about-pages/settings/languages.js +++ b/Base/res/ladybird/about-pages/settings/languages.js @@ -1,9 +1,163 @@ +const languagesAdd = document.querySelector("#languages-add"); +const languagesClose = document.querySelector("#languages-close"); +const languagesDialog = document.querySelector("#languages-dialog"); +const languagesList = document.querySelector("#languages-list"); +const languagesSelect = document.querySelector("#languages-select"); +const languagesSettings = document.querySelector("#languages-settings"); + +let LANGUAGES = {}; + +function loadSettings(settings) { + LANGUAGES = settings.languages; + + if (languagesDialog.open) { + showLanguages(); + } +} + +function languageDisplayName(language) { + const item = AVAILABLE_LANGUAGES.find(item => item.language === language); + return item.displayName; +} + +function saveLanguages() { + ladybird.sendMessage("setLanguages", LANGUAGES); +} + +function moveLanguage(from, to) { + [LANGUAGES[from], LANGUAGES[to]] = [LANGUAGES[to], LANGUAGES[from]]; + saveLanguages(); +} + +function removeLanguage(index) { + LANGUAGES.splice(index, 1); + saveLanguages(); +} + +function loadLanguages() { + for (const language of AVAILABLE_LANGUAGES) { + const option = document.createElement("option"); + option.text = language.displayName; + option.value = language.language; + + languagesSelect.add(option); + } +} + +function showLanguages() { + languagesList.innerHTML = ""; + + LANGUAGES.forEach((language, index) => { + const name = document.createElement("span"); + name.className = "dialog-list-item-label"; + name.textContent = languageDisplayName(language); + + const moveUp = document.createElement("button"); + moveUp.className = "dialog-button"; + moveUp.innerHTML = upwardArrowSVG; + moveUp.title = "Move up"; + + if (index === 0) { + moveUp.disabled = true; + } else { + moveUp.addEventListener("click", () => { + moveLanguage(index, index - 1); + }); + } + + const moveDown = document.createElement("button"); + moveDown.className = "dialog-button"; + moveDown.innerHTML = downwardArrowSVG; + moveDown.title = "Move down"; + + if (index === LANGUAGES.length - 1) { + moveDown.disabled = true; + } else { + moveDown.addEventListener("click", () => { + moveLanguage(index, index + 1); + }); + } + + const remove = document.createElement("button"); + remove.className = "dialog-button"; + remove.innerHTML = "×"; + remove.title = "Remove"; + + if (LANGUAGES.length <= 1) { + remove.disabled = true; + } else { + remove.addEventListener("click", () => { + removeLanguage(index); + }); + } + + const controls = document.createElement("div"); + controls.className = "dialog-controls"; + controls.appendChild(moveUp); + controls.appendChild(moveDown); + controls.appendChild(remove); + + const item = document.createElement("div"); + item.className = "dialog-list-item"; + item.appendChild(name); + item.appendChild(controls); + + languagesList.appendChild(item); + }); + + for (const language of languagesSelect.options) { + language.disabled = LANGUAGES.includes(language.value); + } + + if (!languagesDialog.open) { + setTimeout(() => languagesSelect.focus()); + languagesDialog.showModal(); + } +} + +languagesAdd.addEventListener("click", () => { + const language = languagesSelect.value; + + languagesAdd.disabled = true; + languagesSelect.selectedIndex = 0; + + if (!language || LANGUAGES.includes(language)) { + return; + } + + LANGUAGES.push(language); + saveLanguages(); +}); + +languagesClose.addEventListener("click", () => { + languagesDialog.close(); +}); + +languagesSelect.addEventListener("change", () => { + languagesAdd.disabled = !languagesSelect.value; +}); + +languagesSettings.addEventListener("click", event => { + showLanguages(); + event.stopPropagation(); +}); + +document.addEventListener("WebUILoaded", () => { + loadLanguages(); +}); + +document.addEventListener("WebUIMessage", event => { + if (event.detail.name === "loadSettings") { + loadSettings(event.detail.data); + } +}); + // Rather than creating a list of all languages supported by ICU (of which there are on the order of a thousand), we // create a list of languages that are supported by both Chrome and Firefox. We can extend this list as needed. // // https://github.com/chromium/chromium/blob/main/ui/base/l10n/l10n_util.cc (see kAcceptLanguageList) // https://github.com/mozilla/gecko-dev/blob/master/intl/locale/language.properties -window.languages = (() => { +const AVAILABLE_LANGUAGES = (() => { const display = new Intl.DisplayNames([], { type: "language", languageDisplay: "standard" }); const language = languageID => { diff --git a/Base/res/ladybird/about-pages/settings/network.js b/Base/res/ladybird/about-pages/settings/network.js new file mode 100644 index 00000000000..00956084f66 --- /dev/null +++ b/Base/res/ladybird/about-pages/settings/network.js @@ -0,0 +1,90 @@ +const customDnsSettings = document.querySelector("#custom-dns-settings"); +const dnsForciblyEnabled = document.querySelector("#dns-forcibly-enabled"); + +const dnsUpstream = document.querySelector("#dns-upstream"); +const dnsType = document.querySelector("#dns-type"); +const dnsServer = document.querySelector("#dns-server"); +const dnsPort = document.querySelector("#dns-port"); + +let DNS_SETTINGS = {}; + +function loadSettings(settings) { + DNS_SETTINGS = settings.dnsSettings || {}; + loadDnsSettings(); +} + +function loadDnsSettings() { + dnsUpstream.value = DNS_SETTINGS.mode || "system"; + + if (dnsUpstream.value === "custom") { + dnsType.value = DNS_SETTINGS.type; + dnsServer.value = DNS_SETTINGS.server; + dnsPort.value = DNS_SETTINGS.port; + + customDnsSettings.classList.remove("hidden"); + } else { + dnsType.value = "udp"; + dnsServer.value = ""; + dnsPort.value = "53"; + + customDnsSettings.classList.add("hidden"); + } + + if (DNS_SETTINGS.forciblyEnabled) { + dnsForciblyEnabled.classList.remove("hidden"); + + dnsUpstream.disabled = true; + dnsType.disabled = true; + dnsServer.disabled = true; + dnsPort.disabled = true; + } else { + dnsForciblyEnabled.classList.add("hidden"); + + dnsUpstream.disabled = false; + dnsType.disabled = false; + dnsServer.disabled = false; + dnsPort.disabled = false; + } +} + +dnsUpstream.addEventListener("change", () => { + if (dnsUpstream.value === "custom") { + customDnsSettings.classList.remove("hidden"); + + if (dnsServer.value.length !== 0 && dnsPort.value.length !== 0) { + updateDnsSettings(); + } + } else { + customDnsSettings.classList.add("hidden"); + ladybird.sendMessage("setDNSSettings", { mode: "system" }); + } +}); + +function updateDnsSettings() { + if (dnsUpstream.value !== "custom") { + return; + } + + dnsPort.placeholder = dnsType.value === "tls" ? "853" : "53"; + + if ((dnsPort.value || 0) === 0) { + dnsPort.value = dnsPort.placeholder; + } + + ladybird.sendMessage("setDNSSettings", { + mode: "custom", + type: dnsType.value, + server: dnsServer.value, + port: dnsPort.value | 0, + }); +} + +dnsServer.addEventListener("change", updateDnsSettings); +dnsPort.addEventListener("change", updateDnsSettings); +dnsType.addEventListener("change", updateDnsSettings); + +document.addEventListener("WebUIMessage", event => { + if (event.detail.name === "loadSettings") { + loadSettings(event.detail.data); + } +}); diff --git a/Base/res/ladybird/about-pages/settings/new-tab-page.js b/Base/res/ladybird/about-pages/settings/new-tab-page.js new file mode 100644 index 00000000000..f226ef41908 --- /dev/null +++ b/Base/res/ladybird/about-pages/settings/new-tab-page.js @@ -0,0 +1,29 @@ +const newTabPageURL = document.querySelector("#new-tab-page-url"); + +const loadSettings = settings => { + newTabPageURL.classList.remove("error"); + newTabPageURL.value = settings.newTabPageURL; +}; + +newTabPageURL.addEventListener("change", () => { + newTabPageURL.classList.remove("success"); + newTabPageURL.classList.remove("error"); + + if (!containsValidURL(newTabPageURL)) { + newTabPageURL.classList.add("error"); + return; + } + + ladybird.sendMessage("setNewTabPageURL", newTabPageURL.value); + newTabPageURL.classList.add("success"); + + setTimeout(() => { + newTabPageURL.classList.remove("success"); + }, 1000); +}); + +document.addEventListener("WebUIMessage", event => { + if (event.detail.name === "loadSettings") { + loadSettings(event.detail.data); + } +}); diff --git a/Base/res/ladybird/about-pages/settings/permissions.js b/Base/res/ladybird/about-pages/settings/permissions.js new file mode 100644 index 00000000000..3f0022c04ea --- /dev/null +++ b/Base/res/ladybird/about-pages/settings/permissions.js @@ -0,0 +1,142 @@ +const siteSettings = document.querySelector("#site-settings"); +const siteSettingsAdd = document.querySelector("#site-settings-add"); +const siteSettingsClose = document.querySelector("#site-settings-close"); +const siteSettingsGlobal = document.querySelector("#site-settings-global"); +const siteSettingsList = document.querySelector("#site-settings-list"); +const siteSettingsInput = document.querySelector("#site-settings-input"); +const siteSettingsRemoveAll = document.querySelector("#site-settings-remove-all"); +const siteSettingsTitle = document.querySelector("#site-settings-title"); + +const autoplaySettings = document.querySelector("#autoplay-settings"); + +let AUTOPLAY_SETTINGS = {}; + +function loadSiteSettings(settings) { + AUTOPLAY_SETTINGS = settings.autoplay; + + const siteSetting = currentSiteSetting(); + + if (siteSetting === "autoplay") { + showSiteSettings("Autoplay", AUTOPLAY_SETTINGS); + } +} + +function forciblyEnableSiteSettings(settings) { + settings.forEach(setting => { + const label = document.querySelector(`#${setting}-forcibly-enabled`); + label.classList.remove("hidden"); + + const button = document.querySelector(`#${setting}-settings`); + button.classList.add("hidden"); + }); +} + +function currentSiteSetting() { + if (!siteSettings.open) { + return null; + } + + return siteSettingsTitle.innerText.toLowerCase(); +} + +function showSiteSettings(title, settings) { + siteSettingsTitle.innerText = title; + siteSettingsGlobal.checked = settings.enabledGlobally; + siteSettingsList.innerHTML = ""; + + siteSettingsGlobal.onchange = () => { + ladybird.sendMessage("setSiteSettingEnabledGlobally", { + setting: currentSiteSetting(), + enabled: siteSettingsGlobal.checked, + }); + }; + + if (settings.siteFilters.length === 0) { + const placeholder = document.createElement("div"); + placeholder.className = "dialog-list-item-placeholder"; + placeholder.textContent = "No sites added"; + + siteSettingsList.appendChild(placeholder); + } + + settings.siteFilters.forEach(site => { + const filter = document.createElement("span"); + filter.className = "dialog-list-item-label"; + filter.textContent = site; + + const remove = document.createElement("button"); + remove.className = "dialog-button"; + remove.innerHTML = "×"; + remove.title = `Remove ${site}`; + + remove.addEventListener("click", () => { + ladybird.sendMessage("removeSiteSettingFilter", { + setting: currentSiteSetting(), + filter: site, + }); + }); + + const item = document.createElement("div"); + item.className = "dialog-list-item"; + item.appendChild(filter); + item.appendChild(remove); + + siteSettingsList.appendChild(item); + }); + + if (!siteSettings.open) { + setTimeout(() => siteSettingsInput.focus()); + siteSettings.showModal(); + } +} + +function addSiteSettingFilter() { + ladybird.sendMessage("addSiteSettingFilter", { + setting: currentSiteSetting(), + filter: siteSettingsInput.value, + }); + + siteSettingsInput.classList.add("success"); + siteSettingsInput.value = ""; + + setTimeout(() => { + siteSettingsInput.classList.remove("success"); + }, 1000); + + setTimeout(() => siteSettingsInput.focus()); +} + +siteSettingsAdd.addEventListener("click", addSiteSettingFilter); + +siteSettingsInput.addEventListener("keydown", event => { + if (event.key === "Enter") { + addSiteSettingFilter(); + } +}); + +siteSettingsClose.addEventListener("click", () => { + siteSettings.close(); +}); + +siteSettingsRemoveAll.addEventListener("click", () => { + ladybird.sendMessage("removeAllSiteSettingFilters", { + setting: currentSiteSetting(), + }); +}); + +autoplaySettings.addEventListener("click", event => { + showSiteSettings("Autoplay", AUTOPLAY_SETTINGS); + event.stopPropagation(); +}); + +document.addEventListener("WebUILoaded", () => { + ladybird.sendMessage("loadForciblyEnabledSiteSettings"); +}); + +document.addEventListener("WebUIMessage", event => { + if (event.detail.name === "loadSettings") { + loadSiteSettings(event.detail.data); + } else if (event.detail.name === "forciblyEnableSiteSettings") { + forciblyEnableSiteSettings(event.detail.data); + } +}); diff --git a/Base/res/ladybird/about-pages/settings/privacy.js b/Base/res/ladybird/about-pages/settings/privacy.js new file mode 100644 index 00000000000..30d79649387 --- /dev/null +++ b/Base/res/ladybird/about-pages/settings/privacy.js @@ -0,0 +1,15 @@ +const doNotTrackToggle = document.querySelector("#do-not-track-toggle"); + +function loadSettings(settings) { + doNotTrackToggle.checked = settings.doNotTrack; +} + +doNotTrackToggle.addEventListener("change", () => { + ladybird.sendMessage("setDoNotTrack", doNotTrackToggle.checked); +}); + +document.addEventListener("WebUIMessage", event => { + if (event.detail.name === "loadSettings") { + loadSettings(event.detail.data); + } +}); diff --git a/Base/res/ladybird/about-pages/settings/search.js b/Base/res/ladybird/about-pages/settings/search.js new file mode 100644 index 00000000000..ca01814b78d --- /dev/null +++ b/Base/res/ladybird/about-pages/settings/search.js @@ -0,0 +1,249 @@ +const searchClose = document.querySelector("#search-close"); +const searchCustomAdd = document.querySelector("#search-custom-add"); +const searchCustomName = document.querySelector("#search-custom-name"); +const searchCustomURL = document.querySelector("#search-custom-url"); +const searchDialog = document.querySelector("#search-dialog"); +const searchEngine = document.querySelector("#search-engine"); +const searchList = document.querySelector("#search-list"); +const searchSettings = document.querySelector("#search-settings"); +const searchToggle = document.querySelector("#search-toggle"); + +const autocompleteEngine = document.querySelector("#autocomplete-engine"); +const autocompleteToggle = document.querySelector("#autocomplete-toggle"); + +let SEARCH_ENGINE = {}; +let AUTOCOMPLETE_ENGINE = {}; + +let NATIVE_SEARCH_ENGINE_COUNT = 0; + +const Engine = Object.freeze({ + search: 1, + autocomplete: 2, +}); + +function loadEngineSettings(settings) { + SEARCH_ENGINE = settings.searchEngine || {}; + AUTOCOMPLETE_ENGINE = settings.autocompleteEngine || {}; + + const renderEngineSettings = (type, setting) => { + const [name, toggle, engine] = engineForType(type); + + if (setting.name) { + toggle.checked = true; + engine.value = setting.name; + } else { + toggle.checked = false; + } + + renderEngine(type); + }; + + loadCustomSearchEngines(); + renderEngineSettings(Engine.search, SEARCH_ENGINE); + renderEngineSettings(Engine.autocomplete, AUTOCOMPLETE_ENGINE); + + if (searchDialog.open) { + showSearchEngineSettings(); + } +} + +function engineForType(engine) { + if (engine === Engine.search) { + return ["Search", searchToggle, searchEngine]; + } + if (engine === Engine.autocomplete) { + return ["Autocomplete", autocompleteToggle, autocompleteEngine]; + } + throw Error(`Unrecognized engine type ${engine}`); +} + +function loadEngines(type, engines) { + const [name, toggle, engine] = engineForType(type); + + for (const engineName of engines) { + const option = document.createElement("option"); + option.text = engineName; + option.value = engineName; + + engine.add(option); + } + + if (type === Engine.search) { + NATIVE_SEARCH_ENGINE_COUNT = engine.length; + engine.appendChild(document.createElement("hr")); + } +} + +function renderEngine(type) { + const [name, toggle, engine] = engineForType(type); + + if (toggle.checked) { + engine.closest(".card-group").classList.remove("hidden"); + } else { + engine.closest(".card-group").classList.add("hidden"); + } + + if (toggle.checked && engine.selectedIndex !== 0) { + engine.item(0).disabled = true; + } else if (!toggle.checked) { + engine.item(0).disabled = false; + engine.selectedIndex = 0; + } +} + +function saveEngine(type) { + const [name, toggle, engine] = engineForType(type); + + if (toggle.checked && engine.selectedIndex !== 0) { + ladybird.sendMessage(`set${name}Engine`, engine.value); + } else if (!toggle.checked) { + ladybird.sendMessage(`set${name}Engine`, null); + } + + renderEngine(type); +} + +function setSaveEngineListeners(type) { + const [name, toggle, engine] = engineForType(type); + + toggle.addEventListener("change", () => { + saveEngine(type); + }); + engine.addEventListener("change", () => { + saveEngine(type); + }); +} + +setSaveEngineListeners(Engine.search); +setSaveEngineListeners(Engine.autocomplete); + +function loadCustomSearchEngines() { + while (searchEngine.length > NATIVE_SEARCH_ENGINE_COUNT) { + searchEngine.remove(NATIVE_SEARCH_ENGINE_COUNT); + } + + const custom = SEARCH_ENGINE.custom || []; + + custom.forEach(custom => { + const option = document.createElement("option"); + option.text = custom.name; + option.value = custom.name; + + searchEngine.add(option); + }); +} + +function showSearchEngineSettings() { + searchCustomName.classList.remove("error"); + searchCustomURL.classList.remove("error"); + searchList.innerHTML = ""; + + const custom = SEARCH_ENGINE.custom || []; + + if (custom.length === 0) { + const placeholder = document.createElement("div"); + placeholder.className = "dialog-list-item-placeholder"; + placeholder.textContent = "No custom search engines added"; + + searchList.appendChild(placeholder); + } + + custom.forEach(custom => { + const name = document.createElement("span"); + name.textContent = custom.name; + + const url = document.createElement("span"); + url.className = "dialog-list-item-placeholder"; + url.style = "padding-left: 0"; + url.textContent = ` — ${custom.url}`; + + const engine = document.createElement("span"); + engine.className = "dialog-list-item-label"; + engine.appendChild(name); + engine.appendChild(url); + + const remove = document.createElement("button"); + remove.className = "dialog-button"; + remove.innerHTML = "×"; + remove.title = `Remove ${custom.name}`; + + remove.addEventListener("click", () => { + ladybird.sendMessage("removeCustomSearchEngine", custom); + }); + + const item = document.createElement("div"); + item.className = "dialog-list-item"; + item.appendChild(engine); + item.appendChild(remove); + + searchList.appendChild(item); + }); + + if (!searchDialog.open) { + setTimeout(() => searchCustomName.focus()); + searchDialog.showModal(); + } +} + +function addCustomSearchEngine() { + searchCustomName.classList.remove("error"); + searchCustomURL.classList.remove("error"); + + for (const i = 0; i < searchEngine.length; ++i) { + if (searchCustomName.value === searchEngine.item(i).value) { + searchCustomName.classList.add("error"); + return; + } + } + + if (!containsValidURL(searchCustomURL)) { + searchCustomURL.classList.add("error"); + return; + } + + ladybird.sendMessage("addCustomSearchEngine", { + name: searchCustomName.value, + url: searchCustomURL.value, + }); + + searchCustomName.value = ""; + searchCustomURL.value = ""; + + setTimeout(() => searchCustomName.focus()); +} + +searchCustomAdd.addEventListener("click", addCustomSearchEngine); + +searchCustomName.addEventListener("keydown", event => { + if (event.key === "Enter") { + addCustomSearchEngine(); + } +}); + +searchCustomURL.addEventListener("keydown", event => { + if (event.key === "Enter") { + addCustomSearchEngine(); + } +}); + +searchClose.addEventListener("click", () => { + searchDialog.close(); +}); + +searchSettings.addEventListener("click", event => { + showSearchEngineSettings(); + event.stopPropagation(); +}); + +document.addEventListener("WebUILoaded", () => { + ladybird.sendMessage("loadAvailableEngines"); +}); + +document.addEventListener("WebUIMessage", event => { + if (event.detail.name === "loadSettings") { + loadEngineSettings(event.detail.data); + } else if (event.detail.name === "loadEngines") { + loadEngines(Engine.search, event.detail.data.search); + loadEngines(Engine.autocomplete, event.detail.data.autocomplete); + } +}); diff --git a/UI/cmake/ResourceFiles.cmake b/UI/cmake/ResourceFiles.cmake index 18795afce26..2aecbfc30fd 100644 --- a/UI/cmake/ResourceFiles.cmake +++ b/UI/cmake/ResourceFiles.cmake @@ -76,6 +76,11 @@ list(TRANSFORM ABOUT_PAGES PREPEND "${LADYBIRD_SOURCE_DIR}/Base/res/ladybird/abo set(ABOUT_SETTINGS_RESOURCES languages.js + network.js + new-tab-page.js + permissions.js + privacy.js + search.js ) list(TRANSFORM ABOUT_SETTINGS_RESOURCES PREPEND "${LADYBIRD_SOURCE_DIR}/Base/res/ladybird/about-pages/settings/")