blob: d74a60d5f5bd58a810613971d958b7b4c48f6a8a [file] [log] [blame]
{#
# Copyright 2016 Google Inc. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#}
{% from 'macros.cc.template' import call_cobalt_function %}
{% extends "interface-base.cc.template" %}
{% block includes %}
{{ super() }}
#include "base/lazy_instance.h"
#include "cobalt/script/mozjs/conversion_helpers.h"
#include "cobalt/script/mozjs/mozjs_exception_state.h"
#include "cobalt/script/mozjs/mozjs_callback_function.h"
#include "cobalt/script/mozjs/mozjs_global_object_proxy.h"
#include "cobalt/script/mozjs/mozjs_object_handle.h"
#include "cobalt/script/mozjs/type_traits.h"
#include "cobalt/script/mozjs/wrapper_factory.h"
#include "cobalt/script/mozjs/wrapper_private.h"
#include "third_party/mozjs/js/src/jsapi.h"
#include "third_party/mozjs/js/src/jsfriendapi.h"
{% endblock includes %}
{% block using_directives %}
{{ super() }}
using cobalt::script::CallbackFunction;
using cobalt::script::CallbackInterfaceTraits;
using cobalt::script::mozjs::FromJSValue;
using cobalt::script::mozjs::kConversionFlagNullable;
using cobalt::script::mozjs::kConversionFlagRestricted;
using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
using cobalt::script::mozjs::kConversionFlagTreatUndefinedAsEmptyString;
using cobalt::script::mozjs::kNoConversionFlags;
using cobalt::script::mozjs::InterfaceData;
using cobalt::script::mozjs::MozjsCallbackFunction;
using cobalt::script::mozjs::MozjsExceptionState;
using cobalt::script::mozjs::MozjsGlobalObjectProxy;
using cobalt::script::mozjs::MozjsObjectHandleHolder;
using cobalt::script::mozjs::ToJSValue;
using cobalt::script::mozjs::TypeTraits;
using cobalt::script::mozjs::WrapperPrivate;
using cobalt::script::mozjs::WrapperFactory;
using cobalt::script::Wrappable;
{% endblock using_directives %}
{% block implementation %}
namespace {
{% for constant in constants %}
JSBool get_{{constant.idl_name}}(
JSContext* context, JS::HandleObject object, JS::HandleId id,
JS::MutableHandleValue vp) {
{% if constant.can_use_compile_assert %}
COMPILE_ASSERT({{impl_class}}::{{constant.name}} == {{constant.value}},
ValueFor{{impl_class}}_{{constant.name}}DoesNotMatchIDL);
{% else %}
DCHECK_EQ({{constant.value}}, {{impl_class}}::{{constant.name}}) <<
"The value for {{impl_class}}::{{constant.name}} does not match "
"the value in the interface definition.";
{% endif %}
MozjsExceptionState exception_state(context);
JS::RootedValue result_value(context);
ToJSValue(context, {{constant.value}}, &exception_state, &result_value);
if (!exception_state.IsExceptionSet()) {
vp.set(result_value);
}
return !exception_state.IsExceptionSet();
}
{% endfor %}
{% if constructor %}
JSBool Constructor(JSContext* context, unsigned int argc, JS::Value* args);
{% endif %}
InterfaceData* CreateCachedInterfaceData() {
InterfaceData* interface_data = new InterfaceData();
memset(&interface_data->instance_class_definition, 0,
sizeof(interface_data->instance_class_definition));
memset(&interface_data->prototype_class_definition, 0,
sizeof(interface_data->prototype_class_definition));
memset(&interface_data->interface_object_class_definition, 0,
sizeof(interface_data->interface_object_class_definition));
JSClass* instance_class = &interface_data->instance_class_definition;
const int kGlobalFlags = {{"JSCLASS_GLOBAL_FLAGS" if is_global_interface else 0 }};
instance_class->name = "{{interface_name}}";
instance_class->flags = kGlobalFlags | JSCLASS_HAS_PRIVATE;
instance_class->addProperty = JS_PropertyStub;
instance_class->delProperty = JS_DeletePropertyStub;
instance_class->getProperty = JS_PropertyStub;
instance_class->setProperty = JS_StrictPropertyStub;
instance_class->enumerate = JS_EnumerateStub;
instance_class->resolve = JS_ResolveStub;
instance_class->convert = JS_ConvertStub;
// Function to be called before on object of this class is garbage collected.
instance_class->finalize = &WrapperPrivate::Finalizer;
// Called to trace objects that can be referenced from this object.
instance_class->trace = &WrapperPrivate::Trace;
JSClass* prototype_class = &interface_data->prototype_class_definition;
prototype_class->name = "{{interface_name}}Prototype";
prototype_class->flags = 0;
prototype_class->addProperty = JS_PropertyStub;
prototype_class->delProperty = JS_DeletePropertyStub;
prototype_class->getProperty = JS_PropertyStub;
prototype_class->setProperty = JS_StrictPropertyStub;
prototype_class->enumerate = JS_EnumerateStub;
prototype_class->resolve = JS_ResolveStub;
prototype_class->convert = JS_ConvertStub;
JSClass* interface_object_class = &interface_data->interface_object_class_definition;
interface_object_class->name = "{{interface_name}}Constructor";
interface_object_class->flags = 0;
interface_object_class->addProperty = JS_PropertyStub;
interface_object_class->delProperty = JS_DeletePropertyStub;
interface_object_class->getProperty = JS_PropertyStub;
interface_object_class->setProperty = JS_StrictPropertyStub;
interface_object_class->enumerate = JS_EnumerateStub;
interface_object_class->resolve = JS_ResolveStub;
interface_object_class->convert = JS_ConvertStub;
{% if constructor %}
interface_object_class->construct = Constructor;
{% endif %}
return interface_data;
}
{% for attribute in attributes %}
{% if attribute.conditional %}
#if defined({{attribute.conditional}})
{% endif %}
{% if attribute.is_constructor_attribute %}
JSBool get_{{attribute.idl_name}}(
JSContext* context, JS::HandleObject object, JS::HandleId id,
JS::MutableHandleValue vp) {
JS::RootedObject interface_object(context,
Mozjs{{attribute.interface_name}}::GetInterfaceObject(context));
vp.set(JS::ObjectValue(*interface_object));
return true;
}
{% else %}
JSBool get_{{attribute.idl_name}}(
JSContext* context, JS::HandleObject object, JS::HandleId id,
JS::MutableHandleValue vp) {
MozjsExceptionState exception_state(context);
JS::RootedValue result_value(context);
{{ call_cobalt_function(impl_class, attribute.type,
attribute.getter_function_name, [],
attribute.raises_exception, attribute.call_with) }}
if (!exception_state.IsExceptionSet()) {
vp.set(result_value);
}
return !exception_state.IsExceptionSet();
}
{% if attribute.has_setter %}
JSBool set_{{attribute.idl_name}}(
JSContext* context, JS::HandleObject object, JS::HandleId id,
JSBool strict, JS::MutableHandleValue vp) {
MozjsExceptionState exception_state(context);
JS::RootedValue result_value(context);
TypeTraits<{{attribute.type}} >::ConversionType value;
FromJSValue(context, vp, {{attribute.conversion_flags}}, &exception_state,
&value);
if (exception_state.IsExceptionSet()) {
return false;
}
{% if attribute.put_forwards %}
NOTIMPLEMENTED();
{% else %}
{{ call_cobalt_function(impl_class, "void", attribute.setter_function_name,
["value"], attribute.raises_exception, attribute.call_with) }}
{% endif %}
return !exception_state.IsExceptionSet();
}
{% endif %}
{% endif %}
{% if attribute.conditional %}
#endif // {{attribute.conditional}}
{% endif %}
{% endfor %}
{%- for operation in operations %}
{% if operation.conditional %}
#if defined({{operation.conditional}})
{% endif %}
JSBool fcn_{{operation.idl_name}}(
JSContext* context, uint32_t argc, JS::Value *vp) {
MozjsExceptionState exception_state(context);
JS::RootedValue result_value(context);
// Compute the 'this' value.
JS::RootedValue this_value(context, JS_ComputeThis(context, vp));
// 'this' should be an object.
JS::RootedObject object(context);
if (JS_TypeOfValue(context, this_value) != JSTYPE_OBJECT) {
NOTREACHED();
return false;
}
if (!JS_ValueToObject(context, this_value, object.address())) {
NOTREACHED();
return false;
}
JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
{# TODO: Overload resolution. Just use first overload for now. #}
{% set overload = operation.overloads[0] %}
{# TODO: Optional and variadic arguments. #}
{% if overload.arguments|selectattr('is_optional')|selectattr('is_variadic')|list|length == 0 %}
{% if overload.arguments|length > 0 %}
const size_t kMinArguments = {{overload.arguments|length}};
if (args.length() < kMinArguments) {
exception_state.SetSimpleException(
script::ExceptionState::kTypeError, "Not enough arguments.");
return false;
}
{% endif %}
{% for argument in overload.arguments %}
TypeTraits<{{argument.type}} >::ConversionType {{argument.name}};
DCHECK_LT({{loop.index0}}, args.length());
FromJSValue(context, args.handleAt({{loop.index0}}),
{{argument.conversion_flags}}, &exception_state, &{{argument.name}});
if (exception_state.IsExceptionSet()) {
return false;
}
{% endfor %}
{% set arguments = overload.arguments|map(attribute="name")|list %}
{{ call_cobalt_function(impl_class, overload.type,
overload.name, arguments,
overload.raises_exception,
overload.call_with) }}
{% if operation.type != 'void' %}
if (!exception_state.IsExceptionSet()) {
args.rval().set(result_value);
}
{% endif %}
return !exception_state.IsExceptionSet();
{% else %}
NOTIMPLEMENTED();
return false;
{% endif %}
}
{% if operation.conditional %}
#endif // {{operation.conditional}}
{% endif %}
{% endfor %}
const JSPropertySpec prototype_properties[] = {
{% for constant in constants %}
{
"{{constant.idl_name}}", 0,
JSPROP_PERMANENT | JSPROP_READONLY | JSPROP_ENUMERATE,
JSOP_WRAPPER(&get_{{constant.idl_name}}),
JSOP_NULLWRAPPER,
},
{% endfor %}
{% for attribute in attributes if not attribute.is_constructor_attribute %}
{% if attribute.conditional %}
#if defined({{attribute.conditional}})
{% endif %}
{% if attribute.has_setter %}
{ // Read/Write property
"{{attribute.idl_name}}", 0,
JSPROP_SHARED | JSPROP_ENUMERATE,
JSOP_WRAPPER(&get_{{attribute.idl_name}}),
JSOP_WRAPPER(&set_{{attribute.idl_name}}),
},
{% else %}
{ // Readonly attribute
"{{attribute.idl_name}}", 0,
JSPROP_SHARED | JSPROP_ENUMERATE | JSPROP_READONLY,
JSOP_WRAPPER(&get_{{attribute.idl_name}}),
JSOP_NULLWRAPPER,
},
{% endif %}
{% if attribute.conditional %}
#endif // {{attribute.conditional}}
{% endif %}
{% endfor %}
JS_PS_END
};
const JSFunctionSpec prototype_functions[] = {
{% for operation in operations %}
{% if operation.conditional %}
#if defined({{operation.conditional}})
{% endif %}
{
"{{ operation.idl_name }}",
JSOP_WRAPPER(&fcn_{{operation.idl_name}}),
{{ operation.length }},
JSPROP_ENUMERATE,
NULL,
},
{% if operation.conditional %}
#endif // {{operation.conditional}}
{% endif %}
{% endfor %}
JS_FS_END
};
const JSPropertySpec interface_object_properties[] = {
{% for constant in constants %}
{
"{{constant.idl_name}}", 0,
JSPROP_PERMANENT | JSPROP_READONLY | JSPROP_ENUMERATE,
JSOP_WRAPPER(&get_{{constant.idl_name}}),
JSOP_NULLWRAPPER,
},
{% endfor %}
JS_PS_END
};
const JSPropertySpec own_properties[] = {
{% for attribute in attributes if attribute.is_constructor_attribute %}
{% if attribute.conditional %}
#if defined({{attribute.conditional}})
{% endif %}
{ // Constructor attribute
"{{attribute.idl_name}}", 0,
JSPROP_SHARED,
JSOP_WRAPPER(&get_{{attribute.idl_name}}),
JSOP_NULLWRAPPER,
},
{% if attribute.conditional %}
#endif // {{attribute.conditional}}
{% endif %}
{% endfor %}
JS_PS_END
};
void InitializePrototypeAndInterfaceObject(
InterfaceData* interface_data, JSContext* context) {
DCHECK(!interface_data->prototype);
DCHECK(!interface_data->interface_object);
MozjsGlobalObjectProxy* global_object_proxy =
static_cast<MozjsGlobalObjectProxy*>(JS_GetContextPrivate(context));
JS::RootedObject global_object(context, global_object_proxy->global_object());
DCHECK(global_object);
{% if parent_interface %}
JS::RootedObject parent_prototype(
context, {{parent_interface}}::GetPrototype(context));
{% else %}
JS::RootedObject parent_prototype(
context, JS_GetObjectPrototype(context, global_object));
{% endif %}
DCHECK(parent_prototype);
// Create the Prototype object.
interface_data->prototype = JS_NewObjectWithGivenProto(
context, &interface_data->prototype_class_definition, parent_prototype, NULL);
bool success = JS_DefineProperties(
context, interface_data->prototype, prototype_properties);
DCHECK(success);
success = JS_DefineFunctions(
context, interface_data->prototype, prototype_functions);
DCHECK(success);
{% if has_interface_object %}
JS::RootedObject function_prototype(
context, JS_GetFunctionPrototype(context, global_object));
DCHECK(function_prototype);
// Create the Interface object.
interface_data->interface_object = JS_NewObjectWithGivenProto(
context, &interface_data->interface_object_class_definition,
function_prototype, NULL);
// Add the InterfaceObject.name property.
JS::RootedObject rooted_interface_object(
context, interface_data->interface_object);
JS::RootedValue name_value(context);
const char name[] = "{{ named_constructor if named_constructor else interface.name }}";
name_value.setString(JS_NewStringCopyZ(context, "{{interface.name}}"));
success =
JS_DefineProperty(context, rooted_interface_object, "name", name_value,
JS_PropertyStub, JS_StrictPropertyStub,
JSPROP_READONLY);
DCHECK(success);
// Define interface object properties (including constants).
success = JS_DefineProperties(context, rooted_interface_object,
interface_object_properties);
DCHECK(success);
// Set the Prototype.constructor and Constructor.prototype properties.
DCHECK(interface_data->interface_object);
DCHECK(interface_data->prototype);
JS::RootedObject rooted_prototype(context, interface_data->prototype);
success = JS_LinkConstructorAndPrototype(
context,
rooted_interface_object,
rooted_prototype);
DCHECK(success);
{% endif %}
}
InterfaceData* GetInterfaceData(JSContext* context) {
MozjsGlobalObjectProxy* global_object_proxy =
static_cast<MozjsGlobalObjectProxy*>(JS_GetContextPrivate(context));
// Use the address of the properties definition for this interface as a
// unique key for looking up the InterfaceData for this interface.
intptr_t key = reinterpret_cast<intptr_t>(&own_properties);
InterfaceData* interface_data = global_object_proxy->GetInterfaceData(key);
if (!interface_data) {
interface_data = CreateCachedInterfaceData();
DCHECK(interface_data);
global_object_proxy->CacheInterfaceData(key, interface_data);
DCHECK_EQ(interface_data, global_object_proxy->GetInterfaceData(key));
}
return interface_data;
}
} // namespace
{% if is_global_interface %}
JSObject* {{binding_class}}::CreateInstance(
JSContext* context, const scoped_refptr<Wrappable>& wrappable) {
InterfaceData* interface_data = GetInterfaceData(context);
JS::RootedObject global_object(
context, JS_NewGlobalObject(context,
&interface_data->instance_class_definition,
NULL));
DCHECK(global_object);
// Initialize standard JS constructors prototypes and top-level functions such
// as Object, isNan, etc.
JSAutoCompartment auto_compartment(context, global_object);
bool success = JS_InitStandardClasses(context, global_object);
JS::RootedObject parent_prototype(
context, JS_GetObjectPrototype(context, global_object));
// Set the global object pointer, so we can access the standard classes such
// as the base Object prototype when looking up our prototype.
MozjsGlobalObjectProxy* global_object_proxy =
static_cast<MozjsGlobalObjectProxy*>(JS_GetContextPrivate(context));
global_object_proxy->SetGlobalObject(global_object);
JS::RootedObject prototype(context, {{binding_class}}::GetPrototype(context));
DCHECK(prototype);
JS_SetPrototype(context, global_object, prototype);
// Add own properties.
success = JS_DefineProperties(context, global_object, own_properties);
DCHECK(success);
WrapperPrivate::AddPrivateData(global_object, wrappable);
return global_object;
}
{% else %}
// static
JSObject* {{binding_class}}::CreateInstance(
JSContext* context, const scoped_refptr<Wrappable>& wrappable) {
InterfaceData* interface_data = GetInterfaceData(context);
JS::RootedObject prototype(context, GetPrototype(context));
DCHECK(prototype);
JS::RootedObject new_object(context, JS_NewObjectWithGivenProto(
context, &interface_data->instance_class_definition, prototype, NULL));
DCHECK(new_object);
WrapperPrivate::AddPrivateData(new_object, wrappable);
return new_object;
}
{% endif %}
// static
JSObject* {{binding_class}}::GetPrototype(JSContext* context) {
InterfaceData* interface_data = GetInterfaceData(context);
if (!interface_data->prototype) {
// Create new prototype that has all the props and methods
InitializePrototypeAndInterfaceObject(interface_data, context);
}
DCHECK(interface_data->prototype);
return interface_data->prototype;
}
{% if has_interface_object %}
// static
JSObject* {{binding_class}}::GetInterfaceObject(JSContext* context) {
InterfaceData* interface_data = GetInterfaceData(context);
if (!interface_data->interface_object) {
InitializePrototypeAndInterfaceObject(interface_data, context);
}
DCHECK(interface_data->interface_object);
return interface_data->interface_object;
}
{% endif %}
namespace {
{% if constructor %}
JSBool Constructor(JSContext* context, unsigned int argc, JS::Value* args) {
// TODO: Implement support for constructors.
NOTIMPLEMENTED();
return true;
}
{% endif %}
} // namespace
{% endblock implementation %}
{% block create_global_object_impl %}
MozjsGlobalObjectProxy* mozjs_global_object_proxy =
base::polymorphic_downcast<MozjsGlobalObjectProxy*>(this);
JSContext* context = mozjs_global_object_proxy->context();
JSAutoRequest auto_request(context);
{{binding_class}}::CreateInstance(
context, global_interface);
mozjs_global_object_proxy->SetEnvironmentSettings(environment_settings);
WrapperFactory* wrapper_factory = mozjs_global_object_proxy->wrapper_factory();
{% for interface in all_interfaces %}
{% if interface.conditional %}
#if defined({{interface.conditional}})
{% endif %}
{# Don't register a create method for the global interface, since we do not
create a wrapper for it directly. #}
{% if interface.name != impl_class %}
wrapper_factory->RegisterWrappableType(
{{interface.name}}::{{interface.name}}WrappableType(),
base::Bind(Mozjs{{interface.name}}::CreateInstance));
{% endif %}
{% if interface.conditional %}
#endif // defined({{interface.conditional}})
{% endif %}
{% endfor %}
{% endblock create_global_object_impl %}