blob: e8b53dbc12661dec5cd34064fa434c41dba2baa9 [file] [log] [blame]
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "modules/csspaint/PaintWorkletGlobalScope.h"
#include "bindings/core/v8/V8BindingMacros.h"
#include "bindings/core/v8/WorkerOrWorkletScriptController.h"
#include "core/CSSPropertyNames.h"
#include "core/dom/ExceptionCode.h"
#include "core/inspector/MainThreadDebugger.h"
#include "modules/csspaint/CSSPaintDefinition.h"
#include "modules/csspaint/CSSPaintImageGeneratorImpl.h"
namespace blink {
// static
PaintWorkletGlobalScope* PaintWorkletGlobalScope::create(
LocalFrame* frame,
const KURL& url,
const String& userAgent,
PassRefPtr<SecurityOrigin> securityOrigin,
v8::Isolate* isolate) {
PaintWorkletGlobalScope* paintWorkletGlobalScope =
new PaintWorkletGlobalScope(frame, url, userAgent,
std::move(securityOrigin), isolate);
paintWorkletGlobalScope->scriptController()->initializeContextIfNeeded();
MainThreadDebugger::instance()->contextCreated(
paintWorkletGlobalScope->scriptController()->getScriptState(),
paintWorkletGlobalScope->frame(),
paintWorkletGlobalScope->getSecurityOrigin());
return paintWorkletGlobalScope;
}
PaintWorkletGlobalScope::PaintWorkletGlobalScope(
LocalFrame* frame,
const KURL& url,
const String& userAgent,
PassRefPtr<SecurityOrigin> securityOrigin,
v8::Isolate* isolate)
: MainThreadWorkletGlobalScope(frame,
url,
userAgent,
std::move(securityOrigin),
isolate) {}
PaintWorkletGlobalScope::~PaintWorkletGlobalScope() {}
void PaintWorkletGlobalScope::dispose() {
MainThreadDebugger::instance()->contextWillBeDestroyed(
scriptController()->getScriptState());
// Explicitly clear the paint defininitions to break a reference cycle
// between them and this global scope.
m_paintDefinitions.clear();
WorkletGlobalScope::dispose();
}
void PaintWorkletGlobalScope::registerPaint(const String& name,
const ScriptValue& ctorValue,
ExceptionState& exceptionState) {
if (m_paintDefinitions.contains(name)) {
exceptionState.throwDOMException(
NotSupportedError,
"A class with name:'" + name + "' is already registered.");
return;
}
if (name.isEmpty()) {
exceptionState.throwTypeError("The empty string is not a valid name.");
return;
}
v8::Isolate* isolate = scriptController()->getScriptState()->isolate();
v8::Local<v8::Context> context = scriptController()->context();
ASSERT(ctorValue.v8Value()->IsFunction());
v8::Local<v8::Function> constructor =
v8::Local<v8::Function>::Cast(ctorValue.v8Value());
v8::Local<v8::Value> inputPropertiesValue;
if (!v8Call(constructor->Get(context, v8String(isolate, "inputProperties")),
inputPropertiesValue))
return;
Vector<CSSPropertyID> nativeInvalidationProperties;
Vector<AtomicString> customInvalidationProperties;
if (!isUndefinedOrNull(inputPropertiesValue)) {
Vector<String> properties = toImplArray<Vector<String>>(
inputPropertiesValue, 0, isolate, exceptionState);
if (exceptionState.hadException())
return;
for (const auto& property : properties) {
CSSPropertyID propertyID = cssPropertyID(property);
if (propertyID == CSSPropertyVariable) {
customInvalidationProperties.append(property);
} else if (propertyID != CSSPropertyInvalid) {
nativeInvalidationProperties.append(propertyID);
}
}
}
// Parse 'alpha' AKA hasAlpha property.
v8::Local<v8::Value> alphaValue;
if (!v8Call(constructor->Get(context, v8String(isolate, "alpha")),
alphaValue))
return;
if (!isUndefinedOrNull(alphaValue) && !alphaValue->IsBoolean()) {
exceptionState.throwTypeError(
"The 'alpha' property on the class is not a boolean.");
return;
}
bool hasAlpha = alphaValue->IsBoolean()
? v8::Local<v8::Boolean>::Cast(alphaValue)->Value()
: true;
v8::Local<v8::Value> prototypeValue;
if (!v8Call(constructor->Get(context, v8String(isolate, "prototype")),
prototypeValue))
return;
if (isUndefinedOrNull(prototypeValue)) {
exceptionState.throwTypeError(
"The 'prototype' object on the class does not exist.");
return;
}
if (!prototypeValue->IsObject()) {
exceptionState.throwTypeError(
"The 'prototype' property on the class is not an object.");
return;
}
v8::Local<v8::Object> prototype = v8::Local<v8::Object>::Cast(prototypeValue);
v8::Local<v8::Value> paintValue;
if (!v8Call(prototype->Get(context, v8String(isolate, "paint")), paintValue))
return;
if (isUndefinedOrNull(paintValue)) {
exceptionState.throwTypeError(
"The 'paint' function on the prototype does not exist.");
return;
}
if (!paintValue->IsFunction()) {
exceptionState.throwTypeError(
"The 'paint' property on the prototype is not a function.");
return;
}
v8::Local<v8::Function> paint = v8::Local<v8::Function>::Cast(paintValue);
CSSPaintDefinition* definition = CSSPaintDefinition::create(
scriptController()->getScriptState(), constructor, paint,
nativeInvalidationProperties, customInvalidationProperties, hasAlpha);
m_paintDefinitions.set(name, definition);
// Set the definition on any pending generators.
GeneratorHashSet* set = m_pendingGenerators.get(name);
if (set) {
for (const auto& generator : *set) {
if (generator) {
generator->setDefinition(definition);
}
}
}
m_pendingGenerators.remove(name);
}
CSSPaintDefinition* PaintWorkletGlobalScope::findDefinition(
const String& name) {
return m_paintDefinitions.get(name);
}
void PaintWorkletGlobalScope::addPendingGenerator(
const String& name,
CSSPaintImageGeneratorImpl* generator) {
Member<GeneratorHashSet>& set =
m_pendingGenerators.add(name, nullptr).storedValue->value;
if (!set)
set = new GeneratorHashSet;
set->add(generator);
}
DEFINE_TRACE(PaintWorkletGlobalScope) {
visitor->trace(m_paintDefinitions);
visitor->trace(m_pendingGenerators);
MainThreadWorkletGlobalScope::trace(visitor);
}
} // namespace blink