@@ -2380,164 +2380,137 @@ bool Response::redirect(JSContext *cx, unsigned argc, Value *vp) {
23802380 return true ;
23812381}
23822382
2383- // namespace {
2384- // bool callbackCalled;
2385- // bool write_json_to_buf(const char16_t *str, uint32_t strlen, void *out) {
2386- // callbackCalled = true;
2387- // auto outstr = static_cast<std::u16string *>(out);
2388- // outstr->append(str, strlen);
2389-
2390- // return true;
2391- // }
2392- // } // namespace
2393-
2394- // bool Response::json(JSContext *cx, unsigned argc, JS::Value *vp) {
2395- // JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
2396- // if (!args.requireAtLeast(cx, "json", 1)) {
2397- // return false;
2398- // }
2399- // JS::RootedValue data(cx, args.get(0));
2400- // JS::RootedValue init_val(cx, args.get(1));
2401- // JS::RootedObject replacer(cx);
2402- // JS::RootedValue space(cx);
2403-
2404- // std::u16string out;
2405- // // 1. Let bytes the result of running serialize a JavaScript value to JSON bytes on data.
2406- // callbackCalled = false;
2407- // if (!JS::ToJSON(cx, data, replacer, space, &write_json_to_buf, &out)) {
2408- // return false;
2409- // }
2410- // if (!callbackCalled) {
2411- // return api::throw_error(cx, api::Errors::WrongType, "Response.json", "data", "be a valid JSON
2412- // value");
2413- // }
2414- // // 2. Let body be the result of extracting bytes.
2415-
2416- // // 3. Let responseObject be the result of creating a Response object, given a new response,
2417- // // "response", and this’s relevant Realm.
2418- // JS::RootedValue status_val(cx);
2419- // uint16_t status = 200;
2420-
2421- // JS::RootedValue statusText_val(cx);
2422- // JS::RootedString statusText(cx, JS_GetEmptyString(cx));
2423- // JS::RootedValue headers_val(cx);
2424-
2425- // if (init_val.isObject()) {
2426- // JS::RootedObject init(cx, init_val.toObjectOrNull());
2427- // if (!JS_GetProperty(cx, init, "status", &status_val) ||
2428- // !JS_GetProperty(cx, init, "statusText", &statusText_val) ||
2429- // !JS_GetProperty(cx, init, "headers", &headers_val)) {
2430- // return false;
2431- // }
2432-
2433- // if (!status_val.isUndefined() && !JS::ToUint16(cx, status_val, &status)) {
2434- // return false;
2435- // }
2436-
2437- // if (status == 204 || status == 205 || status == 304) {
2438- // auto status_str = std::to_string(status);
2439- // return api::throw_error(cx, FetchErrors::NonBodyResponseWithBody, "Response.json",
2440- // status_str.c_str());
2441- // }
2442-
2443- // if (!statusText_val.isUndefined() && !(statusText = JS::ToString(cx, statusText_val))) {
2444- // return false;
2445- // }
2446-
2447- // } else if (!init_val.isNullOrUndefined()) {
2448- // return api::throw_error(cx, FetchErrors::InvalidInitArg, "Response.json");
2449- // }
2450-
2451- // auto response_handle_res = host_api::HttpResp::make();
2452- // if (auto *err = response_handle_res.to_err()) {
2453- // HANDLE_ERROR(cx, *err);
2454- // return false;
2455- // }
2456-
2457- // auto response_handle = response_handle_res.unwrap();
2458- // if (!response_handle.is_valid()) {
2459- // return false;
2460- // }
2461-
2462- // auto make_res = host_api::HttpBody::make(response_handle);
2463- // if (auto *err = make_res.to_err()) {
2464- // HANDLE_ERROR(cx, *err);
2465- // return false;
2466- // }
2467-
2468- // auto body = make_res.unwrap();
2469- // JS::RootedString string(cx, JS_NewUCStringCopyN(cx, out.c_str(), out.length()));
2470- // auto stringChars = core::encode(cx, string);
2471-
2472- // auto write_res =
2473- // body.write_all(reinterpret_cast<uint8_t *>(stringChars.begin()), stringChars.len);
2474- // if (auto *err = write_res.to_err()) {
2475- // HANDLE_ERROR(cx, *err);
2476- // return false;
2477- // }
2478- // JS::RootedObject response_instance(cx, JS_NewObjectWithGivenProto(cx, &Response::class_,
2479- // Response::proto_obj));
2480- // if (!response_instance) {
2481- // return false;
2482- // }
2483- // JS::RootedObject response(cx, create(cx, response_instance, response_handle,
2484- // body, false));
2485- // if (!response) {
2486- // return false;
2487- // }
2488-
2489- // // Set `this`’s `response`’s `status` to `init`["status"].
2490- // auto set_res = response_handle.set_status(status);
2491- // if (auto *err = set_res.to_err()) {
2492- // HANDLE_ERROR(cx, *err);
2493- // return false;
2494- // }
2495- // // To ensure that we really have the same status value as the host,
2496- // // we always read it back here.
2497- // auto get_res = response_handle.get_status();
2498- // if (auto *err = get_res.to_err()) {
2499- // HANDLE_ERROR(cx, *err);
2500- // return false;
2501- // }
2502- // status = get_res.unwrap();
2503-
2504- // JS::SetReservedSlot(response, static_cast<uint32_t>(Slots::Status), JS::Int32Value(status));
2505-
2506- // // Set `this`’s `response`’s `status message` to `init`["statusText"].
2507- // JS::SetReservedSlot(response, static_cast<uint32_t>(Slots::StatusMessage),
2508- // JS::StringValue(statusText));
2509-
2510- // // If `init`["headers"] `exists`, then `fill` `this`’s `headers` with
2511- // // `init`["headers"].
2512- // JS::RootedObject headers(cx);
2513- // JS::RootedObject headersInstance(
2514- // cx, JS_NewObjectWithGivenProto(cx, &Headers::class_, Headers::proto_obj));
2515- // if (!headersInstance)
2516- // return false;
2517-
2518- // headers = Headers::create(cx, headersInstance, Headers::Mode::ProxyToResponse,
2519- // response, headers_val);
2520- // if (!headers) {
2521- // return false;
2522- // }
2523- // // 4. Perform initialize a response given responseObject, init, and (body, "application/json").
2524- // if (!Headers::maybe_add(cx, headers, "content-type", "application/json")) {
2525- // return false;
2526- // }
2527- // JS::SetReservedSlot(response, static_cast<uint32_t>(Slots::Headers),
2528- // JS::ObjectValue(*headers)); JS::SetReservedSlot(response,
2529- // static_cast<uint32_t>(Slots::Redirected), JS::FalseValue()); JS::SetReservedSlot(response,
2530- // static_cast<uint32_t>(Slots::HasBody), JS::TrueValue()); RequestOrResponse::set_url(response,
2531- // JS_GetEmptyStringValue(cx));
2532-
2533- // // 5. Return responseObject.
2534- // args.rval().setObjectOrNull(response);
2535- // return true;
2536- // }
2383+ struct JsonCallback {
2384+ std::u16string output;
2385+ bool called = false ;
2386+
2387+ static bool write_to_buffer (const char16_t *str, uint32_t strlen, void *data) {
2388+ auto *callback = static_cast <JsonCallback*>(data);
2389+ callback->called = true ;
2390+ callback->output .append (str, strlen);
2391+ return true ;
2392+ }
2393+ };
2394+
2395+
2396+ bool Response::json (JSContext *cx, unsigned argc, JS::Value *vp) {
2397+ JS::CallArgs args = JS::CallArgsFromVp (argc, vp);
2398+ if (!args.requireAtLeast (cx, " json" , 1 )) {
2399+ return false ;
2400+ }
2401+ JS::RootedValue data (cx, args.get (0 ));
2402+ JS::RootedValue init_val (cx, args.get (1 ));
2403+ JS::RootedObject replacer (cx);
2404+ JS::RootedValue space (cx);
2405+
2406+ // 1. Serialize the data to JSON
2407+ JsonCallback json_callback;
2408+ if (!JS::ToJSON (cx, data, replacer, space, &JsonCallback::write_to_buffer, &json_callback)) {
2409+ return false ;
2410+ }
2411+ if (!json_callback.called ) {
2412+ // undefined never invokes the callback, so we throw an error
2413+ return api::throw_error (cx, api::Errors::TypeError, " Response.json" , " data" , " be a valid JSON value" );
2414+ }
2415+ // 2. Parse init object to get status, statusText, and headers
2416+ JS::RootedValue status_val (cx);
2417+ uint16_t status = 200 ;
2418+
2419+ JS::RootedValue statusText_val (cx);
2420+ JS::RootedString statusText (cx, JS_GetEmptyString (cx));
2421+ JS::RootedValue headers_val (cx);
2422+
2423+ if (init_val.isObject ()) {
2424+ JS::RootedObject init (cx, init_val.toObjectOrNull ());
2425+ if (!JS_GetProperty (cx, init, " status" , &status_val) ||
2426+ !JS_GetProperty (cx, init, " statusText" , &statusText_val) ||
2427+ !JS_GetProperty (cx, init, " headers" , &headers_val)) {
2428+ return false ;
2429+ }
2430+
2431+ if (!status_val.isUndefined () && !JS::ToUint16 (cx, status_val, &status)) {
2432+ return false ;
2433+ }
2434+
2435+ if (status == 204 || status == 205 || status == 304 ) {
2436+ auto status_str = std::to_string (status);
2437+ return api::throw_error (cx, FetchErrors::NonBodyResponseWithBody, status_str.c_str ());
2438+ }
2439+
2440+ if (!statusText_val.isUndefined () && !(statusText = JS::ToString (cx, statusText_val))) {
2441+ return false ;
2442+ }
2443+
2444+ } else if (!init_val.isNullOrUndefined ()) {
2445+ return api::throw_error (cx, FetchErrors::InvalidInitArg, " Response.json" );
2446+ }
2447+
2448+
2449+ // 3. Create the Response JS object first
2450+ JS::RootedObject response_obj (cx, Response::create (cx));
2451+ if (!response_obj) {
2452+ return false ;
2453+ }
2454+
2455+ // Convert JSON string to a proper body value
2456+ JS::RootedString json_string (cx, JS_NewUCStringCopyN (cx, json_callback.output .c_str (), json_callback.output .length ()));
2457+ if (!json_string) {
2458+ return false ;
2459+ }
2460+ JS::RootedValue body_val (cx, JS::StringValue (json_string));
2461+
2462+ // Create init object with headers that include content-type
2463+ JS::RootedObject init_obj (cx, JS_NewPlainObject (cx));
2464+ if (!init_obj) {
2465+ return false ;
2466+ }
2467+
2468+ // Set status
2469+ if (!JS_DefineProperty (cx, init_obj, " status" , status_val, JSPROP_ENUMERATE)) {
2470+ return false ;
2471+ }
2472+
2473+ // Set statusText
2474+ if (!JS_DefineProperty (cx, init_obj, " statusText" , statusText_val, JSPROP_ENUMERATE)) {
2475+ return false ;
2476+ }
2477+
2478+ // Create headers object and ensure content-type is set
2479+ JS::RootedObject headers_obj (cx);
2480+ if (!headers_val.isUndefined ()) {
2481+ headers_obj = Headers::create (cx, headers_val, Headers::HeadersGuard::Response);
2482+ } else {
2483+ headers_obj = Headers::create (cx, Headers::HeadersGuard::Response);
2484+ }
2485+ if (!headers_obj) {
2486+ return false ;
2487+ }
2488+
2489+ // Set content-type header if not already present
2490+ if (!Headers::set_valid_if_undefined (cx, headers_obj, " Content-Type" , " application/json" )) {
2491+ return false ;
2492+ }
2493+
2494+ JS::RootedValue headers_obj_val (cx, JS::ObjectValue (*headers_obj));
2495+ if (!JS_DefineProperty (cx, init_obj, " headers" , headers_obj_val, JSPROP_ENUMERATE)) {
2496+ return false ;
2497+ }
2498+
2499+ JS::RootedValue init_obj_val (cx, JS::ObjectValue (*init_obj));
2500+
2501+ // 5. Use the existing initialize method
2502+ if (!Response::initialize (cx, response_obj, body_val, init_obj_val)) {
2503+ return false ;
2504+ }
2505+
2506+ args.rval ().setObject (*response_obj);
2507+ return true ;
2508+ }
2509+
25372510
25382511const JSFunctionSpec Response::static_methods[] = {
25392512 JS_FN (" redirect" , redirect, 1 , JSPROP_ENUMERATE),
2540- // JS_FN("json", json, 1, JSPROP_ENUMERATE),
2513+ JS_FN (" json" , json, 1 , JSPROP_ENUMERATE),
25412514 JS_FS_END,
25422515};
25432516
0 commit comments