HTMLOptionsCollection.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513
  1. "use strict";
  2. const conversions = require("webidl-conversions");
  3. const utils = require("./utils.js");
  4. const HTMLOptionElement = require("./HTMLOptionElement.js");
  5. const HTMLOptGroupElement = require("./HTMLOptGroupElement.js");
  6. const HTMLElement = require("./HTMLElement.js");
  7. const ceReactionsPreSteps_jsdom_living_helpers_custom_elements =
  8. require("../../jsdom/living/helpers/custom-elements.js").ceReactionsPreSteps;
  9. const ceReactionsPostSteps_jsdom_living_helpers_custom_elements =
  10. require("../../jsdom/living/helpers/custom-elements.js").ceReactionsPostSteps;
  11. const implSymbol = utils.implSymbol;
  12. const ctorRegistrySymbol = utils.ctorRegistrySymbol;
  13. const HTMLCollection = require("./HTMLCollection.js");
  14. const interfaceName = "HTMLOptionsCollection";
  15. exports.is = value => {
  16. return utils.isObject(value) && Object.hasOwn(value, implSymbol) && value[implSymbol] instanceof Impl.implementation;
  17. };
  18. exports.isImpl = value => {
  19. return utils.isObject(value) && value instanceof Impl.implementation;
  20. };
  21. exports.convert = (globalObject, value, { context = "The provided value" } = {}) => {
  22. if (exports.is(value)) {
  23. return utils.implForWrapper(value);
  24. }
  25. throw new globalObject.TypeError(`${context} is not of type 'HTMLOptionsCollection'.`);
  26. };
  27. function makeWrapper(globalObject, newTarget) {
  28. let proto;
  29. if (newTarget !== undefined) {
  30. proto = newTarget.prototype;
  31. }
  32. if (!utils.isObject(proto)) {
  33. proto = globalObject[ctorRegistrySymbol]["HTMLOptionsCollection"].prototype;
  34. }
  35. return Object.create(proto);
  36. }
  37. function makeProxy(wrapper, globalObject) {
  38. let proxyHandler = proxyHandlerCache.get(globalObject);
  39. if (proxyHandler === undefined) {
  40. proxyHandler = new ProxyHandler(globalObject);
  41. proxyHandlerCache.set(globalObject, proxyHandler);
  42. }
  43. return new Proxy(wrapper, proxyHandler);
  44. }
  45. exports.create = (globalObject, constructorArgs, privateData) => {
  46. const wrapper = makeWrapper(globalObject);
  47. return exports.setup(wrapper, globalObject, constructorArgs, privateData);
  48. };
  49. exports.createImpl = (globalObject, constructorArgs, privateData) => {
  50. const wrapper = exports.create(globalObject, constructorArgs, privateData);
  51. return utils.implForWrapper(wrapper);
  52. };
  53. exports._internalSetup = (wrapper, globalObject) => {
  54. HTMLCollection._internalSetup(wrapper, globalObject);
  55. };
  56. exports.setup = (wrapper, globalObject, constructorArgs = [], privateData = {}) => {
  57. privateData.wrapper = wrapper;
  58. exports._internalSetup(wrapper, globalObject);
  59. Object.defineProperty(wrapper, implSymbol, {
  60. value: new Impl.implementation(globalObject, constructorArgs, privateData),
  61. configurable: true
  62. });
  63. wrapper = makeProxy(wrapper, globalObject);
  64. wrapper[implSymbol][utils.wrapperSymbol] = wrapper;
  65. if (Impl.init) {
  66. Impl.init(wrapper[implSymbol]);
  67. }
  68. return wrapper;
  69. };
  70. exports.new = (globalObject, newTarget) => {
  71. let wrapper = makeWrapper(globalObject, newTarget);
  72. exports._internalSetup(wrapper, globalObject);
  73. Object.defineProperty(wrapper, implSymbol, {
  74. value: Object.create(Impl.implementation.prototype),
  75. configurable: true
  76. });
  77. wrapper = makeProxy(wrapper, globalObject);
  78. wrapper[implSymbol][utils.wrapperSymbol] = wrapper;
  79. if (Impl.init) {
  80. Impl.init(wrapper[implSymbol]);
  81. }
  82. return wrapper[implSymbol];
  83. };
  84. const exposed = new Set(["Window"]);
  85. exports.install = (globalObject, globalNames) => {
  86. if (!globalNames.some(globalName => exposed.has(globalName))) {
  87. return;
  88. }
  89. const ctorRegistry = utils.initCtorRegistry(globalObject);
  90. class HTMLOptionsCollection extends globalObject.HTMLCollection {
  91. constructor() {
  92. throw new globalObject.TypeError("Illegal constructor");
  93. }
  94. add(element) {
  95. const esValue = this !== null && this !== undefined ? this : globalObject;
  96. if (!exports.is(esValue)) {
  97. throw new globalObject.TypeError(
  98. "'add' called on an object that is not a valid instance of HTMLOptionsCollection."
  99. );
  100. }
  101. if (arguments.length < 1) {
  102. throw new globalObject.TypeError(
  103. `Failed to execute 'add' on 'HTMLOptionsCollection': 1 argument required, but only ${arguments.length} present.`
  104. );
  105. }
  106. const args = [];
  107. {
  108. let curArg = arguments[0];
  109. if (HTMLOptionElement.is(curArg) || HTMLOptGroupElement.is(curArg)) {
  110. curArg = utils.implForWrapper(curArg);
  111. } else {
  112. throw new globalObject.TypeError(
  113. "Failed to execute 'add' on 'HTMLOptionsCollection': parameter 1" + " is not of any supported type."
  114. );
  115. }
  116. args.push(curArg);
  117. }
  118. {
  119. let curArg = arguments[1];
  120. if (curArg !== undefined) {
  121. if (curArg === null || curArg === undefined) {
  122. curArg = null;
  123. } else {
  124. if (HTMLElement.is(curArg)) {
  125. curArg = utils.implForWrapper(curArg);
  126. } else if (typeof curArg === "number") {
  127. curArg = conversions["long"](curArg, {
  128. context: "Failed to execute 'add' on 'HTMLOptionsCollection': parameter 2",
  129. globals: globalObject
  130. });
  131. } else {
  132. curArg = conversions["long"](curArg, {
  133. context: "Failed to execute 'add' on 'HTMLOptionsCollection': parameter 2",
  134. globals: globalObject
  135. });
  136. }
  137. }
  138. } else {
  139. curArg = null;
  140. }
  141. args.push(curArg);
  142. }
  143. ceReactionsPreSteps_jsdom_living_helpers_custom_elements(globalObject);
  144. try {
  145. return esValue[implSymbol].add(...args);
  146. } finally {
  147. ceReactionsPostSteps_jsdom_living_helpers_custom_elements(globalObject);
  148. }
  149. }
  150. remove(index) {
  151. const esValue = this !== null && this !== undefined ? this : globalObject;
  152. if (!exports.is(esValue)) {
  153. throw new globalObject.TypeError(
  154. "'remove' called on an object that is not a valid instance of HTMLOptionsCollection."
  155. );
  156. }
  157. if (arguments.length < 1) {
  158. throw new globalObject.TypeError(
  159. `Failed to execute 'remove' on 'HTMLOptionsCollection': 1 argument required, but only ${arguments.length} present.`
  160. );
  161. }
  162. const args = [];
  163. {
  164. let curArg = arguments[0];
  165. curArg = conversions["long"](curArg, {
  166. context: "Failed to execute 'remove' on 'HTMLOptionsCollection': parameter 1",
  167. globals: globalObject
  168. });
  169. args.push(curArg);
  170. }
  171. ceReactionsPreSteps_jsdom_living_helpers_custom_elements(globalObject);
  172. try {
  173. return esValue[implSymbol].remove(...args);
  174. } finally {
  175. ceReactionsPostSteps_jsdom_living_helpers_custom_elements(globalObject);
  176. }
  177. }
  178. get length() {
  179. const esValue = this !== null && this !== undefined ? this : globalObject;
  180. if (!exports.is(esValue)) {
  181. throw new globalObject.TypeError(
  182. "'get length' called on an object that is not a valid instance of HTMLOptionsCollection."
  183. );
  184. }
  185. ceReactionsPreSteps_jsdom_living_helpers_custom_elements(globalObject);
  186. try {
  187. return esValue[implSymbol]["length"];
  188. } finally {
  189. ceReactionsPostSteps_jsdom_living_helpers_custom_elements(globalObject);
  190. }
  191. }
  192. set length(V) {
  193. const esValue = this !== null && this !== undefined ? this : globalObject;
  194. if (!exports.is(esValue)) {
  195. throw new globalObject.TypeError(
  196. "'set length' called on an object that is not a valid instance of HTMLOptionsCollection."
  197. );
  198. }
  199. V = conversions["unsigned long"](V, {
  200. context: "Failed to set the 'length' property on 'HTMLOptionsCollection': The provided value",
  201. globals: globalObject
  202. });
  203. ceReactionsPreSteps_jsdom_living_helpers_custom_elements(globalObject);
  204. try {
  205. esValue[implSymbol]["length"] = V;
  206. } finally {
  207. ceReactionsPostSteps_jsdom_living_helpers_custom_elements(globalObject);
  208. }
  209. }
  210. get selectedIndex() {
  211. const esValue = this !== null && this !== undefined ? this : globalObject;
  212. if (!exports.is(esValue)) {
  213. throw new globalObject.TypeError(
  214. "'get selectedIndex' called on an object that is not a valid instance of HTMLOptionsCollection."
  215. );
  216. }
  217. return esValue[implSymbol]["selectedIndex"];
  218. }
  219. set selectedIndex(V) {
  220. const esValue = this !== null && this !== undefined ? this : globalObject;
  221. if (!exports.is(esValue)) {
  222. throw new globalObject.TypeError(
  223. "'set selectedIndex' called on an object that is not a valid instance of HTMLOptionsCollection."
  224. );
  225. }
  226. V = conversions["long"](V, {
  227. context: "Failed to set the 'selectedIndex' property on 'HTMLOptionsCollection': The provided value",
  228. globals: globalObject
  229. });
  230. esValue[implSymbol]["selectedIndex"] = V;
  231. }
  232. }
  233. Object.defineProperties(HTMLOptionsCollection.prototype, {
  234. add: { enumerable: true },
  235. remove: { enumerable: true },
  236. length: { enumerable: true },
  237. selectedIndex: { enumerable: true },
  238. [Symbol.toStringTag]: { value: "HTMLOptionsCollection", configurable: true },
  239. [Symbol.iterator]: { value: globalObject.Array.prototype[Symbol.iterator], configurable: true, writable: true }
  240. });
  241. ctorRegistry[interfaceName] = HTMLOptionsCollection;
  242. Object.defineProperty(globalObject, interfaceName, {
  243. configurable: true,
  244. writable: true,
  245. value: HTMLOptionsCollection
  246. });
  247. };
  248. const proxyHandlerCache = new WeakMap();
  249. class ProxyHandler {
  250. constructor(globalObject) {
  251. this._globalObject = globalObject;
  252. }
  253. get(target, P, receiver) {
  254. if (typeof P === "symbol") {
  255. return Reflect.get(target, P, receiver);
  256. }
  257. const desc = this.getOwnPropertyDescriptor(target, P);
  258. if (desc === undefined) {
  259. const parent = Object.getPrototypeOf(target);
  260. if (parent === null) {
  261. return undefined;
  262. }
  263. return Reflect.get(target, P, receiver);
  264. }
  265. if (!desc.get && !desc.set) {
  266. return desc.value;
  267. }
  268. const getter = desc.get;
  269. if (getter === undefined) {
  270. return undefined;
  271. }
  272. return Reflect.apply(getter, receiver, []);
  273. }
  274. has(target, P) {
  275. if (typeof P === "symbol") {
  276. return Reflect.has(target, P);
  277. }
  278. const desc = this.getOwnPropertyDescriptor(target, P);
  279. if (desc !== undefined) {
  280. return true;
  281. }
  282. const parent = Object.getPrototypeOf(target);
  283. if (parent !== null) {
  284. return Reflect.has(parent, P);
  285. }
  286. return false;
  287. }
  288. ownKeys(target) {
  289. const keys = new Set();
  290. for (const key of target[implSymbol][utils.supportedPropertyIndices]) {
  291. keys.add(`${key}`);
  292. }
  293. for (const key of target[implSymbol][utils.supportedPropertyNames]) {
  294. if (!(key in target)) {
  295. keys.add(`${key}`);
  296. }
  297. }
  298. for (const key of Reflect.ownKeys(target)) {
  299. keys.add(key);
  300. }
  301. return [...keys];
  302. }
  303. getOwnPropertyDescriptor(target, P) {
  304. if (typeof P === "symbol") {
  305. return Reflect.getOwnPropertyDescriptor(target, P);
  306. }
  307. let ignoreNamedProps = false;
  308. if (utils.isArrayIndexPropName(P)) {
  309. const index = P >>> 0;
  310. const indexedValue = target[implSymbol].item(index);
  311. if (indexedValue !== null) {
  312. return {
  313. writable: true,
  314. enumerable: true,
  315. configurable: true,
  316. value: utils.tryWrapperForImpl(indexedValue)
  317. };
  318. }
  319. ignoreNamedProps = true;
  320. }
  321. const namedValue = target[implSymbol].namedItem(P);
  322. if (namedValue !== null && !(P in target) && !ignoreNamedProps) {
  323. return {
  324. writable: false,
  325. enumerable: true,
  326. configurable: true,
  327. value: utils.tryWrapperForImpl(namedValue)
  328. };
  329. }
  330. return Reflect.getOwnPropertyDescriptor(target, P);
  331. }
  332. set(target, P, V, receiver) {
  333. if (typeof P === "symbol") {
  334. return Reflect.set(target, P, V, receiver);
  335. }
  336. // The `receiver` argument refers to the Proxy exotic object or an object
  337. // that inherits from it, whereas `target` refers to the Proxy target:
  338. if (target[implSymbol][utils.wrapperSymbol] === receiver) {
  339. const globalObject = this._globalObject;
  340. if (utils.isArrayIndexPropName(P)) {
  341. const index = P >>> 0;
  342. let indexedValue = V;
  343. if (indexedValue === null || indexedValue === undefined) {
  344. indexedValue = null;
  345. } else {
  346. indexedValue = HTMLOptionElement.convert(globalObject, indexedValue, {
  347. context: "Failed to set the " + index + " property on 'HTMLOptionsCollection': The provided value"
  348. });
  349. }
  350. ceReactionsPreSteps_jsdom_living_helpers_custom_elements(globalObject);
  351. try {
  352. const creating = !(target[implSymbol].item(index) !== null);
  353. if (creating) {
  354. target[implSymbol][utils.indexedSetNew](index, indexedValue);
  355. } else {
  356. target[implSymbol][utils.indexedSetExisting](index, indexedValue);
  357. }
  358. } finally {
  359. ceReactionsPostSteps_jsdom_living_helpers_custom_elements(globalObject);
  360. }
  361. return true;
  362. }
  363. }
  364. let ownDesc;
  365. if (utils.isArrayIndexPropName(P)) {
  366. const index = P >>> 0;
  367. const indexedValue = target[implSymbol].item(index);
  368. if (indexedValue !== null) {
  369. ownDesc = {
  370. writable: true,
  371. enumerable: true,
  372. configurable: true,
  373. value: utils.tryWrapperForImpl(indexedValue)
  374. };
  375. }
  376. }
  377. if (ownDesc === undefined) {
  378. ownDesc = Reflect.getOwnPropertyDescriptor(target, P);
  379. }
  380. return utils.ordinarySetWithOwnDescriptor(target, P, V, receiver, ownDesc);
  381. }
  382. defineProperty(target, P, desc) {
  383. if (typeof P === "symbol") {
  384. return Reflect.defineProperty(target, P, desc);
  385. }
  386. const globalObject = this._globalObject;
  387. if (utils.isArrayIndexPropName(P)) {
  388. if (desc.get || desc.set) {
  389. return false;
  390. }
  391. const index = P >>> 0;
  392. let indexedValue = desc.value;
  393. if (indexedValue === null || indexedValue === undefined) {
  394. indexedValue = null;
  395. } else {
  396. indexedValue = HTMLOptionElement.convert(globalObject, indexedValue, {
  397. context: "Failed to set the " + index + " property on 'HTMLOptionsCollection': The provided value"
  398. });
  399. }
  400. ceReactionsPreSteps_jsdom_living_helpers_custom_elements(globalObject);
  401. try {
  402. const creating = !(target[implSymbol].item(index) !== null);
  403. if (creating) {
  404. target[implSymbol][utils.indexedSetNew](index, indexedValue);
  405. } else {
  406. target[implSymbol][utils.indexedSetExisting](index, indexedValue);
  407. }
  408. } finally {
  409. ceReactionsPostSteps_jsdom_living_helpers_custom_elements(globalObject);
  410. }
  411. return true;
  412. }
  413. if (!Object.hasOwn(target, P)) {
  414. const creating = !(target[implSymbol].namedItem(P) !== null);
  415. if (!creating) {
  416. return false;
  417. }
  418. }
  419. return Reflect.defineProperty(target, P, desc);
  420. }
  421. deleteProperty(target, P) {
  422. if (typeof P === "symbol") {
  423. return Reflect.deleteProperty(target, P);
  424. }
  425. const globalObject = this._globalObject;
  426. if (utils.isArrayIndexPropName(P)) {
  427. const index = P >>> 0;
  428. return !(target[implSymbol].item(index) !== null);
  429. }
  430. if (target[implSymbol].namedItem(P) !== null && !(P in target)) {
  431. return false;
  432. }
  433. return Reflect.deleteProperty(target, P);
  434. }
  435. preventExtensions() {
  436. return false;
  437. }
  438. }
  439. const Impl = require("../../jsdom/living/nodes/HTMLOptionsCollection-impl.js");