@@ -1074,8 +1074,8 @@ bool RequestOrResponse::maybe_stream_body(JSContext *cx, JS::HandleObject body_o
10741074}
10751075
10761076JSObject *RequestOrResponse::create_body_stream (JSContext *cx, JS::HandleObject owner) {
1077- MOZ_ASSERT (is_instance (owner));
10781077 MOZ_ASSERT (!body_stream (owner));
1078+ MOZ_ASSERT (has_body (owner));
10791079 JS::RootedObject source (cx, streams::NativeStreamSource::create (
10801080 cx, owner, JS::UndefinedHandleValue, body_source_pull_algorithm,
10811081 body_source_cancel_algorithm));
@@ -1144,19 +1144,15 @@ JSObject *Request::response_promise(JSObject *obj) {
11441144 .toObject ();
11451145}
11461146
1147- JSString *Request::method (JSContext *cx, JS:: HandleObject obj) {
1147+ JSString *Request::method (HandleObject obj) {
11481148 MOZ_ASSERT (is_instance (obj));
11491149 return JS::GetReservedSlot (obj, static_cast <uint32_t >(Slots::Method)).toString ();
11501150}
11511151
11521152bool Request::method_get (JSContext *cx, unsigned argc, JS::Value *vp) {
11531153 METHOD_HEADER (0 )
11541154
1155- JSString *method = Request::method (cx, self);
1156- if (!method)
1157- return false ;
1158-
1159- args.rval ().setString (method);
1155+ args.rval ().setString (Request::method (self));
11601156 return true ;
11611157}
11621158
@@ -1197,128 +1193,86 @@ bool Request::bodyUsed_get(JSContext *cx, unsigned argc, JS::Value *vp) {
11971193
11981194JSString *GET_atom;
11991195
1200- // bool Request::clone(JSContext *cx, unsigned argc, JS::Value *vp) {
1201- // METHOD_HEADER(0);
1202-
1203- // auto hasBody = RequestOrResponse::has_body(self);
1204-
1205- // if (hasBody) {
1206- // if (RequestOrResponse::body_unusable(self)) {
1207- // return api::throw_error(cx, FetchErrors::BodyStreamUnusable);
1208- // }
1209-
1210- // // Here we get the current request's body stream and call ReadableStream.prototype.tee to
1211- // return
1212- // // two versions of the stream. Once we get the two streams, we create a new request handle
1213- // and
1214- // // attach one of the streams to the new handle and the other stream is attached to the
1215- // request
1216- // // handle that `clone()` was called upon.
1217- // JS::RootedObject body_stream(cx, RequestOrResponse::body_stream(self));
1218- // if (!body_stream) {
1219- // body_stream = RequestOrResponse::create_body_stream(cx, self);
1220- // if (!body_stream) {
1221- // return false;
1222- // }
1223- // }
1224- // JS::RootedValue tee_val(cx);
1225- // if (!JS_GetProperty(cx, body_stream, "tee", &tee_val)) {
1226- // return false;
1227- // }
1228- // JS::Rooted<JSFunction *> tee(cx, JS_GetObjectFunction(&tee_val.toObject()));
1229- // if (!tee) {
1230- // return false;
1231- // }
1232- // JS::RootedVector<JS::Value> argv(cx);
1233- // JS::RootedValue rval(cx);
1234- // if (!JS::Call(cx, body_stream, tee, argv, &rval)) {
1235- // return false;
1236- // }
1237- // JS::RootedObject rval_array(cx, &rval.toObject());
1238- // JS::RootedValue body1_val(cx);
1239- // if (!JS_GetProperty(cx, rval_array, "0", &body1_val)) {
1240- // return false;
1241- // }
1242- // JS::RootedValue body2_val(cx);
1243- // if (!JS_GetProperty(cx, rval_array, "1", &body2_val)) {
1244- // return false;
1245- // }
1246-
1247- // auto res = host_api::HttpBody::make(request_handle);
1248- // if (auto *err = res.to_err()) {
1249- // HANDLE_ERROR(cx, *err);
1250- // return false;
1251- // }
1252-
1253- // auto body_handle = res.unwrap();
1254- // if (!JS::IsReadableStream(&body1_val.toObject())) {
1255- // return false;
1256- // }
1257- // body_stream.set(&body1_val.toObject());
1258- // if (RequestOrResponse::body_unusable(cx, body_stream)) {
1259- // return api::throw_error(cx, FetchErrors::BodyStreamUnusable);
1260- // }
1261-
1262- // JS::SetReservedSlot(requestInstance, static_cast<uint32_t>(Slots::Body),
1263- // JS::Int32Value(body_handle.handle));
1264- // JS::SetReservedSlot(requestInstance, static_cast<uint32_t>(Slots::BodyStream), body1_val);
1265-
1266- // JS::SetReservedSlot(self, static_cast<uint32_t>(Slots::BodyStream), body2_val);
1267- // JS::SetReservedSlot(self, static_cast<uint32_t>(Slots::BodyUsed), JS::FalseValue());
1268- // JS::SetReservedSlot(self, static_cast<uint32_t>(Slots::HasBody), JS::BooleanValue(true));
1269- // }
1270-
1271- // JS::RootedObject headers(cx);
1272- // JS::RootedObject headers_obj(
1273- // cx, RequestOrResponse::headers(cx, self));
1274- // if (!headers_obj) {
1275- // return false;
1276- // }
1277- // JS::RootedObject headersInstance(
1278- // cx, JS_NewObjectWithGivenProto(cx, &Headers::class_, Headers::proto_obj));
1279- // if (!headersInstance)
1280- // return false;
1281-
1282- // headers = Headers::create(cx, headersInstance, Headers::Mode::ProxyToRequest,
1283- // requestInstance, headers_obj);
1284-
1285- // if (!headers) {
1286- // return false;
1287- // }
1288-
1289- // JS::SetReservedSlot(requestInstance, static_cast<uint32_t>(Slots::Headers),
1290- // JS::ObjectValue(*headers));
1196+ // / https://fetch.spec.whatwg.org/#dom-request-clone
1197+ bool Request::clone (JSContext *cx, unsigned argc, JS::Value *vp) {
1198+ METHOD_HEADER (0 );
1199+
1200+ // clone operation step 1.
1201+ // Let newRequest be a copy of request, except for its body.
1202+ // Note that the spec doesn't say what it means to copy a request, exactly.
1203+ // Since a request only has the fields "method", "url", and "headers", and the "Body" mixin,
1204+ // we copy those three fields in this step.
1205+ RootedObject new_request (cx, create (cx));
1206+ if (!new_request) {
1207+ return false ;
1208+ }
1209+ init_slots (new_request);
12911210
1292- // JSString *method = Request::method(cx, self);
1293- // if (!method) {
1294- // return false;
1295- // }
1211+ RootedValue cloned_headers_val (cx, JS::NullValue ());
1212+ RootedObject headers (cx, RequestOrResponse::maybe_headers (self));
1213+ if (headers) {
1214+ RootedValue headers_val (cx, ObjectValue (*headers));
1215+ JSObject *cloned_headers =
1216+ Headers::create (cx, headers_val, host_api::HttpHeadersGuard::Request);
1217+ if (!cloned_headers) {
1218+ return false ;
1219+ }
1220+ cloned_headers_val.set (ObjectValue (*cloned_headers));
1221+ } else if (RequestOrResponse::handle (self)) {
1222+ auto handle =
1223+ RequestOrResponse::headers_handle_clone (cx, self, host_api::HttpHeadersGuard::Request);
1224+ JSObject *cloned_headers =
1225+ Headers::create (cx, handle.release (), host_api::HttpHeadersGuard::Request);
1226+ if (!cloned_headers) {
1227+ return false ;
1228+ }
1229+ cloned_headers_val.set (ObjectValue (*cloned_headers));
1230+ }
1231+
1232+ SetReservedSlot (new_request, static_cast <uint32_t >(Slots::Headers), cloned_headers_val);
1233+ Value url_val = GetReservedSlot (self, static_cast <uint32_t >(Slots::URL));
1234+ SetReservedSlot (new_request, static_cast <uint32_t >(Slots::URL), url_val);
1235+ Value method_val = JS::StringValue (method (self));
1236+ ENGINE->dump_value (method_val, stderr);
1237+ SetReservedSlot (new_request, static_cast <uint32_t >(Slots::Method), method_val);
1238+
1239+ // clone operation step 2.
1240+ // If request’s body is non-null, set newRequest’s body to the result of cloning request’s body.
1241+ RootedObject new_body (cx);
1242+ auto has_body = RequestOrResponse::has_body (self);
1243+ if (!has_body) {
1244+ args.rval ().setObject (*new_request);
1245+ return true ;
1246+ }
12961247
1297- // JS::SetReservedSlot(requestInstance, static_cast<uint32_t>(Slots::Method),
1298- // JS::StringValue(method));
1248+ // Here we get the current request's body stream and call ReadableStream.prototype.tee to
1249+ // get two streams for the same content.
1250+ // One of these is then used to replace the current request's body, the other is used as
1251+ // the body of the clone.
1252+ JS::RootedObject body_stream (cx, RequestOrResponse::body_stream (self));
1253+ if (!body_stream) {
1254+ body_stream = RequestOrResponse::create_body_stream (cx, self);
1255+ if (!body_stream) {
1256+ return false ;
1257+ }
1258+ }
12991259
1300- // auto *request_handle_res = new host_api::HttpOutgoingRequest()
1301- // if (auto *err = request_handle_res.to_err()) {
1302- // HANDLE_ERROR(cx, *err);
1303- // return false;
1304- // }
1260+ if (RequestOrResponse::body_unusable (cx, body_stream)) {
1261+ return api::throw_error (cx, FetchErrors::BodyStreamUnusable);
1262+ }
13051263
1306- // auto request_handle = request_handle_res.unwrap();
1264+ RootedObject self_body (cx);
1265+ if (!ReadableStreamTee (cx, body_stream, &self_body, &new_body)) {
1266+ return false ;
1267+ }
13071268
1308- // JS::RootedObject requestInstance(cx, Request::create_instance(cx));
1309- // JS::SetReservedSlot(requestInstance, static_cast<uint32_t>(Slots::Request),
1310- // JS::Int32Value(request_handle.handle));
1311- // JS::SetReservedSlot(requestInstance, static_cast<uint32_t>(Slots::BodyUsed),
1312- // JS::FalseValue());
1313- // JS::SetReservedSlot(
1314- // requestInstance, static_cast<uint32_t>(Slots::URL),
1315- // JS::GetReservedSlot(self, static_cast<uint32_t>(Slots::URL)));
1316- // JS::SetReservedSlot(requestInstance, static_cast<uint32_t>(Slots::HasBody),
1317- // JS::BooleanValue(hasBody));
1269+ SetReservedSlot (self, static_cast <uint32_t >(Slots::BodyStream), ObjectValue (*self_body));
1270+ SetReservedSlot (new_request, static_cast <uint32_t >(Slots::BodyStream), ObjectValue (*new_body));
1271+ SetReservedSlot (new_request, static_cast <uint32_t >(Slots::HasBody), JS::BooleanValue (true ));
13181272
1319- // args.rval().setObject(*requestInstance );
1320- // return true;
1321- // }
1273+ args.rval ().setObject (*new_request );
1274+ return true ;
1275+ }
13221276
13231277const JSFunctionSpec Request::static_methods[] = {
13241278 JS_FS_END,
@@ -1333,7 +1287,7 @@ const JSFunctionSpec Request::methods[] = {
13331287 JSPROP_ENUMERATE),
13341288 JS_FN (" json" , Request::bodyAll<RequestOrResponse::BodyReadResult::JSON>, 0 , JSPROP_ENUMERATE),
13351289 JS_FN (" text" , Request::bodyAll<RequestOrResponse::BodyReadResult::Text>, 0 , JSPROP_ENUMERATE),
1336- // JS_FN("clone", Request::clone, 0, JSPROP_ENUMERATE),
1290+ JS_FN (" clone" , Request::clone, 0 , JSPROP_ENUMERATE),
13371291 JS_FS_END,
13381292};
13391293
@@ -1372,7 +1326,7 @@ void Request::init_slots(JSObject *requestInstance) {
13721326 * Create a new Request object, roughly according to
13731327 * https://fetch.spec.whatwg.org/#dom-request
13741328 *
1375- * "Roughly" because not all aspects of Request handling make sense in C@E .
1329+ * "Roughly" because not all aspects of Request handling make sense in StarlingMonkey .
13761330 * The places where we deviate from the spec are called out inline.
13771331 */
13781332bool Request::initialize (JSContext *cx, JS::HandleObject request, JS::HandleValue input,
@@ -1416,10 +1370,7 @@ bool Request::initialize(JSContext *cx, JS::HandleObject request, JS::HandleValu
14161370 url_str = RequestOrResponse::url (input_request).toString ();
14171371
14181372 // method: `request`’s method.
1419- method_str = Request::method (cx, input_request);
1420- if (!method_str) {
1421- return false ;
1422- }
1373+ method_str = Request::method (input_request);
14231374
14241375 // referrer: `request`’s referrer.
14251376 // TODO: evaluate whether we want to implement support for setting the
@@ -1430,7 +1381,14 @@ bool Request::initialize(JSContext *cx, JS::HandleObject request, JS::HandleValu
14301381
14311382 // header list: A copy of `request`’s header list.
14321383 // Note: copying the headers is postponed, see step 32 below.
1433- input_headers = RequestOrResponse::maybe_headers (input_request);
1384+ // Note: we're forcing reification of the input request's headers here. That is suboptimal,
1385+ // because we might end up not using them. Additionally, if the headers are represented
1386+ // internally as a handle (e.g. because the input is an incoming request), we would in
1387+ // principle not need to ever reify it just to get a clone.
1388+ // Applying these optimizations is somewhat complex though, so for now we're not doing so.
1389+ if (!(input_headers = RequestOrResponse::headers (cx, input_request))) {
1390+ return false ;
1391+ }
14341392
14351393 // The following properties aren't applicable:
14361394 // unsafe-request flag: Set.
0 commit comments