summaryrefslogtreecommitdiff
path: root/chromium/third_party/blink/renderer/bindings/templates/callback_function.cpp.tmpl
blob: aa00906f809e66899e10755dd6d8e4c3fde1eaca (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
{% from 'utilities.cpp.tmpl' import declare_enum_validation_variable, v8_value_to_local_cpp_value %}
{% filter format_blink_cpp_source_code %}

{% include 'copyright_block.txt' %}

#include "{{this_include_header_path}}"

{% for filename in cpp_includes %}
#include "{{filename}}"
{% endfor %}

namespace blink {

v8::Maybe<{{return_cpp_type}}> {{cpp_class}}::Invoke({{argument_declarations | join(', ')}}) {
  // This function implements "invoke" algorithm defined in
  // "3.10. Invoking callback functions".
  // https://heycam.github.io/webidl/#es-invoking-callback-functions

  {# TODO(yukishiino): This implementation does not support a return type of
     promise type, in which case this function needs to convert any exception
     into a rejected promise. See also step 14.4. to 14.6. #}

  if (!IsCallbackFunctionRunnable(CallbackRelevantScriptState())) {
    // Wrapper-tracing for the callback function makes the function object and
    // its creation context alive. Thus it's safe to use the creation context
    // of the callback function here.
    v8::HandleScope handle_scope(GetIsolate());
    CHECK(!CallbackFunction().IsEmpty());
    v8::Context::Scope context_scope(CallbackFunction()->CreationContext());
    V8ThrowException::ThrowError(
        GetIsolate(),
        ExceptionMessages::FailedToExecute(
            "invoke",
            "{{callback_function_name}}",
            "The provided callback is no longer runnable."));
    return v8::Nothing<{{return_cpp_type}}>();
  }

  // step 4. If ! IsCallable(F) is false:
  //
  // As Blink no longer supports [TreatNonObjectAsNull], there must be no such a
  // case.
#if DCHECK_IS_ON()
  {
    v8::HandleScope handle_scope(GetIsolate());
    DCHECK(CallbackFunction()->IsFunction());
  }
#endif

  // step 8. Prepare to run script with relevant settings.
  ScriptState::Scope callback_relevant_context_scope(
      CallbackRelevantScriptState());
  // step 9. Prepare to run a callback with stored settings.
  {# TODO(yukishiino): Callback function type value must make the incumbent
     environment alive, i.e. the reference to v8::Context must be strong. #}
  if (IncumbentScriptState()->GetContext().IsEmpty()) {
    V8ThrowException::ThrowError(
        GetIsolate(),
        ExceptionMessages::FailedToExecute(
            "invoke",
            "{{callback_function_name}}",
            "The provided callback is no longer runnable."));
    return v8::Nothing<{{return_cpp_type}}>();
  }
  v8::Context::BackupIncumbentScope backup_incumbent_scope(
      IncumbentScriptState()->GetContext());

  v8::Local<v8::Value> this_arg = ToV8(callback_this_value,
                                       CallbackRelevantScriptState());

  {% for argument in arguments if argument.enum_values %}
  // Enum values provided by Blink must be valid, otherwise typo.
#if DCHECK_IS_ON()
  {
    {% set valid_enum_variables = 'valid_' + argument.name + '_values' %}
    {{declare_enum_validation_variable(argument.enum_values, valid_enum_variables) | trim | indent(4)}}
    ExceptionState exception_state(GetIsolate(),
                                   ExceptionState::kExecutionContext,
                                   "{{callback_function_name}}",
                                   "invoke");
    if (!IsValidEnum({{argument.name}}, {{valid_enum_variables}}, arraysize({{valid_enum_variables}}), "{{argument.enum_type}}", exception_state)) {
      NOTREACHED();
      return v8::Nothing<{{return_cpp_type}}>();
    }
  }
#endif

  {% endfor %}

  // step 10. Let esArgs be the result of converting args to an ECMAScript
  //   arguments list. If this throws an exception, set completion to the
  //   completion value representing the thrown exception and jump to the step
  //   labeled return.
  {% if arguments %}
  v8::Local<v8::Object> argument_creation_context =
      CallbackRelevantScriptState()->GetContext()->Global();
  ALLOW_UNUSED_LOCAL(argument_creation_context);
  {% for argument in arguments %}
  v8::Local<v8::Value> {{argument.v8_name}} = {{argument.cpp_value_to_v8_value}};
  {% endfor %}
  v8::Local<v8::Value> argv[] = { {{arguments | join(', ', 'v8_name')}} };
  {% else %}
  {# Zero-length arrays are ill-formed in C++. #}
  v8::Local<v8::Value> *argv = nullptr;
  {% endif %}

  // step 11. Let callResult be Call(X, thisArg, esArgs).
  v8::Local<v8::Value> call_result;
  if (!V8ScriptRunner::CallFunction(
          CallbackFunction(),
          ExecutionContext::From(CallbackRelevantScriptState()),
          this_arg,
          {{arguments | length}},
          argv,
          GetIsolate()).ToLocal(&call_result)) {
    // step 12. If callResult is an abrupt completion, set completion to
    //   callResult and jump to the step labeled return.
    return v8::Nothing<{{return_cpp_type}}>();
  }

  // step 13. Set completion to the result of converting callResult.[[Value]] to
  //   an IDL value of the same type as the operation's return type.
  {% if idl_type == 'void' %}
  return v8::JustVoid();
  {% else %}
  {
    ExceptionState exceptionState(GetIsolate(),
                                  ExceptionState::kExecutionContext,
                                  "{{callback_function_name}}",
                                  "invoke");
    {{v8_value_to_local_cpp_value(return_value_conversion) | trim | indent(4)}}
    return v8::Just<{{return_cpp_type}}>(native_result);
  }
  {% endif %}
}

{% if idl_type == 'void' %}
void {{cpp_class}}::InvokeAndReportException({{argument_declarations | join(', ')}}) {
  v8::TryCatch try_catch(GetIsolate());
  try_catch.SetVerbose(true);

  v8::Maybe<void> maybe_result =
      Invoke({{
                 (['callback_this_value'] +
                  (arguments|map(attribute='name')|list)
                 )|join(', ')
             }});
  // An exception if any is killed with the v8::TryCatch above.
  ALLOW_UNUSED_LOCAL(maybe_result);
}
{% endif %}

{{exported|replace('_EXPORT', '_TEMPLATE_EXPORT')|trim}}
v8::Maybe<{{return_cpp_type}}> V8PersistentCallbackFunction<{{cpp_class}}>::Invoke({{argument_declarations | join(', ')}}) {
  return Proxy()->Invoke(
      {{
         (['callback_this_value'] +
          (arguments|map(attribute='name')|list)
         )|join(', ')
      }});
}

{% if idl_type == 'void' %}
{{exported|replace('_EXPORT', '_TEMPLATE_EXPORT')|trim}}
void V8PersistentCallbackFunction<{{cpp_class}}>::InvokeAndReportException({{argument_declarations | join(', ')}}) {
  Proxy()->InvokeAndReportException(
      {{
         (['callback_this_value'] +
          (arguments|map(attribute='name')|list)
         )|join(', ')
      }});
}
{% endif %}

}  // namespace blink

{% endfilter %}{# format_blink_cpp_source_code #}