|
4 | 4 | */
|
5 | 5 |
|
6 | 6 | #include "js_event_handler.h"
|
| 7 | +#include "core/dom/events/error_event.h" |
| 8 | + |
| 9 | +namespace kraken { |
| 10 | + |
| 11 | +std::unique_ptr<JSEventHandler> JSEventHandler::CreateOrNull(JSContext* ctx, JSValue value, JSEventHandler::HandlerType handler_type) { |
| 12 | + if (!JS_IsFunction(ctx, value)) { |
| 13 | + return nullptr; |
| 14 | + } |
| 15 | + |
| 16 | + return std::make_unique<JSEventHandler>(QJSFunction::Create(ctx, value), handler_type); |
| 17 | +} |
| 18 | + |
| 19 | +bool JSEventHandler::Matches(const EventListener& other) const { |
| 20 | + return this == &other; |
| 21 | +} |
| 22 | + |
| 23 | +// https://html.spec.whatwg.org/C/#the-event-handler-processing-algorithm |
| 24 | +void JSEventHandler::InvokeInternal(EventTarget& event_target, Event& event, ExceptionState& exception_state) { |
| 25 | + // Step 3. Let special error event handling be true if event is an ErrorEvent |
| 26 | + // object, event's type is error, and event's currentTarget implements the |
| 27 | + // WindowOrWorkerGlobalScope mixin. Otherwise, let special error event |
| 28 | + // handling be false. |
| 29 | + const bool special_error_event_handling = |
| 30 | + IsA<ErrorEvent>(event) && event.type() == event_type_names::kError && |
| 31 | + event.currentTarget()->IsWindowOrWorkerGlobalScope(); |
| 32 | + |
| 33 | + // Step 4. Process the Event object event as follows: |
| 34 | + // If special error event handling is true |
| 35 | + // Invoke callback with five arguments, the first one having the value of |
| 36 | + // event's message attribute, the second having the value of event's |
| 37 | + // filename attribute, the third having the value of event's lineno |
| 38 | + // attribute, the fourth having the value of event's colno attribute, the |
| 39 | + // fifth having the value of event's error attribute, and with the |
| 40 | + // callback this value set to event's currentTarget. Let return value be |
| 41 | + // the callback's return value. |
| 42 | + // Otherwise |
| 43 | + // Invoke callback with one argument, the value of which is the Event |
| 44 | + // object event, with the callback this value set to event's |
| 45 | + // currentTarget. Let return value be the callback's return value. |
| 46 | + // If an exception gets thrown by the callback, end these steps and allow |
| 47 | + // the exception to propagate. (It will propagate to the DOM event dispatch |
| 48 | + // logic, which will then report the exception.) |
| 49 | + HeapVector<ScriptValue> arguments; |
| 50 | + ScriptState* script_state_of_listener = |
| 51 | + event_handler_->CallbackRelevantScriptState(); |
| 52 | + v8::Isolate* isolate = script_state_of_listener->GetIsolate(); |
| 53 | + |
| 54 | + if (special_error_event_handling) { |
| 55 | + auto* error_event = To<ErrorEvent>(&event); |
| 56 | + |
| 57 | + // The error argument should be initialized to null for dedicated workers. |
| 58 | + // https://html.spec.whatwg.org/C/#runtime-script-errors-2 |
| 59 | + ScriptValue error_attribute = error_event->error(script_state_of_listener); |
| 60 | + if (error_attribute.IsEmpty() || |
| 61 | + error_event->target()->InterfaceName() == event_target_names::kWorker) { |
| 62 | + error_attribute = ScriptValue::CreateNull(isolate); |
| 63 | + } |
| 64 | + arguments = { |
| 65 | + ScriptValue(isolate, |
| 66 | + ToV8Traits<IDLString>::ToV8(script_state_of_listener, |
| 67 | + error_event->message()) |
| 68 | + .ToLocalChecked()), |
| 69 | + ScriptValue(isolate, |
| 70 | + ToV8Traits<IDLString>::ToV8(script_state_of_listener, |
| 71 | + error_event->filename()) |
| 72 | + .ToLocalChecked()), |
| 73 | + ScriptValue(isolate, |
| 74 | + ToV8Traits<IDLUnsignedLong>::ToV8(script_state_of_listener, |
| 75 | + error_event->lineno()) |
| 76 | + .ToLocalChecked()), |
| 77 | + ScriptValue(isolate, ToV8Traits<IDLUnsignedLong>::ToV8( |
| 78 | + script_state_of_listener, error_event->colno()) |
| 79 | + .ToLocalChecked()), |
| 80 | + error_attribute}; |
| 81 | + } else { |
| 82 | + arguments.push_back(ScriptValue(isolate, js_event)); |
| 83 | + } |
| 84 | + |
| 85 | + if (!event_handler_->IsRunnableOrThrowException( |
| 86 | + event.ShouldDispatchEvenWhenExecutionContextIsPaused() |
| 87 | + ? V8EventHandlerNonNull::IgnorePause::kIgnore |
| 88 | + : V8EventHandlerNonNull::IgnorePause::kDontIgnore)) { |
| 89 | + return; |
| 90 | + } |
| 91 | + ScriptValue result; |
| 92 | + if (!event_handler_ |
| 93 | + ->InvokeWithoutRunnabilityCheck(event.currentTarget(), arguments) |
| 94 | + .To(&result) || |
| 95 | + isolate->IsExecutionTerminating()) |
| 96 | + return; |
| 97 | + v8::Local<v8::Value> v8_return_value = result.V8Value(); |
| 98 | + |
| 99 | + // There is nothing to do if |v8_return_value| is null or undefined. |
| 100 | + // See Step 5. for more information. |
| 101 | + if (v8_return_value->IsNullOrUndefined()) |
| 102 | + return; |
| 103 | + |
| 104 | + // https://webidl.spec.whatwg.org/#invoke-a-callback-function |
| 105 | + // step 13: Set completion to the result of converting callResult.[[Value]] to |
| 106 | + // an IDL value of the same type as the operation's return type. |
| 107 | + // |
| 108 | + // OnBeforeUnloadEventHandler returns DOMString? while OnErrorEventHandler and |
| 109 | + // EventHandler return any, so converting |v8_return_value| to return type is |
| 110 | + // necessary only for OnBeforeUnloadEventHandler. |
| 111 | + String result_for_beforeunload; |
| 112 | + if (IsOnBeforeUnloadEventHandler()) { |
| 113 | + event_handler_->EvaluateAsPartOfCallback(Bind( |
| 114 | + [](v8::Local<v8::Value>& v8_return_value, |
| 115 | + String& result_for_beforeunload) { |
| 116 | + // TODO(yukiy): use |NativeValueTraits|. |
| 117 | + V8StringResource<kTreatNullAsNullString> native_result( |
| 118 | + v8_return_value); |
| 119 | + |
| 120 | + // |native_result.Prepare()| throws exception if it fails to convert |
| 121 | + // |native_result| to String. |
| 122 | + if (!native_result.Prepare()) |
| 123 | + return; |
| 124 | + result_for_beforeunload = native_result; |
| 125 | + }, |
| 126 | + std::ref(v8_return_value), std::ref(result_for_beforeunload))); |
| 127 | + if (!result_for_beforeunload) |
| 128 | + return; |
| 129 | + } |
| 130 | + |
| 131 | + // Step 5. Process return value as follows: |
| 132 | + // If event is a BeforeUnloadEvent object and event's type is beforeunload |
| 133 | + // If return value is not null, then: |
| 134 | + // 1. Set event's canceled flag. |
| 135 | + // 2. If event's returnValue attribute's value is the empty string, then |
| 136 | + // set event's returnValue attribute's value to return value. |
| 137 | + // If special error event handling is true |
| 138 | + // If return value is true, then set event's canceled flag. |
| 139 | + // Otherwise |
| 140 | + // If return value is false, then set event's canceled flag. |
| 141 | + // Note: If we've gotten to this "Otherwise" clause because event's type |
| 142 | + // is beforeunload but event is not a BeforeUnloadEvent object, |
| 143 | + // then return value will never be false, since in such cases |
| 144 | + // return value will have been coerced into either null or a |
| 145 | + // DOMString. |
| 146 | + auto* before_unload_event = DynamicTo<BeforeUnloadEvent>(&event); |
| 147 | + const bool is_beforeunload_event = |
| 148 | + before_unload_event && event.type() == event_type_names::kBeforeunload; |
| 149 | + if (is_beforeunload_event) { |
| 150 | + if (result_for_beforeunload) { |
| 151 | + event.preventDefault(); |
| 152 | + if (before_unload_event->returnValue().IsEmpty()) |
| 153 | + before_unload_event->setReturnValue(result_for_beforeunload); |
| 154 | + } |
| 155 | + } else if (!IsOnBeforeUnloadEventHandler()) { |
| 156 | + if (special_error_event_handling && v8_return_value->IsBoolean() && |
| 157 | + v8_return_value.As<v8::Boolean>()->Value()) |
| 158 | + event.preventDefault(); |
| 159 | + else if (!special_error_event_handling && v8_return_value->IsBoolean() && |
| 160 | + !v8_return_value.As<v8::Boolean>()->Value()) |
| 161 | + event.preventDefault(); |
| 162 | + } |
| 163 | +} |
| 164 | + |
| 165 | +} |
0 commit comments