Window.cpp 82 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828
  1. /*
  2. * Copyright (c) 2020-2023, Andreas Kling <andreas@ladybird.org>
  3. * Copyright (c) 2021-2022, Sam Atkins <atkinssj@serenityos.org>
  4. * Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org>
  5. *
  6. * SPDX-License-Identifier: BSD-2-Clause
  7. */
  8. #include <AK/Utf8View.h>
  9. #include <LibIPC/File.h>
  10. #include <LibJS/Runtime/AbstractOperations.h>
  11. #include <LibJS/Runtime/Accessor.h>
  12. #include <LibJS/Runtime/Completion.h>
  13. #include <LibJS/Runtime/Error.h>
  14. #include <LibJS/Runtime/FunctionObject.h>
  15. #include <LibJS/Runtime/GlobalEnvironment.h>
  16. #include <LibJS/Runtime/NativeFunction.h>
  17. #include <LibJS/Runtime/Shape.h>
  18. #include <LibTextCodec/Decoder.h>
  19. #include <LibURL/Origin.h>
  20. #include <LibWeb/Bindings/ExceptionOrUtils.h>
  21. #include <LibWeb/Bindings/WindowExposedInterfaces.h>
  22. #include <LibWeb/Bindings/WindowPrototype.h>
  23. #include <LibWeb/CSS/MediaQueryList.h>
  24. #include <LibWeb/CSS/Parser/Parser.h>
  25. #include <LibWeb/CSS/ResolvedCSSStyleDeclaration.h>
  26. #include <LibWeb/CSS/Screen.h>
  27. #include <LibWeb/DOM/Document.h>
  28. #include <LibWeb/DOM/Element.h>
  29. #include <LibWeb/DOM/Event.h>
  30. #include <LibWeb/DOM/EventDispatcher.h>
  31. #include <LibWeb/DOM/HTMLCollection.h>
  32. #include <LibWeb/DOMURL/DOMURL.h>
  33. #include <LibWeb/HTML/BrowsingContext.h>
  34. #include <LibWeb/HTML/CloseWatcherManager.h>
  35. #include <LibWeb/HTML/CustomElements/CustomElementRegistry.h>
  36. #include <LibWeb/HTML/DocumentState.h>
  37. #include <LibWeb/HTML/EventHandler.h>
  38. #include <LibWeb/HTML/EventLoop/EventLoop.h>
  39. #include <LibWeb/HTML/Focus.h>
  40. #include <LibWeb/HTML/HTMLEmbedElement.h>
  41. #include <LibWeb/HTML/HTMLFormElement.h>
  42. #include <LibWeb/HTML/HTMLImageElement.h>
  43. #include <LibWeb/HTML/HTMLObjectElement.h>
  44. #include <LibWeb/HTML/Location.h>
  45. #include <LibWeb/HTML/MessageEvent.h>
  46. #include <LibWeb/HTML/MessagePort.h>
  47. #include <LibWeb/HTML/Navigation.h>
  48. #include <LibWeb/HTML/Navigator.h>
  49. #include <LibWeb/HTML/PageTransitionEvent.h>
  50. #include <LibWeb/HTML/Parser/HTMLParser.h>
  51. #include <LibWeb/HTML/Scripting/Environments.h>
  52. #include <LibWeb/HTML/Scripting/ExceptionReporter.h>
  53. #include <LibWeb/HTML/Scripting/TemporaryExecutionContext.h>
  54. #include <LibWeb/HTML/Storage.h>
  55. #include <LibWeb/HTML/TokenizedFeatures.h>
  56. #include <LibWeb/HTML/TraversableNavigable.h>
  57. #include <LibWeb/HTML/Window.h>
  58. #include <LibWeb/HTML/WindowProxy.h>
  59. #include <LibWeb/HighResolutionTime/TimeOrigin.h>
  60. #include <LibWeb/Infra/CharacterTypes.h>
  61. #include <LibWeb/Internals/Inspector.h>
  62. #include <LibWeb/Internals/Internals.h>
  63. #include <LibWeb/Layout/Viewport.h>
  64. #include <LibWeb/Page/Page.h>
  65. #include <LibWeb/Painting/PaintableBox.h>
  66. #include <LibWeb/RequestIdleCallback/IdleDeadline.h>
  67. #include <LibWeb/Selection/Selection.h>
  68. #include <LibWeb/WebIDL/AbstractOperations.h>
  69. namespace Web::HTML {
  70. JS_DEFINE_ALLOCATOR(Window);
  71. // https://html.spec.whatwg.org/#run-the-animation-frame-callbacks
  72. void run_animation_frame_callbacks(DOM::Document& document, double now)
  73. {
  74. // FIXME: Bring this closer to the spec.
  75. document.window()->animation_frame_callback_driver().run(now);
  76. }
  77. class IdleCallback : public RefCounted<IdleCallback> {
  78. public:
  79. explicit IdleCallback(ESCAPING Function<JS::Completion(JS::NonnullGCPtr<RequestIdleCallback::IdleDeadline>)> handler, u32 handle)
  80. : m_handler(move(handler))
  81. , m_handle(handle)
  82. {
  83. }
  84. ~IdleCallback() = default;
  85. JS::Completion invoke(JS::NonnullGCPtr<RequestIdleCallback::IdleDeadline> deadline) { return m_handler(deadline); }
  86. u32 handle() const { return m_handle; }
  87. private:
  88. Function<JS::Completion(JS::NonnullGCPtr<RequestIdleCallback::IdleDeadline>)> m_handler;
  89. u32 m_handle { 0 };
  90. };
  91. JS::NonnullGCPtr<Window> Window::create(JS::Realm& realm)
  92. {
  93. return realm.heap().allocate<Window>(realm, realm);
  94. }
  95. Window::Window(JS::Realm& realm)
  96. : DOM::EventTarget(realm)
  97. {
  98. m_legacy_platform_object_flags = LegacyPlatformObjectFlags {
  99. .supports_named_properties = true,
  100. .has_legacy_unenumerable_named_properties_interface_extended_attribute = true,
  101. .has_global_interface_extended_attribute = true,
  102. };
  103. }
  104. void Window::visit_edges(JS::Cell::Visitor& visitor)
  105. {
  106. Base::visit_edges(visitor);
  107. WindowOrWorkerGlobalScopeMixin::visit_edges(visitor);
  108. visitor.visit(m_associated_document);
  109. visitor.visit(m_current_event);
  110. visitor.visit(m_screen);
  111. visitor.visit(m_location);
  112. visitor.visit(m_navigator);
  113. visitor.visit(m_navigation);
  114. visitor.visit(m_custom_element_registry);
  115. visitor.visit(m_pdf_viewer_plugin_objects);
  116. visitor.visit(m_pdf_viewer_mime_type_objects);
  117. visitor.visit(m_count_queuing_strategy_size_function);
  118. visitor.visit(m_byte_length_queuing_strategy_size_function);
  119. visitor.visit(m_close_watcher_manager);
  120. }
  121. void Window::finalize()
  122. {
  123. Base::finalize();
  124. WindowOrWorkerGlobalScopeMixin::finalize();
  125. }
  126. Window::~Window() = default;
  127. // https://html.spec.whatwg.org/multipage/window-object.html#window-open-steps
  128. WebIDL::ExceptionOr<JS::GCPtr<WindowProxy>> Window::window_open_steps(StringView url, StringView target, StringView features)
  129. {
  130. auto [target_navigable, no_opener, window_type] = TRY(window_open_steps_internal(url, target, features));
  131. if (target_navigable == nullptr)
  132. return nullptr;
  133. // 14. If noopener is true or windowType is "new with no opener", then return null.
  134. if (no_opener == TokenizedFeature::NoOpener::Yes || window_type == Navigable::WindowType::NewWithNoOpener)
  135. return nullptr;
  136. // 15. Return targetNavigable's active WindowProxy.
  137. return target_navigable->active_window_proxy();
  138. }
  139. // https://html.spec.whatwg.org/multipage/window-object.html#window-open-steps
  140. WebIDL::ExceptionOr<Window::OpenedWindow> Window::window_open_steps_internal(StringView url, StringView target, StringView features)
  141. {
  142. // 1. If the event loop's termination nesting level is nonzero, return null.
  143. if (main_thread_event_loop().termination_nesting_level() != 0)
  144. return OpenedWindow {};
  145. // FIXME: Spec-issue: https://github.com/whatwg/html/issues/10681
  146. // We need to check for an invalid URL before running the 'rules for choosing a navigable' otherwise
  147. // and invalid URL will result in a window being created before an exception is thrown.
  148. //
  149. // 12.3. Let urlRecord be the URL record about:blank.
  150. auto url_record = URL::URL("about:blank"sv);
  151. // 12.4. If url is not the empty string, then set urlRecord to the result of encoding-parsing a URL given url, relative to the entry settings object.
  152. if (!url.is_empty()) {
  153. url_record = entry_settings_object().parse_url(url);
  154. // 12.5. If urlRecord is failure, then throw a "SyntaxError" DOMException.
  155. if (!url_record.is_valid())
  156. return WebIDL::SyntaxError::create(realm(), MUST(String::formatted("Invalid URL '{}'", url)));
  157. }
  158. // 2. Let sourceDocument be the entry global object's associated Document.
  159. auto& source_document = verify_cast<Window>(entry_global_object()).associated_document();
  160. // 3. If target is the empty string, then set target to "_blank".
  161. if (target.is_empty())
  162. target = "_blank"sv;
  163. // 4. Let tokenizedFeatures be the result of tokenizing features.
  164. auto tokenized_features = tokenize_open_features(features);
  165. // 5. Let noopener and noreferrer be false.
  166. auto no_opener = TokenizedFeature::NoOpener::No;
  167. auto no_referrer = TokenizedFeature::NoReferrer::No;
  168. // 6. If tokenizedFeatures["noopener"] exists, then:
  169. if (auto no_opener_feature = tokenized_features.get("noopener"sv); no_opener_feature.has_value()) {
  170. // 1. Set noopener to the result of parsing tokenizedFeatures["noopener"] as a boolean feature.
  171. no_opener = parse_boolean_feature<TokenizedFeature::NoOpener>(*no_opener_feature);
  172. // 2. Remove tokenizedFeatures["noopener"].
  173. tokenized_features.remove("noopener"sv);
  174. }
  175. // 7. If tokenizedFeatures["noreferrer"] exists, then:
  176. if (auto no_referrer_feature = tokenized_features.get("noreferrer"sv); no_referrer_feature.has_value()) {
  177. // 1. Set noreferrer to the result of parsing tokenizedFeatures["noreferrer"] as a boolean feature.
  178. no_referrer = parse_boolean_feature<TokenizedFeature::NoReferrer>(*no_referrer_feature);
  179. // 2. Remove tokenizedFeatures["noreferrer"].
  180. tokenized_features.remove("noreferrer"sv);
  181. }
  182. // 8. Let referrerPolicy be the empty string.
  183. auto referrer_policy = ReferrerPolicy::ReferrerPolicy::EmptyString;
  184. // 9. If noreferrer is true, then set noopener to true and set referrerPolicy to "no-referrer".
  185. if (no_referrer == TokenizedFeature::NoReferrer::Yes) {
  186. no_opener = TokenizedFeature::NoOpener::Yes;
  187. referrer_policy = ReferrerPolicy::ReferrerPolicy::NoReferrer;
  188. }
  189. // 10. Let targetNavigable and windowType be the result of applying the rules for choosing a navigable given target, sourceDocument's node navigable, and noopener.
  190. VERIFY(source_document.navigable());
  191. auto [target_navigable, window_type] = source_document.navigable()->choose_a_navigable(target, no_opener, ActivateTab::Yes, tokenized_features);
  192. // 11. If targetNavigable is null, then return null.
  193. if (target_navigable == nullptr)
  194. return OpenedWindow {};
  195. // 12. If windowType is either "new and unrestricted" or "new with no opener", then:
  196. if (window_type == Navigable::WindowType::NewAndUnrestricted || window_type == Navigable::WindowType::NewWithNoOpener) {
  197. // 1. Set the target browsing context's is popup to the result of checking if a popup window is requested, given tokenizedFeatures.
  198. target_navigable->set_is_popup(check_if_a_popup_window_is_requested(tokenized_features));
  199. // 2. Set up browsing context features for target browsing context given tokenizedFeatures. [CSSOMVIEW]
  200. // NOTE: This is implemented in choose_a_navigable when creating the top level traversable.
  201. // NOTE: See spec issue above
  202. // 3. Let urlRecord be the URL record about:blank.
  203. // 4. If url is not the empty string, then set urlRecord to the result of encoding-parsing a URL given url, relative to the entry settings object.
  204. // 5. If urlRecord is failure, then throw a "SyntaxError" DOMException.
  205. // 6. If urlRecord matches about:blank, then perform the URL and history update steps given targetNavigable's active document and urlRecord.
  206. if (url_matches_about_blank(url_record)) {
  207. // AD-HOC: Mark the initial about:blank for the new window as load complete
  208. // FIXME: We do this other places too when creating a new about:blank document. Perhaps it's worth a spec issue?
  209. HTML::HTMLParser::the_end(*target_navigable->active_document());
  210. perform_url_and_history_update_steps(*target_navigable->active_document(), url_record);
  211. }
  212. // 7. Otherwise, navigate targetNavigable to urlRecord using sourceDocument, with referrerPolicy set to referrerPolicy and exceptionsEnabled set to true.
  213. else {
  214. TRY(target_navigable->navigate({ .url = url_record, .source_document = source_document, .exceptions_enabled = true, .referrer_policy = referrer_policy }));
  215. }
  216. }
  217. // 13. Otherwise:
  218. else {
  219. // 1. If url is not the empty string, then:
  220. if (!url.is_empty()) {
  221. // NOTE: See spec issue above
  222. // 1. Let urlRecord be the result of encoding-parsing a URL url, relative to the entry settings object.
  223. // 2. If urlRecord is failure, then throw a "SyntaxError" DOMException.
  224. // 3. Navigate targetNavigable to urlRecord using sourceDocument, with referrerPolicy set to referrerPolicy and exceptionsEnabled set to true.
  225. TRY(target_navigable->navigate({ .url = url_record, .source_document = source_document, .exceptions_enabled = true, .referrer_policy = referrer_policy }));
  226. }
  227. // 2. If noopener is false, then set targetNavigable's active browsing context's opener browsing context to sourceDocument's browsing context.
  228. if (no_opener == TokenizedFeature::NoOpener::No)
  229. target_navigable->active_browsing_context()->set_opener_browsing_context(source_document.browsing_context());
  230. }
  231. return OpenedWindow { target_navigable, no_opener, window_type };
  232. }
  233. bool Window::dispatch_event(DOM::Event& event)
  234. {
  235. return DOM::EventDispatcher::dispatch(*this, event, true);
  236. }
  237. Page& Window::page()
  238. {
  239. return associated_document().page();
  240. }
  241. Page const& Window::page() const
  242. {
  243. return associated_document().page();
  244. }
  245. Optional<CSS::MediaFeatureValue> Window::query_media_feature(CSS::MediaFeatureID media_feature) const
  246. {
  247. // FIXME: Many of these should be dependent on the hardware
  248. // https://www.w3.org/TR/mediaqueries-5/#media-descriptor-table
  249. switch (media_feature) {
  250. case CSS::MediaFeatureID::AnyHover:
  251. return CSS::MediaFeatureValue(CSS::Keyword::Hover);
  252. case CSS::MediaFeatureID::AnyPointer:
  253. return CSS::MediaFeatureValue(CSS::Keyword::Fine);
  254. case CSS::MediaFeatureID::AspectRatio:
  255. return CSS::MediaFeatureValue(CSS::Ratio(inner_width(), inner_height()));
  256. case CSS::MediaFeatureID::Color:
  257. return CSS::MediaFeatureValue(8);
  258. case CSS::MediaFeatureID::ColorGamut:
  259. return CSS::MediaFeatureValue(CSS::Keyword::Srgb);
  260. case CSS::MediaFeatureID::ColorIndex:
  261. return CSS::MediaFeatureValue(0);
  262. // FIXME: device-aspect-ratio
  263. case CSS::MediaFeatureID::DeviceHeight:
  264. return CSS::MediaFeatureValue(CSS::Length::make_px(page().web_exposed_screen_area().height()));
  265. case CSS::MediaFeatureID::DeviceWidth:
  266. return CSS::MediaFeatureValue(CSS::Length::make_px(page().web_exposed_screen_area().width()));
  267. case CSS::MediaFeatureID::DisplayMode:
  268. // FIXME: Detect if window is fullscreen
  269. return CSS::MediaFeatureValue(CSS::Keyword::Browser);
  270. case CSS::MediaFeatureID::DynamicRange:
  271. return CSS::MediaFeatureValue(CSS::Keyword::Standard);
  272. case CSS::MediaFeatureID::EnvironmentBlending:
  273. return CSS::MediaFeatureValue(CSS::Keyword::Opaque);
  274. case CSS::MediaFeatureID::ForcedColors:
  275. return CSS::MediaFeatureValue(CSS::Keyword::None);
  276. case CSS::MediaFeatureID::Grid:
  277. return CSS::MediaFeatureValue(0);
  278. case CSS::MediaFeatureID::Height:
  279. return CSS::MediaFeatureValue(CSS::Length::make_px(inner_height()));
  280. case CSS::MediaFeatureID::HorizontalViewportSegments:
  281. return CSS::MediaFeatureValue(1);
  282. case CSS::MediaFeatureID::Hover:
  283. return CSS::MediaFeatureValue(CSS::Keyword::Hover);
  284. case CSS::MediaFeatureID::InvertedColors:
  285. return CSS::MediaFeatureValue(CSS::Keyword::None);
  286. case CSS::MediaFeatureID::Monochrome:
  287. return CSS::MediaFeatureValue(0);
  288. case CSS::MediaFeatureID::NavControls:
  289. return CSS::MediaFeatureValue(CSS::Keyword::Back);
  290. case CSS::MediaFeatureID::Orientation:
  291. return CSS::MediaFeatureValue(inner_height() >= inner_width() ? CSS::Keyword::Portrait : CSS::Keyword::Landscape);
  292. case CSS::MediaFeatureID::OverflowBlock:
  293. return CSS::MediaFeatureValue(CSS::Keyword::Scroll);
  294. case CSS::MediaFeatureID::OverflowInline:
  295. return CSS::MediaFeatureValue(CSS::Keyword::Scroll);
  296. case CSS::MediaFeatureID::Pointer:
  297. return CSS::MediaFeatureValue(CSS::Keyword::Fine);
  298. case CSS::MediaFeatureID::PrefersColorScheme: {
  299. switch (page().preferred_color_scheme()) {
  300. case CSS::PreferredColorScheme::Light:
  301. return CSS::MediaFeatureValue(CSS::Keyword::Light);
  302. case CSS::PreferredColorScheme::Dark:
  303. return CSS::MediaFeatureValue(CSS::Keyword::Dark);
  304. case CSS::PreferredColorScheme::Auto:
  305. default:
  306. return CSS::MediaFeatureValue(page().palette().is_dark() ? CSS::Keyword::Dark : CSS::Keyword::Light);
  307. }
  308. }
  309. case CSS::MediaFeatureID::PrefersContrast:
  310. switch (page().preferred_contrast()) {
  311. case CSS::PreferredContrast::Less:
  312. return CSS::MediaFeatureValue(CSS::Keyword::Less);
  313. case CSS::PreferredContrast::More:
  314. return CSS::MediaFeatureValue(CSS::Keyword::More);
  315. case CSS::PreferredContrast::NoPreference:
  316. return CSS::MediaFeatureValue(CSS::Keyword::NoPreference);
  317. case CSS::PreferredContrast::Auto:
  318. default:
  319. // FIXME: Fallback to system settings
  320. return CSS::MediaFeatureValue(CSS::Keyword::NoPreference);
  321. }
  322. case CSS::MediaFeatureID::PrefersReducedData:
  323. // FIXME: Make this a preference
  324. return CSS::MediaFeatureValue(CSS::Keyword::NoPreference);
  325. case CSS::MediaFeatureID::PrefersReducedMotion:
  326. switch (page().preferred_motion()) {
  327. case CSS::PreferredMotion::NoPreference:
  328. return CSS::MediaFeatureValue(CSS::Keyword::NoPreference);
  329. case CSS::PreferredMotion::Reduce:
  330. return CSS::MediaFeatureValue(CSS::Keyword::Reduce);
  331. case CSS::PreferredMotion::Auto:
  332. default:
  333. // FIXME: Fallback to system settings
  334. return CSS::MediaFeatureValue(CSS::Keyword::NoPreference);
  335. }
  336. case CSS::MediaFeatureID::PrefersReducedTransparency:
  337. // FIXME: Make this a preference
  338. return CSS::MediaFeatureValue(CSS::Keyword::NoPreference);
  339. case CSS::MediaFeatureID::Resolution:
  340. return CSS::MediaFeatureValue(CSS::Resolution(device_pixel_ratio(), CSS::Resolution::Type::Dppx));
  341. case CSS::MediaFeatureID::Scan:
  342. return CSS::MediaFeatureValue(CSS::Keyword::Progressive);
  343. case CSS::MediaFeatureID::Scripting:
  344. if (associated_document().is_scripting_enabled())
  345. return CSS::MediaFeatureValue(CSS::Keyword::Enabled);
  346. return CSS::MediaFeatureValue(CSS::Keyword::None);
  347. case CSS::MediaFeatureID::Update:
  348. return CSS::MediaFeatureValue(CSS::Keyword::Fast);
  349. case CSS::MediaFeatureID::VerticalViewportSegments:
  350. return CSS::MediaFeatureValue(1);
  351. case CSS::MediaFeatureID::VideoColorGamut:
  352. return CSS::MediaFeatureValue(CSS::Keyword::Srgb);
  353. case CSS::MediaFeatureID::VideoDynamicRange:
  354. return CSS::MediaFeatureValue(CSS::Keyword::Standard);
  355. case CSS::MediaFeatureID::Width:
  356. return CSS::MediaFeatureValue(CSS::Length::make_px(inner_width()));
  357. default:
  358. break;
  359. }
  360. return {};
  361. }
  362. // https://html.spec.whatwg.org/#fire-a-page-transition-event
  363. void Window::fire_a_page_transition_event(FlyString const& event_name, bool persisted)
  364. {
  365. // To fire a page transition event named eventName at a Window window with a boolean persisted,
  366. // fire an event named eventName at window, using PageTransitionEvent,
  367. // with the persisted attribute initialized to persisted,
  368. PageTransitionEventInit event_init {};
  369. event_init.persisted = persisted;
  370. auto event = PageTransitionEvent::create(associated_document().realm(), event_name, event_init);
  371. // ...the cancelable attribute initialized to true,
  372. event->set_cancelable(true);
  373. // the bubbles attribute initialized to true,
  374. event->set_bubbles(true);
  375. // isTrusted is a convenience that indicates whether an event is dispatched by the user agent (as opposed to using dispatchEvent())
  376. event->set_is_trusted(true);
  377. // and legacy target override flag set.
  378. dispatch_event(event);
  379. }
  380. // https://html.spec.whatwg.org/multipage/webstorage.html#dom-localstorage
  381. WebIDL::ExceptionOr<JS::NonnullGCPtr<Storage>> Window::local_storage()
  382. {
  383. // FIXME: Implement according to spec.
  384. static HashMap<URL::Origin, JS::Handle<Storage>> local_storage_per_origin;
  385. auto storage = local_storage_per_origin.ensure(associated_document().origin(), [this]() -> JS::Handle<Storage> {
  386. return Storage::create(realm());
  387. });
  388. return JS::NonnullGCPtr { *storage };
  389. }
  390. // https://html.spec.whatwg.org/multipage/webstorage.html#dom-sessionstorage
  391. WebIDL::ExceptionOr<JS::NonnullGCPtr<Storage>> Window::session_storage()
  392. {
  393. // FIXME: Implement according to spec.
  394. static HashMap<URL::Origin, JS::Handle<Storage>> session_storage_per_origin;
  395. auto storage = session_storage_per_origin.ensure(associated_document().origin(), [this]() -> JS::Handle<Storage> {
  396. return Storage::create(realm());
  397. });
  398. return JS::NonnullGCPtr { *storage };
  399. }
  400. // https://html.spec.whatwg.org/multipage/interaction.html#sticky-activation
  401. bool Window::has_sticky_activation() const
  402. {
  403. // When the current high resolution time given W
  404. auto current_time = HighResolutionTime::current_high_resolution_time(*this);
  405. // is greater than or equal to the last activation timestamp in W
  406. if (current_time >= m_last_activation_timestamp) {
  407. // W is said to have sticky activation.
  408. return true;
  409. }
  410. return false;
  411. }
  412. // https://html.spec.whatwg.org/multipage/interaction.html#transient-activation
  413. bool Window::has_transient_activation() const
  414. {
  415. // The transient activation duration is expected be at most a few seconds, so that the user can possibly
  416. // perceive the link between an interaction with the page and the page calling the activation-gated API.
  417. static constexpr HighResolutionTime::DOMHighResTimeStamp transient_activation_duration_ms = 5000;
  418. // When the current high resolution time given W
  419. auto current_time = HighResolutionTime::current_high_resolution_time(*this);
  420. // is greater than or equal to the last activation timestamp in W
  421. if (current_time >= m_last_activation_timestamp) {
  422. // and less than the last activation timestamp in W plus the transient activation duration
  423. if (current_time < m_last_activation_timestamp + transient_activation_duration_ms) {
  424. // then W is said to have transient activation.
  425. return true;
  426. }
  427. }
  428. return false;
  429. }
  430. // https://html.spec.whatwg.org/multipage/interaction.html#history-action-activation
  431. bool Window::has_history_action_activation() const
  432. {
  433. // When the last history-action activation timestamp of W is not equal to the last activation timestamp of W, then W is said to have history-action activation.
  434. return m_last_history_action_activation_timestamp != m_last_activation_timestamp;
  435. }
  436. // https://html.spec.whatwg.org/multipage/interaction.html#consume-history-action-user-activation
  437. void Window::consume_history_action_user_activation()
  438. {
  439. auto navigable = this->navigable();
  440. // 1. If W's navigable is null, then return.
  441. if (navigable == nullptr)
  442. return;
  443. // 2. Let top be W's navigable's top-level traversable.
  444. auto top = navigable->top_level_traversable();
  445. // 3. Let navigables be the inclusive descendant navigables of top's active document.
  446. auto navigables = top->active_document()->inclusive_descendant_navigables();
  447. // 4. Let windows be the list of Window objects constructed by taking the active window of each item in navigables.
  448. JS::MarkedVector<JS::GCPtr<Window>> windows(heap());
  449. for (auto& n : navigables)
  450. windows.append(n->active_window());
  451. // 5. For each window in windows, set window's last history-action activation timestamp to window's last activation timestamp.
  452. for (auto& window : windows)
  453. window->set_last_history_action_activation_timestamp(window->last_activation_timestamp());
  454. }
  455. // https://html.spec.whatwg.org/multipage/interaction.html#consume-user-activation
  456. void Window::consume_user_activation()
  457. {
  458. auto navigable = this->navigable();
  459. // 1. If W's navigable is null, then return.
  460. if (navigable == nullptr)
  461. return;
  462. // 2. Let top be W's navigable's top-level traversable.
  463. auto top = navigable->top_level_traversable();
  464. // 3. Let navigables be the inclusive descendant navigables of top's active document.
  465. auto navigables = top->active_document()->inclusive_descendant_navigables();
  466. // 4. Let windows be the list of Window objects constructed by taking the active window of each item in navigables.
  467. JS::MarkedVector<JS::GCPtr<Window>> windows(heap());
  468. for (auto& n : navigables)
  469. windows.append(n->active_window());
  470. // 5. For each window in windows, if window's last activation timestamp is not positive infinity, then set window's last activation timestamp to negative infinity.
  471. for (auto& window : windows) {
  472. if (window->last_activation_timestamp() != AK::Infinity<HighResolutionTime::DOMHighResTimeStamp>)
  473. window->set_last_activation_timestamp(-AK::Infinity<HighResolutionTime::DOMHighResTimeStamp>);
  474. }
  475. }
  476. // https://w3c.github.io/requestidlecallback/#start-an-idle-period-algorithm
  477. void Window::start_an_idle_period()
  478. {
  479. // 1. Optionally, if the user agent determines the idle period should be delayed, return from this algorithm.
  480. // 2. Let pending_list be window's list of idle request callbacks.
  481. auto& pending_list = m_idle_request_callbacks;
  482. // 3. Let run_list be window's list of runnable idle callbacks.
  483. auto& run_list = m_runnable_idle_callbacks;
  484. run_list.extend(pending_list);
  485. // 4. Clear pending_list.
  486. pending_list.clear();
  487. // FIXME: This might not agree with the spec, but currently we use 100% CPU if we keep queueing tasks
  488. if (run_list.is_empty())
  489. return;
  490. // 5. Queue a task on the queue associated with the idle-task task source,
  491. // which performs the steps defined in the invoke idle callbacks algorithm with window and getDeadline as parameters.
  492. queue_global_task(Task::Source::IdleTask, *this, JS::create_heap_function(heap(), [this] {
  493. invoke_idle_callbacks();
  494. }));
  495. }
  496. // https://w3c.github.io/requestidlecallback/#invoke-idle-callbacks-algorithm
  497. void Window::invoke_idle_callbacks()
  498. {
  499. auto& event_loop = main_thread_event_loop();
  500. // 1. If the user-agent believes it should end the idle period early due to newly scheduled high-priority work, return from the algorithm.
  501. // 2. Let now be the current time.
  502. auto now = HighResolutionTime::unsafe_shared_current_time();
  503. // 3. If now is less than the result of calling getDeadline and the window's list of runnable idle callbacks is not empty:
  504. if (now < event_loop.compute_deadline() && !m_runnable_idle_callbacks.is_empty()) {
  505. // 1. Pop the top callback from window's list of runnable idle callbacks.
  506. auto callback = m_runnable_idle_callbacks.take_first();
  507. // 2. Let deadlineArg be a new IdleDeadline whose [get deadline time algorithm] is getDeadline.
  508. auto deadline_arg = RequestIdleCallback::IdleDeadline::create(realm());
  509. // 3. Call callback with deadlineArg as its argument. If an uncaught runtime script error occurs, then report the exception.
  510. auto result = callback->invoke(deadline_arg);
  511. if (result.is_error())
  512. report_exception(result, realm());
  513. // 4. If window's list of runnable idle callbacks is not empty, queue a task which performs the steps
  514. // in the invoke idle callbacks algorithm with getDeadline and window as a parameters and return from this algorithm
  515. queue_global_task(Task::Source::IdleTask, *this, JS::create_heap_function(heap(), [this] {
  516. invoke_idle_callbacks();
  517. }));
  518. }
  519. }
  520. void Window::set_associated_document(DOM::Document& document)
  521. {
  522. m_associated_document = &document;
  523. }
  524. void Window::set_current_event(DOM::Event* event)
  525. {
  526. m_current_event = event;
  527. }
  528. BrowsingContext const* Window::browsing_context() const
  529. {
  530. return m_associated_document->browsing_context();
  531. }
  532. BrowsingContext* Window::browsing_context()
  533. {
  534. return m_associated_document->browsing_context();
  535. }
  536. // https://html.spec.whatwg.org/multipage/nav-history-apis.html#window-navigable
  537. JS::GCPtr<Navigable> Window::navigable() const
  538. {
  539. // A Window's navigable is the navigable whose active document is the Window's associated Document's, or null if there is no such navigable.
  540. return Navigable::navigable_with_active_document(*m_associated_document);
  541. }
  542. // https://html.spec.whatwg.org/multipage/system-state.html#pdf-viewer-plugin-objects
  543. Vector<JS::NonnullGCPtr<Plugin>> Window::pdf_viewer_plugin_objects()
  544. {
  545. // Each Window object has a PDF viewer plugin objects list. If the user agent's PDF viewer supported is false, then it is the empty list.
  546. // Otherwise, it is a list containing five Plugin objects, whose names are, respectively:
  547. // 0. "PDF Viewer"
  548. // 1. "Chrome PDF Viewer"
  549. // 2. "Chromium PDF Viewer"
  550. // 3. "Microsoft Edge PDF Viewer"
  551. // 4. "WebKit built-in PDF"
  552. // The values of the above list form the PDF viewer plugin names list. https://html.spec.whatwg.org/multipage/system-state.html#pdf-viewer-plugin-names
  553. if (!page().pdf_viewer_supported())
  554. return {};
  555. if (m_pdf_viewer_plugin_objects.is_empty()) {
  556. // FIXME: Propagate errors.
  557. m_pdf_viewer_plugin_objects.append(realm().heap().allocate<Plugin>(realm(), realm(), "PDF Viewer"_string));
  558. m_pdf_viewer_plugin_objects.append(realm().heap().allocate<Plugin>(realm(), realm(), "Chrome PDF Viewer"_string));
  559. m_pdf_viewer_plugin_objects.append(realm().heap().allocate<Plugin>(realm(), realm(), "Chromium PDF Viewer"_string));
  560. m_pdf_viewer_plugin_objects.append(realm().heap().allocate<Plugin>(realm(), realm(), "Microsoft Edge PDF Viewer"_string));
  561. m_pdf_viewer_plugin_objects.append(realm().heap().allocate<Plugin>(realm(), realm(), "WebKit built-in PDF"_string));
  562. }
  563. return m_pdf_viewer_plugin_objects;
  564. }
  565. // https://html.spec.whatwg.org/multipage/system-state.html#pdf-viewer-mime-type-objects
  566. Vector<JS::NonnullGCPtr<MimeType>> Window::pdf_viewer_mime_type_objects()
  567. {
  568. // Each Window object has a PDF viewer mime type objects list. If the user agent's PDF viewer supported is false, then it is the empty list.
  569. // Otherwise, it is a list containing two MimeType objects, whose types are, respectively:
  570. // 0. "application/pdf"
  571. // 1. "text/pdf"
  572. // The values of the above list form the PDF viewer mime types list. https://html.spec.whatwg.org/multipage/system-state.html#pdf-viewer-mime-types
  573. if (!page().pdf_viewer_supported())
  574. return {};
  575. if (m_pdf_viewer_mime_type_objects.is_empty()) {
  576. m_pdf_viewer_mime_type_objects.append(realm().heap().allocate<MimeType>(realm(), realm(), "application/pdf"_string));
  577. m_pdf_viewer_mime_type_objects.append(realm().heap().allocate<MimeType>(realm(), realm(), "text/pdf"_string));
  578. }
  579. return m_pdf_viewer_mime_type_objects;
  580. }
  581. // https://streams.spec.whatwg.org/#count-queuing-strategy-size-function
  582. JS::NonnullGCPtr<WebIDL::CallbackType> Window::count_queuing_strategy_size_function()
  583. {
  584. auto& realm = this->realm();
  585. if (!m_count_queuing_strategy_size_function) {
  586. // 1. Let steps be the following steps:
  587. auto steps = [](auto const&) {
  588. // 1. Return 1.
  589. return 1.0;
  590. };
  591. // 2. Let F be ! CreateBuiltinFunction(steps, 0, "size", « », globalObject’s relevant Realm).
  592. auto function = JS::NativeFunction::create(realm, move(steps), 0, "size", &realm);
  593. // 3. Set globalObject’s count queuing strategy size function to a Function that represents a reference to F, with callback context equal to globalObject’s relevant settings object.
  594. m_count_queuing_strategy_size_function = heap().allocate<WebIDL::CallbackType>(realm, *function, relevant_settings_object(*this));
  595. }
  596. return JS::NonnullGCPtr { *m_count_queuing_strategy_size_function };
  597. }
  598. // https://streams.spec.whatwg.org/#byte-length-queuing-strategy-size-function
  599. JS::NonnullGCPtr<WebIDL::CallbackType> Window::byte_length_queuing_strategy_size_function()
  600. {
  601. auto& realm = this->realm();
  602. if (!m_byte_length_queuing_strategy_size_function) {
  603. // 1. Let steps be the following steps, given chunk:
  604. auto steps = [](JS::VM& vm) {
  605. auto chunk = vm.argument(0);
  606. // 1. Return ? GetV(chunk, "byteLength").
  607. return chunk.get(vm, vm.names.byteLength);
  608. };
  609. // 2. Let F be ! CreateBuiltinFunction(steps, 1, "size", « », globalObject’s relevant Realm).
  610. auto function = JS::NativeFunction::create(realm, move(steps), 1, "size", &realm);
  611. // 3. Set globalObject’s byte length queuing strategy size function to a Function that represents a reference to F, with callback context equal to globalObject’s relevant settings object.
  612. m_byte_length_queuing_strategy_size_function = heap().allocate<WebIDL::CallbackType>(realm, *function, relevant_settings_object(*this));
  613. }
  614. return JS::NonnullGCPtr { *m_byte_length_queuing_strategy_size_function };
  615. }
  616. static bool s_inspector_object_exposed = false;
  617. static bool s_internals_object_exposed = false;
  618. void Window::set_inspector_object_exposed(bool exposed)
  619. {
  620. s_inspector_object_exposed = exposed;
  621. }
  622. void Window::set_internals_object_exposed(bool exposed)
  623. {
  624. s_internals_object_exposed = exposed;
  625. }
  626. WebIDL::ExceptionOr<void> Window::initialize_web_interfaces(Badge<WindowEnvironmentSettingsObject>)
  627. {
  628. auto& realm = this->realm();
  629. add_window_exposed_interfaces(*this);
  630. WEB_SET_PROTOTYPE_FOR_INTERFACE(Window);
  631. Bindings::WindowGlobalMixin::initialize(realm, *this);
  632. WindowOrWorkerGlobalScopeMixin::initialize(realm);
  633. if (s_inspector_object_exposed)
  634. define_direct_property("inspector", heap().allocate<Internals::Inspector>(realm, realm), JS::default_attributes);
  635. if (s_internals_object_exposed)
  636. define_direct_property("internals", heap().allocate<Internals::Internals>(realm, realm), JS::default_attributes);
  637. return {};
  638. }
  639. // https://webidl.spec.whatwg.org/#platform-object-setprototypeof
  640. JS::ThrowCompletionOr<bool> Window::internal_set_prototype_of(JS::Object* prototype)
  641. {
  642. // 1. Return ? SetImmutablePrototype(O, V).
  643. return set_immutable_prototype(prototype);
  644. }
  645. // https://html.spec.whatwg.org/multipage/window-object.html#dom-window
  646. JS::NonnullGCPtr<WindowProxy> Window::window() const
  647. {
  648. // The window, frames, and self getter steps are to return this's relevant realm.[[GlobalEnv]].[[GlobalThisValue]].
  649. return verify_cast<WindowProxy>(relevant_realm(*this).global_environment().global_this_value());
  650. }
  651. // https://html.spec.whatwg.org/multipage/window-object.html#dom-self
  652. JS::NonnullGCPtr<WindowProxy> Window::self() const
  653. {
  654. // The window, frames, and self getter steps are to return this's relevant realm.[[GlobalEnv]].[[GlobalThisValue]].
  655. return verify_cast<WindowProxy>(relevant_realm(*this).global_environment().global_this_value());
  656. }
  657. // https://html.spec.whatwg.org/multipage/nav-history-apis.html#dom-document-2
  658. JS::NonnullGCPtr<DOM::Document const> Window::document() const
  659. {
  660. // The document getter steps are to return this's associated Document.
  661. return associated_document();
  662. }
  663. // https://html.spec.whatwg.org/multipage/nav-history-apis.html#dom-name
  664. String Window::name() const
  665. {
  666. // 1. If this's navigable is null, then return the empty string.
  667. if (!navigable())
  668. return String {};
  669. // 2. Return this's navigable's target name.
  670. return navigable()->target_name();
  671. }
  672. // https://html.spec.whatwg.org/multipage/nav-history-apis.html#apis-for-creating-and-navigating-browsing-contexts-by-name:dom-name
  673. void Window::set_name(String const& name)
  674. {
  675. // 1. If this's navigable is null, then return.
  676. if (!navigable())
  677. return;
  678. // 2. Set this's navigable's active session history entry's document state's navigable target name to the given value.
  679. navigable()->active_session_history_entry()->document_state()->set_navigable_target_name(name);
  680. }
  681. // https://html.spec.whatwg.org/multipage/nav-history-apis.html#dom-window-status
  682. String Window::status() const
  683. {
  684. // the status attribute on the Window object must, on getting, return the last string it was set to
  685. return m_status;
  686. }
  687. // https://html.spec.whatwg.org/multipage/nav-history-apis.html#dom-window-close
  688. void Window::close()
  689. {
  690. // 1. Let thisTraversable be this's navigable.
  691. auto traversable = navigable();
  692. // 2. If thisTraversable is not a top-level traversable, then return.
  693. if (!traversable || !traversable->is_top_level_traversable())
  694. return;
  695. // 3. If thisTraversable's is closing is true, then return.
  696. if (traversable->is_closing())
  697. return;
  698. // 4. Let browsingContext be thisTraversable's active browsing context.
  699. auto browsing_context = traversable->active_browsing_context();
  700. // 5. Let sourceSnapshotParams be the result of snapshotting source snapshot params given thisTraversable's active document.
  701. auto source_snapshot_params = traversable->active_document()->snapshot_source_snapshot_params();
  702. auto& incumbent_global_object = verify_cast<HTML::Window>(HTML::incumbent_global_object());
  703. // 6. If all the following are true:
  704. if (
  705. // thisTraversable is script-closable;
  706. traversable->is_script_closable()
  707. // the incumbent global object's browsing context is familiar with browsingContext; and
  708. && incumbent_global_object.browsing_context()->is_familiar_with(*browsing_context)
  709. // the incumbent global object's navigable is allowed by sandboxing to navigate thisTraversable, given sourceSnapshotParams,
  710. && incumbent_global_object.navigable()->allowed_by_sandboxing_to_navigate(*traversable, source_snapshot_params))
  711. // then:
  712. {
  713. // 1. Set thisTraversable's is closing to true.
  714. traversable->set_closing(true);
  715. // 2. Queue a task on the DOM manipulation task source to definitely close thisTraversable.
  716. HTML::queue_global_task(HTML::Task::Source::DOMManipulation, incumbent_global_object, JS::create_heap_function(heap(), [traversable] {
  717. verify_cast<TraversableNavigable>(*traversable).definitely_close_top_level_traversable();
  718. }));
  719. }
  720. }
  721. // https://html.spec.whatwg.org/multipage/nav-history-apis.html#dom-window-closed
  722. bool Window::closed() const
  723. {
  724. // The closed getter steps are to return true if this's browsing context is null or its is closing is true;
  725. // otherwise false.
  726. if (!browsing_context())
  727. return true;
  728. // FIXME: The spec seems a bit out of date. The `is closing` flag is on the navigable, not the browsing context.
  729. if (auto navigable = this->navigable(); !navigable || navigable->is_closing())
  730. return true;
  731. return false;
  732. }
  733. // https://html.spec.whatwg.org/multipage/nav-history-apis.html#dom-window-status
  734. void Window::set_status(String const& status)
  735. {
  736. // on setting, must set itself to the new value.
  737. m_status = status;
  738. }
  739. // https://html.spec.whatwg.org/multipage/nav-history-apis.html#dom-location
  740. JS::NonnullGCPtr<Location> Window::location()
  741. {
  742. auto& realm = this->realm();
  743. // The Window object's location getter steps are to return this's Location object.
  744. if (!m_location)
  745. m_location = heap().allocate<Location>(realm, realm);
  746. return JS::NonnullGCPtr { *m_location };
  747. }
  748. // https://html.spec.whatwg.org/multipage/nav-history-apis.html#dom-history
  749. JS::NonnullGCPtr<History> Window::history() const
  750. {
  751. // The history getter steps are to return this's associated Document's history object.
  752. return associated_document().history();
  753. }
  754. // https://html.spec.whatwg.org/multipage/interaction.html#dom-window-focus
  755. void Window::focus()
  756. {
  757. // 1. Let current be this Window object's navigable.
  758. auto current = navigable();
  759. // 2. If current is null, then return.
  760. if (!current)
  761. return;
  762. // 3. Run the focusing steps with current.
  763. // FIXME: We should pass in the browsing context itself instead of the active document, however the focusing steps don't currently accept browsing contexts.
  764. // Passing in a browsing context always makes it resolve to its active document for focus, so this is fine for now.
  765. run_focusing_steps(current->active_document());
  766. // FIXME: 4. If current is a top-level traversable, user agents are encouraged to trigger some sort of notification to
  767. // indicate to the user that the page is attempting to gain focus.
  768. }
  769. // https://html.spec.whatwg.org/multipage/interaction.html#dom-window-blur
  770. void Window::blur()
  771. {
  772. // The blur() method steps are to do nothing.
  773. }
  774. // https://html.spec.whatwg.org/multipage/window-object.html#dom-frames
  775. JS::NonnullGCPtr<WindowProxy> Window::frames() const
  776. {
  777. // The window, frames, and self getter steps are to return this's relevant realm.[[GlobalEnv]].[[GlobalThisValue]].
  778. return verify_cast<WindowProxy>(relevant_realm(*this).global_environment().global_this_value());
  779. }
  780. // https://html.spec.whatwg.org/multipage/window-object.html#dom-length
  781. u32 Window::length()
  782. {
  783. // The length getter steps are to return this's associated Document's document-tree child navigables's size.
  784. return associated_document().document_tree_child_navigables().size();
  785. }
  786. // https://html.spec.whatwg.org/multipage/nav-history-apis.html#dom-top
  787. JS::GCPtr<WindowProxy const> Window::top() const
  788. {
  789. // 1. If this's navigable is null, then return null.
  790. auto navigable = this->navigable();
  791. if (!navigable)
  792. return {};
  793. // 2. Return this's navigable's top-level traversable's active WindowProxy.
  794. return navigable->top_level_traversable()->active_window_proxy();
  795. }
  796. // https://html.spec.whatwg.org/multipage/nav-history-apis.html#dom-opener
  797. JS::GCPtr<WindowProxy const> Window::opener() const
  798. {
  799. // 1. Let current be this's browsing context.
  800. auto const* current = browsing_context();
  801. // 2. If current is null, then return null.
  802. if (!current)
  803. return {};
  804. // 3. If current's opener browsing context is null, then return null.
  805. auto opener_browsing_context = current->opener_browsing_context();
  806. if (!opener_browsing_context)
  807. return {};
  808. // 4. Return current's opener browsing context's WindowProxy object.
  809. return opener_browsing_context->window_proxy();
  810. }
  811. WebIDL::ExceptionOr<void> Window::set_opener(JS::Value value)
  812. {
  813. // 1. If the given value is null and this's browsing context is non-null, then set this's browsing context's opener browsing context to null.
  814. auto* browsing_context = this->browsing_context();
  815. if (value.is_null() && browsing_context)
  816. browsing_context->set_opener_browsing_context(nullptr);
  817. // 2. If the given value is non-null, then perform ? DefinePropertyOrThrow(this, "opener", { [[Value]]: the given value, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true }).
  818. if (!value.is_null()) {
  819. static JS::PropertyKey opener_property_key { "opener", JS::PropertyKey::StringMayBeNumber::No };
  820. TRY(define_property_or_throw(opener_property_key, { .value = value, .writable = true, .enumerable = true, .configurable = true }));
  821. }
  822. return {};
  823. }
  824. // https://html.spec.whatwg.org/multipage/nav-history-apis.html#dom-parent
  825. JS::GCPtr<WindowProxy const> Window::parent() const
  826. {
  827. // 1. Let navigable be this's navigable.
  828. auto navigable = this->navigable();
  829. // 2. If navigable is null, then return null.
  830. if (!navigable)
  831. return {};
  832. // 3. If navigable's parent is not null, then set navigable to navigable's parent.
  833. if (auto parent = navigable->parent())
  834. navigable = parent;
  835. // 4. Return navigable's active WindowProxy.
  836. return navigable->active_window_proxy();
  837. }
  838. // https://html.spec.whatwg.org/multipage/nav-history-apis.html#dom-frameelement
  839. JS::GCPtr<DOM::Element const> Window::frame_element() const
  840. {
  841. // 1. Let current be this's node navigable.
  842. auto current = navigable();
  843. // 2. If current is null, then return null.
  844. if (!current)
  845. return {};
  846. // 3. Let container be current's container.
  847. auto container = current->container();
  848. // 4. If container is null, then return null.
  849. if (!container)
  850. return {};
  851. // 5. If container's node document's origin is not same origin-domain with the current settings object's origin, then return null.
  852. if (!container->document().origin().is_same_origin_domain(current_settings_object().origin()))
  853. return {};
  854. // 6. Return container.
  855. return container;
  856. }
  857. // https://html.spec.whatwg.org/multipage/nav-history-apis.html#dom-open
  858. WebIDL::ExceptionOr<JS::GCPtr<WindowProxy>> Window::open(Optional<String> const& url, Optional<String> const& target, Optional<String> const& features)
  859. {
  860. // The open(url, target, features) method steps are to run the window open steps with url, target, and features.
  861. return window_open_steps(*url, *target, *features);
  862. }
  863. // https://html.spec.whatwg.org/multipage/system-state.html#dom-navigator
  864. JS::NonnullGCPtr<Navigator> Window::navigator()
  865. {
  866. auto& realm = this->realm();
  867. // The navigator and clientInformation getter steps are to return this's associated Navigator.
  868. if (!m_navigator)
  869. m_navigator = heap().allocate<Navigator>(realm, realm);
  870. return JS::NonnullGCPtr { *m_navigator };
  871. }
  872. // https://html.spec.whatwg.org/multipage/interaction.html#close-watcher-manager
  873. JS::NonnullGCPtr<CloseWatcherManager> Window::close_watcher_manager()
  874. {
  875. auto& realm = this->realm();
  876. if (!m_close_watcher_manager)
  877. m_close_watcher_manager = heap().allocate<CloseWatcherManager>(realm, realm);
  878. return JS::NonnullGCPtr { *m_close_watcher_manager };
  879. }
  880. // https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#dom-alert
  881. void Window::alert(String const& message)
  882. {
  883. // https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#simple-dialogs
  884. // Note: This method is defined using two overloads, instead of using an optional argument,
  885. // for historical reasons. The practical impact of this is that alert(undefined) is
  886. // treated as alert("undefined"), but alert() is treated as alert("").
  887. // FIXME: Make this fully spec compliant.
  888. page().did_request_alert(message);
  889. }
  890. // https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#dom-confirm
  891. bool Window::confirm(Optional<String> const& message)
  892. {
  893. // FIXME: Make this fully spec compliant.
  894. // NOTE: `message` has an IDL-provided default value and is never empty.
  895. return page().did_request_confirm(*message);
  896. }
  897. // https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#dom-prompt
  898. Optional<String> Window::prompt(Optional<String> const& message, Optional<String> const& default_)
  899. {
  900. // FIXME: Make this fully spec compliant.
  901. return page().did_request_prompt(*message, *default_);
  902. }
  903. // https://html.spec.whatwg.org/multipage/web-messaging.html#window-post-message-steps
  904. WebIDL::ExceptionOr<void> Window::window_post_message_steps(JS::Value message, WindowPostMessageOptions const& options)
  905. {
  906. // 1. Let targetRealm be targetWindow's realm.
  907. auto& target_realm = this->realm();
  908. // 2. Let incumbentSettings be the incumbent settings object.
  909. auto& incumbent_settings = incumbent_settings_object();
  910. // 3. Let targetOrigin be options["targetOrigin"].
  911. Variant<String, URL::Origin> target_origin = options.target_origin;
  912. // 4. If targetOrigin is a single U+002F SOLIDUS character (/), then set targetOrigin to incumbentSettings's origin.
  913. if (options.target_origin == "/"sv) {
  914. target_origin = incumbent_settings.origin();
  915. }
  916. // 5. Otherwise, if targetOrigin is not a single U+002A ASTERISK character (*), then:
  917. else if (options.target_origin != "*"sv) {
  918. // 1. Let parsedURL be the result of running the URL parser on targetOrigin.
  919. auto parsed_url = DOMURL::parse(options.target_origin);
  920. // 2. If parsedURL is failure, then throw a "SyntaxError" DOMException.
  921. if (!parsed_url.is_valid())
  922. return WebIDL::SyntaxError::create(target_realm, MUST(String::formatted("Invalid URL for targetOrigin: '{}'", options.target_origin)));
  923. // 3. Set targetOrigin to parsedURL's origin.
  924. target_origin = parsed_url.origin();
  925. }
  926. // 6. Let transfer be options["transfer"].
  927. auto& transfer = options.transfer;
  928. // 7. Let serializeWithTransferResult be StructuredSerializeWithTransfer(message, transfer). Rethrow any exceptions.
  929. auto serialize_with_transfer_result = TRY(structured_serialize_with_transfer(target_realm.vm(), message, transfer));
  930. // 8. Queue a global task on the posted message task source given targetWindow to run the following steps:
  931. queue_global_task(Task::Source::PostedMessage, *this, JS::create_heap_function(heap(), [this, serialize_with_transfer_result = move(serialize_with_transfer_result), target_origin = move(target_origin), &incumbent_settings, &target_realm]() mutable {
  932. // 1. If the targetOrigin argument is not a single literal U+002A ASTERISK character (*) and targetWindow's
  933. // associated Document's origin is not same origin with targetOrigin, then return.
  934. // NOTE: Due to step 4 and 5 above, the only time it's not '*' is if target_origin contains an Origin.
  935. if (!target_origin.has<String>()) {
  936. auto const& actual_target_origin = target_origin.get<URL::Origin>();
  937. if (!document()->origin().is_same_origin(actual_target_origin))
  938. return;
  939. }
  940. // 2. Let origin be the serialization of incumbentSettings's origin.
  941. auto origin = incumbent_settings.origin().serialize();
  942. // 3. Let source be the WindowProxy object corresponding to incumbentSettings's global object (a Window object).
  943. auto& source = verify_cast<WindowProxy>(incumbent_settings.realm().global_environment().global_this_value());
  944. // 4. Let deserializeRecord be StructuredDeserializeWithTransfer(serializeWithTransferResult, targetRealm).
  945. auto& settings_object = Bindings::host_defined_environment_settings_object(target_realm);
  946. auto temporary_execution_context = TemporaryExecutionContext { settings_object };
  947. auto deserialize_record_or_error = structured_deserialize_with_transfer(vm(), serialize_with_transfer_result);
  948. // If this throws an exception, catch it, fire an event named messageerror at targetWindow, using MessageEvent,
  949. // with the origin attribute initialized to origin and the source attribute initialized to source, and then return.
  950. if (deserialize_record_or_error.is_exception()) {
  951. MessageEventInit message_event_init {};
  952. message_event_init.origin = MUST(String::from_byte_string(origin));
  953. message_event_init.source = JS::make_handle(source);
  954. auto message_error_event = MessageEvent::create(target_realm, EventNames::messageerror, message_event_init);
  955. dispatch_event(message_error_event);
  956. return;
  957. }
  958. // 5. Let messageClone be deserializeRecord.[[Deserialized]].
  959. auto deserialize_record = deserialize_record_or_error.release_value();
  960. auto message_clone = deserialize_record.deserialized;
  961. // 6. Let newPorts be a new frozen array consisting of all MessagePort objects in deserializeRecord.[[TransferredValues]],
  962. // if any, maintaining their relative order.
  963. // FIXME: Use a FrozenArray
  964. Vector<JS::Handle<MessagePort>> new_ports;
  965. for (auto const& object : deserialize_record.transferred_values) {
  966. if (is<HTML::MessagePort>(*object)) {
  967. new_ports.append(verify_cast<MessagePort>(*object));
  968. }
  969. }
  970. // 7. Fire an event named message at targetWindow, using MessageEvent, with the origin attribute initialized to origin,
  971. // the source attribute initialized to source, the data attribute initialized to messageClone, and the ports attribute
  972. // initialized to newPorts.
  973. MessageEventInit message_event_init {};
  974. message_event_init.origin = MUST(String::from_byte_string(origin));
  975. message_event_init.source = JS::make_handle(source);
  976. message_event_init.data = message_clone;
  977. message_event_init.ports = move(new_ports);
  978. auto message_event = MessageEvent::create(target_realm, EventNames::message, message_event_init);
  979. dispatch_event(message_event);
  980. }));
  981. return {};
  982. }
  983. // https://html.spec.whatwg.org/multipage/web-messaging.html#dom-window-postmessage-options
  984. WebIDL::ExceptionOr<void> Window::post_message(JS::Value message, WindowPostMessageOptions const& options)
  985. {
  986. // The Window interface's postMessage(message, options) method steps are to run the window post message steps given
  987. // this, message, and options.
  988. return window_post_message_steps(message, options);
  989. }
  990. // https://html.spec.whatwg.org/multipage/web-messaging.html#dom-window-postmessage
  991. WebIDL::ExceptionOr<void> Window::post_message(JS::Value message, String const& target_origin, Vector<JS::Handle<JS::Object>> const& transfer)
  992. {
  993. // The Window interface's postMessage(message, targetOrigin, transfer) method steps are to run the window post message
  994. // steps given this, message, and «[ "targetOrigin" → targetOrigin, "transfer" → transfer ]».
  995. return window_post_message_steps(message, WindowPostMessageOptions { { .transfer = transfer }, target_origin });
  996. }
  997. // https://dom.spec.whatwg.org/#dom-window-event
  998. Variant<JS::Handle<DOM::Event>, JS::Value> Window::event() const
  999. {
  1000. // The event getter steps are to return this’s current event.
  1001. if (auto* current_event = this->current_event())
  1002. return make_handle(const_cast<DOM::Event&>(*current_event));
  1003. return JS::js_undefined();
  1004. }
  1005. // https://w3c.github.io/csswg-drafts/cssom/#dom-window-getcomputedstyle
  1006. JS::NonnullGCPtr<CSS::CSSStyleDeclaration> Window::get_computed_style(DOM::Element& element, Optional<String> const& pseudo_element) const
  1007. {
  1008. // 1. Let doc be elt’s node document.
  1009. // 2. Let obj be elt.
  1010. Optional<CSS::Selector::PseudoElement::Type> obj_pseudo;
  1011. // 3. If pseudoElt is provided, is not the empty string, and starts with a colon, then:
  1012. if (pseudo_element.has_value() && pseudo_element.value().starts_with(':')) {
  1013. // 1. Parse pseudoElt as a <pseudo-element-selector>, and let type be the result.
  1014. auto type = parse_pseudo_element_selector(CSS::Parser::ParsingContext(associated_document()), pseudo_element.value());
  1015. // 2. If type is failure, or is an ::slotted() or ::part() pseudo-element, let obj be null.
  1016. // FIXME: We can't pass a null element to ResolvedCSSStyleDeclaration
  1017. if (!type.has_value()) {
  1018. }
  1019. // 3. Otherwise let obj be the given pseudo-element of elt.
  1020. else {
  1021. // TODO: Keep the function arguments of the pseudo-element if there are any.
  1022. obj_pseudo = type.value().type();
  1023. }
  1024. }
  1025. // AD-HOC: Just return a ResolvedCSSStyleDeclaration because that's what we have for now.
  1026. // FIXME: Implement CSSStyleProperties, and then follow the rest of these steps instead.
  1027. return heap().allocate<CSS::ResolvedCSSStyleDeclaration>(realm(), element, obj_pseudo);
  1028. // 4. Let decls be an empty list of CSS declarations.
  1029. // 5. If obj is not null, and elt is connected, part of the flat tree, and its shadow-including root
  1030. // has a browsing context which either doesn’t have a browsing context container, or whose browsing
  1031. // context container is being rendered, set decls to a list of all longhand properties that are
  1032. // supported CSS properties, in lexicographical order, with the value being the resolved value
  1033. // computed for obj using the style rules associated with doc. Additionally, append to decls all
  1034. // the custom properties whose computed value for obj is not the guaranteed-invalid value.
  1035. // 6. Return a live CSSStyleProperties object with the following properties:
  1036. // computed flag
  1037. // Set.
  1038. // readonly flag
  1039. // Set.
  1040. // declarations
  1041. // decls.
  1042. // parent CSS rule
  1043. // Null.
  1044. // owner node
  1045. // obj.
  1046. }
  1047. // https://w3c.github.io/csswg-drafts/cssom-view/#dom-window-matchmedia
  1048. WebIDL::ExceptionOr<JS::NonnullGCPtr<CSS::MediaQueryList>> Window::match_media(String const& query)
  1049. {
  1050. // 1. Let parsed media query list be the result of parsing query.
  1051. auto parsed_media_query_list = parse_media_query_list(CSS::Parser::ParsingContext(associated_document()), query);
  1052. // 2. Return a new MediaQueryList object, with this's associated Document as the document, with parsed media query list as its associated media query list.
  1053. auto media_query_list = heap().allocate<CSS::MediaQueryList>(realm(), associated_document(), move(parsed_media_query_list));
  1054. associated_document().add_media_query_list(media_query_list);
  1055. return media_query_list;
  1056. }
  1057. // https://w3c.github.io/csswg-drafts/cssom-view/#dom-window-screen
  1058. JS::NonnullGCPtr<CSS::Screen> Window::screen()
  1059. {
  1060. // The screen attribute must return the Screen object associated with the Window object.
  1061. if (!m_screen)
  1062. m_screen = heap().allocate<CSS::Screen>(realm(), *this);
  1063. return JS::NonnullGCPtr { *m_screen };
  1064. }
  1065. JS::GCPtr<CSS::VisualViewport> Window::visual_viewport()
  1066. {
  1067. // If the associated document is fully active, the visualViewport attribute must return
  1068. // the VisualViewport object associated with the Window object’s associated document.
  1069. if (associated_document().is_fully_active())
  1070. return associated_document().visual_viewport();
  1071. // Otherwise, it must return null.
  1072. return nullptr;
  1073. }
  1074. // https://w3c.github.io/csswg-drafts/cssom-view/#dom-window-innerwidth
  1075. i32 Window::inner_width() const
  1076. {
  1077. // The innerWidth attribute must return the viewport width including the size of a rendered scroll bar (if any),
  1078. // or zero if there is no viewport.
  1079. if (auto const navigable = associated_document().navigable())
  1080. return navigable->viewport_rect().width().to_int();
  1081. return 0;
  1082. }
  1083. // https://w3c.github.io/csswg-drafts/cssom-view/#dom-window-innerheight
  1084. i32 Window::inner_height() const
  1085. {
  1086. // The innerHeight attribute must return the viewport height including the size of a rendered scroll bar (if any),
  1087. // or zero if there is no viewport.
  1088. if (auto const navigable = associated_document().navigable())
  1089. return navigable->viewport_rect().height().to_int();
  1090. return 0;
  1091. }
  1092. // https://drafts.csswg.org/cssom-view/#dom-window-moveto
  1093. void Window::move_to(long, long) const
  1094. {
  1095. // 1. Optionally, return.
  1096. return;
  1097. // NOTE: For now, the other steps are unimplemented because we always return in step 1.
  1098. // FIXME: Implement these for popup windows?
  1099. // 2. Let target be this's relevant global object's browsing context.
  1100. // 3. If target is not an auxiliary browsing context that was created by a script (as opposed to by an action of the user), then return.
  1101. // 4. Optionally, clamp x and y in a user-agent-defined manner so that the window does not move outside the available space.
  1102. // 5. Move target’s window such that the window’s top left corner is at coordinates (x, y) relative to the top left corner of the output device, measured in CSS pixels of target. The positive axes are rightward and downward.
  1103. }
  1104. // https://drafts.csswg.org/cssom-view/#dom-window-moveby
  1105. void Window::move_by(long, long) const
  1106. {
  1107. // 1. Optionally, return.
  1108. return;
  1109. // NOTE: For now, the other steps are unimplemented because we always return in step 1.
  1110. // FIXME: Implement these for popup windows?
  1111. // 2. Let target be this's relevant global object's browsing context.
  1112. // 3. If target is not an auxiliary browsing context that was created by a script (as opposed to by an action of the user), then return.
  1113. // 4. Optionally, clamp x and y in a user-agent-defined manner so that the window does not move outside the available space.
  1114. // 5. Move target’s window x CSS pixels of target rightward and y CSS pixels of target downward.
  1115. }
  1116. // https://drafts.csswg.org/cssom-view/#dom-window-resizeto
  1117. void Window::resize_to(long, long) const
  1118. {
  1119. // 1. Optionally, return.
  1120. return;
  1121. // NOTE: For now, the other steps are unimplemented because we always return in step 1.
  1122. // FIXME: Implement these for popup windows?
  1123. // 2. Let target be this's relevant global object's browsing context.
  1124. // 3. If target is not an auxiliary browsing context that was created by a script (as opposed to by an action of the user), then return.
  1125. // 4. Optionally, clamp width and height in a user-agent-defined manner so that the window does not get too small or bigger than the available space.
  1126. // 5. Resize target’s window by moving its right and bottom edges such that the distance between the left and right edges of the viewport are width CSS pixels of target and the distance between the top and bottom edges of the viewport are height CSS pixels of target.
  1127. // 6. Optionally, move target’s window in a user-agent-defined manner so that it does not grow outside the available space.
  1128. }
  1129. // https://drafts.csswg.org/cssom-view/#dom-window-resizeby
  1130. void Window::resize_by(long, long) const
  1131. {
  1132. // 1. Optionally, return.
  1133. return;
  1134. // NOTE: For now, the other steps are unimplemented because we always return in step 1.
  1135. // FIXME: Implement these for popup windows?
  1136. // 2. Let target be this's relevant global object's browsing context.
  1137. // 3. If target is not an auxiliary browsing context that was created by a script (as opposed to by an action of the user), then return.
  1138. // 4. Optionally, clamp x and y in a user-agent-defined manner so that the window does not get too small or bigger than the available space.
  1139. // 5. Resize target’s window by moving its right edge x CSS pixels of target rightward and its bottom edge y CSS pixels of target downward.
  1140. // 6. Optionally, move target’s window in a user-agent-defined manner so that it does not grow outside the available space.
  1141. }
  1142. // https://w3c.github.io/csswg-drafts/cssom-view/#dom-window-scrollx
  1143. double Window::scroll_x() const
  1144. {
  1145. // The scrollX attribute must return the x-coordinate, relative to the initial containing block origin,
  1146. // of the left of the viewport, or zero if there is no viewport.
  1147. if (auto const navigable = associated_document().navigable())
  1148. return navigable->viewport_rect().x().to_double();
  1149. return 0;
  1150. }
  1151. // https://w3c.github.io/csswg-drafts/cssom-view/#dom-window-scrolly
  1152. double Window::scroll_y() const
  1153. {
  1154. // The scrollY attribute must return the y-coordinate, relative to the initial containing block origin,
  1155. // of the top of the viewport, or zero if there is no viewport.
  1156. if (auto const navigable = associated_document().navigable())
  1157. return navigable->viewport_rect().y().to_double();
  1158. return 0;
  1159. }
  1160. // https://w3c.github.io/csswg-drafts/cssom-view/#dom-window-scroll
  1161. void Window::scroll(ScrollToOptions const& options)
  1162. {
  1163. // 4. If there is no viewport, abort these steps.
  1164. auto navigable = associated_document().navigable();
  1165. if (!navigable)
  1166. return;
  1167. // 1. If invoked with one argument, follow these substeps:
  1168. // 1. Let options be the argument.
  1169. auto viewport_rect = navigable->viewport_rect().to_type<float>();
  1170. // 2. Let x be the value of the left dictionary member of options, if present, or the viewport’s current scroll
  1171. // position on the x axis otherwise.
  1172. auto x = options.left.value_or(viewport_rect.x());
  1173. // 3. Let y be the value of the top dictionary member of options, if present, or the viewport’s current scroll
  1174. // position on the y axis otherwise.
  1175. auto y = options.top.value_or(viewport_rect.y());
  1176. // 3. Normalize non-finite values for x and y.
  1177. x = HTML::normalize_non_finite_values(x);
  1178. y = HTML::normalize_non_finite_values(y);
  1179. // 5. Let viewport width be the width of the viewport excluding the width of the scroll bar, if any.
  1180. auto viewport_width = viewport_rect.width();
  1181. // 6. Let viewport height be the height of the viewport excluding the height of the scroll bar, if any.
  1182. auto viewport_height = viewport_rect.height();
  1183. auto const document = navigable->active_document();
  1184. VERIFY(document);
  1185. // Make sure layout is up-to-date before looking at scrollable overflow metrics.
  1186. document->update_layout();
  1187. VERIFY(document->paintable_box());
  1188. auto scrolling_area = document->paintable_box()->scrollable_overflow_rect()->to_type<float>();
  1189. // 7. FIXME: For now we always assume overflow direction is rightward
  1190. // -> If the viewport has rightward overflow direction
  1191. // Let x be max(0, min(x, viewport scrolling area width - viewport width)).
  1192. x = max(0.0f, min(x, scrolling_area.width() - viewport_width));
  1193. // -> If the viewport has leftward overflow direction
  1194. // Let x be min(0, max(x, viewport width - viewport scrolling area width)).
  1195. // 8. FIXME: For now we always assume overflow direction is downward
  1196. // -> If the viewport has downward overflow direction
  1197. // Let y be max(0, min(y, viewport scrolling area height - viewport height)).
  1198. y = max(0.0f, min(y, scrolling_area.height() - viewport_height));
  1199. // -> If the viewport has upward overflow direction
  1200. // Let y be min(0, max(y, viewport height - viewport scrolling area height)).
  1201. // FIXME: 9. Let position be the scroll position the viewport would have by aligning the x-coordinate x of the viewport
  1202. // scrolling area with the left of the viewport and aligning the y-coordinate y of the viewport scrolling area
  1203. // with the top of the viewport.
  1204. auto position = Gfx::FloatPoint { x, y };
  1205. // 10. If position is the same as the viewport’s current scroll position, and the viewport does not have an ongoing
  1206. // smooth scroll, abort these steps.
  1207. if (position == viewport_rect.location())
  1208. return;
  1209. // 11. Let document be the viewport’s associated Document.
  1210. // NOTE: document is already defined above.
  1211. // 12. Perform a scroll of the viewport to position, document’s root element as the associated element, if there is
  1212. // one, or null otherwise, and the scroll behavior being the value of the behavior dictionary member of options.
  1213. navigable->perform_scroll_of_viewport({ x, y });
  1214. }
  1215. // https://w3c.github.io/csswg-drafts/cssom-view/#dom-window-scroll
  1216. void Window::scroll(double x, double y)
  1217. {
  1218. // 2. If invoked with two arguments, follow these substeps:
  1219. // 1. Let options be null converted to a ScrollToOptions dictionary. [WEBIDL]
  1220. auto options = ScrollToOptions {};
  1221. // 2. Let x and y be the arguments, respectively.
  1222. options.left = x;
  1223. options.top = y;
  1224. scroll(options);
  1225. }
  1226. // https://w3c.github.io/csswg-drafts/cssom-view/#dom-window-scrollby
  1227. void Window::scroll_by(ScrollToOptions options)
  1228. {
  1229. // 2. Normalize non-finite values for the left and top dictionary members of options.
  1230. auto left = HTML::normalize_non_finite_values(options.left);
  1231. auto top = HTML::normalize_non_finite_values(options.top);
  1232. // 3. Add the value of scrollX to the left dictionary member.
  1233. options.left = left + scroll_x();
  1234. // 4. Add the value of scrollY to the top dictionary member.
  1235. options.top = top + scroll_y();
  1236. // 5. Act as if the scroll() method was invoked with options as the only argument.
  1237. scroll(options);
  1238. }
  1239. // https://w3c.github.io/csswg-drafts/cssom-view/#dom-window-scrollby
  1240. void Window::scroll_by(double x, double y)
  1241. {
  1242. // 1. If invoked with two arguments, follow these substeps:
  1243. // 1. Let options be null converted to a ScrollToOptions dictionary. [WEBIDL]
  1244. auto options = ScrollToOptions {};
  1245. // 2. Let x and y be the arguments, respectively.
  1246. // 3. Let the left dictionary member of options have the value x.
  1247. options.left = x;
  1248. // 4. Let the top dictionary member of options have the value y.
  1249. options.top = y;
  1250. scroll_by(options);
  1251. }
  1252. // https://w3c.github.io/csswg-drafts/cssom-view/#dom-window-screenx
  1253. i32 Window::screen_x() const
  1254. {
  1255. // The screenX and screenLeft attributes must return the x-coordinate, relative to the origin of the Web-exposed
  1256. // screen area, of the left of the client window as number of CSS pixels, or zero if there is no such thing.
  1257. return page().window_position().x().value();
  1258. }
  1259. // https://w3c.github.io/csswg-drafts/cssom-view/#dom-window-screeny
  1260. i32 Window::screen_y() const
  1261. {
  1262. // The screenY and screenTop attributes must return the y-coordinate, relative to the origin of the screen of the
  1263. // Web-exposed screen area, of the top of the client window as number of CSS pixels, or zero if there is no such thing.
  1264. return page().window_position().y().value();
  1265. }
  1266. // https://w3c.github.io/csswg-drafts/cssom-view/#dom-window-outerwidth
  1267. i32 Window::outer_width() const
  1268. {
  1269. // The outerWidth attribute must return the width of the client window. If there is no client window this
  1270. // attribute must return zero.
  1271. return page().window_size().width().value();
  1272. }
  1273. // https://w3c.github.io/csswg-drafts/cssom-view/#dom-window-outerheight
  1274. i32 Window::outer_height() const
  1275. {
  1276. // The outerHeight attribute must return the height of the client window. If there is no client window this
  1277. // attribute must return zero.
  1278. return page().window_size().height().value();
  1279. }
  1280. // https://w3c.github.io/csswg-drafts/cssom-view/#dom-window-devicepixelratio
  1281. double Window::device_pixel_ratio() const
  1282. {
  1283. // 1. If there is no output device, return 1 and abort these steps.
  1284. // 2. Let CSS pixel size be the size of a CSS pixel at the current page zoom and using a scale factor of 1.0.
  1285. // 3. Let device pixel size be the vertical size of a device pixel of the output device.
  1286. // 4. Return the result of dividing CSS pixel size by device pixel size.
  1287. return page().client().device_pixels_per_css_pixel();
  1288. }
  1289. // https://html.spec.whatwg.org/multipage/imagebitmap-and-animations.html#dom-animationframeprovider-requestanimationframe
  1290. WebIDL::UnsignedLong Window::request_animation_frame(WebIDL::CallbackType& callback)
  1291. {
  1292. // FIXME: Make this fully spec compliant. Currently implements a mix of 'requestAnimationFrame()' and 'run the animation frame callbacks'.
  1293. return m_animation_frame_callback_driver.add([this, callback = JS::make_handle(callback)](double now) {
  1294. // 3. Invoke callback, passing now as the only argument, and if an exception is thrown, report the exception.
  1295. auto result = WebIDL::invoke_callback(*callback, {}, JS::Value(now));
  1296. if (result.is_error())
  1297. report_exception(result, realm());
  1298. });
  1299. }
  1300. // https://html.spec.whatwg.org/multipage/imagebitmap-and-animations.html#animationframeprovider-cancelanimationframe
  1301. void Window::cancel_animation_frame(WebIDL::UnsignedLong handle)
  1302. {
  1303. // 1. If this is not supported, then throw a "NotSupportedError" DOMException.
  1304. // NOTE: Doesn't apply in this Window-specific implementation.
  1305. // 2. Let callbacks be this's target object's map of animation frame callbacks.
  1306. // 3. Remove callbacks[handle].
  1307. (void)m_animation_frame_callback_driver.remove(handle);
  1308. }
  1309. // https://w3c.github.io/requestidlecallback/#dom-window-requestidlecallback
  1310. u32 Window::request_idle_callback(WebIDL::CallbackType& callback, RequestIdleCallback::IdleRequestOptions const& options)
  1311. {
  1312. // 1. Let window be this Window object.
  1313. // 2. Increment the window's idle callback identifier by one.
  1314. m_idle_callback_identifier++;
  1315. // 3. Let handle be the current value of window's idle callback identifier.
  1316. auto handle = m_idle_callback_identifier;
  1317. // 4. Push callback to the end of window's list of idle request callbacks, associated with handle.
  1318. auto handler = [callback = JS::make_handle(callback)](JS::NonnullGCPtr<RequestIdleCallback::IdleDeadline> deadline) -> JS::Completion {
  1319. return WebIDL::invoke_callback(*callback, {}, deadline.ptr());
  1320. };
  1321. m_idle_request_callbacks.append(adopt_ref(*new IdleCallback(move(handler), handle)));
  1322. // 5. Return handle and then continue running this algorithm asynchronously.
  1323. return handle;
  1324. // FIXME: 6. If the timeout property is present in options and has a positive value:
  1325. // FIXME: 1. Wait for timeout milliseconds.
  1326. // FIXME: 2. Wait until all invocations of this algorithm, whose timeout added to their posted time occurred before this one's, have completed.
  1327. // FIXME: 3. Optionally, wait a further user-agent defined length of time.
  1328. // FIXME: 4. Queue a task on the queue associated with the idle-task task source, which performs the invoke idle callback timeout algorithm, passing handle and window as arguments.
  1329. (void)options;
  1330. }
  1331. // https://w3c.github.io/requestidlecallback/#dom-window-cancelidlecallback
  1332. void Window::cancel_idle_callback(u32 handle)
  1333. {
  1334. // 1. Let window be this Window object.
  1335. // 2. Find the entry in either the window's list of idle request callbacks or list of runnable idle callbacks
  1336. // that is associated with the value handle.
  1337. // 3. If there is such an entry, remove it from both window's list of idle request callbacks and the list of runnable idle callbacks.
  1338. m_idle_request_callbacks.remove_first_matching([&](auto& callback) {
  1339. return callback->handle() == handle;
  1340. });
  1341. m_runnable_idle_callbacks.remove_first_matching([&](auto& callback) {
  1342. return callback->handle() == handle;
  1343. });
  1344. }
  1345. // https://w3c.github.io/selection-api/#dom-window-getselection
  1346. JS::GCPtr<Selection::Selection> Window::get_selection() const
  1347. {
  1348. // The method must invoke and return the result of getSelection() on this's Window.document attribute.
  1349. return associated_document().get_selection();
  1350. }
  1351. // https://html.spec.whatwg.org/multipage/obsolete.html#dom-window-captureevents
  1352. void Window::capture_events()
  1353. {
  1354. // Do nothing.
  1355. }
  1356. // https://html.spec.whatwg.org/multipage/obsolete.html#dom-document-releaseevents
  1357. void Window::release_events()
  1358. {
  1359. // Do nothing.
  1360. }
  1361. // https://html.spec.whatwg.org/multipage/nav-history-apis.html#dom-navigation
  1362. JS::NonnullGCPtr<Navigation> Window::navigation()
  1363. {
  1364. // Upon creation of the Window object, its navigation API must be set
  1365. // to a new Navigation object created in the Window object's relevant realm.
  1366. if (!m_navigation) {
  1367. auto& realm = relevant_realm(*this);
  1368. m_navigation = heap().allocate<Navigation>(realm, realm);
  1369. }
  1370. // The navigation getter steps are to return this's navigation API.
  1371. return *m_navigation;
  1372. }
  1373. // https://html.spec.whatwg.org/multipage/custom-elements.html#dom-window-customelements
  1374. JS::NonnullGCPtr<CustomElementRegistry> Window::custom_elements()
  1375. {
  1376. auto& realm = this->realm();
  1377. // The customElements attribute of the Window interface must return the CustomElementRegistry object for that Window object.
  1378. if (!m_custom_element_registry)
  1379. m_custom_element_registry = heap().allocate<CustomElementRegistry>(realm, realm);
  1380. return JS::NonnullGCPtr { *m_custom_element_registry };
  1381. }
  1382. // https://html.spec.whatwg.org/#document-tree-child-navigable-target-name-property-set
  1383. OrderedHashMap<FlyString, JS::NonnullGCPtr<Navigable>> Window::document_tree_child_navigable_target_name_property_set()
  1384. {
  1385. // The document-tree child navigable target name property set of a Window object window is the return value of running these steps:
  1386. // 1. Let children be the document-tree child navigables of window's associated Document.
  1387. auto children = associated_document().document_tree_child_navigables();
  1388. // 2. Let firstNamedChildren be an empty ordered set.
  1389. OrderedHashMap<FlyString, JS::NonnullGCPtr<Navigable>> first_named_children;
  1390. // 3. For each navigable of children:
  1391. for (auto const& navigable : children) {
  1392. // 1. Let name be navigable's target name.
  1393. auto const& name = navigable->target_name();
  1394. // 2. If name is the empty string, then continue.
  1395. if (name.is_empty())
  1396. continue;
  1397. // 3. If firstNamedChildren contains a navigable whose target name is name, then continue.
  1398. if (first_named_children.contains(name))
  1399. continue;
  1400. // 4. Append navigable to firstNamedChildren.
  1401. (void)first_named_children.set(name, *navigable);
  1402. }
  1403. // 4. Let names be an empty ordered set.
  1404. OrderedHashMap<FlyString, JS::NonnullGCPtr<Navigable>> names;
  1405. // 5. For each navigable of firstNamedChildren:
  1406. for (auto const& [name, navigable] : first_named_children) {
  1407. // 1. Let name be navigable's target name.
  1408. // 2. If navigable's active document's origin is same origin with window's relevant settings object's origin, then append name to names.
  1409. if (navigable->active_document()->origin().is_same_origin(relevant_settings_object(*this).origin()))
  1410. names.set(name, *navigable);
  1411. }
  1412. return names;
  1413. }
  1414. // https://html.spec.whatwg.org/#named-access-on-the-window-object
  1415. Vector<FlyString> Window::supported_property_names() const
  1416. {
  1417. // FIXME: Make the const-correctness of the methods this method calls less cowboy.
  1418. auto& mutable_this = const_cast<Window&>(*this);
  1419. // The Window object supports named properties.
  1420. // The supported property names of a Window object window at any moment consist of the following,
  1421. // in tree order according to the element that contributed them, ignoring later duplicates:
  1422. HashTable<FlyString> property_names;
  1423. // - window's document-tree child navigable target name property set;
  1424. auto child_navigable_property_set = mutable_this.document_tree_child_navigable_target_name_property_set();
  1425. for (auto& entry : child_navigable_property_set)
  1426. property_names.set(entry.key, AK::HashSetExistingEntryBehavior::Keep);
  1427. // - the value of the name content attribute for all embed, form, img, and object elements
  1428. // that have a non-empty name content attribute and are in a document tree with window's associated Document as their root; and
  1429. // - the value of the id content attribute for all HTML elements that have a non-empty id content attribute
  1430. // and are in a document tree with window's associated Document as their root.
  1431. associated_document().for_each_in_subtree_of_type<DOM::Element>([&property_names](auto& element) -> TraversalDecision {
  1432. if (is<HTMLEmbedElement>(element) || is<HTMLFormElement>(element) || is<HTMLImageElement>(element) || is<HTMLObjectElement>(element)) {
  1433. if (element.name().has_value())
  1434. property_names.set(element.name().value(), AK::HashSetExistingEntryBehavior::Keep);
  1435. }
  1436. if (auto const& name = element.id(); name.has_value())
  1437. property_names.set(name.value().to_string(), AK::HashSetExistingEntryBehavior::Keep);
  1438. return TraversalDecision::Continue;
  1439. });
  1440. return property_names.values();
  1441. }
  1442. // https://html.spec.whatwg.org/#named-access-on-the-window-object
  1443. JS::Value Window::named_item_value(FlyString const& name) const
  1444. {
  1445. // FIXME: Make the const-correctness of the methods this method calls less cowboy.
  1446. auto& mutable_this = const_cast<Window&>(*this);
  1447. // To determine the value of a named property name in a Window object window, the user agent must return the value obtained using the following steps:
  1448. // 1. Let objects be the list of named objects of window with the name name.
  1449. // NOTE: There will be at least one such object, since the algorithm would otherwise not have been invoked by Web IDL.
  1450. auto objects = mutable_this.named_objects(name);
  1451. // 2. If objects contains a navigable, then:
  1452. if (!objects.navigables.is_empty()) {
  1453. // 1. Let container be the first navigable container in window's associated Document's descendants whose content navigable is in objects.
  1454. JS::GCPtr<NavigableContainer> container = nullptr;
  1455. mutable_this.associated_document().for_each_in_subtree_of_type<HTML::NavigableContainer>([&](HTML::NavigableContainer& navigable_container) {
  1456. if (!navigable_container.content_navigable())
  1457. return TraversalDecision::Continue;
  1458. if (objects.navigables.contains_slow(JS::NonnullGCPtr { *navigable_container.content_navigable() })) {
  1459. container = navigable_container;
  1460. return TraversalDecision::Break;
  1461. }
  1462. return TraversalDecision::Continue;
  1463. });
  1464. // 2. Return container's content navigable's active WindowProxy.
  1465. VERIFY(container);
  1466. return container->content_navigable()->active_window_proxy();
  1467. }
  1468. // 3. Otherwise, if objects has only one element, return that element.
  1469. if (objects.elements.size() == 1)
  1470. return objects.elements[0];
  1471. // 4. Otherwise return an HTMLCollection rooted at window's associated Document,
  1472. // whose filter matches only named objects of window with the name name. (By definition, these will all be elements.)
  1473. return DOM::HTMLCollection::create(mutable_this.associated_document(), DOM::HTMLCollection::Scope::Descendants, [name](auto& element) -> bool {
  1474. if ((is<HTMLEmbedElement>(element) || is<HTMLFormElement>(element) || is<HTMLImageElement>(element) || is<HTMLObjectElement>(element))
  1475. && (element.name() == name))
  1476. return true;
  1477. return element.id() == name;
  1478. });
  1479. }
  1480. // https://html.spec.whatwg.org/#dom-window-nameditem-filter
  1481. Window::NamedObjects Window::named_objects(StringView name)
  1482. {
  1483. // NOTE: Since the Window interface has the [Global] extended attribute, its named properties
  1484. // follow the rules for named properties objects rather than legacy platform objects.
  1485. // Named objects of Window object window with the name name, for the purposes of the above algorithm, consist of the following:
  1486. NamedObjects objects;
  1487. // document-tree child navigables of window's associated Document whose target name is name;
  1488. auto children = associated_document().document_tree_child_navigables();
  1489. for (auto& navigable : children) {
  1490. if (navigable->target_name() == name) {
  1491. objects.navigables.append(*navigable);
  1492. }
  1493. }
  1494. // embed, form, img, or object elements that have a name content attribute whose value is name
  1495. // and are in a document tree with window's associated Document as their root; and
  1496. // HTML elements that have an id content attribute whose value is name and are in a document tree with window's associated Document as their root.
  1497. associated_document().for_each_in_subtree_of_type<DOM::Element>([&objects, &name](auto& element) -> TraversalDecision {
  1498. if ((is<HTMLEmbedElement>(element) || is<HTMLFormElement>(element) || is<HTMLImageElement>(element) || is<HTMLObjectElement>(element))
  1499. && (element.name() == name))
  1500. objects.elements.append(element);
  1501. else if (element.id() == name)
  1502. objects.elements.append(element);
  1503. return TraversalDecision::Continue;
  1504. });
  1505. return objects;
  1506. }
  1507. bool Window::find(String const& string)
  1508. {
  1509. if (string.is_empty())
  1510. return false;
  1511. auto& page = this->page();
  1512. Optional<Page::FindInPageResult> result;
  1513. if (auto last_query = page.last_find_in_page_query(); last_query.has_value() && last_query->string == string) {
  1514. result = page.find_in_page_next_match();
  1515. } else {
  1516. Page::FindInPageQuery query {
  1517. string,
  1518. CaseSensitivity::CaseInsensitive,
  1519. Page::WrapAround::No,
  1520. };
  1521. result = page.find_in_page(query);
  1522. }
  1523. return result.has_value() && result->total_match_count.has_value() && *result->total_match_count > 0;
  1524. }
  1525. }