1 /* 2 * Copyright (C) 2011, Google Inc. All rights reserved. 3 * Copyright (C) 2012, 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 #if ENABLE(NAVIGATOR_CONTENT_UTILS) 31 32 #include "bindings/v8/ExceptionState.h" 33 #include "core/dom/Document.h" 34 #include "core/dom/ExceptionCode.h" 35 #include "core/page/Frame.h" 36 #include "core/page/Navigator.h" 37 #include "core/page/Page.h" 38 #include "wtf/HashSet.h" 39 40 namespace WebCore { 41 42 static HashSet<String>* protocolWhitelist; 43 44 static void initProtocolHandlerWhitelist() 45 { 46 protocolWhitelist = new HashSet<String>; 47 static const char* protocols[] = { 48 "bitcoin", 49 "geo", 50 "im", 51 "irc", 52 "ircs", 53 "magnet", 54 "mailto", 55 "mms", 56 "news", 57 "nntp", 58 "sip", 59 "sms", 60 "smsto", 61 "ssh", 62 "tel", 63 "urn", 64 "webcal", 65 "webtai", 66 "xmpp", 67 }; 68 for (size_t i = 0; i < WTF_ARRAY_LENGTH(protocols); ++i) 69 protocolWhitelist->add(protocols[i]); 70 } 71 72 static bool verifyCustomHandlerURL(const String& baseURL, const String& url, ExceptionState& es) 73 { 74 // The specification requires that it is a SyntaxError if the "%s" token is 75 // not present. 76 static const char token[] = "%s"; 77 int index = url.find(token); 78 if (-1 == index) { 79 es.throwDOMException(SyntaxError); 80 return false; 81 } 82 83 // It is also a SyntaxError if the custom handler URL, as created by removing 84 // the "%s" token and prepending the base url, does not resolve. 85 String newURL = url; 86 newURL.remove(index, WTF_ARRAY_LENGTH(token) - 1); 87 88 KURL base(ParsedURLString, baseURL); 89 KURL kurl(base, newURL); 90 91 if (kurl.isEmpty() || !kurl.isValid()) { 92 es.throwDOMException(SyntaxError); 93 return false; 94 } 95 96 return true; 97 } 98 99 static bool isProtocolWhitelisted(const String& scheme) 100 { 101 if (!protocolWhitelist) 102 initProtocolHandlerWhitelist(); 103 return protocolWhitelist->contains(scheme); 104 } 105 106 static bool verifyProtocolHandlerScheme(const String& scheme, ExceptionState& es) 107 { 108 if (scheme.startsWith("web+")) { 109 if (isValidProtocol(scheme)) 110 return true; 111 es.throwDOMException(SecurityError); 112 return false; 113 } 114 115 if (isProtocolWhitelisted(scheme)) 116 return true; 117 es.throwDOMException(SecurityError); 118 return false; 119 } 120 121 NavigatorContentUtils* NavigatorContentUtils::from(Page* page) 122 { 123 return static_cast<NavigatorContentUtils*>(RefCountedSupplement<Page, NavigatorContentUtils>::from(page, NavigatorContentUtils::supplementName())); 124 } 125 126 NavigatorContentUtils::~NavigatorContentUtils() 127 { 128 } 129 130 PassRefPtr<NavigatorContentUtils> NavigatorContentUtils::create(NavigatorContentUtilsClient* client) 131 { 132 return adoptRef(new NavigatorContentUtils(client)); 133 } 134 135 void NavigatorContentUtils::registerProtocolHandler(Navigator* navigator, const String& scheme, const String& url, const String& title, ExceptionState& es) 136 { 137 if (!navigator->frame()) 138 return; 139 140 Document* document = navigator->frame()->document(); 141 if (!document) 142 return; 143 144 String baseURL = document->baseURL().baseAsString(); 145 146 if (!verifyCustomHandlerURL(baseURL, url, es)) 147 return; 148 149 if (!verifyProtocolHandlerScheme(scheme, es)) 150 return; 151 152 NavigatorContentUtils::from(navigator->frame()->page())->client()->registerProtocolHandler(scheme, baseURL, url, navigator->frame()->displayStringModifiedByEncoding(title)); 153 } 154 155 #if ENABLE(CUSTOM_SCHEME_HANDLER) 156 static String customHandlersStateString(const NavigatorContentUtilsClient::CustomHandlersState state) 157 { 158 DEFINE_STATIC_LOCAL(const String, newHandler, ("new")); 159 DEFINE_STATIC_LOCAL(const String, registeredHandler, ("registered")); 160 DEFINE_STATIC_LOCAL(const String, declinedHandler, ("declined")); 161 162 switch (state) { 163 case NavigatorContentUtilsClient::CustomHandlersNew: 164 return newHandler; 165 case NavigatorContentUtilsClient::CustomHandlersRegistered: 166 return registeredHandler; 167 case NavigatorContentUtilsClient::CustomHandlersDeclined: 168 return declinedHandler; 169 } 170 171 ASSERT_NOT_REACHED(); 172 return String(); 173 } 174 175 String NavigatorContentUtils::isProtocolHandlerRegistered(Navigator* navigator, const String& scheme, const String& url, ExceptionState& es) 176 { 177 DEFINE_STATIC_LOCAL(const String, declined, ("declined")); 178 179 if (!navigator->frame()) 180 return declined; 181 182 Document* document = navigator->frame()->document(); 183 String baseURL = document->baseURL().baseAsString(); 184 185 if (!verifyCustomHandlerURL(baseURL, url, es)) 186 return declined; 187 188 if (!verifyProtocolHandlerScheme(scheme, es)) 189 return declined; 190 191 return customHandlersStateString(NavigatorContentUtils::from(navigator->frame()->page())->client()->isProtocolHandlerRegistered(scheme, baseURL, url)); 192 } 193 194 void NavigatorContentUtils::unregisterProtocolHandler(Navigator* navigator, const String& scheme, const String& url, ExceptionState& es) 195 { 196 if (!navigator->frame()) 197 return; 198 199 Document* document = navigator->frame()->document(); 200 String baseURL = document->baseURL().baseAsString(); 201 202 if (!verifyCustomHandlerURL(baseURL, url, es)) 203 return; 204 205 if (!verifyProtocolHandlerScheme(scheme, es)) 206 return; 207 208 NavigatorContentUtils::from(navigator->frame()->page())->client()->unregisterProtocolHandler(scheme, baseURL, url); 209 } 210 #endif 211 212 const char* NavigatorContentUtils::supplementName() 213 { 214 return "NavigatorContentUtils"; 215 } 216 217 void provideNavigatorContentUtilsTo(Page* page, NavigatorContentUtilsClient* client) 218 { 219 RefCountedSupplement<Page, NavigatorContentUtils>::provideTo(page, NavigatorContentUtils::supplementName(), NavigatorContentUtils::create(client)); 220 } 221 222 } // namespace WebCore 223 224 #endif // ENABLE(NAVIGATOR_CONTENT_UTILS) 225 226