Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: fix report error cause stack overflow. #1164

Merged
merged 7 commits into from Feb 18, 2022
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 3 additions & 2 deletions bridge/bindings/qjs/dom/event_target.cc
Expand Up @@ -187,8 +187,9 @@ bool EventTargetInstance::internalDispatchEvent(EventInstance* eventInstance) {
std::string eventTypeStr = toUTF8(u16EventType);
JSAtom eventType = JS_NewAtom(m_ctx, eventTypeStr.c_str());

// Modify the currentTarget to this.
eventInstance->nativeEvent->currentTarget = this;
// Modify the currentTarget and target to this.
// https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/core/dom/events/event_target.cc;l=713;drc=197ed39fa22e5ce84a2ea31aef039b5179a549ff;bpv=1;bpt=1
eventInstance->nativeEvent->currentTarget = eventInstance->nativeEvent->target = this;

// Dispatch event listeners writen by addEventListener
auto _dispatchEvent = [&eventInstance, this](JSValue handler) {
Expand Down
33 changes: 29 additions & 4 deletions bridge/bindings/qjs/executing_context.cc
Expand Up @@ -270,6 +270,31 @@ void ExecutionContext::reportError(JSValueConst error) {
JS_FreeCString(m_ctx, type);
}

void ExecutionContext::reportErrorEvent(EventInstance* errorEvent) {
JSValue error = JS_GetPropertyStr(m_ctx, errorEvent->jsObject, "error");
reportError(error);
JS_FreeValue(m_ctx, error);
}

void ExecutionContext::dispatchErrorEvent(EventInstance* errorEvent) {
if (m_inDispatchErrorEvent_) {
return;
}

dispatchErrorEventInternal(errorEvent);
reportErrorEvent(errorEvent);
}

void ExecutionContext::dispatchErrorEventInternal(EventInstance* errorEvent) {
if (m_window == nullptr)
return;

assert(!m_inDispatchErrorEvent_);
m_inDispatchErrorEvent_ = true;
m_window->dispatchEvent(errorEvent);
m_inDispatchErrorEvent_ = false;
}

void ExecutionContext::drainPendingPromiseJobs() {
// should executing pending promise jobs.
JSContext* pctx;
Expand Down Expand Up @@ -306,7 +331,7 @@ void ExecutionContext::dispatchGlobalErrorEvent(ExecutionContext* context, JSVal
auto* window = static_cast<WindowInstance*>(JS_GetOpaque(context->global(), Window::classId()));

{
JSValue ErrorEventValue = JS_GetPropertyStr(ctx, context->global(), "ErrorEvent");
JSValue errorEventConstructor = JS_GetPropertyStr(ctx, context->global(), "ErrorEvent");
JSValue errorType = JS_NewString(ctx, "error");
JSValue errorInit = JS_NewObject(ctx);
JS_SetPropertyStr(ctx, errorInit, "error", JS_DupValue(ctx, error));
Expand All @@ -315,16 +340,16 @@ void ExecutionContext::dispatchGlobalErrorEvent(ExecutionContext* context, JSVal
JS_SetPropertyStr(ctx, errorInit, "filename", JS_GetPropertyStr(ctx, error, "fileName"));
JS_SetPropertyStr(ctx, errorInit, "colno", JS_NewUint32(ctx, 0));
JSValue arguments[] = {errorType, errorInit};
JSValue errorEventValue = JS_CallConstructor(context->ctx(), ErrorEventValue, 2, arguments);
JSValue errorEventValue = JS_CallConstructor(context->ctx(), errorEventConstructor, 2, arguments);
if (JS_IsException(errorEventValue)) {
context->handleException(&errorEventValue);
return;
}

auto* errorEvent = static_cast<EventInstance*>(JS_GetOpaque(errorEventValue, Event::kEventClassID));
window->dispatchEvent(errorEvent);
context->dispatchErrorEvent(errorEvent);

JS_FreeValue(ctx, ErrorEventValue);
JS_FreeValue(ctx, errorEventConstructor);
JS_FreeValue(ctx, errorEventValue);
JS_FreeValue(ctx, errorType);
JS_FreeValue(ctx, errorInit);
Expand Down
5 changes: 5 additions & 0 deletions bridge/bindings/qjs/executing_context.h
Expand Up @@ -33,6 +33,7 @@ static std::once_flag kinitJSClassIDFlag;
class WindowInstance;
class DocumentInstance;
class ExecutionContext;
class EventInstance;
struct DOMTimerCallbackContext;

std::string jsAtomToStdString(JSContext* ctx, JSAtom atom);
Expand Down Expand Up @@ -119,6 +120,9 @@ class ExecutionContext {
static void dispatchGlobalErrorEvent(ExecutionContext* context, JSValueConst error);

void reportError(JSValueConst error);
void reportErrorEvent(EventInstance* errorEvent);
void dispatchErrorEvent(EventInstance* errorEvent);
void dispatchErrorEventInternal(EventInstance* errorEvent);

private:
static void promiseRejectTracker(JSContext* ctx, JSValueConst promise, JSValueConst reason, JS_BOOL is_handled, void* opaque);
Expand All @@ -129,6 +133,7 @@ class ExecutionContext {
JSValue globalObject{JS_NULL};
bool ctxInvalid_{false};
JSContext* m_ctx{nullptr};
bool m_inDispatchErrorEvent_{false};
friend WindowInstance;
friend DocumentInstance;
WindowInstance* m_window{nullptr};
Expand Down
14 changes: 14 additions & 0 deletions bridge/bindings/qjs/js_context_test.cc
Expand Up @@ -26,6 +26,20 @@ TEST(Context, evalWithError) {
EXPECT_EQ(errorHandlerExecuted, true);
}

TEST(Context, recursionThrowError) {
static bool errorHandlerExecuted = false;
auto errorHandler = [](int32_t contextId, const char* errmsg) { errorHandlerExecuted = true; };
auto bridge = TEST_init(errorHandler);
const char* code =
"addEventListener('error', (evt) => {\n"
" console.log('tagName', evt.target.tagName());\n"
"});\n"
"\n"
"throw Error('foo');";
bridge->evaluateScript(code, strlen(code), "file://", 0);
EXPECT_EQ(errorHandlerExecuted, true);
}

TEST(Context, unrejectPromiseError) {
static bool errorHandlerExecuted = false;
auto errorHandler = [](int32_t contextId, const char* errmsg) {
Expand Down