Class: Dommy::Window
- Inherits:
-
Object
- Object
- Dommy::Window
- Includes:
- Bridge::Methods, EventTarget
- Defined in:
- lib/dommy/window.rb
Overview
Instance Attribute Summary collapse
-
#custom_elements ⇒ Object
readonly
Returns the value of attribute custom_elements.
-
#document ⇒ Object
readonly
Returns the value of attribute document.
-
#globals ⇒ Object
readonly
Returns the value of attribute globals.
-
#location ⇒ Object
readonly
Returns the value of attribute location.
-
#navigator ⇒ Object
readonly
Returns the value of attribute navigator.
-
#scheduler ⇒ Object
readonly
Returns the value of attribute scheduler.
Instance Method Summary collapse
- #__internal_event_parent__ ⇒ Object
- #__js_call__(method, args) ⇒ Object
-
#__js_get__(key) ⇒ Object
Bridge protocol: respond to a JS-style property read by name.
- #__js_set__(key, value) ⇒ Object
- #fire_hashchange(old_hash, new_hash) ⇒ Object
-
#fire_popstate(state) ⇒ Object
Called by History#go and Location.href= to fire popstate / hashchange events.
-
#initialize(host = nil, nokogiri_doc: nil) ⇒ Window
constructor
A new instance of Window.
Methods included from Bridge::Methods
Methods included from EventTarget
#__internal_deliver_event__, #add_event_listener, capture_flag, #deliver_at, #dispatch_event, js_truthy?, #remove_event_listener
Constructor Details
#initialize(host = nil, nokogiri_doc: nil) ⇒ Window
Returns a new instance of Window.
23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
# File 'lib/dommy/window.rb', line 23 def initialize(host = nil, nokogiri_doc: nil) @host = host @scheduler = Scheduler.new @crypto = Crypto.new(self) @css_namespace = CSSNamespace.new @cookie_store = CookieStore.new(self) @local_storage = Storage.new @session_storage = Storage.new @location = Location.new(self) @history = History.new(self, @location) # `JS.global[:__some_key__] = ...` from user code lands here. Test code # uses this for stub installation (e.g. a custom `__fetch_stub__`); # production code stays on the typed accessors. Kept last in the read # fallback so it can't shadow intentional getters. @globals = {} @document = Document.new(host, nokogiri_doc: nokogiri_doc) @document.default_view = self @custom_elements = CustomElementRegistry.new(self) @navigator = Navigator.new(self) # All JS global constructors (`new Event()`, `new URL()`, ...) live in a # single name→Constructor registry rather than one ivar + one __js_get__ # arm each. @constructors = Bridge::ConstructorRegistry.new(build_constructors) end |
Instance Attribute Details
#custom_elements ⇒ Object (readonly)
Returns the value of attribute custom_elements.
21 22 23 |
# File 'lib/dommy/window.rb', line 21 def custom_elements @custom_elements end |
#document ⇒ Object (readonly)
Returns the value of attribute document.
21 22 23 |
# File 'lib/dommy/window.rb', line 21 def document @document end |
#globals ⇒ Object (readonly)
Returns the value of attribute globals.
21 22 23 |
# File 'lib/dommy/window.rb', line 21 def globals @globals end |
#location ⇒ Object (readonly)
Returns the value of attribute location.
21 22 23 |
# File 'lib/dommy/window.rb', line 21 def location @location end |
#navigator ⇒ Object (readonly)
Returns the value of attribute navigator.
21 22 23 |
# File 'lib/dommy/window.rb', line 21 def navigator @navigator end |
#scheduler ⇒ Object (readonly)
Returns the value of attribute scheduler.
21 22 23 |
# File 'lib/dommy/window.rb', line 21 def scheduler @scheduler end |
Instance Method Details
#__internal_event_parent__ ⇒ Object
183 184 185 |
# File 'lib/dommy/window.rb', line 183 def __internal_event_parent__ nil end |
#__js_call__(method, args) ⇒ Object
130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 |
# File 'lib/dommy/window.rb', line 130 def __js_call__(method, args) case method when "fetch" FetchFn.new(self).__js_call__("call", args) when "encodeURIComponent" Internal::GlobalFunctions.encode_uri_component(args[0]) when "decodeURIComponent" Internal::GlobalFunctions.decode_uri_component(args[0]) when "addEventListener" add_event_listener(args[0], args[1], args[2]) when "removeEventListener" remove_event_listener(args[0], args[1], args[2]) when "dispatchEvent" dispatch_event(args[0]) when "setTimeout" @scheduler.set_timeout(args[0], timer_delay(args[1])) when "clearTimeout" @scheduler.clear_timeout(args[0]) when "setInterval" @scheduler.set_interval(args[0], timer_delay(args[1])) when "clearInterval" @scheduler.clear_interval(args[0]) when "requestAnimationFrame" @scheduler.request_animation_frame(args[0]) when "cancelAnimationFrame" @scheduler.cancel_animation_frame(args[0]) when "queueMicrotask" @scheduler.queue_microtask(args[0]) when "requestIdleCallback" @scheduler.request_idle_callback(args[0], (args[1].is_a?(Hash) && args[1]["timeout"]) || 0) when "cancelIdleCallback" @scheduler.cancel_idle_callback(args[0]) when "structuredClone" Dommy.structured_clone(args[0]) when "matchMedia" MediaQueryList.new(self, args[0].to_s) when "getComputedStyle" # No CSS engine — return the element's inline style. That # covers `getComputedStyle(el).getPropertyValue("color")` for # values the test set inline via `el.style.color = "..."`. target = args[0] target.respond_to?(:style) ? target.style : nil when "scroll", "scrollTo" scroll_to(*args) when "scrollBy" scroll_by(*args) else # Additional window-level methods (fetch, location, history, # Promise, MutationObserver, etc.) arrive in later sessions. nil end end |
#__js_get__(key) ⇒ Object
Bridge protocol: respond to a JS-style property read by name. Returns either a Ruby primitive (Integer / String / true / false / nil), a Hash/Array (for JS object/array literals), or a Dom::* instance for live DOM/BOM objects.
Anything outside the surface we’ve explicitly polyfilled returns nil (= JS undefined). Spec failures here are the signal to widen the surface in a future session.
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 |
# File 'lib/dommy/window.rb', line 56 def __js_get__(key) ctor = @constructors[key] return ctor if ctor case key when "document" @document when "window", "self", "parent", "top", "frames" # A top-level browsing context refers to itself for these. Returning the # window (not nil) lets `window === window.parent` and frame-walking # loops (e.g. testharness.js's `while (w != w.parent)`) terminate. self when "crypto" @crypto when "cookieStore" @cookie_store when "console" :console when "Object" :object_ctor when "Array" :array_ctor when "JSON" :json_ctor when "performance" @performance ||= Performance.new(self) when "localStorage" @local_storage when "sessionStorage" @session_storage when "location" @location when "history" @history when "CSS" @css_namespace when "fetch" FetchFn.new(self) when "customElements" @custom_elements when "navigator" @navigator when "scrollX", "pageXOffset" @scroll_x || 0 when "scrollY", "pageYOffset" @scroll_y || 0 when "scrollMaxX", "scrollMaxY" # No real content box to scroll past, so the max offset is 0. 0 else @globals[key] end end |
#__js_set__(key, value) ⇒ Object
110 111 112 113 114 115 116 117 118 119 120 121 |
# File 'lib/dommy/window.rb', line 110 def __js_set__(key, value) # Stash arbitrary keys for later reads (e.g. # `JS.global[:__fetchy_stub__] = map`). @globals[key] = value # The Fetchy spec's `install_fetch_stub` resets `__fetch_count__` # to 0 inside its JS installer (`globalThis.__fetch_count__ = 0; # globalThis.fetch = ...`). Our polyfill ignores raw JS, so we # piggy-back on the stub assignment to perform the same reset # — without it the count accumulates across tests in one VM run. @globals["__fetch_count__"] = 0 if %w[__fetchy_stub__ __resource_fetch_stub__ __inject_fetch_stub__].include?(key) nil end |
#fire_hashchange(old_hash, new_hash) ⇒ Object
197 198 199 200 |
# File 'lib/dommy/window.rb', line 197 def fire_hashchange(old_hash, new_hash) event = CustomEvent.new("hashchange", "detail" => {"oldURL" => old_hash, "newURL" => new_hash}) dispatch_event(event) end |
#fire_popstate(state) ⇒ Object
Called by History#go and Location.href= to fire popstate / hashchange events. Listeners registered on the Window via ‘addEventListener(“popstate”|“hashchange”, cb)` receive them.
190 191 192 193 194 195 |
# File 'lib/dommy/window.rb', line 190 def fire_popstate(state) # PopStateEvent exposes the entry's state as `event.state` (the spec # property). Routers (Turbo) branch on `event.state`. event = PopStateEvent.new("popstate", "state" => state) dispatch_event(event) end |