mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-08-11 18:50:50 +00:00
LibWebView: Support custom search engines
This allows the user to store custom search engines via about:settings. Custom engines will be displayed below the builtin engines in the drop- down to select the default engine. A couple of edge cases here: 1. We currently reject a custom engine if one with the same name already exists. In the future, we should allow editing custom engines. 2. If a custom engine which was the default engine is removed, we will disable search rather than falling back to any other engine.
This commit is contained in:
parent
26ca18a05b
commit
79836b95ca
7 changed files with 317 additions and 28 deletions
|
@ -185,6 +185,19 @@
|
||||||
margin: 8px 4px 10px 4px;
|
margin: 8px 4px 10px 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dialog .dialog-form {
|
||||||
|
border-top: 1px solid var(--border-color);
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
padding: 15px 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
dialog .form-group {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
dialog .dialog-footer {
|
dialog .dialog-footer {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
|
@ -318,10 +331,13 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="card-group hidden">
|
<div class="card-group hidden">
|
||||||
<label for="search-engine">Default Search Engine</label>
|
<label for="search-engine">Default Search Engine</label>
|
||||||
<select id="search-engine">
|
<div class="inline-container">
|
||||||
<option value="">Please select a search engine</option>
|
<select id="search-engine">
|
||||||
<hr />
|
<option value="">Please select a search engine</option>
|
||||||
</select>
|
<hr />
|
||||||
|
</select>
|
||||||
|
<button id="search-settings" class="secondary-button">Settings...</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<hr />
|
<hr />
|
||||||
|
@ -386,6 +402,35 @@
|
||||||
</div>
|
</div>
|
||||||
</dialog>
|
</dialog>
|
||||||
|
|
||||||
|
<dialog id="search-dialog">
|
||||||
|
<div class="dialog-header">
|
||||||
|
<h3 class="dialog-title">Search Settings</h3>
|
||||||
|
<button id="search-close" class="close-button dialog-button">×</button>
|
||||||
|
</div>
|
||||||
|
<div class="dialog-body" style="height: 300px">
|
||||||
|
<p class="dialog-description">Manage custom search engines</p>
|
||||||
|
<div id="search-list" class="dialog-list"></div>
|
||||||
|
</div>
|
||||||
|
<div class="dialog-form">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="search-custom-name">Search Engine Name</label>
|
||||||
|
<input id="search-custom-name" type="text" placeholder="Example" />
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="search-custom-url">Search URL</label>
|
||||||
|
<p class="dialog-description">Use %s as a placeholder for the search query</p>
|
||||||
|
<input
|
||||||
|
id="search-custom-url"
|
||||||
|
type="url"
|
||||||
|
placeholder="https://example.com/search?q=%s"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="dialog-controls">
|
||||||
|
<button id="search-custom-add" class="primary-button">Add Engine</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</dialog>
|
||||||
|
|
||||||
<dialog id="site-settings">
|
<dialog id="site-settings">
|
||||||
<div class="dialog-header">
|
<div class="dialog-header">
|
||||||
<h3 id="site-settings-title" class="dialog-title"></h3>
|
<h3 id="site-settings-title" class="dialog-title"></h3>
|
||||||
|
@ -421,10 +466,17 @@
|
||||||
const languagesList = document.querySelector("#languages-list");
|
const languagesList = document.querySelector("#languages-list");
|
||||||
const languagesSelect = document.querySelector("#languages-select");
|
const languagesSelect = document.querySelector("#languages-select");
|
||||||
const languagesSettings = document.querySelector("#languages-settings");
|
const languagesSettings = document.querySelector("#languages-settings");
|
||||||
const searchToggle = document.querySelector("#search-toggle");
|
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 searchEngine = document.querySelector("#search-engine");
|
||||||
const autocompleteToggle = document.querySelector("#autocomplete-toggle");
|
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 autocompleteEngine = document.querySelector("#autocomplete-engine");
|
||||||
|
const autocompleteToggle = document.querySelector("#autocomplete-toggle");
|
||||||
const autoplaySettings = document.querySelector("#autoplay-settings");
|
const autoplaySettings = document.querySelector("#autoplay-settings");
|
||||||
const siteSettings = document.querySelector("#site-settings");
|
const siteSettings = document.querySelector("#site-settings");
|
||||||
const siteSettingsAdd = document.querySelector("#site-settings-add");
|
const siteSettingsAdd = document.querySelector("#site-settings-add");
|
||||||
|
@ -445,6 +497,7 @@
|
||||||
'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"></polyline></svg>';
|
'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"></polyline></svg>';
|
||||||
|
|
||||||
window.settings = {};
|
window.settings = {};
|
||||||
|
window.nativeSearchEngineCount = 0;
|
||||||
|
|
||||||
const loadSettings = settings => {
|
const loadSettings = settings => {
|
||||||
window.settings = settings;
|
window.settings = settings;
|
||||||
|
@ -472,9 +525,14 @@
|
||||||
renderEngine(type);
|
renderEngine(type);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
loadCustomSearchEngines();
|
||||||
renderEngineSettings(Engine.search, window.settings.searchEngine);
|
renderEngineSettings(Engine.search, window.settings.searchEngine);
|
||||||
renderEngineSettings(Engine.autocomplete, window.settings.autocompleteEngine);
|
renderEngineSettings(Engine.autocomplete, window.settings.autocompleteEngine);
|
||||||
|
|
||||||
|
if (searchDialog.open) {
|
||||||
|
showSearchEngineSettings();
|
||||||
|
}
|
||||||
|
|
||||||
const siteSetting = currentSiteSetting();
|
const siteSetting = currentSiteSetting();
|
||||||
|
|
||||||
if (siteSetting === "autoplay") {
|
if (siteSetting === "autoplay") {
|
||||||
|
@ -484,11 +542,15 @@
|
||||||
doNotTrackToggle.checked = window.settings.doNotTrack;
|
doNotTrackToggle.checked = window.settings.doNotTrack;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const containsValidURL = input => {
|
||||||
|
return input.value.length !== 0 && input.checkValidity();
|
||||||
|
};
|
||||||
|
|
||||||
newTabPageURL.addEventListener("change", () => {
|
newTabPageURL.addEventListener("change", () => {
|
||||||
newTabPageURL.classList.remove("success");
|
newTabPageURL.classList.remove("success");
|
||||||
newTabPageURL.classList.remove("error");
|
newTabPageURL.classList.remove("error");
|
||||||
|
|
||||||
if (!newTabPageURL.checkValidity()) {
|
if (!containsValidURL(newTabPageURL)) {
|
||||||
newTabPageURL.classList.add("error");
|
newTabPageURL.classList.add("error");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -657,15 +719,20 @@
|
||||||
|
|
||||||
engine.add(option);
|
engine.add(option);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (type === Engine.search) {
|
||||||
|
window.nativeSearchEngineCount = engine.length;
|
||||||
|
engine.appendChild(document.createElement("hr"));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderEngine = type => {
|
const renderEngine = type => {
|
||||||
const [name, toggle, engine] = engineForType(type);
|
const [name, toggle, engine] = engineForType(type);
|
||||||
|
|
||||||
if (toggle.checked) {
|
if (toggle.checked) {
|
||||||
engine.parentElement.classList.remove("hidden");
|
engine.closest(".card-group").classList.remove("hidden");
|
||||||
} else {
|
} else {
|
||||||
engine.parentElement.classList.add("hidden");
|
engine.closest(".card-group").classList.add("hidden");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (toggle.checked && engine.selectedIndex !== 0) {
|
if (toggle.checked && engine.selectedIndex !== 0) {
|
||||||
|
@ -702,6 +769,124 @@
|
||||||
setSaveEngineListeners(Engine.search);
|
setSaveEngineListeners(Engine.search);
|
||||||
setSaveEngineListeners(Engine.autocomplete);
|
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 => {
|
const forciblyEnableSiteSettings = settings => {
|
||||||
settings.forEach(setting => {
|
settings.forEach(setting => {
|
||||||
const label = document.querySelector(`#${setting}-forcibly-enabled`);
|
const label = document.querySelector(`#${setting}-forcibly-enabled`);
|
||||||
|
@ -837,8 +1022,7 @@
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
close(languagesDialog);
|
document.querySelectorAll("dialog").forEach(close);
|
||||||
close(siteSettings);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
document.addEventListener("WebUILoaded", () => {
|
document.addEventListener("WebUILoaded", () => {
|
||||||
|
|
|
@ -4,13 +4,12 @@
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <AK/Find.h>
|
|
||||||
#include <LibURL/URL.h>
|
#include <LibURL/URL.h>
|
||||||
#include <LibWebView/SearchEngine.h>
|
#include <LibWebView/SearchEngine.h>
|
||||||
|
|
||||||
namespace WebView {
|
namespace WebView {
|
||||||
|
|
||||||
static auto builtin_search_engines = to_array<SearchEngine>({
|
static auto s_builtin_search_engines = to_array<SearchEngine>({
|
||||||
{ "Bing"_string, "https://www.bing.com/search?q=%s"_string },
|
{ "Bing"_string, "https://www.bing.com/search?q=%s"_string },
|
||||||
{ "Brave"_string, "https://search.brave.com/search?q=%s"_string },
|
{ "Brave"_string, "https://search.brave.com/search?q=%s"_string },
|
||||||
{ "DuckDuckGo"_string, "https://duckduckgo.com/?q=%s"_string },
|
{ "DuckDuckGo"_string, "https://duckduckgo.com/?q=%s"_string },
|
||||||
|
@ -23,16 +22,9 @@ static auto builtin_search_engines = to_array<SearchEngine>({
|
||||||
{ "Yandex"_string, "https://yandex.com/search/?text=%s"_string },
|
{ "Yandex"_string, "https://yandex.com/search/?text=%s"_string },
|
||||||
});
|
});
|
||||||
|
|
||||||
ReadonlySpan<SearchEngine> search_engines()
|
ReadonlySpan<SearchEngine> builtin_search_engines()
|
||||||
{
|
{
|
||||||
return builtin_search_engines;
|
return s_builtin_search_engines;
|
||||||
}
|
|
||||||
|
|
||||||
Optional<SearchEngine> find_search_engine_by_name(StringView name)
|
|
||||||
{
|
|
||||||
return find_value(builtin_search_engines, [&](auto const& engine) {
|
|
||||||
return engine.name == name;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
String SearchEngine::format_search_query_for_display(StringView query) const
|
String SearchEngine::format_search_query_for_display(StringView query) const
|
||||||
|
|
|
@ -20,7 +20,6 @@ struct SearchEngine {
|
||||||
String query_url;
|
String query_url;
|
||||||
};
|
};
|
||||||
|
|
||||||
ReadonlySpan<SearchEngine> search_engines();
|
ReadonlySpan<SearchEngine> builtin_search_engines();
|
||||||
Optional<SearchEngine> find_search_engine_by_name(StringView name);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <AK/ByteString.h>
|
#include <AK/ByteString.h>
|
||||||
|
#include <AK/Find.h>
|
||||||
#include <AK/JsonArray.h>
|
#include <AK/JsonArray.h>
|
||||||
#include <AK/JsonObject.h>
|
#include <AK/JsonObject.h>
|
||||||
#include <AK/JsonValue.h>
|
#include <AK/JsonValue.h>
|
||||||
|
@ -26,7 +27,9 @@ static constexpr auto languages_key = "languages"sv;
|
||||||
static auto default_language = "en"_string;
|
static auto default_language = "en"_string;
|
||||||
|
|
||||||
static constexpr auto search_engine_key = "searchEngine"sv;
|
static constexpr auto search_engine_key = "searchEngine"sv;
|
||||||
|
static constexpr auto search_engine_custom_key = "custom"sv;
|
||||||
static constexpr auto search_engine_name_key = "name"sv;
|
static constexpr auto search_engine_name_key = "name"sv;
|
||||||
|
static constexpr auto search_engine_url_key = "url"sv;
|
||||||
|
|
||||||
static constexpr auto autocomplete_engine_key = "autocompleteEngine"sv;
|
static constexpr auto autocomplete_engine_key = "autocompleteEngine"sv;
|
||||||
static constexpr auto autocomplete_engine_name_key = "name"sv;
|
static constexpr auto autocomplete_engine_name_key = "name"sv;
|
||||||
|
@ -89,8 +92,18 @@ Settings Settings::create(Badge<Application>)
|
||||||
settings.m_languages = parse_json_languages(*languages);
|
settings.m_languages = parse_json_languages(*languages);
|
||||||
|
|
||||||
if (auto search_engine = settings_json.value().get_object(search_engine_key); search_engine.has_value()) {
|
if (auto search_engine = settings_json.value().get_object(search_engine_key); search_engine.has_value()) {
|
||||||
|
if (auto custom_engines = search_engine->get_array(search_engine_custom_key); custom_engines.has_value()) {
|
||||||
|
custom_engines->for_each([&](JsonValue const& engine) {
|
||||||
|
auto custom_engine = parse_custom_search_engine(engine);
|
||||||
|
if (!custom_engine.has_value() || settings.find_search_engine_by_name(custom_engine->name).has_value())
|
||||||
|
return;
|
||||||
|
|
||||||
|
settings.m_custom_search_engines.append(custom_engine.release_value());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (auto search_engine_name = search_engine->get_string(search_engine_name_key); search_engine_name.has_value())
|
if (auto search_engine_name = search_engine->get_string(search_engine_name_key); search_engine_name.has_value())
|
||||||
settings.m_search_engine = find_search_engine_by_name(*search_engine_name);
|
settings.m_search_engine = settings.find_search_engine_by_name(*search_engine_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (auto autocomplete_engine = settings_json.value().get_object(autocomplete_engine_key); autocomplete_engine.has_value()) {
|
if (auto autocomplete_engine = settings_json.value().get_object(autocomplete_engine_key); autocomplete_engine.has_value()) {
|
||||||
|
@ -144,12 +157,25 @@ JsonValue Settings::serialize_json() const
|
||||||
|
|
||||||
settings.set(languages_key, move(languages));
|
settings.set(languages_key, move(languages));
|
||||||
|
|
||||||
if (m_search_engine.has_value()) {
|
JsonArray custom_search_engines;
|
||||||
|
custom_search_engines.ensure_capacity(m_custom_search_engines.size());
|
||||||
|
|
||||||
|
for (auto const& engine : m_custom_search_engines) {
|
||||||
JsonObject search_engine;
|
JsonObject search_engine;
|
||||||
|
search_engine.set(search_engine_name_key, engine.name);
|
||||||
|
search_engine.set(search_engine_url_key, engine.query_url);
|
||||||
|
|
||||||
|
custom_search_engines.must_append(move(search_engine));
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonObject search_engine;
|
||||||
|
if (!custom_search_engines.is_empty())
|
||||||
|
search_engine.set(search_engine_custom_key, move(custom_search_engines));
|
||||||
|
if (m_search_engine.has_value())
|
||||||
search_engine.set(search_engine_name_key, m_search_engine->name);
|
search_engine.set(search_engine_name_key, m_search_engine->name);
|
||||||
|
|
||||||
|
if (!search_engine.is_empty())
|
||||||
settings.set(search_engine_key, move(search_engine));
|
settings.set(search_engine_key, move(search_engine));
|
||||||
}
|
|
||||||
|
|
||||||
if (m_autocomplete_engine.has_value()) {
|
if (m_autocomplete_engine.has_value()) {
|
||||||
JsonObject autocomplete_engine;
|
JsonObject autocomplete_engine;
|
||||||
|
@ -184,6 +210,7 @@ void Settings::restore_defaults()
|
||||||
m_new_tab_page_url = URL::about_newtab();
|
m_new_tab_page_url = URL::about_newtab();
|
||||||
m_languages = { default_language };
|
m_languages = { default_language };
|
||||||
m_search_engine.clear();
|
m_search_engine.clear();
|
||||||
|
m_custom_search_engines.clear();
|
||||||
m_autocomplete_engine.clear();
|
m_autocomplete_engine.clear();
|
||||||
m_autoplay = SiteSetting {};
|
m_autoplay = SiteSetting {};
|
||||||
m_do_not_track = DoNotTrack::No;
|
m_do_not_track = DoNotTrack::No;
|
||||||
|
@ -250,6 +277,62 @@ void Settings::set_search_engine(Optional<StringView> search_engine_name)
|
||||||
observer.search_engine_changed();
|
observer.search_engine_changed();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Optional<SearchEngine> Settings::parse_custom_search_engine(JsonValue const& search_engine)
|
||||||
|
{
|
||||||
|
if (!search_engine.is_object())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
auto name = search_engine.as_object().get_string(search_engine_name_key);
|
||||||
|
auto url = search_engine.as_object().get_string(search_engine_url_key);
|
||||||
|
if (!name.has_value() || !url.has_value())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
auto parsed_url = URL::Parser::basic_parse(*url);
|
||||||
|
if (!parsed_url.has_value())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
return SearchEngine { .name = name.release_value(), .query_url = url.release_value() };
|
||||||
|
}
|
||||||
|
|
||||||
|
void Settings::add_custom_search_engine(SearchEngine search_engine)
|
||||||
|
{
|
||||||
|
if (find_search_engine_by_name(search_engine.name).has_value())
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_custom_search_engines.append(move(search_engine));
|
||||||
|
persist_settings();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Settings::remove_custom_search_engine(SearchEngine const& search_engine)
|
||||||
|
{
|
||||||
|
auto reset_default_search_engine = m_search_engine.has_value() && m_search_engine->name == search_engine.name;
|
||||||
|
if (reset_default_search_engine)
|
||||||
|
m_search_engine.clear();
|
||||||
|
|
||||||
|
m_custom_search_engines.remove_all_matching([&](auto const& engine) {
|
||||||
|
return engine.name == search_engine.name;
|
||||||
|
});
|
||||||
|
|
||||||
|
persist_settings();
|
||||||
|
|
||||||
|
if (reset_default_search_engine) {
|
||||||
|
for (auto& observer : m_observers)
|
||||||
|
observer.search_engine_changed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Optional<SearchEngine> Settings::find_search_engine_by_name(StringView name)
|
||||||
|
{
|
||||||
|
auto comparator = [&](auto const& engine) { return engine.name == name; };
|
||||||
|
|
||||||
|
if (auto result = find_value(builtin_search_engines(), comparator); result.has_value())
|
||||||
|
return result.copy();
|
||||||
|
if (auto result = find_value(m_custom_search_engines, comparator); result.has_value())
|
||||||
|
return result.copy();
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
void Settings::set_autocomplete_engine(Optional<StringView> autocomplete_engine_name)
|
void Settings::set_autocomplete_engine(Optional<StringView> autocomplete_engine_name)
|
||||||
{
|
{
|
||||||
if (autocomplete_engine_name.has_value())
|
if (autocomplete_engine_name.has_value())
|
||||||
|
|
|
@ -60,6 +60,10 @@ public:
|
||||||
Optional<SearchEngine> const& search_engine() const { return m_search_engine; }
|
Optional<SearchEngine> const& search_engine() const { return m_search_engine; }
|
||||||
void set_search_engine(Optional<StringView> search_engine_name);
|
void set_search_engine(Optional<StringView> search_engine_name);
|
||||||
|
|
||||||
|
static Optional<SearchEngine> parse_custom_search_engine(JsonValue const&);
|
||||||
|
void add_custom_search_engine(SearchEngine);
|
||||||
|
void remove_custom_search_engine(SearchEngine const&);
|
||||||
|
|
||||||
Optional<AutocompleteEngine> const& autocomplete_engine() const { return m_autocomplete_engine; }
|
Optional<AutocompleteEngine> const& autocomplete_engine() const { return m_autocomplete_engine; }
|
||||||
void set_autocomplete_engine(Optional<StringView> autocomplete_engine_name);
|
void set_autocomplete_engine(Optional<StringView> autocomplete_engine_name);
|
||||||
|
|
||||||
|
@ -80,11 +84,14 @@ private:
|
||||||
|
|
||||||
void persist_settings();
|
void persist_settings();
|
||||||
|
|
||||||
|
Optional<SearchEngine> find_search_engine_by_name(StringView name);
|
||||||
|
|
||||||
ByteString m_settings_path;
|
ByteString m_settings_path;
|
||||||
|
|
||||||
URL::URL m_new_tab_page_url;
|
URL::URL m_new_tab_page_url;
|
||||||
Vector<String> m_languages;
|
Vector<String> m_languages;
|
||||||
Optional<SearchEngine> m_search_engine;
|
Optional<SearchEngine> m_search_engine;
|
||||||
|
Vector<SearchEngine> m_custom_search_engines;
|
||||||
Optional<AutocompleteEngine> m_autocomplete_engine;
|
Optional<AutocompleteEngine> m_autocomplete_engine;
|
||||||
SiteSetting m_autoplay;
|
SiteSetting m_autoplay;
|
||||||
DoNotTrack m_do_not_track { DoNotTrack::No };
|
DoNotTrack m_do_not_track { DoNotTrack::No };
|
||||||
|
|
|
@ -34,6 +34,12 @@ void SettingsUI::register_interfaces()
|
||||||
register_interface("setSearchEngine"sv, [this](auto const& data) {
|
register_interface("setSearchEngine"sv, [this](auto const& data) {
|
||||||
set_search_engine(data);
|
set_search_engine(data);
|
||||||
});
|
});
|
||||||
|
register_interface("addCustomSearchEngine"sv, [this](auto const& data) {
|
||||||
|
add_custom_search_engine(data);
|
||||||
|
});
|
||||||
|
register_interface("removeCustomSearchEngine"sv, [this](auto const& data) {
|
||||||
|
remove_custom_search_engine(data);
|
||||||
|
});
|
||||||
register_interface("setAutocompleteEngine"sv, [this](auto const& data) {
|
register_interface("setAutocompleteEngine"sv, [this](auto const& data) {
|
||||||
set_autocomplete_engine(data);
|
set_autocomplete_engine(data);
|
||||||
});
|
});
|
||||||
|
@ -94,7 +100,7 @@ void SettingsUI::set_languages(JsonValue const& languages)
|
||||||
void SettingsUI::load_available_engines()
|
void SettingsUI::load_available_engines()
|
||||||
{
|
{
|
||||||
JsonArray search_engines;
|
JsonArray search_engines;
|
||||||
for (auto const& engine : WebView::search_engines())
|
for (auto const& engine : WebView::builtin_search_engines())
|
||||||
search_engines.must_append(engine.name);
|
search_engines.must_append(engine.name);
|
||||||
|
|
||||||
JsonArray autocomplete_engines;
|
JsonArray autocomplete_engines;
|
||||||
|
@ -116,6 +122,22 @@ void SettingsUI::set_search_engine(JsonValue const& search_engine)
|
||||||
WebView::Application::settings().set_search_engine(search_engine.as_string());
|
WebView::Application::settings().set_search_engine(search_engine.as_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SettingsUI::add_custom_search_engine(JsonValue const& search_engine)
|
||||||
|
{
|
||||||
|
if (auto custom_engine = Settings::parse_custom_search_engine(search_engine); custom_engine.has_value())
|
||||||
|
WebView::Application::settings().add_custom_search_engine(custom_engine.release_value());
|
||||||
|
|
||||||
|
load_current_settings();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SettingsUI::remove_custom_search_engine(JsonValue const& search_engine)
|
||||||
|
{
|
||||||
|
if (auto custom_engine = Settings::parse_custom_search_engine(search_engine); custom_engine.has_value())
|
||||||
|
WebView::Application::settings().remove_custom_search_engine(*custom_engine);
|
||||||
|
|
||||||
|
load_current_settings();
|
||||||
|
}
|
||||||
|
|
||||||
void SettingsUI::set_autocomplete_engine(JsonValue const& autocomplete_engine)
|
void SettingsUI::set_autocomplete_engine(JsonValue const& autocomplete_engine)
|
||||||
{
|
{
|
||||||
if (autocomplete_engine.is_null())
|
if (autocomplete_engine.is_null())
|
||||||
|
|
|
@ -24,6 +24,8 @@ private:
|
||||||
|
|
||||||
void load_available_engines();
|
void load_available_engines();
|
||||||
void set_search_engine(JsonValue const&);
|
void set_search_engine(JsonValue const&);
|
||||||
|
void add_custom_search_engine(JsonValue const&);
|
||||||
|
void remove_custom_search_engine(JsonValue const&);
|
||||||
void set_autocomplete_engine(JsonValue const&);
|
void set_autocomplete_engine(JsonValue const&);
|
||||||
|
|
||||||
void load_forcibly_enabled_site_settings();
|
void load_forcibly_enabled_site_settings();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue