Skip to content

Commit 03f608f

Browse files
committed
fix(autoHeightBridge): track media with ResizeObserver and stabilize measurements
- Add `once` helper and `scheduleMediaMeasure` to consolidate double-measure logic - Introduce media ResizeObserver (ensureMediaObserver) stored on state and cleaned up - Track media elements when WeakSet unavailable via per-element TRACKED_FLAG and remove flag on cleanup - Observe media elements (img, iframe, video) and use observer + event handlers to trigger stable measurements - Use element.decode() for images and wrap image/iframe/video handlers with `once` to avoid duplicate work - Reset mediaObserver in cleanup to avoid leaked observers
1 parent e2f8f3f commit 03f608f

File tree

1 file changed

+85
-29
lines changed

1 file changed

+85
-29
lines changed

src/constants/autoHeightBridge.ts

Lines changed: 85 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
export const AUTO_HEIGHT_BRIDGE = `(() => {
66
var GLOBAL_KEY = '__RN_SIZED_WEBVIEW__';
77
var WRAPPER_ID = '__RN_SIZED_WEBVIEW_WRAPPER__';
8+
var TRACKED_FLAG = '__RN_SIZED_WEBVIEW_MEDIA__';
89
var MESSAGE_KEY = '__AUTO_HEIGHT__';
910
var ACTIVE_DEBOUNCE_MS = 48;
1011
var IDLE_DEBOUNCE_MS = 160;
@@ -36,6 +37,19 @@ export const AUTO_HEIGHT_BRIDGE = `(() => {
3637
setTimeout(callback, 0);
3738
};
3839
40+
var once = function (fn) {
41+
var called = false;
42+
43+
return function () {
44+
if (called) {
45+
return;
46+
}
47+
48+
called = true;
49+
return fn.apply(this, arguments);
50+
};
51+
};
52+
3953
var state = {
4054
frame: null,
4155
timer: null,
@@ -48,6 +62,7 @@ export const AUTO_HEIGHT_BRIDGE = `(() => {
4862
fallbackDelay: INITIAL_FALLBACK_MS,
4963
cleanup: [],
5064
wrapper: null,
65+
mediaObserver: null,
5166
};
5267
5368
window[GLOBAL_KEY] = state;
@@ -104,6 +119,7 @@ export const AUTO_HEIGHT_BRIDGE = `(() => {
104119
105120
state.cleanup.length = 0;
106121
state.wrapper = null;
122+
state.mediaObserver = null;
107123
window[GLOBAL_KEY] = undefined;
108124
};
109125
@@ -402,6 +418,32 @@ export const AUTO_HEIGHT_BRIDGE = `(() => {
402418
});
403419
};
404420
421+
var scheduleMediaMeasure = function () {
422+
scheduleMeasure(true);
423+
requestFrame(function () {
424+
scheduleMeasure(true);
425+
});
426+
};
427+
428+
var ensureMediaObserver = function () {
429+
if (state.mediaObserver || typeof ResizeObserver !== 'function') {
430+
return state.mediaObserver;
431+
}
432+
433+
var observer = new ResizeObserver(function () {
434+
scheduleMediaMeasure();
435+
});
436+
437+
state.mediaObserver = observer;
438+
439+
addCleanup(function () {
440+
observer.disconnect();
441+
state.mediaObserver = null;
442+
});
443+
444+
return observer;
445+
};
446+
405447
var tryTrackMedia = function (element) {
406448
if (!element || typeof element.tagName !== 'string') {
407449
return;
@@ -413,16 +455,38 @@ export const AUTO_HEIGHT_BRIDGE = `(() => {
413455
414456
if (trackedMedia) {
415457
trackedMedia.add(element);
458+
} else if (element[TRACKED_FLAG]) {
459+
return;
460+
} else {
461+
try {
462+
element[TRACKED_FLAG] = true;
463+
} catch (error) {
464+
// no-op
465+
}
466+
467+
addCleanup(function () {
468+
try {
469+
delete element[TRACKED_FLAG];
470+
} catch (error) {
471+
// no-op
472+
}
473+
});
474+
}
475+
476+
var observer = ensureMediaObserver();
477+
if (observer) {
478+
try {
479+
observer.observe(element);
480+
} catch (error) {
481+
// no-op
482+
}
416483
}
417484
418485
var tag = element.tagName.toUpperCase();
419486
420487
if (tag === 'IMG') {
421488
if (element.complete && element.naturalHeight) {
422-
scheduleMeasure(true);
423-
requestFrame(function () {
424-
scheduleMeasure(true);
425-
});
489+
scheduleMediaMeasure();
426490
return;
427491
}
428492
@@ -431,18 +495,19 @@ export const AUTO_HEIGHT_BRIDGE = `(() => {
431495
var cleanupLoad = function () {};
432496
var cleanupError = function () {};
433497
434-
var onSettled = function () {
498+
var finalizeImage = once(function () {
435499
cleanupLoad();
436500
cleanupError();
437501
clearLoading();
438-
scheduleMeasure(true);
439-
requestFrame(function () {
440-
scheduleMeasure(true);
441-
});
442-
};
502+
scheduleMediaMeasure();
503+
});
443504
444-
cleanupLoad = addEvent(element, 'load', onSettled, { once: true });
445-
cleanupError = addEvent(element, 'error', onSettled, { once: true });
505+
cleanupLoad = addEvent(element, 'load', finalizeImage, { once: true });
506+
cleanupError = addEvent(element, 'error', finalizeImage, { once: true });
507+
508+
if (typeof element.decode === 'function') {
509+
element.decode().then(finalizeImage).catch(finalizeImage);
510+
}
446511
447512
return;
448513
}
@@ -453,23 +518,20 @@ export const AUTO_HEIGHT_BRIDGE = `(() => {
453518
var cleanupLoadIframe = function () {};
454519
var cleanupErrorIframe = function () {};
455520
456-
var onIframe = function () {
521+
var onIframe = once(function () {
457522
cleanupLoadIframe();
458523
cleanupErrorIframe();
459524
clearLoading();
460-
scheduleMeasure(true);
461-
requestFrame(function () {
462-
scheduleMeasure(true);
463-
});
464-
};
525+
scheduleMediaMeasure();
526+
});
465527
466528
cleanupLoadIframe = addEvent(element, 'load', onIframe, { once: true });
467529
cleanupErrorIframe = addEvent(element, 'error', onIframe, { once: true });
468530
469531
try {
470532
var iframeDoc = element.contentDocument;
471533
if (iframeDoc && iframeDoc.readyState === 'complete') {
472-
scheduleMeasure(true);
534+
scheduleMediaMeasure();
473535
requestFrame(onIframe);
474536
}
475537
} catch (error) {
@@ -484,10 +546,7 @@ export const AUTO_HEIGHT_BRIDGE = `(() => {
484546
typeof element.readyState === 'number' &&
485547
element.readyState >= 2
486548
) {
487-
scheduleMeasure(true);
488-
requestFrame(function () {
489-
scheduleMeasure(true);
490-
});
549+
scheduleMediaMeasure();
491550
return;
492551
}
493552
@@ -497,16 +556,13 @@ export const AUTO_HEIGHT_BRIDGE = `(() => {
497556
var cleanupMetadata = function () {};
498557
var cleanupEnded = function () {};
499558
500-
var onVideo = function () {
559+
var onVideo = once(function () {
501560
cleanupData();
502561
cleanupMetadata();
503562
cleanupEnded();
504563
clearLoading();
505-
scheduleMeasure(true);
506-
requestFrame(function () {
507-
scheduleMeasure(true);
508-
});
509-
};
564+
scheduleMediaMeasure();
565+
});
510566
511567
cleanupData = addEvent(element, 'loadeddata', onVideo, { once: true });
512568
cleanupMetadata = addEvent(element, 'loadedmetadata', onVideo, {

0 commit comments

Comments
 (0)