cef: remove reddit-specific video JS injection
- commit
- 91bd67170d4e6ca397743029e204c015a46d7ff5
- author
- brice · · 2026-04-28 03:43 UTC
- parent
- bf4a0b5
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
1 file · +0 -229
M internal/infrastructure/cef/content_injector.go +0 -229
| @@ -3,8 +3,6 @@ package cef | |||
| 3 | 3 | import ( | |
| 4 | 4 | "context" | |
| 5 | 5 | "fmt" | |
| 6 | - | "net/url" | |
| 7 | - | "strings" | |
| 8 | 6 | "sync" | |
| 9 | 7 | ||
| 10 | 8 | "github.com/bnema/dumber/internal/infrastructure/webutil" | |
| @@ -164,227 +162,6 @@ const videoDiagnosticJS = `(function(){ | |||
| 164 | 162 | 'aac:', MediaSource ? MediaSource.isTypeSupported('audio/mp4; codecs="mp4a.40.2"') : 'n/a'); | |
| 165 | 163 | })();` | |
| 166 | 164 | ||
| 167 | - | // redditDirectVideoJS replaces Reddit's blob/MSE playback path with a direct | |
| 168 | - | // HLS source when MP4 MSE buffers are unsupported. This lets the browser issue | |
| 169 | - | // a normal media request that our transcoding handler can answer with WebM. | |
| 170 | - | const redditDirectVideoJS = `(function(){ | |
| 171 | - | var tag = '[REDDIT-VIDEO-PATCH]'; | |
| 172 | - | if (!/(\.|^)reddit\.com$/i.test(location.hostname)) return; | |
| 173 | - | if (!window.MediaSource) return; | |
| 174 | - | if (MediaSource.isTypeSupported('video/mp4; codecs="avc1.4d401e"') && | |
| 175 | - | MediaSource.isTypeSupported('audio/mp4; codecs="mp4a.40.2"')) { | |
| 176 | - | return; | |
| 177 | - | } | |
| 178 | - | ||
| 179 | - | console.warn(tag, 'active for unsupported Reddit MSE playback'); | |
| 180 | - | ||
| 181 | - | function ensureStyle() { | |
| 182 | - | if (document.getElementById('dumber-reddit-direct-video-style')) return; | |
| 183 | - | var style = document.createElement('style'); | |
| 184 | - | style.id = 'dumber-reddit-direct-video-style'; | |
| 185 | - | style.textContent = | |
| 186 | - | 'shreddit-player[data-dumber-direct-video="1"] { position: relative !important; }' + | |
| 187 | - | 'shreddit-player[data-dumber-direct-video="1"] > .dumber-direct-video {' + | |
| 188 | - | ' position: absolute !important; inset: 0 !important; width: 100% !important; height: 100% !important;' + | |
| 189 | - | ' object-fit: contain !important; background: #000 !important; z-index: 2147483647 !important;' + | |
| 190 | - | ' display: block !important; }'; | |
| 191 | - | document.head.appendChild(style); | |
| 192 | - | } | |
| 193 | - | ||
| 194 | - | function candidateRoots(video) { | |
| 195 | - | var roots = []; | |
| 196 | - | var seen = []; | |
| 197 | - | function add(root) { | |
| 198 | - | if (!root) return; | |
| 199 | - | if (seen.indexOf(root) !== -1) return; | |
| 200 | - | seen.push(root); | |
| 201 | - | roots.push(root); | |
| 202 | - | } | |
| 203 | - | ||
| 204 | - | var current = video; | |
| 205 | - | while (current) { | |
| 206 | - | add(current); | |
| 207 | - | if (current.shadowRoot) add(current.shadowRoot); | |
| 208 | - | current = current.parentElement; | |
| 209 | - | } | |
| 210 | - | add(document); | |
| 211 | - | return roots; | |
| 212 | - | } | |
| 213 | - | ||
| 214 | - | function findHlsSource(video) { | |
| 215 | - | var selectors = [ | |
| 216 | - | 'source[type="application/vnd.apple.mpegURL"][src]', | |
| 217 | - | 'source[type="application/x-mpegURL"][src]', | |
| 218 | - | 'source[src*="HLSPlaylist.m3u8"]', | |
| 219 | - | '[src*="HLSPlaylist.m3u8"]' | |
| 220 | - | ]; | |
| 221 | - | ||
| 222 | - | var roots = candidateRoots(video); | |
| 223 | - | for (var i = 0; i < roots.length; i++) { | |
| 224 | - | var root = roots[i]; | |
| 225 | - | if (!root.querySelector) continue; | |
| 226 | - | for (var j = 0; j < selectors.length; j++) { | |
| 227 | - | var el = root.querySelector(selectors[j]); | |
| 228 | - | if (el && el.src) return el.src; | |
| 229 | - | } | |
| 230 | - | if (root.querySelectorAll) { | |
| 231 | - | var all = root.querySelectorAll('*'); | |
| 232 | - | for (var k = 0; k < all.length; k++) { | |
| 233 | - | if (all[k].shadowRoot) { | |
| 234 | - | roots.push(all[k].shadowRoot); | |
| 235 | - | } | |
| 236 | - | } | |
| 237 | - | } | |
| 238 | - | } | |
| 239 | - | return ''; | |
| 240 | - | } | |
| 241 | - | ||
| 242 | - | function buildPlaybackURL(hlsURL) { | |
| 243 | - | return location.origin + '/__dumber__/transcode.webm?src=' + encodeURIComponent(hlsURL) + | |
| 244 | - | '&referer=' + encodeURIComponent(location.href) + | |
| 245 | - | '&origin=' + encodeURIComponent(location.origin); | |
| 246 | - | } | |
| 247 | - | ||
| 248 | - | function getPlayerRoot(video) { | |
| 249 | - | return video.closest('shreddit-player') || video.parentElement; | |
| 250 | - | } | |
| 251 | - | ||
| 252 | - | function monitorReplacement(video) { | |
| 253 | - | if (!video || video._dumberReplacementDiag) return; | |
| 254 | - | video._dumberReplacementDiag = true; | |
| 255 | - | [ | |
| 256 | - | 'loadstart', 'loadedmetadata', 'loadeddata', 'canplay', 'playing', | |
| 257 | - | 'waiting', 'stalled', 'suspend', 'abort', 'emptied', 'error', | |
| 258 | - | ].forEach(function(evt) { | |
| 259 | - | video.addEventListener(evt, function() { | |
| 260 | - | var err = video.error ? ('code=' + video.error.code + ' msg=' + video.error.message) : 'none'; | |
| 261 | - | console.warn(tag, 'replacement', evt, 'ready:', video.readyState, 'net:', video.networkState, | |
| 262 | - | 'paused:', video.paused, 'error:', err, 'src:', | |
| 263 | - | (video.currentSrc || video.src || '').substring(0, 120)); | |
| 264 | - | }); | |
| 265 | - | }); | |
| 266 | - | } | |
| 267 | - | ||
| 268 | - | function ensureReplacement(video, playbackURL) { | |
| 269 | - | var root = getPlayerRoot(video); | |
| 270 | - | if (!root) return null; | |
| 271 | - | ensureStyle(); | |
| 272 | - | ||
| 273 | - | var replacement = root.querySelector(':scope > .dumber-direct-video'); | |
| 274 | - | if (!replacement) { | |
| 275 | - | replacement = document.createElement('video'); | |
| 276 | - | replacement.className = 'dumber-direct-video'; | |
| 277 | - | replacement.controls = true; | |
| 278 | - | replacement.playsInline = true; | |
| 279 | - | replacement.preload = 'auto'; | |
| 280 | - | replacement.autoplay = true; | |
| 281 | - | replacement.muted = true; | |
| 282 | - | replacement.loop = video.loop; | |
| 283 | - | root.setAttribute('data-dumber-direct-video', '1'); | |
| 284 | - | root.appendChild(replacement); | |
| 285 | - | } | |
| 286 | - | ||
| 287 | - | replacement.poster = video.poster || replacement.poster || ''; | |
| 288 | - | monitorReplacement(replacement); | |
| 289 | - | if (replacement.src !== playbackURL) { | |
| 290 | - | replacement.src = playbackURL; | |
| 291 | - | replacement.load(); | |
| 292 | - | } | |
| 293 | - | return replacement; | |
| 294 | - | } | |
| 295 | - | ||
| 296 | - | function shouldForce(video) { | |
| 297 | - | var current = video.currentSrc || video.src || ''; | |
| 298 | - | return current.indexOf('blob:') === 0; | |
| 299 | - | } | |
| 300 | - | ||
| 301 | - | function forceDirectPlayback(video) { | |
| 302 | - | if (!video || !shouldForce(video)) return; | |
| 303 | - | ||
| 304 | - | var hlsURL = findHlsSource(video); | |
| 305 | - | if (!hlsURL) { | |
| 306 | - | console.warn(tag, 'blob video found without HLS source'); | |
| 307 | - | return; | |
| 308 | - | } | |
| 309 | - | var playbackURL = buildPlaybackURL(hlsURL); | |
| 310 | - | if (video._dumberPlaybackURL === playbackURL) return; | |
| 311 | - | video._dumberPlaybackURL = playbackURL; | |
| 312 | - | ||
| 313 | - | console.warn(tag, 'forcing replacement video source:', playbackURL.substring(0, 120), 'from:', hlsURL.substring(0, 120)); | |
| 314 | - | ||
| 315 | - | var replacement = ensureReplacement(video, playbackURL); | |
| 316 | - | if (!replacement) return; | |
| 317 | - | ||
| 318 | - | var root = getPlayerRoot(video); | |
| 319 | - | if (root && root.querySelectorAll) { | |
| 320 | - | root.querySelectorAll('video').forEach(function(candidate) { | |
| 321 | - | if (candidate === replacement) return; | |
| 322 | - | try { candidate.pause(); } catch (e) {} | |
| 323 | - | try { candidate.removeAttribute('src'); } catch (e) {} | |
| 324 | - | try { candidate.srcObject = null; } catch (e) {} | |
| 325 | - | try { candidate.load(); } catch (e) {} | |
| 326 | - | candidate.style.display = 'none'; | |
| 327 | - | }); | |
| 328 | - | } else { | |
| 329 | - | try { video.pause(); } catch (e) {} | |
| 330 | - | try { video.removeAttribute('src'); } catch (e) {} | |
| 331 | - | try { video.srcObject = null; } catch (e) {} | |
| 332 | - | try { video.load(); } catch (e) {} | |
| 333 | - | video.style.display = 'none'; | |
| 334 | - | } | |
| 335 | - | ||
| 336 | - | var playPromise = replacement.play(); | |
| 337 | - | if (playPromise && typeof playPromise.catch === 'function') { | |
| 338 | - | playPromise.catch(function(err) { | |
| 339 | - | console.warn(tag, 'replacement video.play() rejected:', err && err.message ? err.message : err); | |
| 340 | - | }); | |
| 341 | - | } | |
| 342 | - | } | |
| 343 | - | ||
| 344 | - | function patchTree(root) { | |
| 345 | - | if (!root || !root.querySelectorAll) return; | |
| 346 | - | root.querySelectorAll('video').forEach(forceDirectPlayback); | |
| 347 | - | root.querySelectorAll('*').forEach(function(el) { | |
| 348 | - | if (el.shadowRoot) patchTree(el.shadowRoot); | |
| 349 | - | }); | |
| 350 | - | } | |
| 351 | - | ||
| 352 | - | new MutationObserver(function(mutations) { | |
| 353 | - | mutations.forEach(function(mutation) { | |
| 354 | - | if (mutation.type === 'attributes' && mutation.target && mutation.target.nodeName === 'VIDEO') { | |
| 355 | - | forceDirectPlayback(mutation.target); | |
| 356 | - | } | |
| 357 | - | mutation.addedNodes.forEach(function(node) { | |
| 358 | - | if (node.nodeName === 'VIDEO') forceDirectPlayback(node); | |
| 359 | - | patchTree(node); | |
| 360 | - | if (node.shadowRoot) patchTree(node.shadowRoot); | |
| 361 | - | }); | |
| 362 | - | }); | |
| 363 | - | }).observe(document.documentElement, { | |
| 364 | - | subtree: true, | |
| 365 | - | childList: true, | |
| 366 | - | attributes: true, | |
| 367 | - | attributeFilter: ['src'] | |
| 368 | - | }); | |
| 369 | - | ||
| 370 | - | // Chain to existing attachShadow patch (e.g. videoDiagnosticJS) instead of overwriting. | |
| 371 | - | var prevAttach = Element.prototype.attachShadow; | |
| 372 | - | Element.prototype.attachShadow = function(opts) { | |
| 373 | - | var sr = prevAttach.call(this, opts); | |
| 374 | - | patchTree(sr); | |
| 375 | - | return sr; | |
| 376 | - | }; | |
| 377 | - | ||
| 378 | - | patchTree(document); | |
| 379 | - | var pollCount = 0; | |
| 380 | - | var maxPolls = 30; | |
| 381 | - | var pollID = setInterval(function() { | |
| 382 | - | patchTree(document); | |
| 383 | - | pollCount++; | |
| 384 | - | if (pollCount >= maxPolls) clearInterval(pollID); | |
| 385 | - | }, 1000); | |
| 386 | - | })();` | |
| 387 | - | ||
| 388 | 165 | // contentInjector implements port.ContentInjector for the CEF engine. | |
| 389 | 166 | // It stores CSS strings and injects them into webviews via ExecuteJavaScript. | |
| 390 | 167 | // Thread-safe: InjectThemeCSS may be called from the UI thread while OnLoadEnd | |
| @@ -524,12 +301,6 @@ func (ci *contentInjector) onLoadEnd(wv *WebView) { | |||
| 524 | 301 | wv.RunJavaScript(context.Background(), videoDiagnosticJS) | |
| 525 | 302 | } | |
| 526 | 303 | ||
| 527 | - | if parsed, err := url.Parse(uri); err == nil { | |
| 528 | - | host := parsed.Hostname() | |
| 529 | - | if host == "reddit.com" || strings.HasSuffix(host, ".reddit.com") { | |
| 530 | - | wv.RunJavaScript(context.Background(), redditDirectVideoJS) | |
| 531 | - | } | |
| 532 | - | } | |
| 533 | 304 | } | |
| 534 | 305 | ||
| 535 | 306 | // injectCSS injects a CSS string as a <style> element via JavaScript. | |
| @@ -3,8 +3,6 @@ package cef | |||||
| 3 | import ( | 3 | import ( | ||
| 4 | "context" | 4 | "context" | ||
| 5 | "fmt" | 5 | "fmt" | ||
| 6 | - | "net/url" | |||
| 7 | - | "strings" | |||
| 8 | "sync" | 6 | "sync" | ||
| 9 | 7 | ||||
| 10 | "github.com/bnema/dumber/internal/infrastructure/webutil" | 8 | "github.com/bnema/dumber/internal/infrastructure/webutil" | ||
| @@ -164,227 +162,6 @@ const videoDiagnosticJS = `(function(){ | |||||
| 164 | 'aac:', MediaSource ? MediaSource.isTypeSupported('audio/mp4; codecs="mp4a.40.2"') : 'n/a'); | 162 | 'aac:', MediaSource ? MediaSource.isTypeSupported('audio/mp4; codecs="mp4a.40.2"') : 'n/a'); | ||
| 165 | })();` | 163 | })();` | ||
| 166 | 164 | ||||
| 167 | - | // redditDirectVideoJS replaces Reddit's blob/MSE playback path with a direct | |||
| 168 | - | // HLS source when MP4 MSE buffers are unsupported. This lets the browser issue | |||
| 169 | - | // a normal media request that our transcoding handler can answer with WebM. | |||
| 170 | - | const redditDirectVideoJS = `(function(){ | |||
| 171 | - | var tag = '[REDDIT-VIDEO-PATCH]'; | |||
| 172 | - | if (!/(\.|^)reddit\.com$/i.test(location.hostname)) return; | |||
| 173 | - | if (!window.MediaSource) return; | |||
| 174 | - | if (MediaSource.isTypeSupported('video/mp4; codecs="avc1.4d401e"') && | |||
| 175 | - | MediaSource.isTypeSupported('audio/mp4; codecs="mp4a.40.2"')) { | |||
| 176 | - | return; | |||
| 177 | - | } | |||
| 178 | - | ||||
| 179 | - | console.warn(tag, 'active for unsupported Reddit MSE playback'); | |||
| 180 | - | ||||
| 181 | - | function ensureStyle() { | |||
| 182 | - | if (document.getElementById('dumber-reddit-direct-video-style')) return; | |||
| 183 | - | var style = document.createElement('style'); | |||
| 184 | - | style.id = 'dumber-reddit-direct-video-style'; | |||
| 185 | - | style.textContent = | |||
| 186 | - | 'shreddit-player[data-dumber-direct-video="1"] { position: relative !important; }' + | |||
| 187 | - | 'shreddit-player[data-dumber-direct-video="1"] > .dumber-direct-video {' + | |||
| 188 | - | ' position: absolute !important; inset: 0 !important; width: 100% !important; height: 100% !important;' + | |||
| 189 | - | ' object-fit: contain !important; background: #000 !important; z-index: 2147483647 !important;' + | |||
| 190 | - | ' display: block !important; }'; | |||
| 191 | - | document.head.appendChild(style); | |||
| 192 | - | } | |||
| 193 | - | ||||
| 194 | - | function candidateRoots(video) { | |||
| 195 | - | var roots = []; | |||
| 196 | - | var seen = []; | |||
| 197 | - | function add(root) { | |||
| 198 | - | if (!root) return; | |||
| 199 | - | if (seen.indexOf(root) !== -1) return; | |||
| 200 | - | seen.push(root); | |||
| 201 | - | roots.push(root); | |||
| 202 | - | } | |||
| 203 | - | ||||
| 204 | - | var current = video; | |||
| 205 | - | while (current) { | |||
| 206 | - | add(current); | |||
| 207 | - | if (current.shadowRoot) add(current.shadowRoot); | |||
| 208 | - | current = current.parentElement; | |||
| 209 | - | } | |||
| 210 | - | add(document); | |||
| 211 | - | return roots; | |||
| 212 | - | } | |||
| 213 | - | ||||
| 214 | - | function findHlsSource(video) { | |||
| 215 | - | var selectors = [ | |||
| 216 | - | 'source[type="application/vnd.apple.mpegURL"][src]', | |||
| 217 | - | 'source[type="application/x-mpegURL"][src]', | |||
| 218 | - | 'source[src*="HLSPlaylist.m3u8"]', | |||
| 219 | - | '[src*="HLSPlaylist.m3u8"]' | |||
| 220 | - | ]; | |||
| 221 | - | ||||
| 222 | - | var roots = candidateRoots(video); | |||
| 223 | - | for (var i = 0; i < roots.length; i++) { | |||
| 224 | - | var root = roots[i]; | |||
| 225 | - | if (!root.querySelector) continue; | |||
| 226 | - | for (var j = 0; j < selectors.length; j++) { | |||
| 227 | - | var el = root.querySelector(selectors[j]); | |||
| 228 | - | if (el && el.src) return el.src; | |||
| 229 | - | } | |||
| 230 | - | if (root.querySelectorAll) { | |||
| 231 | - | var all = root.querySelectorAll('*'); | |||
| 232 | - | for (var k = 0; k < all.length; k++) { | |||
| 233 | - | if (all[k].shadowRoot) { | |||
| 234 | - | roots.push(all[k].shadowRoot); | |||
| 235 | - | } | |||
| 236 | - | } | |||
| 237 | - | } | |||
| 238 | - | } | |||
| 239 | - | return ''; | |||
| 240 | - | } | |||
| 241 | - | ||||
| 242 | - | function buildPlaybackURL(hlsURL) { | |||
| 243 | - | return location.origin + '/__dumber__/transcode.webm?src=' + encodeURIComponent(hlsURL) + | |||
| 244 | - | '&referer=' + encodeURIComponent(location.href) + | |||
| 245 | - | '&origin=' + encodeURIComponent(location.origin); | |||
| 246 | - | } | |||
| 247 | - | ||||
| 248 | - | function getPlayerRoot(video) { | |||
| 249 | - | return video.closest('shreddit-player') || video.parentElement; | |||
| 250 | - | } | |||
| 251 | - | ||||
| 252 | - | function monitorReplacement(video) { | |||
| 253 | - | if (!video || video._dumberReplacementDiag) return; | |||
| 254 | - | video._dumberReplacementDiag = true; | |||
| 255 | - | [ | |||
| 256 | - | 'loadstart', 'loadedmetadata', 'loadeddata', 'canplay', 'playing', | |||
| 257 | - | 'waiting', 'stalled', 'suspend', 'abort', 'emptied', 'error', | |||
| 258 | - | ].forEach(function(evt) { | |||
| 259 | - | video.addEventListener(evt, function() { | |||
| 260 | - | var err = video.error ? ('code=' + video.error.code + ' msg=' + video.error.message) : 'none'; | |||
| 261 | - | console.warn(tag, 'replacement', evt, 'ready:', video.readyState, 'net:', video.networkState, | |||
| 262 | - | 'paused:', video.paused, 'error:', err, 'src:', | |||
| 263 | - | (video.currentSrc || video.src || '').substring(0, 120)); | |||
| 264 | - | }); | |||
| 265 | - | }); | |||
| 266 | - | } | |||
| 267 | - | ||||
| 268 | - | function ensureReplacement(video, playbackURL) { | |||
| 269 | - | var root = getPlayerRoot(video); | |||
| 270 | - | if (!root) return null; | |||
| 271 | - | ensureStyle(); | |||
| 272 | - | ||||
| 273 | - | var replacement = root.querySelector(':scope > .dumber-direct-video'); | |||
| 274 | - | if (!replacement) { | |||
| 275 | - | replacement = document.createElement('video'); | |||
| 276 | - | replacement.className = 'dumber-direct-video'; | |||
| 277 | - | replacement.controls = true; | |||
| 278 | - | replacement.playsInline = true; | |||
| 279 | - | replacement.preload = 'auto'; | |||
| 280 | - | replacement.autoplay = true; | |||
| 281 | - | replacement.muted = true; | |||
| 282 | - | replacement.loop = video.loop; | |||
| 283 | - | root.setAttribute('data-dumber-direct-video', '1'); | |||
| 284 | - | root.appendChild(replacement); | |||
| 285 | - | } | |||
| 286 | - | ||||
| 287 | - | replacement.poster = video.poster || replacement.poster || ''; | |||
| 288 | - | monitorReplacement(replacement); | |||
| 289 | - | if (replacement.src !== playbackURL) { | |||
| 290 | - | replacement.src = playbackURL; | |||
| 291 | - | replacement.load(); | |||
| 292 | - | } | |||
| 293 | - | return replacement; | |||
| 294 | - | } | |||
| 295 | - | ||||
| 296 | - | function shouldForce(video) { | |||
| 297 | - | var current = video.currentSrc || video.src || ''; | |||
| 298 | - | return current.indexOf('blob:') === 0; | |||
| 299 | - | } | |||
| 300 | - | ||||
| 301 | - | function forceDirectPlayback(video) { | |||
| 302 | - | if (!video || !shouldForce(video)) return; | |||
| 303 | - | ||||
| 304 | - | var hlsURL = findHlsSource(video); | |||
| 305 | - | if (!hlsURL) { | |||
| 306 | - | console.warn(tag, 'blob video found without HLS source'); | |||
| 307 | - | return; | |||
| 308 | - | } | |||
| 309 | - | var playbackURL = buildPlaybackURL(hlsURL); | |||
| 310 | - | if (video._dumberPlaybackURL === playbackURL) return; | |||
| 311 | - | video._dumberPlaybackURL = playbackURL; | |||
| 312 | - | ||||
| 313 | - | console.warn(tag, 'forcing replacement video source:', playbackURL.substring(0, 120), 'from:', hlsURL.substring(0, 120)); | |||
| 314 | - | ||||
| 315 | - | var replacement = ensureReplacement(video, playbackURL); | |||
| 316 | - | if (!replacement) return; | |||
| 317 | - | ||||
| 318 | - | var root = getPlayerRoot(video); | |||
| 319 | - | if (root && root.querySelectorAll) { | |||
| 320 | - | root.querySelectorAll('video').forEach(function(candidate) { | |||
| 321 | - | if (candidate === replacement) return; | |||
| 322 | - | try { candidate.pause(); } catch (e) {} | |||
| 323 | - | try { candidate.removeAttribute('src'); } catch (e) {} | |||
| 324 | - | try { candidate.srcObject = null; } catch (e) {} | |||
| 325 | - | try { candidate.load(); } catch (e) {} | |||
| 326 | - | candidate.style.display = 'none'; | |||
| 327 | - | }); | |||
| 328 | - | } else { | |||
| 329 | - | try { video.pause(); } catch (e) {} | |||
| 330 | - | try { video.removeAttribute('src'); } catch (e) {} | |||
| 331 | - | try { video.srcObject = null; } catch (e) {} | |||
| 332 | - | try { video.load(); } catch (e) {} | |||
| 333 | - | video.style.display = 'none'; | |||
| 334 | - | } | |||
| 335 | - | ||||
| 336 | - | var playPromise = replacement.play(); | |||
| 337 | - | if (playPromise && typeof playPromise.catch === 'function') { | |||
| 338 | - | playPromise.catch(function(err) { | |||
| 339 | - | console.warn(tag, 'replacement video.play() rejected:', err && err.message ? err.message : err); | |||
| 340 | - | }); | |||
| 341 | - | } | |||
| 342 | - | } | |||
| 343 | - | ||||
| 344 | - | function patchTree(root) { | |||
| 345 | - | if (!root || !root.querySelectorAll) return; | |||
| 346 | - | root.querySelectorAll('video').forEach(forceDirectPlayback); | |||
| 347 | - | root.querySelectorAll('*').forEach(function(el) { | |||
| 348 | - | if (el.shadowRoot) patchTree(el.shadowRoot); | |||
| 349 | - | }); | |||
| 350 | - | } | |||
| 351 | - | ||||
| 352 | - | new MutationObserver(function(mutations) { | |||
| 353 | - | mutations.forEach(function(mutation) { | |||
| 354 | - | if (mutation.type === 'attributes' && mutation.target && mutation.target.nodeName === 'VIDEO') { | |||
| 355 | - | forceDirectPlayback(mutation.target); | |||
| 356 | - | } | |||
| 357 | - | mutation.addedNodes.forEach(function(node) { | |||
| 358 | - | if (node.nodeName === 'VIDEO') forceDirectPlayback(node); | |||
| 359 | - | patchTree(node); | |||
| 360 | - | if (node.shadowRoot) patchTree(node.shadowRoot); | |||
| 361 | - | }); | |||
| 362 | - | }); | |||
| 363 | - | }).observe(document.documentElement, { | |||
| 364 | - | subtree: true, | |||
| 365 | - | childList: true, | |||
| 366 | - | attributes: true, | |||
| 367 | - | attributeFilter: ['src'] | |||
| 368 | - | }); | |||
| 369 | - | ||||
| 370 | - | // Chain to existing attachShadow patch (e.g. videoDiagnosticJS) instead of overwriting. | |||
| 371 | - | var prevAttach = Element.prototype.attachShadow; | |||
| 372 | - | Element.prototype.attachShadow = function(opts) { | |||
| 373 | - | var sr = prevAttach.call(this, opts); | |||
| 374 | - | patchTree(sr); | |||
| 375 | - | return sr; | |||
| 376 | - | }; | |||
| 377 | - | ||||
| 378 | - | patchTree(document); | |||
| 379 | - | var pollCount = 0; | |||
| 380 | - | var maxPolls = 30; | |||
| 381 | - | var pollID = setInterval(function() { | |||
| 382 | - | patchTree(document); | |||
| 383 | - | pollCount++; | |||
| 384 | - | if (pollCount >= maxPolls) clearInterval(pollID); | |||
| 385 | - | }, 1000); | |||
| 386 | - | })();` | |||
| 387 | - | ||||
| 388 | // contentInjector implements port.ContentInjector for the CEF engine. | 165 | // contentInjector implements port.ContentInjector for the CEF engine. | ||
| 389 | // It stores CSS strings and injects them into webviews via ExecuteJavaScript. | 166 | // It stores CSS strings and injects them into webviews via ExecuteJavaScript. | ||
| 390 | // Thread-safe: InjectThemeCSS may be called from the UI thread while OnLoadEnd | 167 | // Thread-safe: InjectThemeCSS may be called from the UI thread while OnLoadEnd | ||
| @@ -524,12 +301,6 @@ func (ci *contentInjector) onLoadEnd(wv *WebView) { | |||||
| 524 | wv.RunJavaScript(context.Background(), videoDiagnosticJS) | 301 | wv.RunJavaScript(context.Background(), videoDiagnosticJS) | ||
| 525 | } | 302 | } | ||
| 526 | 303 | ||||
| 527 | - | if parsed, err := url.Parse(uri); err == nil { | |||
| 528 | - | host := parsed.Hostname() | |||
| 529 | - | if host == "reddit.com" || strings.HasSuffix(host, ".reddit.com") { | |||
| 530 | - | wv.RunJavaScript(context.Background(), redditDirectVideoJS) | |||
| 531 | - | } | |||
| 532 | - | } | |||
| 533 | } | 304 | } | ||
| 534 | 305 | ||||
| 535 | // injectCSS injects a CSS string as a <style> element via JavaScript. | 306 | // injectCSS injects a CSS string as a <style> element via JavaScript. | ||