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