URL-impl.js 5.0 KB

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