180 lines
7.1 KiB
C++
180 lines
7.1 KiB
C++
#include "useragent.h"
|
|
|
|
#include <QWebEngineClientHints>
|
|
#include <QWebEngineProfile>
|
|
#include <QtWebEngineCore/qtwebenginecoreglobal.h>
|
|
|
|
namespace useragent {
|
|
|
|
namespace {
|
|
|
|
// Gecko and WebKit versions can't come from the engine, so they are pinned
|
|
// here; bump them now and then so att doesn't look like an outdated browser.
|
|
constexpr auto kFirefoxVersion = "155.0";
|
|
constexpr auto kSafariVersion = "27.0";
|
|
|
|
// Chrome-based presets claim the engine's own Chromium major version, as
|
|
// qutebrowser does: a newer claim would make sites use features the engine
|
|
// lacks. Chrome's reduced UA string zeroes the minor parts.
|
|
QString chromeVersion()
|
|
{
|
|
return QString::fromLatin1(qWebEngineChromiumVersion()).section(QLatin1Char('.'), 0, 0)
|
|
+ QStringLiteral(".0.0.0");
|
|
}
|
|
|
|
// What Sec-CH-UA-* (and navigator.userAgentData) should say, so they don't
|
|
// contradict the User-Agent string. Chromium always sends the low-entropy ones
|
|
// (brands, platform, mobile), even with client hints disabled, so every preset
|
|
// sets a matching platform. Firefox and Safari send no hints at all, so for
|
|
// them the high-entropy ones are disabled.
|
|
struct Hints {
|
|
bool enabled;
|
|
QString brand; // "Google Chrome" etc., next to Chromium's own brands
|
|
QString platform;
|
|
QString platformVersion;
|
|
QString arch;
|
|
QString bitness;
|
|
bool mobile;
|
|
};
|
|
|
|
// The full version list for a brand: Chrome sends its own brand next to
|
|
// Chromium's (and a GREASE "Not A Brand"); an empty brand gives the engine's.
|
|
QVariantMap brands(const QString &brand)
|
|
{
|
|
const QString full = QString::fromLatin1(qWebEngineChromiumVersion());
|
|
QVariantMap list{{QStringLiteral("Chromium"), full},
|
|
{QStringLiteral("Not=A?Brand"), QStringLiteral("24.0.0.0")}};
|
|
if (!brand.isEmpty())
|
|
list.insert(brand, full);
|
|
return list;
|
|
}
|
|
|
|
struct Entry {
|
|
Preset preset;
|
|
Hints hints;
|
|
};
|
|
|
|
QList<Entry> buildEntries()
|
|
{
|
|
const QString chrome = chromeVersion();
|
|
const QString firefox = QString::fromLatin1(kFirefoxVersion);
|
|
const QString safari = QString::fromLatin1(kSafariVersion);
|
|
const auto chromeOn = [&](const QString &platform) {
|
|
return QStringLiteral("Mozilla/5.0 (%1) AppleWebKit/537.36 (KHTML, like Gecko) "
|
|
"Chrome/%2 Safari/537.36")
|
|
.arg(platform, chrome);
|
|
};
|
|
const auto firefoxOn = [&](const QString &platform) {
|
|
return QStringLiteral("Mozilla/5.0 (%1; rv:%2) Gecko/20100101 Firefox/%2")
|
|
.arg(platform, firefox);
|
|
};
|
|
|
|
return {
|
|
{{QStringLiteral("chrome-linux"), chromeOn(QStringLiteral("X11; Linux x86_64"))},
|
|
{true, QStringLiteral("Google Chrome"), QStringLiteral("Linux"), QString(),
|
|
QStringLiteral("x86"), QStringLiteral("64"), false}},
|
|
{{QStringLiteral("chrome-windows"),
|
|
chromeOn(QStringLiteral("Windows NT 10.0; Win64; x64"))},
|
|
{true, QStringLiteral("Google Chrome"), QStringLiteral("Windows"),
|
|
QStringLiteral("19.0.0"), QStringLiteral("x86"), QStringLiteral("64"), false}},
|
|
{{QStringLiteral("chrome-mac"),
|
|
chromeOn(QStringLiteral("Macintosh; Intel Mac OS X 10_15_7"))},
|
|
{true, QStringLiteral("Google Chrome"), QStringLiteral("macOS"),
|
|
QStringLiteral("15.0.0"), QStringLiteral("arm"), QStringLiteral("64"), false}},
|
|
{{QStringLiteral("chrome-android"),
|
|
QStringLiteral("Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 "
|
|
"(KHTML, like Gecko) "
|
|
"Chrome/%1 Mobile Safari/537.36")
|
|
.arg(chrome)},
|
|
{true, QStringLiteral("Google Chrome"), QStringLiteral("Android"),
|
|
QStringLiteral("15.0.0"), QString(), QString(), true}},
|
|
{{QStringLiteral("edge-windows"),
|
|
chromeOn(QStringLiteral("Windows NT 10.0; Win64; x64"))
|
|
+ QStringLiteral(" Edg/%1").arg(chrome)},
|
|
{true, QStringLiteral("Microsoft Edge"), QStringLiteral("Windows"),
|
|
QStringLiteral("19.0.0"), QStringLiteral("x86"), QStringLiteral("64"), false}},
|
|
{{QStringLiteral("firefox-linux"), firefoxOn(QStringLiteral("X11; Linux x86_64"))},
|
|
{false, {}, QStringLiteral("Linux"), {}, {}, {}, false}},
|
|
{{QStringLiteral("firefox-windows"),
|
|
firefoxOn(QStringLiteral("Windows NT 10.0; Win64; x64"))},
|
|
{false, {}, QStringLiteral("Windows"), {}, {}, {}, false}},
|
|
{{QStringLiteral("firefox-mac"),
|
|
firefoxOn(QStringLiteral("Macintosh; Intel Mac OS X 10.15"))},
|
|
{false, {}, QStringLiteral("macOS"), {}, {}, {}, false}},
|
|
{{QStringLiteral("safari-mac"),
|
|
QStringLiteral("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) "
|
|
"AppleWebKit/605.1.15 "
|
|
"(KHTML, like Gecko) Version/%1 Safari/605.1.15")
|
|
.arg(safari)},
|
|
{false, {}, QStringLiteral("macOS"), {}, {}, {}, false}},
|
|
{{QStringLiteral("safari-iphone"),
|
|
QStringLiteral("Mozilla/5.0 (iPhone; CPU iPhone OS 18_6 like Mac OS X) "
|
|
"AppleWebKit/605.1.15 "
|
|
"(KHTML, like Gecko) Version/%1 Mobile/15E148 Safari/604.1")
|
|
.arg(safari)},
|
|
{false, {}, QStringLiteral("iOS"), {}, {}, {}, true}},
|
|
// QtWebEngine's own string, which names it and gets some sites to
|
|
// refuse or degrade service.
|
|
{{QStringLiteral("qtwebengine"),
|
|
QStringLiteral("Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 "
|
|
"(KHTML, like Gecko) "
|
|
"QtWebEngine/%1 Chrome/%2 Safari/537.36")
|
|
.arg(QString::fromLatin1(qVersion()),
|
|
QString::fromLatin1(qWebEngineChromiumVersion()))},
|
|
{true, {}, QStringLiteral("Linux"), QString(), QStringLiteral("x86"),
|
|
QStringLiteral("64"), false}},
|
|
};
|
|
}
|
|
|
|
const QList<Entry> &entries()
|
|
{
|
|
static const QList<Entry> all = buildEntries();
|
|
return all;
|
|
}
|
|
|
|
QString ¤tName()
|
|
{
|
|
static QString name;
|
|
return name;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
const QList<Preset> &presets()
|
|
{
|
|
static const QList<Preset> all = [] {
|
|
QList<Preset> list;
|
|
for (const Entry &entry : entries())
|
|
list.append(entry.preset);
|
|
return list;
|
|
}();
|
|
return all;
|
|
}
|
|
|
|
bool apply(QWebEngineProfile *profile, const QString &name)
|
|
{
|
|
for (const Entry &entry : entries()) {
|
|
if (entry.preset.name != name)
|
|
continue;
|
|
profile->setHttpUserAgent(entry.preset.userAgent);
|
|
QWebEngineClientHints *hints = profile->clientHints();
|
|
hints->setAllClientHintsEnabled(entry.hints.enabled);
|
|
hints->setPlatform(entry.hints.platform);
|
|
hints->setPlatformVersion(entry.hints.platformVersion);
|
|
hints->setArch(entry.hints.arch);
|
|
hints->setBitness(entry.hints.bitness);
|
|
hints->setIsMobile(entry.hints.mobile);
|
|
hints->setFullVersionList(brands(entry.hints.brand));
|
|
currentName() = name;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
QString current()
|
|
{
|
|
return currentName();
|
|
}
|
|
|
|
} // namespace useragent
|