Location.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387
  1. /*
  2. * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
  3. * Copyright (c) 2022-2023, Linus Groh <linusg@serenityos.org>
  4. *
  5. * SPDX-License-Identifier: BSD-2-Clause
  6. */
  7. #include <AK/DeprecatedFlyString.h>
  8. #include <AK/StringBuilder.h>
  9. #include <LibJS/Heap/MarkedVector.h>
  10. #include <LibJS/Runtime/Completion.h>
  11. #include <LibJS/Runtime/PropertyDescriptor.h>
  12. #include <LibJS/Runtime/PropertyKey.h>
  13. #include <LibWeb/Bindings/LocationPrototype.h>
  14. #include <LibWeb/DOM/Document.h>
  15. #include <LibWeb/HTML/CrossOrigin/AbstractOperations.h>
  16. #include <LibWeb/HTML/Location.h>
  17. #include <LibWeb/HTML/Window.h>
  18. #include <LibWeb/WebIDL/DOMException.h>
  19. namespace Web::HTML {
  20. // https://html.spec.whatwg.org/multipage/history.html#the-location-interface
  21. Location::Location(JS::Realm& realm)
  22. : PlatformObject(realm)
  23. {
  24. }
  25. Location::~Location() = default;
  26. void Location::visit_edges(Cell::Visitor& visitor)
  27. {
  28. Base::visit_edges(visitor);
  29. for (auto& property : m_default_properties)
  30. visitor.visit(property);
  31. }
  32. void Location::initialize(JS::Realm& realm)
  33. {
  34. Object::initialize(realm);
  35. set_prototype(&Bindings::ensure_web_prototype<Bindings::LocationPrototype>(realm, "Location"));
  36. // FIXME: Implement steps 2.-4.
  37. // 5. Set the value of the [[DefaultProperties]] internal slot of location to location.[[OwnPropertyKeys]]().
  38. // NOTE: In LibWeb this happens before the ESO is set up, so we must avoid location's custom [[OwnPropertyKeys]].
  39. m_default_properties.extend(MUST(Object::internal_own_property_keys()));
  40. }
  41. // https://html.spec.whatwg.org/multipage/history.html#relevant-document
  42. DOM::Document const* Location::relevant_document() const
  43. {
  44. // A Location object has an associated relevant Document, which is this Location object's
  45. // relevant global object's browsing context's active document, if this Location object's
  46. // relevant global object's browsing context is non-null, and null otherwise.
  47. auto* browsing_context = verify_cast<HTML::Window>(HTML::relevant_global_object(*this)).browsing_context();
  48. return browsing_context ? browsing_context->active_document() : nullptr;
  49. }
  50. // https://html.spec.whatwg.org/multipage/history.html#concept-location-url
  51. AK::URL Location::url() const
  52. {
  53. // A Location object has an associated url, which is this Location object's relevant Document's URL,
  54. // if this Location object's relevant Document is non-null, and about:blank otherwise.
  55. auto const* relevant_document = this->relevant_document();
  56. return relevant_document ? relevant_document->url() : "about:blank"sv;
  57. }
  58. // https://html.spec.whatwg.org/multipage/history.html#dom-location-href
  59. DeprecatedString Location::href() const
  60. {
  61. // FIXME: 1. If this's relevant Document is non-null and its origin is not same origin-domain with the entry settings object's origin, then throw a "SecurityError" DOMException.
  62. // 2. Return this's url, serialized.
  63. return url().to_deprecated_string();
  64. }
  65. // https://html.spec.whatwg.org/multipage/history.html#the-location-interface:dom-location-href-2
  66. JS::ThrowCompletionOr<void> Location::set_href(DeprecatedString const& new_href)
  67. {
  68. auto& vm = this->vm();
  69. auto& window = verify_cast<HTML::Window>(HTML::current_global_object());
  70. // FIXME: 1. If this's relevant Document is null, then return.
  71. // 2. Parse the given value relative to the entry settings object. If that failed, throw a TypeError exception.
  72. auto href_url = window.associated_document().parse_url(new_href);
  73. if (!href_url.is_valid())
  74. return vm.throw_completion<JS::URIError>(DeprecatedString::formatted("Invalid URL '{}'", new_href));
  75. // 3. Location-object navigate given the resulting URL record.
  76. window.did_set_location_href({}, href_url);
  77. return {};
  78. }
  79. // https://html.spec.whatwg.org/multipage/nav-history-apis.html#dom-location-origin
  80. DeprecatedString Location::origin() const
  81. {
  82. // FIXME: 1. If this's relevant Document is non-null and its origin is not same origin-domain with the entry settings object's origin, then throw a "SecurityError" DOMException.
  83. // 2. Return the serialization of this's url's origin.
  84. return url().serialize_origin();
  85. }
  86. // https://html.spec.whatwg.org/multipage/history.html#dom-location-protocol
  87. DeprecatedString Location::protocol() const
  88. {
  89. // FIXME: 1. If this's relevant Document is non-null and its origin is not same origin-domain with the entry settings object's origin, then throw a "SecurityError" DOMException.
  90. // 2. Return this's url's scheme, followed by ":".
  91. return DeprecatedString::formatted("{}:", url().scheme());
  92. }
  93. JS::ThrowCompletionOr<void> Location::set_protocol(DeprecatedString const&)
  94. {
  95. auto& vm = this->vm();
  96. return vm.throw_completion<JS::InternalError>(JS::ErrorType::NotImplemented, "Location.protocol setter");
  97. }
  98. // https://html.spec.whatwg.org/multipage/history.html#dom-location-host
  99. DeprecatedString Location::host() const
  100. {
  101. // FIXME: 1. If this's relevant Document is non-null and its origin is not same origin-domain with the entry settings object's origin, then throw a "SecurityError" DOMException.
  102. // 2. Let url be this's url.
  103. auto url = this->url();
  104. // 3. If url's host is null, return the empty string.
  105. if (url.host().is_null())
  106. return DeprecatedString::empty();
  107. // 4. If url's port is null, return url's host, serialized.
  108. if (!url.port().has_value())
  109. return url.host();
  110. // 5. Return url's host, serialized, followed by ":" and url's port, serialized.
  111. return DeprecatedString::formatted("{}:{}", url.host(), *url.port());
  112. }
  113. JS::ThrowCompletionOr<void> Location::set_host(DeprecatedString const&)
  114. {
  115. auto& vm = this->vm();
  116. return vm.throw_completion<JS::InternalError>(JS::ErrorType::NotImplemented, "Location.host setter");
  117. }
  118. // https://html.spec.whatwg.org/multipage/history.html#dom-location-hostname
  119. DeprecatedString Location::hostname() const
  120. {
  121. // FIXME: 1. If this's relevant Document is non-null and its origin is not same origin-domain with the entry settings object's origin, then throw a "SecurityError" DOMException.
  122. auto url = this->url();
  123. // 2. If this's url's host is null, return the empty string.
  124. if (url.host().is_null())
  125. return DeprecatedString::empty();
  126. // 3. Return this's url's host, serialized.
  127. return url.host();
  128. }
  129. JS::ThrowCompletionOr<void> Location::set_hostname(DeprecatedString const&)
  130. {
  131. auto& vm = this->vm();
  132. return vm.throw_completion<JS::InternalError>(JS::ErrorType::NotImplemented, "Location.hostname setter");
  133. }
  134. // https://html.spec.whatwg.org/multipage/history.html#dom-location-port
  135. DeprecatedString Location::port() const
  136. {
  137. // FIXME: 1. If this's relevant Document is non-null and its origin is not same origin-domain with the entry settings object's origin, then throw a "SecurityError" DOMException.
  138. auto url = this->url();
  139. // 2. If this's url's port is null, return the empty string.
  140. if (!url.port().has_value())
  141. return DeprecatedString::empty();
  142. // 3. Return this's url's port, serialized.
  143. return DeprecatedString::number(*url.port());
  144. }
  145. JS::ThrowCompletionOr<void> Location::set_port(DeprecatedString const&)
  146. {
  147. auto& vm = this->vm();
  148. return vm.throw_completion<JS::InternalError>(JS::ErrorType::NotImplemented, "Location.port setter");
  149. }
  150. // https://html.spec.whatwg.org/multipage/history.html#dom-location-pathname
  151. DeprecatedString Location::pathname() const
  152. {
  153. // FIXME: 1. If this's relevant Document is non-null and its origin is not same origin-domain with the entry settings object's origin, then throw a "SecurityError" DOMException.
  154. // 2. Return the result of URL path serializing this Location object's url.
  155. return url().path();
  156. }
  157. JS::ThrowCompletionOr<void> Location::set_pathname(DeprecatedString const&)
  158. {
  159. auto& vm = this->vm();
  160. return vm.throw_completion<JS::InternalError>(JS::ErrorType::NotImplemented, "Location.pathname setter");
  161. }
  162. // https://html.spec.whatwg.org/multipage/history.html#dom-location-search
  163. DeprecatedString Location::search() const
  164. {
  165. // FIXME: 1. If this's relevant Document is non-null and its origin is not same origin-domain with the entry settings object's origin, then throw a "SecurityError" DOMException.
  166. auto url = this->url();
  167. // 2. If this's url's query is either null or the empty string, return the empty string.
  168. if (url.query().is_empty())
  169. return DeprecatedString::empty();
  170. // 3. Return "?", followed by this's url's query.
  171. return DeprecatedString::formatted("?{}", url.query());
  172. }
  173. JS::ThrowCompletionOr<void> Location::set_search(DeprecatedString const&)
  174. {
  175. auto& vm = this->vm();
  176. return vm.throw_completion<JS::InternalError>(JS::ErrorType::NotImplemented, "Location.search setter");
  177. }
  178. // https://html.spec.whatwg.org/multipage/history.html#dom-location-hash
  179. DeprecatedString Location::hash() const
  180. {
  181. // FIXME: 1. If this's relevant Document is non-null and its origin is not same origin-domain with the entry settings object's origin, then throw a "SecurityError" DOMException.
  182. auto url = this->url();
  183. // 2. If this's url's fragment is either null or the empty string, return the empty string.
  184. if (url.fragment().is_empty())
  185. return DeprecatedString::empty();
  186. // 3. Return "#", followed by this's url's fragment.
  187. return DeprecatedString::formatted("#{}", url.fragment());
  188. }
  189. JS::ThrowCompletionOr<void> Location::set_hash(DeprecatedString const&)
  190. {
  191. auto& vm = this->vm();
  192. return vm.throw_completion<JS::InternalError>(JS::ErrorType::NotImplemented, "Location.hash setter");
  193. }
  194. // https://html.spec.whatwg.org/multipage/history.html#dom-location-reload
  195. void Location::reload() const
  196. {
  197. auto& window = verify_cast<HTML::Window>(HTML::current_global_object());
  198. window.did_call_location_reload({});
  199. }
  200. // https://html.spec.whatwg.org/multipage/history.html#dom-location-replace
  201. void Location::replace(DeprecatedString url) const
  202. {
  203. auto& window = verify_cast<HTML::Window>(HTML::current_global_object());
  204. // FIXME: This needs spec compliance work.
  205. window.did_call_location_replace({}, move(url));
  206. }
  207. // 7.10.5.1 [[GetPrototypeOf]] ( ), https://html.spec.whatwg.org/multipage/history.html#location-getprototypeof
  208. JS::ThrowCompletionOr<JS::Object*> Location::internal_get_prototype_of() const
  209. {
  210. // 1. If IsPlatformObjectSameOrigin(this) is true, then return ! OrdinaryGetPrototypeOf(this).
  211. if (HTML::is_platform_object_same_origin(*this))
  212. return MUST(JS::Object::internal_get_prototype_of());
  213. // 2. Return null.
  214. return nullptr;
  215. }
  216. // 7.10.5.2 [[SetPrototypeOf]] ( V ), https://html.spec.whatwg.org/multipage/history.html#location-setprototypeof
  217. JS::ThrowCompletionOr<bool> Location::internal_set_prototype_of(Object* prototype)
  218. {
  219. // 1. Return ! SetImmutablePrototype(this, V).
  220. return MUST(set_immutable_prototype(prototype));
  221. }
  222. // 7.10.5.3 [[IsExtensible]] ( ), https://html.spec.whatwg.org/multipage/history.html#location-isextensible
  223. JS::ThrowCompletionOr<bool> Location::internal_is_extensible() const
  224. {
  225. // 1. Return true.
  226. return true;
  227. }
  228. // 7.10.5.4 [[PreventExtensions]] ( ), https://html.spec.whatwg.org/multipage/history.html#location-preventextensions
  229. JS::ThrowCompletionOr<bool> Location::internal_prevent_extensions()
  230. {
  231. // 1. Return false.
  232. return false;
  233. }
  234. // 7.10.5.5 [[GetOwnProperty]] ( P ), https://html.spec.whatwg.org/multipage/history.html#location-getownproperty
  235. JS::ThrowCompletionOr<Optional<JS::PropertyDescriptor>> Location::internal_get_own_property(JS::PropertyKey const& property_key) const
  236. {
  237. auto& vm = this->vm();
  238. // 1. If IsPlatformObjectSameOrigin(this) is true, then:
  239. if (HTML::is_platform_object_same_origin(*this)) {
  240. // 1. Let desc be OrdinaryGetOwnProperty(this, P).
  241. auto descriptor = MUST(Object::internal_get_own_property(property_key));
  242. // 2. If the value of the [[DefaultProperties]] internal slot of this contains P, then set desc.[[Configurable]] to true.
  243. auto property_key_value = property_key.is_symbol()
  244. ? JS::Value { property_key.as_symbol() }
  245. : JS::PrimitiveString::create(vm, property_key.to_string());
  246. if (m_default_properties.contains_slow(property_key_value))
  247. descriptor->configurable = true;
  248. // 3. Return desc.
  249. return descriptor;
  250. }
  251. // 2. Let property be CrossOriginGetOwnPropertyHelper(this, P).
  252. auto property = HTML::cross_origin_get_own_property_helper(const_cast<Location*>(this), property_key);
  253. // 3. If property is not undefined, then return property.
  254. if (property.has_value())
  255. return property;
  256. // 4. Return ? CrossOriginPropertyFallback(P).
  257. return TRY(HTML::cross_origin_property_fallback(vm, property_key));
  258. }
  259. // 7.10.5.6 [[DefineOwnProperty]] ( P, Desc ), https://html.spec.whatwg.org/multipage/history.html#location-defineownproperty
  260. JS::ThrowCompletionOr<bool> Location::internal_define_own_property(JS::PropertyKey const& property_key, JS::PropertyDescriptor const& descriptor)
  261. {
  262. // 1. If IsPlatformObjectSameOrigin(this) is true, then:
  263. if (HTML::is_platform_object_same_origin(*this)) {
  264. // 1. If the value of the [[DefaultProperties]] internal slot of this contains P, then return false.
  265. // 2. Return ? OrdinaryDefineOwnProperty(this, P, Desc).
  266. return JS::Object::internal_define_own_property(property_key, descriptor);
  267. }
  268. // 2. Throw a "SecurityError" DOMException.
  269. return throw_completion(WebIDL::SecurityError::create(realm(), DeprecatedString::formatted("Can't define property '{}' on cross-origin object", property_key)));
  270. }
  271. // 7.10.5.7 [[Get]] ( P, Receiver ), https://html.spec.whatwg.org/multipage/history.html#location-get
  272. JS::ThrowCompletionOr<JS::Value> Location::internal_get(JS::PropertyKey const& property_key, JS::Value receiver) const
  273. {
  274. auto& vm = this->vm();
  275. // 1. If IsPlatformObjectSameOrigin(this) is true, then return ? OrdinaryGet(this, P, Receiver).
  276. if (HTML::is_platform_object_same_origin(*this))
  277. return JS::Object::internal_get(property_key, receiver);
  278. // 2. Return ? CrossOriginGet(this, P, Receiver).
  279. return HTML::cross_origin_get(vm, static_cast<JS::Object const&>(*this), property_key, receiver);
  280. }
  281. // 7.10.5.8 [[Set]] ( P, V, Receiver ), https://html.spec.whatwg.org/multipage/history.html#location-set
  282. JS::ThrowCompletionOr<bool> Location::internal_set(JS::PropertyKey const& property_key, JS::Value value, JS::Value receiver)
  283. {
  284. auto& vm = this->vm();
  285. // 1. If IsPlatformObjectSameOrigin(this) is true, then return ? OrdinarySet(this, P, V, Receiver).
  286. if (HTML::is_platform_object_same_origin(*this))
  287. return JS::Object::internal_set(property_key, value, receiver);
  288. // 2. Return ? CrossOriginSet(this, P, V, Receiver).
  289. return HTML::cross_origin_set(vm, static_cast<JS::Object&>(*this), property_key, value, receiver);
  290. }
  291. // 7.10.5.9 [[Delete]] ( P ), https://html.spec.whatwg.org/multipage/history.html#location-delete
  292. JS::ThrowCompletionOr<bool> Location::internal_delete(JS::PropertyKey const& property_key)
  293. {
  294. // 1. If IsPlatformObjectSameOrigin(this) is true, then return ? OrdinaryDelete(this, P).
  295. if (HTML::is_platform_object_same_origin(*this))
  296. return JS::Object::internal_delete(property_key);
  297. // 2. Throw a "SecurityError" DOMException.
  298. return throw_completion(WebIDL::SecurityError::create(realm(), DeprecatedString::formatted("Can't delete property '{}' on cross-origin object", property_key)));
  299. }
  300. // 7.10.5.10 [[OwnPropertyKeys]] ( ), https://html.spec.whatwg.org/multipage/history.html#location-ownpropertykeys
  301. JS::ThrowCompletionOr<JS::MarkedVector<JS::Value>> Location::internal_own_property_keys() const
  302. {
  303. // 1. If IsPlatformObjectSameOrigin(this) is true, then return OrdinaryOwnPropertyKeys(this).
  304. if (HTML::is_platform_object_same_origin(*this))
  305. return JS::Object::internal_own_property_keys();
  306. // 2. Return CrossOriginOwnPropertyKeys(this).
  307. return HTML::cross_origin_own_property_keys(this);
  308. }
  309. }