ApplicationManifestParser.cpp [plain text]
#include "config.h"
#include "ApplicationManifestParser.h"
#if ENABLE(APPLICATION_MANIFEST)
#include "SecurityOrigin.h"
#include <JavaScriptCore/ConsoleMessage.h>
namespace WebCore {
ApplicationManifest ApplicationManifestParser::parse(ScriptExecutionContext& scriptExecutionContext, const String& source, const URL& manifestURL, const URL& documentURL)
{
ApplicationManifestParser parser { &scriptExecutionContext };
return parser.parseManifest(source, manifestURL, documentURL);
}
ApplicationManifest ApplicationManifestParser::parse(const String& source, const URL& manifestURL, const URL& documentURL)
{
ApplicationManifestParser parser { nullptr };
return parser.parseManifest(source, manifestURL, documentURL);
}
ApplicationManifestParser::ApplicationManifestParser(RefPtr<ScriptExecutionContext> consoleContext)
: m_consoleContext(consoleContext)
{
}
ApplicationManifest ApplicationManifestParser::parseManifest(const String& text, const URL& manifestURL, const URL& documentURL)
{
m_manifestURL = manifestURL;
RefPtr<JSON::Value> jsonValue;
if (!JSON::Value::parseJSON(text, jsonValue)) {
logDeveloperWarning("The manifest is not valid JSON data."_s);
jsonValue = JSON::Object::create();
}
RefPtr<JSON::Object> manifest;
if (!jsonValue->asObject(manifest)) {
logDeveloperWarning("The manifest is not a JSON value of type \"object\"."_s);
manifest = JSON::Object::create();
}
ApplicationManifest parsedManifest;
parsedManifest.startURL = parseStartURL(*manifest, documentURL);
parsedManifest.display = parseDisplay(*manifest);
parsedManifest.name = parseName(*manifest);
parsedManifest.description = parseDescription(*manifest);
parsedManifest.shortName = parseShortName(*manifest);
parsedManifest.scope = parseScope(*manifest, documentURL, parsedManifest.startURL);
return parsedManifest;
}
void ApplicationManifestParser::logManifestPropertyNotAString(const String& propertyName)
{
logDeveloperWarning(makeString("The value of \""_s, propertyName, "\" is not a string."_s));
}
void ApplicationManifestParser::logManifestPropertyInvalidURL(const String& propertyName)
{
logDeveloperWarning(makeString("The value of \""_s, propertyName, "\" is not a valid URL."_s));
}
void ApplicationManifestParser::logDeveloperWarning(const String& message)
{
if (m_consoleContext)
m_consoleContext->addConsoleMessage(std::make_unique<Inspector::ConsoleMessage>(JSC::MessageSource::Other, JSC::MessageType::Log, JSC::MessageLevel::Warning, makeString("Parsing application manifest "_s, m_manifestURL.string(), ": "_s, message)));
}
URL ApplicationManifestParser::parseStartURL(const JSON::Object& manifest, const URL& documentURL)
{
RefPtr<JSON::Value> value;
if (!manifest.getValue("start_url", value))
return documentURL;
String stringValue;
if (!value->asString(stringValue)) {
logManifestPropertyNotAString("start_url"_s);
return documentURL;
}
if (stringValue.isEmpty())
return documentURL;
URL startURL(m_manifestURL, stringValue);
if (!startURL.isValid()) {
logManifestPropertyInvalidURL("start_url"_s);
return documentURL;
}
if (!protocolHostAndPortAreEqual(startURL, documentURL)) {
auto startURLOrigin = SecurityOrigin::create(startURL);
auto documentOrigin = SecurityOrigin::create(documentURL);
logDeveloperWarning(makeString("The start_url's origin of \""_s, startURLOrigin->toString(), "\" is different from the document's origin of \""_s, documentOrigin->toString(), "\"."_s));
return documentURL;
}
return startURL;
}
ApplicationManifest::Display ApplicationManifestParser::parseDisplay(const JSON::Object& manifest)
{
RefPtr<JSON::Value> value;
if (!manifest.getValue("display"_s, value))
return ApplicationManifest::Display::Browser;
String stringValue;
if (!value->asString(stringValue)) {
logManifestPropertyNotAString("display"_s);
return ApplicationManifest::Display::Browser;
}
stringValue = stringValue.stripWhiteSpace().convertToASCIILowercase();
if (stringValue == "fullscreen")
return ApplicationManifest::Display::Fullscreen;
if (stringValue == "standalone")
return ApplicationManifest::Display::Standalone;
if (stringValue == "minimal-ui")
return ApplicationManifest::Display::MinimalUI;
if (stringValue == "browser")
return ApplicationManifest::Display::Browser;
logDeveloperWarning(makeString("\""_s, stringValue, "\" is not a valid display mode."_s));
return ApplicationManifest::Display::Browser;
}
String ApplicationManifestParser::parseName(const JSON::Object& manifest)
{
return parseGenericString(manifest, "name"_s);
}
String ApplicationManifestParser::parseDescription(const JSON::Object& manifest)
{
return parseGenericString(manifest, "description"_s);
}
String ApplicationManifestParser::parseShortName(const JSON::Object& manifest)
{
return parseGenericString(manifest, "short_name"_s);
}
static bool isInScope(const URL& scopeURL, const URL& targetURL)
{
if (scopeURL.isNull() || scopeURL.isEmpty())
return true;
if (!targetURL.isValid())
return false;
auto scopePath = scopeURL.path();
auto targetPath = targetURL.path();
if (protocolHostAndPortAreEqual(scopeURL, targetURL) && targetPath.startsWith(scopePath))
return true;
return false;
}
URL ApplicationManifestParser::parseScope(const JSON::Object& manifest, const URL& documentURL, const URL& startURL)
{
URL defaultScope { startURL, "./" };
RefPtr<JSON::Value> value;
if (!manifest.getValue("scope", value))
return defaultScope;
String stringValue;
if (!value->asString(stringValue)) {
logManifestPropertyNotAString("scope"_s);
return defaultScope;
}
if (stringValue.isEmpty())
return defaultScope;
URL scopeURL(m_manifestURL, stringValue);
if (!scopeURL.isValid()) {
logManifestPropertyInvalidURL("scope"_s);
return defaultScope;
}
if (!protocolHostAndPortAreEqual(scopeURL, documentURL)) {
auto scopeURLOrigin = SecurityOrigin::create(scopeURL);
auto documentOrigin = SecurityOrigin::create(documentURL);
logDeveloperWarning(makeString("The scope's origin of \""_s, scopeURLOrigin->toString(), "\" is different from the document's origin of \""_s, documentOrigin->toString(), "\"."_s));
return defaultScope;
}
if (!isInScope(scopeURL, startURL)) {
logDeveloperWarning("The start URL is not within scope of the provided scope URL."_s);
return defaultScope;
}
return scopeURL;
}
String ApplicationManifestParser::parseGenericString(const JSON::Object& manifest, const String& propertyName)
{
RefPtr<JSON::Value> value;
if (!manifest.getValue(propertyName, value))
return { };
String stringValue;
if (!value->asString(stringValue)) {
logManifestPropertyNotAString(propertyName);
return { };
}
return stringValue.stripWhiteSpace();
}
}
#endif // ENABLE(APPLICATION_MANIFEST)