Extract UTI mapping and allow for additions
[WebKit-https.git] / Source / WebCore / worklets / PaintWorkletGlobalScope.cpp
1 /*
2  * Copyright (C) 2018 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "PaintWorkletGlobalScope.h"
28
29 #if ENABLE(CSS_PAINTING_API)
30
31 #include "DOMWindow.h"
32 #include "Document.h"
33 #include "JSCSSPaintCallback.h"
34 #include "JSDOMConvertCallbacks.h"
35 #include "JSDOMConvertSequences.h"
36 #include "RenderView.h"
37 #include <wtf/IsoMallocInlines.h>
38 #include <wtf/SetForScope.h>
39
40 namespace WebCore {
41 using namespace JSC;
42
43 WTF_MAKE_ISO_ALLOCATED_IMPL(PaintWorkletGlobalScope);
44
45 Ref<PaintWorkletGlobalScope> PaintWorkletGlobalScope::create(Document& document, ScriptSourceCode&& code)
46 {
47     return adoptRef(*new PaintWorkletGlobalScope(document, WTFMove(code)));
48 }
49
50 PaintWorkletGlobalScope::PaintWorkletGlobalScope(Document& document, ScriptSourceCode&& code)
51     : WorkletGlobalScope(document, WTFMove(code))
52 {
53 }
54
55 double PaintWorkletGlobalScope::devicePixelRatio() const
56 {
57     if (!responsibleDocument() || !responsibleDocument()->domWindow())
58         return 1.0;
59     return responsibleDocument()->domWindow()->devicePixelRatio();
60 }
61
62 PaintWorkletGlobalScope::PaintDefinition::PaintDefinition(const AtomicString& name, JSC::JSObject* paintConstructor, Ref<CSSPaintCallback>&& paintCallback, Vector<String>&& inputProperties, Vector<String>&& inputArguments)
63     : name(name)
64     , paintConstructor(paintConstructor)
65     , paintCallback(WTFMove(paintCallback))
66     , inputProperties(WTFMove(inputProperties))
67     , inputArguments(WTFMove(inputArguments))
68 {
69 }
70
71 // https://drafts.css-houdini.org/css-paint-api/#registering-custom-paint
72 ExceptionOr<void> PaintWorkletGlobalScope::registerPaint(JSC::ExecState& state, JSDOMGlobalObject& globalObject, const String& name, Strong<JSObject> paintConstructor)
73 {
74     auto& vm = *paintConstructor->vm();
75     JSC::JSLockHolder lock(vm);
76     auto scope = DECLARE_THROW_SCOPE(vm);
77
78     // Validate that paintConstructor is a VoidFunction
79     CallData callData;
80     if (JSC::getCallData(vm, paintConstructor.get(), callData) == JSC::CallType::None)
81         return Exception { TypeError, "paintConstructor must be callable" };
82
83     if (name.isEmpty())
84         return Exception { TypeError, "The first argument must not be the empty string" };
85
86     {
87         auto locker = holdLock(paintDefinitionLock());
88
89         if (paintDefinitionMap().contains(name))
90             return Exception { InvalidModificationError, "This name has already been registered" };
91
92         Vector<String> inputProperties;
93
94         JSValue inputPropertiesIterableValue = paintConstructor->get(&state, Identifier::fromString(&vm, "inputProperties"));
95         RETURN_IF_EXCEPTION(scope, Exception { ExistingExceptionError });
96
97         if (!inputPropertiesIterableValue.isUndefined())
98             inputProperties = convert<IDLSequence<IDLDOMString>>(state, inputPropertiesIterableValue);
99         RETURN_IF_EXCEPTION(scope, Exception { ExistingExceptionError });
100
101         // FIXME: Validate input properties here (step 7).
102
103         Vector<String> inputArguments;
104
105         JSValue inputArgumentsIterableValue = paintConstructor->get(&state, Identifier::fromString(&vm, "inputArguments"));
106         RETURN_IF_EXCEPTION(scope, Exception { ExistingExceptionError });
107
108         if (!inputArgumentsIterableValue.isUndefined())
109             inputArguments = convert<IDLSequence<IDLDOMString>>(state, inputArgumentsIterableValue);
110         RETURN_IF_EXCEPTION(scope, Exception { ExistingExceptionError });
111
112         // FIXME: Parse syntax for inputArguments here (steps 11 and 12).
113
114         JSValue contextOptionsValue = paintConstructor->get(&state, Identifier::fromString(&vm, "contextOptions"));
115         RETURN_IF_EXCEPTION(scope, Exception { ExistingExceptionError });
116         UNUSED_PARAM(contextOptionsValue);
117
118         // FIXME: Convert to PaintRenderingContext2DSettings here (step 14).
119
120         if (!paintConstructor->isConstructor(vm))
121             return Exception { TypeError, "The second argument must be a constructor" };
122
123         JSValue prototypeValue = paintConstructor->get(&state, vm.propertyNames->prototype);
124         RETURN_IF_EXCEPTION(scope, Exception { ExistingExceptionError });
125
126         if (!prototypeValue.isObject())
127             return Exception { TypeError, "The second argument must have a prototype that is an object" };
128
129         JSValue paintValue = prototypeValue.get(&state, Identifier::fromString(&vm, "paint"));
130         RETURN_IF_EXCEPTION(scope, Exception { ExistingExceptionError });
131
132         if (paintValue.isUndefined())
133             return Exception { TypeError, "The class must have a paint method" };
134
135         RefPtr<JSCSSPaintCallback> paint = convert<IDLCallbackFunction<JSCSSPaintCallback>>(state, paintValue, globalObject);
136         RETURN_IF_EXCEPTION(scope, Exception { ExistingExceptionError });
137
138         auto paintDefinition = std::make_unique<PaintDefinition>(name, paintConstructor.get(), paint.releaseNonNull(), WTFMove(inputProperties), WTFMove(inputArguments));
139         paintDefinitionMap().add(name, WTFMove(paintDefinition));
140     }
141
142     // This is for the case when we have already visited the paint definition map, and the GC is currently running in the background.
143     vm.heap.writeBarrier(&globalObject);
144
145     // FIXME: construct documentDefinition (step 22).
146
147     // FIXME: we should only repaint affected custom paint images <https://bugs.webkit.org/show_bug.cgi?id=192322>.
148     if (responsibleDocument() && responsibleDocument()->renderView())
149         responsibleDocument()->renderView()->repaintRootContents();
150
151     return { };
152 }
153
154 } // namespace WebCore
155
156 #endif