@browserless/screenshot 13.6.5 → 13.6.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@browserless/screenshot",
3
3
  "description": "Capture high-quality screenshots of websites with overlay support, device emulation, and automated image optimization.",
4
4
  "homepage": "https://browserless.js.org/#/?id=screenshoturl-options",
5
- "version": "13.6.5",
5
+ "version": "13.6.6",
6
6
  "main": "src/index.js",
7
7
  "author": {
8
8
  "email": "hello@microlink.io",
@@ -33,7 +33,7 @@
33
33
  ],
34
34
  "dependencies": {
35
35
  "@browserless/errors": "13.1.7",
36
- "@browserless/goto": "13.6.5",
36
+ "@browserless/goto": "13.6.6",
37
37
  "@kikobeats/content-type": "~1.0.4",
38
38
  "@kikobeats/time-span": "~1.0.12",
39
39
  "automad-prism-themes": "~0.3.7",
@@ -52,7 +52,7 @@
52
52
  "svg-gradient": "~1.0.4"
53
53
  },
54
54
  "devDependencies": {
55
- "@browserless/test": "13.6.5",
55
+ "@browserless/test": "13.6.6",
56
56
  "ava": "5"
57
57
  },
58
58
  "engines": {
@@ -71,5 +71,5 @@
71
71
  "timeout": "2m",
72
72
  "workerThreads": false
73
73
  },
74
- "gitHead": "679c9a8f0db8a6a98bb54fd3d19532ea8a64d76b"
74
+ "gitHead": "6c4a44b903f7e7382d49a27289bec6b3a1833108"
75
75
  }
@@ -34,8 +34,12 @@ const { isContextDestroyed } = require('@browserless/errors')
34
34
  // without relying on `page.viewport()`, which is null under
35
35
  // `defaultViewport: null`.
36
36
  const snapshot = () => {
37
- const vw = window.innerWidth || document.documentElement.clientWidth
38
- const vh = window.innerHeight || document.documentElement.clientHeight
37
+ // A document that is mid-parse or detached can have a null `documentElement`
38
+ // (the same state the text walk below guards against), so every dereference
39
+ // of it in this snapshot goes through `root`.
40
+ const root = document.documentElement
41
+ const vw = window.innerWidth || (root ? root.clientWidth : 0)
42
+ const vh = window.innerHeight || (root ? root.clientHeight : 0)
39
43
 
40
44
  // Computed colors resolve to `rgb()`/`rgba()`; anything else is unknown.
41
45
  const parseColor = value => {
@@ -121,36 +125,46 @@ const snapshot = () => {
121
125
  // Counting stops at 200 chars, so a text-heavy page costs a handful of nodes,
122
126
  // not a full DOM walk; only a near-blank shell walks every text node.
123
127
  let text = 0
124
- const walker = document.createTreeWalker(
125
- document.body || document.documentElement,
126
- window.NodeFilter.SHOW_TEXT
127
- )
128
- while (text < 200) {
129
- const node = walker.nextNode()
130
- if (!node) break
131
- const value = node.nodeValue.trim()
132
- if (!value) continue
133
- const el = node.parentElement
134
- if (!el) continue
135
- const tag = el.tagName
136
- if (tag === 'SCRIPT' || tag === 'STYLE' || tag === 'NOSCRIPT' || tag === 'TEMPLATE') continue
137
- if (
138
- typeof el.checkVisibility === 'function' &&
139
- !el.checkVisibility({ visibilityProperty: true, opacityProperty: true })
140
- ) {
141
- continue
128
+ // A document that is mid-parse or detached can have neither `body` nor
129
+ // `documentElement`, and `createTreeWalker` throws on a null root. Text is only
130
+ // a paint signal, so a count that cannot be taken means "no text seen yet": the
131
+ // gate stays conservative and re-polls, rather than failing a render that would
132
+ // otherwise succeed. The same holds mid-walk — a DOM mutating under the walker
133
+ // must not take the capture down with it.
134
+ const textRoot = document.body || root
135
+ const walker = textRoot
136
+ ? document.createTreeWalker(textRoot, window.NodeFilter.SHOW_TEXT)
137
+ : { nextNode: () => null }
138
+ try {
139
+ while (text < 200) {
140
+ const node = walker.nextNode()
141
+ if (!node) break
142
+ const value = node.nodeValue.trim()
143
+ if (!value) continue
144
+ const el = node.parentElement
145
+ if (!el) continue
146
+ const tag = el.tagName
147
+ if (tag === 'SCRIPT' || tag === 'STYLE' || tag === 'NOSCRIPT' || tag === 'TEMPLATE') continue
148
+ if (
149
+ typeof el.checkVisibility === 'function' &&
150
+ !el.checkVisibility({ visibilityProperty: true, opacityProperty: true })
151
+ ) {
152
+ continue
153
+ }
154
+ const range = document.createRange()
155
+ range.selectNodeContents(node)
156
+ const rect = range.getBoundingClientRect()
157
+ if (rect.width === 0 || rect.height === 0) continue
158
+ if (rect.bottom <= 0 || rect.right <= 0 || rect.top >= vh || rect.left >= vw) continue
159
+ // White-on-white (or any color-on-same-color) text passes every geometry
160
+ // check while a capture shows nothing: don't count it as painted text.
161
+ const color = parseColor(window.getComputedStyle(el).color)
162
+ if (color && (color.a < 0.05 || sameColor(color, effectiveBackground(el)))) continue
163
+ text += value.length
164
+ samplePoint(el, rect)
142
165
  }
143
- const range = document.createRange()
144
- range.selectNodeContents(node)
145
- const rect = range.getBoundingClientRect()
146
- if (rect.width === 0 || rect.height === 0) continue
147
- if (rect.bottom <= 0 || rect.right <= 0 || rect.top >= vh || rect.left >= vw) continue
148
- // White-on-white (or any color-on-same-color) text passes every geometry
149
- // check while a capture shows nothing: don't count it as painted text.
150
- const color = parseColor(window.getComputedStyle(el).color)
151
- if (color && (color.a < 0.05 || sameColor(color, effectiveBackground(el)))) continue
152
- text += value.length
153
- samplePoint(el, rect)
166
+ } catch {
167
+ // Keep whatever was counted before the DOM shifted underneath the walk.
154
168
  }
155
169
 
156
170
  // Is this content sample's paint hidden behind an unrelated opaque,
@@ -196,7 +210,7 @@ const snapshot = () => {
196
210
  }
197
211
 
198
212
  return {
199
- height: document.documentElement.scrollHeight,
213
+ height: root ? root.scrollHeight : 0,
200
214
  viewport: vh,
201
215
  images,
202
216
  decoded,