1/*
2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3 *           (C) 1999 Antti Koivisto (koivisto@kde.org)
4 *           (C) 2001 Dirk Mueller (mueller@kde.org)
5 * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
6 * Copyright (C) 2006 Samuel Weinig (sam@webkit.org)
7 * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Library General Public
11 * License as published by the Free Software Foundation; either
12 * version 2 of the License, or (at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17 * Library General Public License for more details.
18 *
19 * You should have received a copy of the GNU Library General Public License
20 * along with this library; see the file COPYING.LIB.  If not, write to
21 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22 * Boston, MA 02110-1301, USA.
23 */
24
25#include "config.h"
26#include "core/dom/DOMImplementation.h"
27
28#include "bindings/core/v8/ExceptionState.h"
29#include "core/HTMLNames.h"
30#include "core/SVGNames.h"
31#include "core/css/CSSStyleSheet.h"
32#include "core/css/MediaList.h"
33#include "core/css/StyleSheetContents.h"
34#include "core/dom/ContextFeatures.h"
35#include "core/dom/DocumentInit.h"
36#include "core/dom/DocumentType.h"
37#include "core/dom/Element.h"
38#include "core/dom/ExceptionCode.h"
39#include "core/dom/Text.h"
40#include "core/dom/XMLDocument.h"
41#include "core/dom/custom/CustomElementRegistrationContext.h"
42#include "core/frame/LocalFrame.h"
43#include "core/frame/UseCounter.h"
44#include "core/html/HTMLDocument.h"
45#include "core/html/HTMLHeadElement.h"
46#include "core/html/HTMLMediaElement.h"
47#include "core/html/HTMLTitleElement.h"
48#include "core/html/HTMLViewSourceDocument.h"
49#include "core/html/ImageDocument.h"
50#include "core/html/MediaDocument.h"
51#include "core/html/PluginDocument.h"
52#include "core/html/TextDocument.h"
53#include "core/loader/FrameLoader.h"
54#include "core/page/Page.h"
55#include "platform/ContentType.h"
56#include "platform/MIMETypeRegistry.h"
57#include "platform/graphics/Image.h"
58#include "platform/graphics/media/MediaPlayer.h"
59#include "platform/plugins/PluginData.h"
60#include "platform/weborigin/SecurityOrigin.h"
61#include "wtf/StdLibExtras.h"
62
63namespace blink {
64
65typedef HashSet<String, CaseFoldingHash> FeatureSet;
66
67static void addString(FeatureSet& set, const char* string)
68{
69    set.add(string);
70}
71
72static bool isSupportedSVG10Feature(const String& feature, const String& version)
73{
74    if (!version.isEmpty() && version != "1.0")
75        return false;
76
77    static bool initialized = false;
78    DEFINE_STATIC_LOCAL(FeatureSet, svgFeatures, ());
79    if (!initialized) {
80#if ENABLE(SVG_FONTS)
81        addString(svgFeatures, "svg");
82        addString(svgFeatures, "svg.static");
83#endif
84//      addString(svgFeatures, "svg.animation");
85//      addString(svgFeatures, "svg.dynamic");
86//      addString(svgFeatures, "svg.dom.animation");
87//      addString(svgFeatures, "svg.dom.dynamic");
88#if ENABLE(SVG_FONTS)
89        addString(svgFeatures, "dom");
90        addString(svgFeatures, "dom.svg");
91        addString(svgFeatures, "dom.svg.static");
92#endif
93//      addString(svgFeatures, "svg.all");
94//      addString(svgFeatures, "dom.svg.all");
95        initialized = true;
96    }
97    return feature.startsWith("org.w3c.", false)
98        && svgFeatures.contains(feature.right(feature.length() - 8));
99}
100
101static bool isSupportedSVG11Feature(const String& feature, const String& version)
102{
103    if (!version.isEmpty() && version != "1.1")
104        return false;
105
106    static bool initialized = false;
107    DEFINE_STATIC_LOCAL(FeatureSet, svgFeatures, ());
108    if (!initialized) {
109        // Sadly, we cannot claim to implement any of the SVG 1.1 generic feature sets
110        // lack of Font and Filter support.
111        // http://bugs.webkit.org/show_bug.cgi?id=15480
112#if ENABLE(SVG_FONTS)
113        addString(svgFeatures, "SVG");
114        addString(svgFeatures, "SVGDOM");
115        addString(svgFeatures, "SVG-static");
116        addString(svgFeatures, "SVGDOM-static");
117#endif
118        addString(svgFeatures, "SVG-animation");
119        addString(svgFeatures, "SVGDOM-animation");
120//      addString(svgFeatures, "SVG-dynamic);
121//      addString(svgFeatures, "SVGDOM-dynamic);
122        addString(svgFeatures, "CoreAttribute");
123        addString(svgFeatures, "Structure");
124        addString(svgFeatures, "BasicStructure");
125        addString(svgFeatures, "ContainerAttribute");
126        addString(svgFeatures, "ConditionalProcessing");
127        addString(svgFeatures, "Image");
128        addString(svgFeatures, "Style");
129        addString(svgFeatures, "ViewportAttribute");
130        addString(svgFeatures, "Shape");
131        addString(svgFeatures, "Text");
132        addString(svgFeatures, "BasicText");
133        addString(svgFeatures, "PaintAttribute");
134        addString(svgFeatures, "BasicPaintAttribute");
135        addString(svgFeatures, "OpacityAttribute");
136        addString(svgFeatures, "GraphicsAttribute");
137        addString(svgFeatures, "BaseGraphicsAttribute");
138        addString(svgFeatures, "Marker");
139//      addString(svgFeatures, "ColorProfile");
140        addString(svgFeatures, "Gradient");
141        addString(svgFeatures, "Pattern");
142        addString(svgFeatures, "Clip");
143        addString(svgFeatures, "BasicClip");
144        addString(svgFeatures, "Mask");
145        addString(svgFeatures, "Filter");
146        addString(svgFeatures, "BasicFilter");
147        addString(svgFeatures, "DocumentEventsAttribute");
148        addString(svgFeatures, "GraphicalEventsAttribute");
149//      addString(svgFeatures, "AnimationEventsAttribute");
150        addString(svgFeatures, "Cursor");
151        addString(svgFeatures, "Hyperlinking");
152        addString(svgFeatures, "XlinkAttribute");
153        addString(svgFeatures, "View");
154        addString(svgFeatures, "Script");
155        addString(svgFeatures, "Animation");
156#if ENABLE(SVG_FONTS)
157        addString(svgFeatures, "Font");
158        addString(svgFeatures, "BasicFont");
159#endif
160        addString(svgFeatures, "Extensibility");
161        initialized = true;
162    }
163    return feature.startsWith("http://www.w3.org/tr/svg11/feature#", false)
164        && svgFeatures.contains(feature.right(feature.length() - 35));
165}
166
167DOMImplementation::DOMImplementation(Document& document)
168    : m_document(document)
169{
170}
171
172bool DOMImplementation::hasFeature(const String& feature, const String& version)
173{
174    if (feature.startsWith("http://www.w3.org/TR/SVG", false)
175    || feature.startsWith("org.w3c.dom.svg", false)
176    || feature.startsWith("org.w3c.svg", false)) {
177        // FIXME: SVG 2.0 support?
178        return isSupportedSVG10Feature(feature, version) || isSupportedSVG11Feature(feature, version);
179    }
180    return true;
181}
182
183bool DOMImplementation::hasFeatureForBindings(const String& feature, const String& version)
184{
185    if (!hasFeature(feature, version)) {
186        UseCounter::count(m_document, UseCounter::DOMImplementationHasFeatureReturnFalse);
187        return false;
188    }
189    return true;
190}
191
192PassRefPtrWillBeRawPtr<DocumentType> DOMImplementation::createDocumentType(const AtomicString& qualifiedName,
193    const String& publicId, const String& systemId, ExceptionState& exceptionState)
194{
195    AtomicString prefix, localName;
196    if (!Document::parseQualifiedName(qualifiedName, prefix, localName, exceptionState))
197        return nullptr;
198
199    return DocumentType::create(m_document, qualifiedName, publicId, systemId);
200}
201
202PassRefPtrWillBeRawPtr<XMLDocument> DOMImplementation::createDocument(const AtomicString& namespaceURI,
203    const AtomicString& qualifiedName, DocumentType* doctype, ExceptionState& exceptionState)
204{
205    RefPtrWillBeRawPtr<XMLDocument> doc = nullptr;
206    DocumentInit init = DocumentInit::fromContext(document().contextDocument());
207    if (namespaceURI == SVGNames::svgNamespaceURI) {
208        doc = XMLDocument::createSVG(init);
209    } else if (namespaceURI == HTMLNames::xhtmlNamespaceURI) {
210        doc = XMLDocument::createXHTML(init.withRegistrationContext(document().registrationContext()));
211    } else {
212        doc = XMLDocument::create(init);
213    }
214
215    doc->setSecurityOrigin(document().securityOrigin()->isolatedCopy());
216    doc->setContextFeatures(document().contextFeatures());
217
218    RefPtrWillBeRawPtr<Node> documentElement = nullptr;
219    if (!qualifiedName.isEmpty()) {
220        documentElement = doc->createElementNS(namespaceURI, qualifiedName, exceptionState);
221        if (exceptionState.hadException())
222            return nullptr;
223    }
224
225    if (doctype)
226        doc->appendChild(doctype);
227    if (documentElement)
228        doc->appendChild(documentElement.release());
229
230    return doc.release();
231}
232
233bool DOMImplementation::isXMLMIMEType(const String& mimeType)
234{
235    if (equalIgnoringCase(mimeType, "text/xml")
236        || equalIgnoringCase(mimeType, "application/xml")
237        || equalIgnoringCase(mimeType, "text/xsl"))
238        return true;
239
240    // Per RFCs 3023 and 2045, an XML MIME type is of the form:
241    // ^[0-9a-zA-Z_\\-+~!$\\^{}|.%'`#&*]+/[0-9a-zA-Z_\\-+~!$\\^{}|.%'`#&*]+\+xml$
242
243    int length = mimeType.length();
244    if (length < 7)
245        return false;
246
247    if (mimeType[0] == '/' || mimeType[length - 5] == '/' || !mimeType.endsWith("+xml", false))
248        return false;
249
250    bool hasSlash = false;
251    for (int i = 0; i < length - 4; ++i) {
252        UChar ch = mimeType[i];
253        if (ch >= '0' && ch <= '9')
254            continue;
255        if (ch >= 'a' && ch <= 'z')
256            continue;
257        if (ch >= 'A' && ch <= 'Z')
258            continue;
259        switch (ch) {
260        case '_':
261        case '-':
262        case '+':
263        case '~':
264        case '!':
265        case '$':
266        case '^':
267        case '{':
268        case '}':
269        case '|':
270        case '.':
271        case '%':
272        case '\'':
273        case '`':
274        case '#':
275        case '&':
276        case '*':
277            continue;
278        case '/':
279            if (hasSlash)
280                return false;
281            hasSlash = true;
282            continue;
283        default:
284            return false;
285        }
286    }
287
288    return true;
289}
290
291bool DOMImplementation::isJSONMIMEType(const String& mimeType)
292{
293    if (mimeType.startsWith("application/json", false))
294        return true;
295    if (mimeType.startsWith("application/", false)) {
296        size_t subtype = mimeType.find("+json", 12, false);
297        if (subtype != kNotFound) {
298            // Just check that a parameter wasn't matched.
299            size_t parameterMarker = mimeType.find(";");
300            if (parameterMarker == kNotFound) {
301                unsigned endSubtype = static_cast<unsigned>(subtype) + 5;
302                return endSubtype == mimeType.length() || isASCIISpace(mimeType[endSubtype]);
303            }
304            return parameterMarker > subtype;
305        }
306    }
307    return false;
308}
309
310static bool isTextPlainType(const String& mimeType)
311{
312    return mimeType.startsWith("text/", false)
313        && !(equalIgnoringCase(mimeType, "text/html")
314            || equalIgnoringCase(mimeType, "text/xml")
315            || equalIgnoringCase(mimeType, "text/xsl"));
316}
317
318bool DOMImplementation::isTextMIMEType(const String& mimeType)
319{
320    return MIMETypeRegistry::isSupportedJavaScriptMIMEType(mimeType) || isJSONMIMEType(mimeType) || isTextPlainType(mimeType);
321}
322
323PassRefPtrWillBeRawPtr<HTMLDocument> DOMImplementation::createHTMLDocument(const String& title)
324{
325    DocumentInit init = DocumentInit::fromContext(document().contextDocument())
326        .withRegistrationContext(document().registrationContext());
327    RefPtrWillBeRawPtr<HTMLDocument> d = HTMLDocument::create(init);
328    d->open();
329    d->write("<!doctype html><html><head></head><body></body></html>");
330    if (!title.isNull()) {
331        HTMLHeadElement* headElement = d->head();
332        ASSERT(headElement);
333        RefPtrWillBeRawPtr<HTMLTitleElement> titleElement = HTMLTitleElement::create(*d);
334        headElement->appendChild(titleElement);
335        titleElement->appendChild(d->createTextNode(title), ASSERT_NO_EXCEPTION);
336    }
337    d->setSecurityOrigin(document().securityOrigin()->isolatedCopy());
338    d->setContextFeatures(document().contextFeatures());
339    return d.release();
340}
341
342PassRefPtrWillBeRawPtr<Document> DOMImplementation::createDocument(const String& type, LocalFrame* frame, const KURL& url, bool inViewSourceMode)
343{
344    return createDocument(type, DocumentInit(url, frame), inViewSourceMode);
345}
346
347PassRefPtrWillBeRawPtr<Document> DOMImplementation::createDocument(const String& type, const DocumentInit& init, bool inViewSourceMode)
348{
349    if (inViewSourceMode)
350        return HTMLViewSourceDocument::create(init, type);
351
352    // Plugins cannot take HTML and XHTML from us, and we don't even need to initialize the plugin database for those.
353    if (type == "text/html")
354        return HTMLDocument::create(init);
355    if (type == "application/xhtml+xml")
356        return XMLDocument::createXHTML(init);
357
358    PluginData* pluginData = 0;
359    if (init.frame() && init.frame()->page() && init.frame()->loader().allowPlugins(NotAboutToInstantiatePlugin))
360        pluginData = init.frame()->page()->pluginData();
361
362    // PDF is one image type for which a plugin can override built-in support.
363    // We do not want QuickTime to take over all image types, obviously.
364    if ((type == "application/pdf" || type == "text/pdf") && pluginData && pluginData->supportsMimeType(type))
365        return PluginDocument::create(init);
366    if (Image::supportsType(type))
367        return ImageDocument::create(init);
368
369    // Check to see if the type can be played by our MediaPlayer, if so create a MediaDocument
370    if (HTMLMediaElement::supportsType(ContentType(type)))
371        return MediaDocument::create(init);
372
373    // Everything else except text/plain can be overridden by plugins. In particular, Adobe SVG Viewer should be used for SVG, if installed.
374    // Disallowing plug-ins to use text/plain prevents plug-ins from hijacking a fundamental type that the browser is expected to handle,
375    // and also serves as an optimization to prevent loading the plug-in database in the common case.
376    if (type != "text/plain" && pluginData && pluginData->supportsMimeType(type))
377        return PluginDocument::create(init);
378    if (isTextMIMEType(type))
379        return TextDocument::create(init);
380    if (type == "image/svg+xml")
381        return XMLDocument::createSVG(init);
382    if (isXMLMIMEType(type))
383        return XMLDocument::create(init);
384
385    return HTMLDocument::create(init);
386}
387
388void DOMImplementation::trace(Visitor* visitor)
389{
390    visitor->trace(m_document);
391}
392
393}
394