URL.js 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. /**
  2. * Copyright (c) Facebook, Inc. and its affiliates.
  3. *
  4. * This source code is licensed under the MIT license found in the
  5. * LICENSE file in the root directory of this source tree.
  6. *
  7. * @format
  8. */
  9. 'use strict';
  10. const Blob = require('./Blob');
  11. import NativeBlobModule from './NativeBlobModule';
  12. let BLOB_URL_PREFIX = null;
  13. if (
  14. NativeBlobModule &&
  15. typeof NativeBlobModule.getConstants().BLOB_URI_SCHEME === 'string'
  16. ) {
  17. const constants = NativeBlobModule.getConstants();
  18. BLOB_URL_PREFIX = constants.BLOB_URI_SCHEME + ':';
  19. if (typeof constants.BLOB_URI_HOST === 'string') {
  20. BLOB_URL_PREFIX += `//${constants.BLOB_URI_HOST}/`;
  21. }
  22. }
  23. /**
  24. * To allow Blobs be accessed via `content://` URIs,
  25. * you need to register `BlobProvider` as a ContentProvider in your app's `AndroidManifest.xml`:
  26. *
  27. * ```xml
  28. * <manifest>
  29. * <application>
  30. * <provider
  31. * android:name="com.facebook.react.modules.blob.BlobProvider"
  32. * android:authorities="@string/blob_provider_authority"
  33. * android:exported="false"
  34. * />
  35. * </application>
  36. * </manifest>
  37. * ```
  38. * And then define the `blob_provider_authority` string in `res/values/strings.xml`.
  39. * Use a dotted name that's entirely unique to your app:
  40. *
  41. * ```xml
  42. * <resources>
  43. * <string name="blob_provider_authority">your.app.package.blobs</string>
  44. * </resources>
  45. * ```
  46. */
  47. // Small subset from whatwg-url: https://github.com/jsdom/whatwg-url/tree/master/lib
  48. // The reference code bloat comes from Unicode issues with URLs, so those won't work here.
  49. export class URLSearchParams {
  50. _searchParams = [];
  51. constructor(params: any) {
  52. if (typeof params === 'object') {
  53. Object.keys(params).forEach(key => this.append(key, params[key]));
  54. }
  55. }
  56. append(key: string, value: string) {
  57. this._searchParams.push([key, value]);
  58. }
  59. delete(name) {
  60. throw new Error('not implemented');
  61. }
  62. get(name) {
  63. throw new Error('not implemented');
  64. }
  65. getAll(name) {
  66. throw new Error('not implemented');
  67. }
  68. has(name) {
  69. throw new Error('not implemented');
  70. }
  71. set(name, value) {
  72. throw new Error('not implemented');
  73. }
  74. sort() {
  75. throw new Error('not implemented');
  76. }
  77. [Symbol.iterator]() {
  78. return this._searchParams[Symbol.iterator]();
  79. }
  80. toString() {
  81. if (this._searchParams.length === 0) {
  82. return '';
  83. }
  84. const last = this._searchParams.length - 1;
  85. return this._searchParams.reduce((acc, curr, index) => {
  86. return acc + curr.join('=') + (index === last ? '' : '&');
  87. }, '');
  88. }
  89. }
  90. function validateBaseUrl(url: string) {
  91. // from this MIT-licensed gist: https://gist.github.com/dperini/729294
  92. return /^(?:(?:(?:https?|ftp):)?\/\/)(?:(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,}))?)(?::\d{2,5})?(?:[/?#]\S*)?$/i.test(
  93. url,
  94. );
  95. }
  96. export class URL {
  97. _searchParamsInstance = null;
  98. static createObjectURL(blob: Blob) {
  99. if (BLOB_URL_PREFIX === null) {
  100. throw new Error('Cannot create URL for blob!');
  101. }
  102. return `${BLOB_URL_PREFIX}${blob.data.blobId}?offset=${
  103. blob.data.offset
  104. }&size=${blob.size}`;
  105. }
  106. static revokeObjectURL(url: string) {
  107. // Do nothing.
  108. }
  109. constructor(url: string, base: string) {
  110. let baseUrl = null;
  111. if (!base || validateBaseUrl(url)) {
  112. this._url = url;
  113. if (!this._url.endsWith('/')) {
  114. this._url += '/';
  115. }
  116. } else {
  117. if (typeof base === 'string') {
  118. baseUrl = base;
  119. if (!validateBaseUrl(baseUrl)) {
  120. throw new TypeError(`Invalid base URL: ${baseUrl}`);
  121. }
  122. } else if (typeof base === 'object') {
  123. baseUrl = base.toString();
  124. }
  125. if (baseUrl.endsWith('/')) {
  126. baseUrl = baseUrl.slice(0, baseUrl.length - 1);
  127. }
  128. if (!url.startsWith('/')) {
  129. url = `/${url}`;
  130. }
  131. if (baseUrl.endsWith(url)) {
  132. url = '';
  133. }
  134. this._url = `${baseUrl}${url}`;
  135. }
  136. }
  137. get hash() {
  138. throw new Error('not implemented');
  139. }
  140. get host() {
  141. throw new Error('not implemented');
  142. }
  143. get hostname() {
  144. throw new Error('not implemented');
  145. }
  146. get href(): string {
  147. return this.toString();
  148. }
  149. get origin() {
  150. throw new Error('not implemented');
  151. }
  152. get password() {
  153. throw new Error('not implemented');
  154. }
  155. get pathname() {
  156. throw new Error('not implemented');
  157. }
  158. get port() {
  159. throw new Error('not implemented');
  160. }
  161. get protocol() {
  162. throw new Error('not implemented');
  163. }
  164. get search() {
  165. throw new Error('not implemented');
  166. }
  167. get searchParams(): URLSearchParams {
  168. if (this._searchParamsInstance == null) {
  169. this._searchParamsInstance = new URLSearchParams();
  170. }
  171. return this._searchParamsInstance;
  172. }
  173. toJSON(): string {
  174. return this.toString();
  175. }
  176. toString(): string {
  177. if (this._searchParamsInstance === null) {
  178. return this._url;
  179. }
  180. const separator = this._url.indexOf('?') > -1 ? '&' : '?';
  181. return this._url + separator + this._searchParamsInstance.toString();
  182. }
  183. get username() {
  184. throw new Error('not implemented');
  185. }
  186. }