QMimeDatabase: fix handling of glob-deleteall

Binary provider:
  It was not possible to remove the first glob in a local override,
  because the mainPattern handling would re-add the first glob back.

XML provider:
  It didn't support glob-deleteall.
  Also, the order of the providers was wrong. We want to pick local
  overrides first, the internal DB has to go last in the list.

Fixes: QTBUG-85436
Pick-to: 5.15
Change-Id: I9a4523f37cd962c730df9a6ed992bd01c075bf03
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
David Faure 2020-09-11 10:05:51 +02:00
parent ad9108e2e9
commit fc8f5afc87
5 changed files with 33 additions and 19 deletions

View File

@ -101,27 +101,13 @@ void QMimeDatabasePrivate::loadProviders()
const auto fdoIterator = std::find_if(mimeDirs.constBegin(), mimeDirs.constEnd(), [](const QString &mimeDir) -> bool {
return QFileInfo::exists(mimeDir + QStringLiteral("/packages/freedesktop.org.xml")); }
);
const bool needInternalDB = QMimeXMLProvider::InternalDatabaseAvailable && fdoIterator == mimeDirs.constEnd();
//qDebug() << "mime dirs:" << mimeDirs;
Providers currentProviders;
std::swap(m_providers, currentProviders);
if (QMimeXMLProvider::InternalDatabaseAvailable && fdoIterator == mimeDirs.constEnd()) {
m_providers.reserve(mimeDirs.size() + 1);
// Check if we already have a provider for the InternalDatabase
const auto isInternal = [](const std::unique_ptr<QMimeProviderBase> &prov)
{
return prov && prov->isInternalDatabase();
};
const auto it = std::find_if(currentProviders.begin(), currentProviders.end(), isInternal);
if (it == currentProviders.end()) {
m_providers.push_back(Providers::value_type(new QMimeXMLProvider(this, QMimeXMLProvider::InternalDatabase)));
} else {
m_providers.push_back(std::move(*it));
}
} else {
m_providers.reserve(mimeDirs.size());
}
m_providers.reserve(mimeDirs.size() + (needInternalDB ? 1 : 0));
for (const QString &mimeDir : mimeDirs) {
const QString cacheFile = mimeDir + QStringLiteral("/mime.cache");
@ -158,6 +144,21 @@ void QMimeDatabasePrivate::loadProviders()
m_providers.push_back(std::move(provider));
}
}
// mimeDirs is sorted "most local first, most global last"
// so the internal XML DB goes at the end
if (needInternalDB) {
// Check if we already have a provider for the InternalDatabase
const auto isInternal = [](const std::unique_ptr<QMimeProviderBase> &prov)
{
return prov && prov->isInternalDatabase();
};
const auto it = std::find_if(currentProviders.begin(), currentProviders.end(), isInternal);
if (it == currentProviders.end()) {
m_providers.push_back(Providers::value_type(new QMimeXMLProvider(this, QMimeXMLProvider::InternalDatabase)));
} else {
m_providers.push_back(std::move(*it));
}
}
}
const QMimeDatabasePrivate::Providers &QMimeDatabasePrivate::providers()
@ -215,7 +216,6 @@ QStringList QMimeDatabasePrivate::mimeTypeForFileName(const QString &fileName)
QMimeGlobMatchResult QMimeDatabasePrivate::findByFileName(const QString &fileName)
{
QMimeGlobMatchResult result;
// TODO this parses in the order (local, global). Check that it handles "NOGLOBS" correctly.
for (const auto &provider : providers())
provider->addFileNameMatches(fileName, result);
return result;

View File

@ -542,6 +542,7 @@ void QMimeBinaryProvider::loadMimeTypePrivate(QMimeTypePrivate &data)
data.iconName = xml.attributes().value(QLatin1String("name")).toString();
} else if (tag == QLatin1String("glob-deleteall")) { // as written out by shared-mime-info >= 0.70
data.globPatterns.clear();
mainPattern.clear();
} else if (tag == QLatin1String("glob")) { // as written out by shared-mime-info >= 0.70
const QString pattern = xml.attributes().value(QLatin1String("pattern")).toString();
if (mainPattern.isEmpty() && pattern.startsWith(QLatin1Char('*'))) {

View File

@ -63,6 +63,7 @@ static const char genericIconTagC[] = "generic-icon";
static const char iconTagC[] = "icon";
static const char nameAttributeC[] = "name";
static const char globTagC[] = "glob";
static const char globDeleteAllTagC[] = "glob-deleteall";
static const char aliasTagC[] = "alias";
static const char patternAttributeC[] = "pattern";
static const char weightAttributeC[] = "weight";
@ -123,6 +124,7 @@ QMimeTypeParserBase::ParseState QMimeTypeParserBase::nextState(ParseState curren
case ParseGenericIcon:
case ParseIcon:
case ParseGlobPattern:
case ParseGlobDeleteAll:
case ParseSubClass:
case ParseAlias:
case ParseOtherMimeTypeSubTag:
@ -137,6 +139,8 @@ QMimeTypeParserBase::ParseState QMimeTypeParserBase::nextState(ParseState curren
return ParseIcon;
if (startElement == QLatin1String(globTagC))
return ParseGlobPattern;
if (startElement == QLatin1String(globDeleteAllTagC))
return ParseGlobDeleteAll;
if (startElement == QLatin1String(subClassTagC))
return ParseSubClass;
if (startElement == QLatin1String(aliasTagC))
@ -242,6 +246,9 @@ bool QMimeTypeParserBase::parse(QIODevice *dev, const QString &fileName, QString
data.addGlobPattern(pattern); // just for QMimeType::globPatterns()
}
break;
case ParseGlobDeleteAll:
data.globPatterns.clear();
break;
case ParseSubClass: {
const QString inheritsFrom = atts.value(QLatin1String(mimeTypeAttributeC)).toString();
if (!inheritsFrom.isEmpty())

View File

@ -90,6 +90,7 @@ private:
ParseGenericIcon,
ParseIcon,
ParseGlobPattern,
ParseGlobDeleteAll,
ParseSubClass,
ParseAlias,
ParseMagic,

View File

@ -985,11 +985,10 @@ void tst_QMimeDatabase::installNewGlobalMimeType()
QCOMPARE(db.mimeTypeForFile(qmlTestFile).name(),
QString::fromLatin1("text/x-qml"));
// ensure we can access the empty glob list
{
QMimeType objcsrc = db.mimeTypeForName(QStringLiteral("text/x-objcsrc"));
QVERIFY(objcsrc.isValid());
qDebug() << objcsrc.globPatterns();
QCOMPARE(objcsrc.globPatterns(), QStringList());
}
const QString fooTestFile = QLatin1String(RESOURCE_PREFIX "magic-and-hierarchy.foo");
@ -1066,6 +1065,12 @@ void tst_QMimeDatabase::installNewLocalMimeType()
QCOMPARE(db.mimeTypeForName(QLatin1String("text/x-SuSE-ymu")).comment(), QString("URL of a YaST Meta Package"));
checkHasMimeType("text/x-suse-ymp");
{ // QTBUG-85436
QMimeType objcsrc = db.mimeTypeForName(QStringLiteral("text/x-objcsrc"));
QVERIFY(objcsrc.isValid());
QCOMPARE(objcsrc.globPatterns(), QStringList());
}
// Test that a double-definition of a mimetype doesn't lead to sniffing ("conflicting globs").
const QString qmlTestFile = QLatin1String(RESOURCE_PREFIX "test.qml");
QVERIFY2(!qmlTestFile.isEmpty(),