Skip to content
Merged
2 changes: 1 addition & 1 deletion bridge/bindings/qjs/cppgc/member.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ class Member {
// Two is by free directly when running out of function body.
// We detect the GC phase to handle case two, and free our members by hand(call JS_FreeValueRT directly).
JSGCPhaseEnum phase = JS_GetEnginePhase(runtime_);
if (phase == JS_GC_PHASE_DECREF) {
if (phase == JS_GC_PHASE_DECREF || phase == JS_GC_PHASE_REMOVE_CYCLES) {
JS_FreeValueRT(runtime_, raw_->ToQuickJSUnsafe());
}
}
Expand Down
11 changes: 11 additions & 0 deletions bridge/bindings/qjs/script_wrappable.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@
*/

#include "script_wrappable.h"
#include "built_in_string.h"
#include "core/executing_context.h"
#include "cppgc/gc_visitor.h"
#include "foundation/logging.h"

namespace webf {

Expand Down Expand Up @@ -263,9 +265,18 @@ void ScriptWrappable::InitializeQuickJSObject() {
jsObject_ = JS_NewObjectClass(ctx_, wrapper_type_info->classId);
JS_SetOpaque(jsObject_, this);

// Print className for toString.
JS_DefinePropertyValue(ctx_, jsObject_, JS_ATOM_Symbol_toStringTag, JS_NewString(ctx_, wrapper_type_info->className),
JS_PROP_NORMAL);

// Let our instance into inherit prototype methods.
JSValue prototype = GetExecutingContext()->contextData()->prototypeForType(wrapper_type_info);
JS_SetPrototype(ctx_, jsObject_, prototype);

// Set .constructor to the constructor function.
JSValue constructor = context_->contextData()->constructorForType(wrapper_type_info);
JS_DefinePropertyValue(ctx_, prototype, built_in_string::kconstructor.Impl(), JS_DupValue(ctx_, constructor),
JS_PROP_NORMAL);
}

void ScriptWrappable::KeepAlive() {
Expand Down
2 changes: 1 addition & 1 deletion bridge/core/executing_context_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ TEST(Context, unrejectPromiseWillTriggerUnhandledRejectionEvent) {
};
auto env = TEST_init(errorHandler);
static int logIndex = 0;
static std::string logs[] = {"unhandled event {promise: Promise {...}, reason: Error {...}} true"};
static std::string logs[] = {"unhandled event {constructor: ƒ (), promise: Promise {...}, reason: Error {...}} true"};
webf::WebFPage::consoleMessageHandler = [](void* ctx, const std::string& message, int logLevel) {
logCalled = true;
EXPECT_STREQ(logs[logIndex++].c_str(), message.c_str());
Expand Down
4 changes: 2 additions & 2 deletions bridge/core/timing/performance_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ TEST(Performance, mark) {
bool static logCalled = false;
webf::WebFPage::consoleMessageHandler = [](void* ctx, const std::string& message, int logLevel) {
logCalled = true;
EXPECT_STREQ(message.c_str(), "{detail: null}");
EXPECT_STREQ(message.c_str(), "{constructor: ƒ (), detail: null}");
};
auto env = TEST_init([](int32_t contextId, const char* errmsg) {
WEBF_LOG(VERBOSE) << errmsg;
Expand All @@ -90,7 +90,7 @@ TEST(Performance, markWithDetail) {
bool static logCalled = false;
webf::WebFPage::consoleMessageHandler = [](void* ctx, const std::string& message, int logLevel) {
logCalled = true;
EXPECT_STREQ(message.c_str(), "{detail: {...}}");
EXPECT_STREQ(message.c_str(), "{constructor: ƒ (), detail: {...}}");
};
auto env = TEST_init([](int32_t contextId, const char* errmsg) {
WEBF_LOG(VERBOSE) << errmsg;
Expand Down
12 changes: 12 additions & 0 deletions bridge/polyfill/src/dom-exception.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@

// @ts-ignore
export class DOMException extends Error {
private message: string;
private name: string;

constructor(message = '', name = 'Error') {
super();
this.name = name;
this.message = message;
}
}
2 changes: 2 additions & 0 deletions bridge/polyfill/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { asyncStorage } from './async-storage';
import { URLSearchParams } from './url-search-params';
import { localStorage } from './local-storage';
import { sessionStorage } from './session-storage';
import { DOMException } from './dom-exception';
import { Storage } from './storage';
import { URL } from './url';
import { webf } from './webf';
Expand All @@ -34,6 +35,7 @@ defineGlobalProperty('localStorage', localStorage);
defineGlobalProperty('sessionStorage', sessionStorage);
defineGlobalProperty('Storage', Storage);
defineGlobalProperty('URLSearchParams', URLSearchParams);
defineGlobalProperty('DOMException', DOMException);
defineGlobalProperty('URL', URL);
defineGlobalProperty('webf', webf);

Expand Down
19 changes: 19 additions & 0 deletions integration_tests/specs/dom/nodes/element.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,4 +147,23 @@ describe('DOM Element API', () => {
document.body.appendChild(el);
expect(el.matches('.a1')).toBeTrue();
});

it('should have constructor property for DOM elements', () => {
const div = document.createElement('div');
expect(div.constructor.prototype.addEventListener).toEqual(div.addEventListener);

function isObject(o) {
return typeof o === 'object' && o !== null && o.constructor && o.constructor === Object;
}
expect(isObject(div)).toBe(false);
});

it('should have className for DOM elements', () => {
function isObject(o) {
return typeof o === "object" && o !== null && o.constructor && Object.prototype.toString.call(o).slice(8, -1) === "Object";
}
const div = document.createElement('div');
expect(isObject(div)).toBe(false);
expect(Object.prototype.toString.call(div)).toBe('[object HTMLDivElement]');
});
});