blob: 471639ba8d03d09a085b10cdee6fa4a58ab23b7b [file] [log] [blame]
/*
* Copyright (C) 2011, Google Inc. All rights reserved.
* Copyright (C) 2012, Samsung Electronics. All rights reserved.
* Copyright (C) 2017 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*/
#include "config.h"
#include "NavigatorContentUtils.h"
#if ENABLE(NAVIGATOR_CONTENT_UTILS)
#include "Document.h"
#include "Frame.h"
#include "Navigator.h"
#include "Page.h"
#include <wtf/HashSet.h>
#include <wtf/NeverDestroyed.h>
namespace WebCore {
static bool verifyCustomHandlerURL(const URL& baseURL, const String& url)
{
// The specification requires that it is a SyntaxError if the "%s" token is
// not present.
static const char token[] = "%s";
int index = url.find(token);
if (-1 == index)
return false;
// It is also a SyntaxError if the custom handler URL, as created by removing
// the "%s" token and prepending the base url, does not resolve.
String newURL = url;
newURL.remove(index, WTF_ARRAY_LENGTH(token) - 1);
URL kurl(baseURL, newURL);
if (kurl.isEmpty() || !kurl.isValid())
return false;
return true;
}
static inline bool isProtocolWhitelisted(const String& scheme)
{
static NeverDestroyed<HashSet<String, ASCIICaseInsensitiveHash>> protocolWhitelist = []() {
HashSet<String, ASCIICaseInsensitiveHash> set;
for (auto* protocol : { "bitcoin", "geo", "im", "irc", "ircs", "magnet", "mailto", "mms", "news", "nntp", "sip", "sms", "smsto", "ssh", "tel", "urn", "webcal", "wtai", "xmpp" })
set.add(protocol);
return set;
}();
return protocolWhitelist.get().contains(scheme);
}
static bool verifyProtocolHandlerScheme(const String& scheme)
{
if (isProtocolWhitelisted(scheme))
return true;
// FIXME: Should this be case sensitive, or should it be ASCII case-insensitive?
if (scheme.startsWith("web+")) {
// The specification requires that the length of scheme is at least five characters (including 'web+' prefix).
if (scheme.length() >= 5 && isValidProtocol(scheme))
return true;
}
return false;
}
NavigatorContentUtils* NavigatorContentUtils::from(Page* page)
{
return static_cast<NavigatorContentUtils*>(Supplement<Page>::from(page, supplementName()));
}
NavigatorContentUtils::~NavigatorContentUtils() = default;
ExceptionOr<void> NavigatorContentUtils::registerProtocolHandler(Navigator& navigator, const String& scheme, const String& url, const String& title)
{
if (!navigator.frame())
return { };
URL baseURL = navigator.frame()->document()->baseURL();
if (!verifyCustomHandlerURL(baseURL, url))
return Exception { SyntaxError };
if (!verifyProtocolHandlerScheme(scheme))
return Exception { SecurityError };
NavigatorContentUtils::from(navigator.frame()->page())->client()->registerProtocolHandler(scheme, baseURL, URL(ParsedURLString, url), navigator.frame()->displayStringModifiedByEncoding(title));
return { };
}
#if ENABLE(CUSTOM_SCHEME_HANDLER)
static String customHandlersStateString(const NavigatorContentUtilsClient::CustomHandlersState state)
{
static NeverDestroyed<String> newHandler(MAKE_STATIC_STRING_IMPL("new"));
static NeverDestroyed<String> registeredHandler(MAKE_STATIC_STRING_IMPL("registered"));
static NeverDestroyed<String> declinedHandler(MAKE_STATIC_STRING_IMPL("declined"));
switch (state) {
case NavigatorContentUtilsClient::CustomHandlersNew:
return newHandler;
case NavigatorContentUtilsClient::CustomHandlersRegistered:
return registeredHandler;
case NavigatorContentUtilsClient::CustomHandlersDeclined:
return declinedHandler;
}
ASSERT_NOT_REACHED();
return String();
}
ExceptionOr<String> NavigatorContentUtils::isProtocolHandlerRegistered(Navigator& navigator, const String& scheme, const String& url)
{
static NeverDestroyed<String> declined(MAKE_STATIC_STRING_IMPL("declined"));
if (!navigator.frame())
return String { declined };
URL baseURL = navigator.frame()->document()->baseURL();
if (!verifyCustomHandlerURL(baseURL, url))
return Exception { SyntaxError };
if (!verifyProtocolHandlerScheme(scheme))
return Exception { SecurityError };
return customHandlersStateString(NavigatorContentUtils::from(navigator.frame()->page())->client()->isProtocolHandlerRegistered(scheme, baseURL, URL(ParsedURLString, url)));
}
ExceptionOr<void> NavigatorContentUtils::unregisterProtocolHandler(Navigator& navigator, const String& scheme, const String& url)
{
if (!navigator.frame())
return { };
URL baseURL = navigator.frame()->document()->baseURL();
if (!verifyCustomHandlerURL(baseURL, url))
return Exception { SyntaxError };
if (!verifyProtocolHandlerScheme(scheme))
return Exception { SecurityError };
NavigatorContentUtils::from(navigator.frame()->page())->client()->unregisterProtocolHandler(scheme, baseURL, URL(ParsedURLString, url));
return { };
}
#endif
const char* NavigatorContentUtils::supplementName()
{
return "NavigatorContentUtils";
}
void provideNavigatorContentUtilsTo(Page* page, std::unique_ptr<NavigatorContentUtilsClient> client)
{
NavigatorContentUtils::provideTo(page, NavigatorContentUtils::supplementName(), std::make_unique<NavigatorContentUtils>(WTFMove(client)));
}
} // namespace WebCore
#endif // ENABLE(NAVIGATOR_CONTENT_UTILS)