/* * Copyright (C) 2011, Google Inc. All rights reserved. * Copyright (C) 2012, Samsung Electronics. 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 "ExceptionCode.h" #include "Frame.h" #include "Navigator.h" #include "Page.h" #include #include namespace WebCore { static bool verifyCustomHandlerURL(const URL& baseURL, const String& url) { // The specification requires that it is a SYNTAX_ERR 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 SYNTAX_ERR 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> protocolWhitelist = []() { HashSet 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(Supplement::from(page, supplementName())); } NavigatorContentUtils::~NavigatorContentUtils() { } ExceptionOr 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 { SYNTAX_ERR }; if (!verifyProtocolHandlerScheme(scheme)) return Exception { SECURITY_ERR }; 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 newHandler(ASCIILiteral("new")); static NeverDestroyed registeredHandler(ASCIILiteral("registered")); static NeverDestroyed declinedHandler(ASCIILiteral("declined")); switch (state) { case NavigatorContentUtilsClient::CustomHandlersNew: return newHandler; case NavigatorContentUtilsClient::CustomHandlersRegistered: return registeredHandler; case NavigatorContentUtilsClient::CustomHandlersDeclined: return declinedHandler; } ASSERT_NOT_REACHED(); return String(); } ExceptionOr NavigatorContentUtils::isProtocolHandlerRegistered(Navigator& navigator, const String& scheme, const String& url) { static NeverDestroyed declined(ASCIILiteral("declined")); if (!navigator.frame()) return String { declined }; URL baseURL = navigator.frame()->document()->baseURL(); if (!verifyCustomHandlerURL(baseURL, url)) return Exception { SYNTAX_ERR }; if (!verifyProtocolHandlerScheme(scheme)) return Exception { SECURITY_ERR }; return customHandlersStateString(NavigatorContentUtils::from(navigator.frame()->page())->client()->isProtocolHandlerRegistered(scheme, baseURL, URL(ParsedURLString, url))); } ExceptionOr 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 { SYNTAX_ERR }; if (!verifyProtocolHandlerScheme(scheme)) return Exception { SECURITY_ERR }; 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 client) { NavigatorContentUtils::provideTo(page, NavigatorContentUtils::supplementName(), std::make_unique(WTFMove(client))); } } // namespace WebCore #endif // ENABLE(NAVIGATOR_CONTENT_UTILS)