URL-impl.js 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. "use strict";
  2. const usm = require("./url-state-machine");
  3. const urlencoded = require("./urlencoded");
  4. const URLSearchParams = require("./URLSearchParams");
  5. exports.implementation = class URLImpl {
  6. // Unlike the spec, we duplicate some code between the constructor and canParse, because we want to give useful error
  7. // messages in the constructor that distinguish between the different causes of failure.
  8. constructor(globalObject, [url, base]) {
  9. let parsedBase = null;
  10. if (base !== undefined) {
  11. parsedBase = usm.basicURLParse(base);
  12. if (parsedBase === null) {
  13. throw new TypeError(`Invalid base URL: ${base}`);
  14. }
  15. }
  16. const parsedURL = usm.basicURLParse(url, { baseURL: parsedBase });
  17. if (parsedURL === null) {
  18. throw new TypeError(`Invalid URL: ${url}`);
  19. }
  20. const query = parsedURL.query !== null ? parsedURL.query : "";
  21. this._url = parsedURL;
  22. // We cannot invoke the "new URLSearchParams object" algorithm without going through the constructor, which strips
  23. // question mark by default. Therefore the doNotStripQMark hack is used.
  24. this._query = URLSearchParams.createImpl(globalObject, [query], { doNotStripQMark: true });
  25. this._query._url = this;
  26. }
  27. static parse(globalObject, input, base) {
  28. try {
  29. return new URLImpl(globalObject, [input, base]);
  30. } catch {
  31. return null;
  32. }
  33. }
  34. static canParse(url, base) {
  35. let parsedBase = null;
  36. if (base !== undefined) {
  37. parsedBase = usm.basicURLParse(base);
  38. if (parsedBase === null) {
  39. return false;
  40. }
  41. }
  42. const parsedURL = usm.basicURLParse(url, { baseURL: parsedBase });
  43. if (parsedURL === null) {
  44. return false;
  45. }
  46. return true;
  47. }
  48. get href() {
  49. return usm.serializeURL(this._url);
  50. }
  51. set href(v) {
  52. const parsedURL = usm.basicURLParse(v);
  53. if (parsedURL === null) {
  54. throw new TypeError(`Invalid URL: ${v}`);
  55. }
  56. this._url = parsedURL;
  57. this._query._list.splice(0);
  58. const { query } = parsedURL;
  59. if (query !== null) {
  60. this._query._list = urlencoded.parseUrlencodedString(query);
  61. }
  62. }
  63. get origin() {
  64. return usm.serializeURLOrigin(this._url);
  65. }
  66. get protocol() {
  67. return `${this._url.scheme}:`;
  68. }
  69. set protocol(v) {
  70. usm.basicURLParse(`${v}:`, { url: this._url, stateOverride: "scheme start" });
  71. }
  72. get username() {
  73. return this._url.username;
  74. }
  75. set username(v) {
  76. if (usm.cannotHaveAUsernamePasswordPort(this._url)) {
  77. return;
  78. }
  79. usm.setTheUsername(this._url, v);
  80. }
  81. get password() {
  82. return this._url.password;
  83. }
  84. set password(v) {
  85. if (usm.cannotHaveAUsernamePasswordPort(this._url)) {
  86. return;
  87. }
  88. usm.setThePassword(this._url, v);
  89. }
  90. get host() {
  91. const url = this._url;
  92. if (url.host === null) {
  93. return "";
  94. }
  95. if (url.port === null) {
  96. return usm.serializeHost(url.host);
  97. }
  98. return `${usm.serializeHost(url.host)}:${usm.serializeInteger(url.port)}`;
  99. }
  100. set host(v) {
  101. if (usm.hasAnOpaquePath(this._url)) {
  102. return;
  103. }
  104. usm.basicURLParse(v, { url: this._url, stateOverride: "host" });
  105. }
  106. get hostname() {
  107. if (this._url.host === null) {
  108. return "";
  109. }
  110. return usm.serializeHost(this._url.host);
  111. }
  112. set hostname(v) {
  113. if (usm.hasAnOpaquePath(this._url)) {
  114. return;
  115. }
  116. usm.basicURLParse(v, { url: this._url, stateOverride: "hostname" });
  117. }
  118. get port() {
  119. if (this._url.port === null) {
  120. return "";
  121. }
  122. return usm.serializeInteger(this._url.port);
  123. }
  124. set port(v) {
  125. if (usm.cannotHaveAUsernamePasswordPort(this._url)) {
  126. return;
  127. }
  128. if (v === "") {
  129. this._url.port = null;
  130. } else {
  131. usm.basicURLParse(v, { url: this._url, stateOverride: "port" });
  132. }
  133. }
  134. get pathname() {
  135. return usm.serializePath(this._url);
  136. }
  137. set pathname(v) {
  138. if (usm.hasAnOpaquePath(this._url)) {
  139. return;
  140. }
  141. this._url.path = [];
  142. usm.basicURLParse(v, { url: this._url, stateOverride: "path start" });
  143. }
  144. get search() {
  145. if (this._url.query === null || this._url.query === "") {
  146. return "";
  147. }
  148. return `?${this._url.query}`;
  149. }
  150. set search(v) {
  151. const url = this._url;
  152. if (v === "") {
  153. url.query = null;
  154. this._query._list = [];
  155. return;
  156. }
  157. const input = v[0] === "?" ? v.substring(1) : v;
  158. url.query = "";
  159. usm.basicURLParse(input, { url, stateOverride: "query" });
  160. this._query._list = urlencoded.parseUrlencodedString(input);
  161. }
  162. get searchParams() {
  163. return this._query;
  164. }
  165. get hash() {
  166. if (this._url.fragment === null || this._url.fragment === "") {
  167. return "";
  168. }
  169. return `#${this._url.fragment}`;
  170. }
  171. set hash(v) {
  172. if (v === "") {
  173. this._url.fragment = null;
  174. return;
  175. }
  176. const input = v[0] === "#" ? v.substring(1) : v;
  177. this._url.fragment = "";
  178. usm.basicURLParse(input, { url: this._url, stateOverride: "fragment" });
  179. }
  180. toJSON() {
  181. return this.href;
  182. }
  183. };