Home | History | Annotate | Download | only in navigatorcontentutils
      1 /*
      2  * Copyright (C) 2011, Google Inc. All rights reserved.
      3  * Copyright (C) 2014, Samsung Electronics. All rights reserved.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions are met:
      7  *
      8  * 1. Redistributions of source code must retain the above copyright
      9  *    notice, this list of conditions and the following disclaimer.
     10  * 2. Redistributions in binary form must reproduce the above copyright
     11  *    notice, this list of conditions and the following disclaimer in the
     12  *    documentation and/or other materials provided with the distribution.
     13  *
     14  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND
     15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     17  * ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE
     18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
     20  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
     21  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
     24  * DAMAGE.
     25  */
     26 
     27 #include "config.h"
     28 #include "modules/navigatorcontentutils/NavigatorContentUtils.h"
     29 
     30 #include "bindings/core/v8/ExceptionState.h"
     31 #include "core/dom/Document.h"
     32 #include "core/dom/ExceptionCode.h"
     33 #include "core/frame/LocalFrame.h"
     34 #include "core/frame/Navigator.h"
     35 #include "core/page/Page.h"
     36 #include "wtf/HashSet.h"
     37 #include "wtf/text/StringBuilder.h"
     38 
     39 namespace blink {
     40 
     41 static HashSet<String>* schemeWhitelist;
     42 
     43 static void initCustomSchemeHandlerWhitelist()
     44 {
     45     schemeWhitelist = new HashSet<String>;
     46     static const char* const schemes[] = {
     47         "bitcoin",
     48         "geo",
     49         "im",
     50         "irc",
     51         "ircs",
     52         "magnet",
     53         "mailto",
     54         "mms",
     55         "news",
     56         "nntp",
     57         "sip",
     58         "sms",
     59         "smsto",
     60         "ssh",
     61         "tel",
     62         "urn",
     63         "webcal",
     64         "wtai",
     65         "xmpp",
     66     };
     67     for (size_t i = 0; i < WTF_ARRAY_LENGTH(schemes); ++i)
     68         schemeWhitelist->add(schemes[i]);
     69 }
     70 
     71 static bool verifyCustomHandlerURL(const Document& document, const String& url, ExceptionState& exceptionState)
     72 {
     73     // The specification requires that it is a SyntaxError if the "%s" token is
     74     // not present.
     75     static const char token[] = "%s";
     76     int index = url.find(token);
     77     if (-1 == index) {
     78         exceptionState.throwDOMException(SyntaxError, "The url provided ('" + url + "') does not contain '%s'.");
     79         return false;
     80     }
     81 
     82     // It is also a SyntaxError if the custom handler URL, as created by removing
     83     // the "%s" token and prepending the base url, does not resolve.
     84     String newURL = url;
     85     newURL.remove(index, WTF_ARRAY_LENGTH(token) - 1);
     86 
     87     KURL kurl = document.completeURL(url);
     88 
     89     if (kurl.isEmpty() || !kurl.isValid()) {
     90         exceptionState.throwDOMException(SyntaxError, "The custom handler URL created by removing '%s' and prepending '" + document.baseURL().string() + "' is invalid.");
     91         return false;
     92     }
     93 
     94     // The specification says that the API throws SecurityError exception if the
     95     // URL's origin differs from the document's origin.
     96     if (!document.securityOrigin()->canRequest(kurl)) {
     97         exceptionState.throwSecurityError("Can only register custom handler in the document's origin.");
     98         return false;
     99     }
    100 
    101     return true;
    102 }
    103 
    104 static bool isSchemeWhitelisted(const String& scheme)
    105 {
    106     if (!schemeWhitelist)
    107         initCustomSchemeHandlerWhitelist();
    108 
    109     StringBuilder builder;
    110     unsigned length = scheme.length();
    111     for (unsigned i = 0; i < length; ++i)
    112         builder.append(toASCIILower(scheme[i]));
    113 
    114     return schemeWhitelist->contains(builder.toString());
    115 }
    116 
    117 static bool verifyCustomHandlerScheme(const String& scheme, ExceptionState& exceptionState)
    118 {
    119     if (!isValidProtocol(scheme)) {
    120         exceptionState.throwSecurityError("The scheme '" + scheme + "' is not valid protocol");
    121         return false;
    122     }
    123 
    124     if (scheme.startsWith("web+")) {
    125         // The specification requires that the length of scheme is at least five characteres (including 'web+' prefix).
    126         if (scheme.length() >= 5)
    127             return true;
    128 
    129         exceptionState.throwSecurityError("The scheme '" + scheme + "' is less than five characters long.");
    130         return false;
    131     }
    132 
    133     if (isSchemeWhitelisted(scheme))
    134         return true;
    135 
    136     exceptionState.throwSecurityError("The scheme '" + scheme + "' doesn't belong to the scheme whitelist. Please prefix non-whitelisted schemes with the string 'web+'.");
    137     return false;
    138 }
    139 
    140 NavigatorContentUtils* NavigatorContentUtils::from(Page& page)
    141 {
    142     return static_cast<NavigatorContentUtils*>(WillBeHeapSupplement<Page>::from(page, supplementName()));
    143 }
    144 
    145 NavigatorContentUtils::~NavigatorContentUtils()
    146 {
    147 }
    148 
    149 PassOwnPtrWillBeRawPtr<NavigatorContentUtils> NavigatorContentUtils::create(PassOwnPtr<NavigatorContentUtilsClient> client)
    150 {
    151     return adoptPtrWillBeNoop(new NavigatorContentUtils(client));
    152 }
    153 
    154 void NavigatorContentUtils::registerProtocolHandler(Navigator& navigator, const String& scheme, const String& url, const String& title, ExceptionState& exceptionState)
    155 {
    156     if (!navigator.frame())
    157         return;
    158 
    159     Document* document = navigator.frame()->document();
    160     ASSERT(document);
    161 
    162     if (!verifyCustomHandlerURL(*document, url, exceptionState))
    163         return;
    164 
    165     if (!verifyCustomHandlerScheme(scheme, exceptionState))
    166         return;
    167 
    168     ASSERT(navigator.frame()->page());
    169     NavigatorContentUtils::from(*navigator.frame()->page())->client()->registerProtocolHandler(scheme, document->completeURL(url), title);
    170 }
    171 
    172 static String customHandlersStateString(const NavigatorContentUtilsClient::CustomHandlersState state)
    173 {
    174     DEFINE_STATIC_LOCAL(const String, newHandler, ("new"));
    175     DEFINE_STATIC_LOCAL(const String, registeredHandler, ("registered"));
    176     DEFINE_STATIC_LOCAL(const String, declinedHandler, ("declined"));
    177 
    178     switch (state) {
    179     case NavigatorContentUtilsClient::CustomHandlersNew:
    180         return newHandler;
    181     case NavigatorContentUtilsClient::CustomHandlersRegistered:
    182         return registeredHandler;
    183     case NavigatorContentUtilsClient::CustomHandlersDeclined:
    184         return declinedHandler;
    185     }
    186 
    187     ASSERT_NOT_REACHED();
    188     return String();
    189 }
    190 
    191 String NavigatorContentUtils::isProtocolHandlerRegistered(Navigator& navigator, const String& scheme, const String& url, ExceptionState& exceptionState)
    192 {
    193     DEFINE_STATIC_LOCAL(const String, declined, ("declined"));
    194 
    195     if (!navigator.frame())
    196         return declined;
    197 
    198     Document* document = navigator.frame()->document();
    199     ASSERT(document);
    200     if (document->activeDOMObjectsAreStopped())
    201         return declined;
    202 
    203     if (!verifyCustomHandlerURL(*document, url, exceptionState))
    204         return declined;
    205 
    206     if (!verifyCustomHandlerScheme(scheme, exceptionState))
    207         return declined;
    208 
    209     ASSERT(navigator.frame()->page());
    210     return customHandlersStateString(NavigatorContentUtils::from(*navigator.frame()->page())->client()->isProtocolHandlerRegistered(scheme, document->completeURL(url)));
    211 }
    212 
    213 void NavigatorContentUtils::unregisterProtocolHandler(Navigator& navigator, const String& scheme, const String& url, ExceptionState& exceptionState)
    214 {
    215     if (!navigator.frame())
    216         return;
    217 
    218     Document* document = navigator.frame()->document();
    219     ASSERT(document);
    220 
    221     if (!verifyCustomHandlerURL(*document, url, exceptionState))
    222         return;
    223 
    224     if (!verifyCustomHandlerScheme(scheme, exceptionState))
    225         return;
    226 
    227     ASSERT(navigator.frame()->page());
    228     NavigatorContentUtils::from(*navigator.frame()->page())->client()->unregisterProtocolHandler(scheme, document->completeURL(url));
    229 }
    230 
    231 const char* NavigatorContentUtils::supplementName()
    232 {
    233     return "NavigatorContentUtils";
    234 }
    235 
    236 void provideNavigatorContentUtilsTo(Page& page, PassOwnPtr<NavigatorContentUtilsClient> client)
    237 {
    238     NavigatorContentUtils::provideTo(page, NavigatorContentUtils::supplementName(), NavigatorContentUtils::create(client));
    239 }
    240 
    241 } // namespace blink
    242