1*26ad19fcSEvan Bacon// Copyright © 2023 650 Industries. 2*26ad19fcSEvan Bacon// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. 3*26ad19fcSEvan Bacon 4*26ad19fcSEvan Baconimport URL from 'url-parse'; 5*26ad19fcSEvan Bacon 6*26ad19fcSEvan Baconclass DOMException extends Error { 7*26ad19fcSEvan Bacon constructor(message: string, name: string) { 8*26ad19fcSEvan Bacon super(message); 9*26ad19fcSEvan Bacon this.name = name; 10*26ad19fcSEvan Bacon } 11*26ad19fcSEvan Bacon} 12*26ad19fcSEvan Bacon 13*26ad19fcSEvan Bacon// The differences between the definitions of `Location` and `WorkerLocation` 14*26ad19fcSEvan Bacon// are because of the `LegacyUnforgeable` attribute only specified upon 15*26ad19fcSEvan Bacon// `Location`'s properties. See: 16*26ad19fcSEvan Bacon// - https://html.spec.whatwg.org/multipage/history.html#the-location-interface 17*26ad19fcSEvan Bacon// - https://heycam.github.io/webidl/#LegacyUnforgeable 18*26ad19fcSEvan Baconclass Location { 19*26ad19fcSEvan Bacon constructor(href: string | null = null) { 20*26ad19fcSEvan Bacon const url = new URL( 21*26ad19fcSEvan Bacon // @ts-expect-error 22*26ad19fcSEvan Bacon href 23*26ad19fcSEvan Bacon ); 24*26ad19fcSEvan Bacon // @ts-expect-error 25*26ad19fcSEvan Bacon url.username = ''; 26*26ad19fcSEvan Bacon // @ts-expect-error 27*26ad19fcSEvan Bacon url.password = ''; 28*26ad19fcSEvan Bacon Object.defineProperties(this, { 29*26ad19fcSEvan Bacon hash: { 30*26ad19fcSEvan Bacon get() { 31*26ad19fcSEvan Bacon return url.hash; 32*26ad19fcSEvan Bacon }, 33*26ad19fcSEvan Bacon set() { 34*26ad19fcSEvan Bacon throw new DOMException(`Cannot set "location.hash".`, 'NotSupportedError'); 35*26ad19fcSEvan Bacon }, 36*26ad19fcSEvan Bacon enumerable: true, 37*26ad19fcSEvan Bacon }, 38*26ad19fcSEvan Bacon host: { 39*26ad19fcSEvan Bacon get() { 40*26ad19fcSEvan Bacon return url.host; 41*26ad19fcSEvan Bacon }, 42*26ad19fcSEvan Bacon set() { 43*26ad19fcSEvan Bacon throw new DOMException(`Cannot set "location.host".`, 'NotSupportedError'); 44*26ad19fcSEvan Bacon }, 45*26ad19fcSEvan Bacon enumerable: true, 46*26ad19fcSEvan Bacon }, 47*26ad19fcSEvan Bacon hostname: { 48*26ad19fcSEvan Bacon get() { 49*26ad19fcSEvan Bacon return url.hostname; 50*26ad19fcSEvan Bacon }, 51*26ad19fcSEvan Bacon set() { 52*26ad19fcSEvan Bacon throw new DOMException(`Cannot set "location.hostname".`, 'NotSupportedError'); 53*26ad19fcSEvan Bacon }, 54*26ad19fcSEvan Bacon enumerable: true, 55*26ad19fcSEvan Bacon }, 56*26ad19fcSEvan Bacon href: { 57*26ad19fcSEvan Bacon get() { 58*26ad19fcSEvan Bacon return url.href; 59*26ad19fcSEvan Bacon }, 60*26ad19fcSEvan Bacon set() { 61*26ad19fcSEvan Bacon throw new DOMException(`Cannot set "location.href".`, 'NotSupportedError'); 62*26ad19fcSEvan Bacon }, 63*26ad19fcSEvan Bacon enumerable: true, 64*26ad19fcSEvan Bacon }, 65*26ad19fcSEvan Bacon origin: { 66*26ad19fcSEvan Bacon get() { 67*26ad19fcSEvan Bacon return url.origin; 68*26ad19fcSEvan Bacon }, 69*26ad19fcSEvan Bacon enumerable: true, 70*26ad19fcSEvan Bacon }, 71*26ad19fcSEvan Bacon pathname: { 72*26ad19fcSEvan Bacon get() { 73*26ad19fcSEvan Bacon return url.pathname; 74*26ad19fcSEvan Bacon }, 75*26ad19fcSEvan Bacon set() { 76*26ad19fcSEvan Bacon throw new DOMException(`Cannot set "location.pathname".`, 'NotSupportedError'); 77*26ad19fcSEvan Bacon }, 78*26ad19fcSEvan Bacon enumerable: true, 79*26ad19fcSEvan Bacon }, 80*26ad19fcSEvan Bacon port: { 81*26ad19fcSEvan Bacon get() { 82*26ad19fcSEvan Bacon return url.port; 83*26ad19fcSEvan Bacon }, 84*26ad19fcSEvan Bacon set() { 85*26ad19fcSEvan Bacon throw new DOMException(`Cannot set "location.port".`, 'NotSupportedError'); 86*26ad19fcSEvan Bacon }, 87*26ad19fcSEvan Bacon enumerable: true, 88*26ad19fcSEvan Bacon }, 89*26ad19fcSEvan Bacon protocol: { 90*26ad19fcSEvan Bacon get() { 91*26ad19fcSEvan Bacon return url.protocol; 92*26ad19fcSEvan Bacon }, 93*26ad19fcSEvan Bacon set() { 94*26ad19fcSEvan Bacon throw new DOMException(`Cannot set "location.protocol".`, 'NotSupportedError'); 95*26ad19fcSEvan Bacon }, 96*26ad19fcSEvan Bacon enumerable: true, 97*26ad19fcSEvan Bacon }, 98*26ad19fcSEvan Bacon search: { 99*26ad19fcSEvan Bacon get() { 100*26ad19fcSEvan Bacon // @ts-expect-error 101*26ad19fcSEvan Bacon return url.search; 102*26ad19fcSEvan Bacon }, 103*26ad19fcSEvan Bacon set() { 104*26ad19fcSEvan Bacon throw new DOMException(`Cannot set "location.search".`, 'NotSupportedError'); 105*26ad19fcSEvan Bacon }, 106*26ad19fcSEvan Bacon enumerable: true, 107*26ad19fcSEvan Bacon }, 108*26ad19fcSEvan Bacon ancestorOrigins: { 109*26ad19fcSEvan Bacon get() { 110*26ad19fcSEvan Bacon return { 111*26ad19fcSEvan Bacon length: 0, 112*26ad19fcSEvan Bacon item: () => null, 113*26ad19fcSEvan Bacon contains: () => false, 114*26ad19fcSEvan Bacon }; 115*26ad19fcSEvan Bacon }, 116*26ad19fcSEvan Bacon enumerable: true, 117*26ad19fcSEvan Bacon }, 118*26ad19fcSEvan Bacon assign: { 119*26ad19fcSEvan Bacon value: function assign() { 120*26ad19fcSEvan Bacon throw new DOMException(`Cannot call "location.assign()".`, 'NotSupportedError'); 121*26ad19fcSEvan Bacon }, 122*26ad19fcSEvan Bacon enumerable: true, 123*26ad19fcSEvan Bacon }, 124*26ad19fcSEvan Bacon reload: { 125*26ad19fcSEvan Bacon value: function reload() { 126*26ad19fcSEvan Bacon if (process.env.NODE_ENV !== 'production') { 127*26ad19fcSEvan Bacon // NOTE: This does change how native fast refresh works. The upstream metro-runtime will check 128*26ad19fcSEvan Bacon // if `location.reload` exists before falling back on an implementation that is nearly identical to 129*26ad19fcSEvan Bacon // this. The main difference is that on iOS there is a "reason" message sent, but at the time of writing 130*26ad19fcSEvan Bacon // this, that message is unused (ref: `RCTTriggerReloadCommandNotification`). 131*26ad19fcSEvan Bacon const DevSettings = (require('react-native') as typeof import('react-native')) 132*26ad19fcSEvan Bacon .DevSettings; 133*26ad19fcSEvan Bacon return DevSettings.reload(); 134*26ad19fcSEvan Bacon } else { 135*26ad19fcSEvan Bacon throw new DOMException(`Cannot call "location.reload()".`, 'NotSupportedError'); 136*26ad19fcSEvan Bacon } 137*26ad19fcSEvan Bacon }, 138*26ad19fcSEvan Bacon enumerable: true, 139*26ad19fcSEvan Bacon }, 140*26ad19fcSEvan Bacon replace: { 141*26ad19fcSEvan Bacon value: function replace() { 142*26ad19fcSEvan Bacon throw new DOMException(`Cannot call "location.replace()".`, 'NotSupportedError'); 143*26ad19fcSEvan Bacon }, 144*26ad19fcSEvan Bacon enumerable: true, 145*26ad19fcSEvan Bacon }, 146*26ad19fcSEvan Bacon toString: { 147*26ad19fcSEvan Bacon value: function toString() { 148*26ad19fcSEvan Bacon return url.href; 149*26ad19fcSEvan Bacon }, 150*26ad19fcSEvan Bacon enumerable: true, 151*26ad19fcSEvan Bacon }, 152*26ad19fcSEvan Bacon [Symbol.for('Expo.privateCustomInspect')]: { 153*26ad19fcSEvan Bacon value(inspect: any) { 154*26ad19fcSEvan Bacon const object = { 155*26ad19fcSEvan Bacon hash: this.hash, 156*26ad19fcSEvan Bacon host: this.host, 157*26ad19fcSEvan Bacon hostname: this.hostname, 158*26ad19fcSEvan Bacon href: this.href, 159*26ad19fcSEvan Bacon origin: this.origin, 160*26ad19fcSEvan Bacon pathname: this.pathname, 161*26ad19fcSEvan Bacon port: this.port, 162*26ad19fcSEvan Bacon protocol: this.protocol, 163*26ad19fcSEvan Bacon search: this.search, 164*26ad19fcSEvan Bacon }; 165*26ad19fcSEvan Bacon return `${this.constructor.name} ${inspect(object)}`; 166*26ad19fcSEvan Bacon }, 167*26ad19fcSEvan Bacon }, 168*26ad19fcSEvan Bacon }); 169*26ad19fcSEvan Bacon } 170*26ad19fcSEvan Bacon} 171*26ad19fcSEvan Bacon 172*26ad19fcSEvan BaconObject.defineProperties(Location.prototype, { 173*26ad19fcSEvan Bacon [Symbol.toString()]: { 174*26ad19fcSEvan Bacon value: 'Location', 175*26ad19fcSEvan Bacon configurable: true, 176*26ad19fcSEvan Bacon }, 177*26ad19fcSEvan Bacon}); 178*26ad19fcSEvan Bacon 179*26ad19fcSEvan Baconlet location: Location | undefined = undefined; 180*26ad19fcSEvan Bacon 181*26ad19fcSEvan Baconexport function setLocationHref(href: string) { 182*26ad19fcSEvan Bacon location = new Location(href); 183*26ad19fcSEvan Bacon} 184*26ad19fcSEvan Bacon 185*26ad19fcSEvan Baconexport function install() { 186*26ad19fcSEvan Bacon Object.defineProperty(global, 'Location', { 187*26ad19fcSEvan Bacon value: Location, 188*26ad19fcSEvan Bacon configurable: true, 189*26ad19fcSEvan Bacon writable: true, 190*26ad19fcSEvan Bacon }); 191*26ad19fcSEvan Bacon 192*26ad19fcSEvan Bacon Object.defineProperty(window, 'location', { 193*26ad19fcSEvan Bacon get() { 194*26ad19fcSEvan Bacon return location; 195*26ad19fcSEvan Bacon }, 196*26ad19fcSEvan Bacon set() { 197*26ad19fcSEvan Bacon throw new DOMException(`Cannot set "location".`, 'NotSupportedError'); 198*26ad19fcSEvan Bacon }, 199*26ad19fcSEvan Bacon enumerable: true, 200*26ad19fcSEvan Bacon }); 201*26ad19fcSEvan Bacon} 202