mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-22 15:40:19 +00:00
5e1499d104
This commit un-deprecates DeprecatedString, and repurposes it as a byte string. As the null state has already been removed, there are no other particularly hairy blockers in repurposing this type as a byte string (what it _really_ is). This commit is auto-generated: $ xs=$(ack -l \bDeprecatedString\b\|deprecated_string AK Userland \ Meta Ports Ladybird Tests Kernel) $ perl -pie 's/\bDeprecatedString\b/ByteString/g; s/deprecated_string/byte_string/g' $xs $ clang-format --style=file -i \ $(git diff --name-only | grep \.cpp\|\.h) $ gn format $(git ls-files '*.gn' '*.gni')
148 lines
4.9 KiB
C++
148 lines
4.9 KiB
C++
/*
|
|
* Copyright (c) 2023, Cameron Youell <cameronyouell@gmail.com>
|
|
*
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
*/
|
|
|
|
#include "AutoComplete.h"
|
|
#include "Settings.h"
|
|
#include <AK/JsonArray.h>
|
|
#include <AK/JsonObject.h>
|
|
#include <AK/JsonParser.h>
|
|
#include <AK/URL.h>
|
|
|
|
namespace Ladybird {
|
|
|
|
AutoComplete::AutoComplete(QWidget* parent)
|
|
: QCompleter(parent)
|
|
{
|
|
m_tree_view = new QTreeView(parent);
|
|
m_manager = new QNetworkAccessManager(this);
|
|
m_auto_complete_model = new AutoCompleteModel(this);
|
|
|
|
setCompletionMode(QCompleter::UnfilteredPopupCompletion);
|
|
setModel(m_auto_complete_model);
|
|
setPopup(m_tree_view);
|
|
|
|
m_tree_view->setRootIsDecorated(false);
|
|
m_tree_view->setHeaderHidden(true);
|
|
|
|
connect(this, QOverload<QModelIndex const&>::of(&QCompleter::activated), this, [&](QModelIndex const& index) {
|
|
emit activated(index);
|
|
});
|
|
|
|
connect(m_manager, &QNetworkAccessManager::finished, this, [&](QNetworkReply* reply) {
|
|
auto result = got_network_response(reply);
|
|
if (result.is_error())
|
|
dbgln("AutoComplete::got_network_response: Error {}", result.error());
|
|
});
|
|
}
|
|
|
|
ErrorOr<void> AutoComplete::parse_google_autocomplete(Vector<JsonValue> const& json)
|
|
{
|
|
if (json.size() != 5)
|
|
return Error::from_string_view("Invalid JSON, expected 5 elements in array"sv);
|
|
|
|
if (!json[0].is_string())
|
|
return Error::from_string_view("Invalid JSON, expected first element to be a string"sv);
|
|
auto query = TRY(String::from_byte_string(json[0].as_string()));
|
|
|
|
if (!json[1].is_array())
|
|
return Error::from_string_view("Invalid JSON, expected second element to be an array"sv);
|
|
auto suggestions_array = json[1].as_array().values();
|
|
|
|
if (query != m_query)
|
|
return Error::from_string_view("Invalid JSON, query does not match"sv);
|
|
|
|
for (auto& suggestion : suggestions_array) {
|
|
m_auto_complete_model->add(TRY(String::from_byte_string(suggestion.as_string())));
|
|
}
|
|
|
|
return {};
|
|
}
|
|
|
|
ErrorOr<void> AutoComplete::parse_duckduckgo_autocomplete(Vector<JsonValue> const& json)
|
|
{
|
|
for (auto const& suggestion : json) {
|
|
auto maybe_value = suggestion.as_object().get("phrase"sv);
|
|
if (!maybe_value.has_value())
|
|
continue;
|
|
m_auto_complete_model->add(TRY(String::from_byte_string(maybe_value->as_string())));
|
|
}
|
|
|
|
return {};
|
|
}
|
|
|
|
ErrorOr<void> AutoComplete::parse_yahoo_autocomplete(JsonObject const& json)
|
|
{
|
|
if (!json.get("q"sv).has_value() || !json.get("q"sv)->is_string())
|
|
return Error::from_string_view("Invalid JSON, expected \"q\" to be a string"sv);
|
|
auto query = TRY(String::from_byte_string(json.get("q"sv)->as_string()));
|
|
|
|
if (!json.get("r"sv).has_value() || !json.get("r"sv)->is_array())
|
|
return Error::from_string_view("Invalid JSON, expected \"r\" to be an object"sv);
|
|
auto suggestions_object = json.get("r"sv)->as_array().values();
|
|
|
|
if (query != m_query)
|
|
return Error::from_string_view("Invalid JSON, query does not match"sv);
|
|
|
|
for (auto& suggestion_object : suggestions_object) {
|
|
if (!suggestion_object.is_object())
|
|
return Error::from_string_view("Invalid JSON, expected value to be an object"sv);
|
|
auto suggestion = suggestion_object.as_object();
|
|
|
|
if (!suggestion.get("k"sv).has_value() || !suggestion.get("k"sv)->is_string())
|
|
return Error::from_string_view("Invalid JSON, expected \"k\" to be a string"sv);
|
|
|
|
m_auto_complete_model->add(TRY(String::from_byte_string(suggestion.get("k"sv)->as_string())));
|
|
};
|
|
|
|
return {};
|
|
}
|
|
|
|
ErrorOr<void> AutoComplete::got_network_response(QNetworkReply* reply)
|
|
{
|
|
if (reply->error() == QNetworkReply::NetworkError::OperationCanceledError)
|
|
return {};
|
|
|
|
AK::JsonParser parser(ak_byte_string_from_qstring(reply->readAll()));
|
|
auto json = TRY(parser.parse());
|
|
|
|
auto engine_name = Settings::the()->autocomplete_engine().name;
|
|
if (engine_name == "Google")
|
|
return parse_google_autocomplete(json.as_array().values());
|
|
|
|
if (engine_name == "DuckDuckGo")
|
|
return parse_duckduckgo_autocomplete(json.as_array().values());
|
|
|
|
if (engine_name == "Yahoo")
|
|
return parse_yahoo_autocomplete(json.as_object());
|
|
|
|
return Error::from_string_view("Invalid engine name"sv);
|
|
}
|
|
|
|
String AutoComplete::auto_complete_url_from_query(StringView query)
|
|
{
|
|
auto autocomplete_engine = ak_string_from_qstring(Settings::the()->autocomplete_engine().url);
|
|
return MUST(autocomplete_engine.replace("{}"sv, AK::URL::percent_encode(query), ReplaceMode::FirstOnly));
|
|
}
|
|
|
|
void AutoComplete::clear_suggestions()
|
|
{
|
|
m_auto_complete_model->clear();
|
|
}
|
|
|
|
void AutoComplete::get_search_suggestions(String search_string)
|
|
{
|
|
m_query = move(search_string);
|
|
if (m_reply)
|
|
m_reply->abort();
|
|
|
|
m_auto_complete_model->clear();
|
|
m_auto_complete_model->add(m_query);
|
|
|
|
QNetworkRequest request { QUrl(qstring_from_ak_string(auto_complete_url_from_query(m_query))) };
|
|
m_reply = m_manager->get(request);
|
|
}
|
|
|
|
}
|