@skrillex1224/playwright-toolkit 3.0.25 → 3.0.27
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/dist/browser.js +233 -2
- package/dist/browser.js.map +3 -3
- package/dist/index.cjs +557 -422
- package/dist/index.cjs.map +4 -4
- package/dist/index.js +555 -420
- package/dist/index.js.map +4 -4
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -229,7 +229,7 @@ var ActorInfo = {
|
|
|
229
229
|
mode: "response",
|
|
230
230
|
prefix: "https://www.doubao.com/thread/",
|
|
231
231
|
xurl: [
|
|
232
|
-
"/
|
|
232
|
+
"/share/save",
|
|
233
233
|
"data",
|
|
234
234
|
"share_id"
|
|
235
235
|
]
|
|
@@ -256,7 +256,6 @@ var ActorInfo = {
|
|
|
256
256
|
name: "\u6587\u5FC3\u4E00\u8A00",
|
|
257
257
|
domain: "wenxin.baidu.com",
|
|
258
258
|
path: "/",
|
|
259
|
-
device: Device.Mobile,
|
|
260
259
|
share: {
|
|
261
260
|
mode: "custom",
|
|
262
261
|
prefix: "",
|
|
@@ -495,9 +494,7 @@ function createInternalLogger(moduleName, explicitLogger) {
|
|
|
495
494
|
|
|
496
495
|
// src/internals/screenshot.js
|
|
497
496
|
var import_delay = __toESM(require("delay"), 1);
|
|
498
|
-
|
|
499
|
-
// src/internals/constants.js
|
|
500
|
-
var PageRuntimeStateKey = "__playwright_toolkit_runtime_state__";
|
|
497
|
+
var import_jimp = require("jimp");
|
|
501
498
|
|
|
502
499
|
// src/internals/viewport.js
|
|
503
500
|
var toPositiveInt = (value) => {
|
|
@@ -529,21 +526,22 @@ var resolveCurrentViewportSize = async (page, fallback = {}) => {
|
|
|
529
526
|
// src/internals/screenshot.js
|
|
530
527
|
var logger = createInternalLogger("Screenshot");
|
|
531
528
|
var DEFAULT_TIMEOUT_MS = 5e3;
|
|
532
|
-
var FORCED_FULLPAGE_TYPE = "jpeg";
|
|
533
|
-
var FORCED_FULLPAGE_QUALITY = 50;
|
|
534
|
-
var SUPPORTED_TYPES = /* @__PURE__ */ new Set(["png", "jpeg", "webp"]);
|
|
535
|
-
var EXPANDED_SCROLLABLE_CLASS = "__pk_expanded__";
|
|
536
529
|
var DEFAULT_MAX_HEIGHT = 8e3;
|
|
537
|
-
var DEFAULT_SETTLE_MS =
|
|
538
|
-
var
|
|
530
|
+
var DEFAULT_SETTLE_MS = 260;
|
|
531
|
+
var DEFAULT_QUALITY_RETRY_ATTEMPTS = 4;
|
|
532
|
+
var DEFAULT_QUALITY_RETRY_DELAY_MS = 2500;
|
|
533
|
+
var EXPANDED_ATTR = "data-pk-screenshot-expanded";
|
|
534
|
+
var SUPPORTED_TYPES = /* @__PURE__ */ new Set(["png", "jpeg", "webp"]);
|
|
535
|
+
var FULLPAGE_FALLBACK_TYPE = "jpeg";
|
|
536
|
+
var FULLPAGE_FALLBACK_QUALITY = 50;
|
|
539
537
|
var toPositiveNumber = (value, fallback = 0) => {
|
|
540
538
|
const n = Number(value);
|
|
541
539
|
if (!Number.isFinite(n) || n <= 0) return fallback;
|
|
542
540
|
return n;
|
|
543
541
|
};
|
|
544
542
|
var toPositiveInteger = (value, fallback = 0) => {
|
|
545
|
-
const
|
|
546
|
-
return
|
|
543
|
+
const n = Math.round(Number(value) || 0);
|
|
544
|
+
return n > 0 ? n : fallback;
|
|
547
545
|
};
|
|
548
546
|
var normalizeType = (value) => {
|
|
549
547
|
const raw = String(value || "png").trim().toLowerCase();
|
|
@@ -558,488 +556,622 @@ var normalizeQuality = (value, type) => {
|
|
|
558
556
|
if (rounded < 0 || rounded > 100) return void 0;
|
|
559
557
|
return rounded;
|
|
560
558
|
};
|
|
561
|
-
var resolvePageDevice = (page) => normalizeDevice(page?.[PageRuntimeStateKey]?.device);
|
|
562
559
|
var buildFullPageClip = (metrics, viewport, maxClipHeight) => {
|
|
563
560
|
const contentSize = metrics && typeof metrics === "object" ? metrics.contentSize || null : null;
|
|
564
561
|
const width = Math.max(1, Math.ceil(viewport.width || contentSize?.width || 1));
|
|
565
562
|
let height = Math.max(1, Math.ceil(contentSize?.height || viewport.height || 1));
|
|
566
|
-
if (maxClipHeight > 0)
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
563
|
+
if (maxClipHeight > 0) height = Math.min(height, maxClipHeight);
|
|
564
|
+
return { x: 0, y: 0, width, height, scale: 1 };
|
|
565
|
+
};
|
|
566
|
+
var capturePageScreenshot = async (page, options = {}) => {
|
|
567
|
+
const fullPage = Boolean(options.fullPage);
|
|
568
|
+
const type = fullPage ? FULLPAGE_FALLBACK_TYPE : normalizeType(options.type);
|
|
569
|
+
const quality = fullPage ? FULLPAGE_FALLBACK_QUALITY : normalizeQuality(options.quality, type);
|
|
570
|
+
const timeout = toPositiveNumber(options.timeout, DEFAULT_TIMEOUT_MS);
|
|
571
|
+
const maxClipHeight = Math.round(toPositiveNumber(options.maxClipHeight, 0));
|
|
572
|
+
const fallbackOptions = {
|
|
573
|
+
type: type === "webp" ? "png" : type,
|
|
574
|
+
fullPage
|
|
575
575
|
};
|
|
576
|
+
if (quality !== void 0 && fallbackOptions.type === "jpeg") fallbackOptions.quality = quality;
|
|
577
|
+
if (timeout > 0) fallbackOptions.timeout = timeout;
|
|
578
|
+
try {
|
|
579
|
+
const context = page && typeof page.context === "function" ? page.context() : null;
|
|
580
|
+
if (!context || typeof context.newCDPSession !== "function") {
|
|
581
|
+
throw new Error("CDP session is not available");
|
|
582
|
+
}
|
|
583
|
+
const session = await context.newCDPSession(page);
|
|
584
|
+
try {
|
|
585
|
+
const viewport = await resolveCurrentViewportSize(page);
|
|
586
|
+
const captureParams = {
|
|
587
|
+
format: type,
|
|
588
|
+
fromSurface: true,
|
|
589
|
+
captureBeyondViewport: fullPage,
|
|
590
|
+
optimizeForSpeed: true
|
|
591
|
+
};
|
|
592
|
+
if (quality !== void 0) captureParams.quality = quality;
|
|
593
|
+
if (fullPage) {
|
|
594
|
+
const metrics = await session.send("Page.getLayoutMetrics");
|
|
595
|
+
captureParams.clip = buildFullPageClip(metrics, viewport, maxClipHeight);
|
|
596
|
+
}
|
|
597
|
+
const result = await session.send("Page.captureScreenshot", captureParams);
|
|
598
|
+
if (!result || typeof result.data !== "string" || !result.data) {
|
|
599
|
+
throw new Error("CDP returned empty screenshot data");
|
|
600
|
+
}
|
|
601
|
+
return Buffer.from(result.data, "base64");
|
|
602
|
+
} finally {
|
|
603
|
+
if (typeof session.detach === "function") {
|
|
604
|
+
await session.detach().catch(() => {
|
|
605
|
+
});
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
} catch (error) {
|
|
609
|
+
logger.warning(`CDP \u622A\u56FE\u5931\u8D25\uFF0C\u56DE\u9000 page.screenshot: ${error?.message || error}`);
|
|
610
|
+
return await page.screenshot(fallbackOptions);
|
|
611
|
+
}
|
|
576
612
|
};
|
|
577
|
-
var
|
|
578
|
-
return await page.evaluate((
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
613
|
+
var readMainFrameScroll = async (page) => {
|
|
614
|
+
return await page.evaluate(() => ({
|
|
615
|
+
x: Math.max(0, Math.round(window.scrollX || document.scrollingElement?.scrollLeft || 0)),
|
|
616
|
+
y: Math.max(0, Math.round(window.scrollY || document.scrollingElement?.scrollTop || 0))
|
|
617
|
+
})).catch(() => ({ x: 0, y: 0 }));
|
|
618
|
+
};
|
|
619
|
+
var restoreMainFrameScroll = async (page, state2) => {
|
|
620
|
+
if (!state2) return;
|
|
621
|
+
await page.evaluate(({ x, y }) => {
|
|
622
|
+
window.scrollTo(Math.max(0, Math.round(Number(x) || 0)), Math.max(0, Math.round(Number(y) || 0)));
|
|
623
|
+
}, state2).catch(() => {
|
|
624
|
+
});
|
|
625
|
+
};
|
|
626
|
+
var freezeViewportMetrics = async (page) => {
|
|
627
|
+
const viewport = await resolveCurrentViewportSize(page).catch(() => null);
|
|
628
|
+
if (!viewport?.width || !viewport?.height) return false;
|
|
629
|
+
return await page.evaluate(({ width, height }) => {
|
|
630
|
+
const key = "__pkScreenshotViewportFreeze";
|
|
631
|
+
if (window[key]?.active) return true;
|
|
632
|
+
const state2 = {
|
|
633
|
+
active: true,
|
|
634
|
+
entries: []
|
|
589
635
|
};
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
636
|
+
const override = (target, prop, value) => {
|
|
637
|
+
if (!target) return;
|
|
638
|
+
try {
|
|
639
|
+
const descriptor = Object.getOwnPropertyDescriptor(target, prop);
|
|
640
|
+
state2.entries.push({
|
|
641
|
+
target,
|
|
642
|
+
prop,
|
|
643
|
+
hadOwn: Boolean(descriptor),
|
|
644
|
+
descriptor
|
|
645
|
+
});
|
|
646
|
+
Object.defineProperty(target, prop, {
|
|
647
|
+
configurable: true,
|
|
648
|
+
get: () => value
|
|
649
|
+
});
|
|
650
|
+
} catch {
|
|
651
|
+
}
|
|
652
|
+
};
|
|
653
|
+
override(window, "innerWidth", width);
|
|
654
|
+
override(window, "innerHeight", height);
|
|
655
|
+
if (window.visualViewport) {
|
|
656
|
+
override(window.visualViewport, "width", width);
|
|
657
|
+
override(window.visualViewport, "height", height);
|
|
658
|
+
}
|
|
659
|
+
window[key] = state2;
|
|
660
|
+
return true;
|
|
661
|
+
}, {
|
|
662
|
+
width: Math.max(1, Math.round(viewport.width)),
|
|
663
|
+
height: Math.max(1, Math.round(viewport.height))
|
|
664
|
+
}).catch((error) => {
|
|
665
|
+
logger.warning(`\u51BB\u7ED3 viewport \u6307\u6807\u5931\u8D25: ${error?.message || error}`);
|
|
666
|
+
return false;
|
|
667
|
+
});
|
|
668
|
+
};
|
|
669
|
+
var restoreViewportMetrics = async (page) => {
|
|
670
|
+
await page.evaluate(() => {
|
|
671
|
+
const key = "__pkScreenshotViewportFreeze";
|
|
672
|
+
const state2 = window[key];
|
|
673
|
+
if (!state2?.active) return;
|
|
674
|
+
for (const entry of [...state2.entries].reverse()) {
|
|
675
|
+
try {
|
|
676
|
+
if (entry.hadOwn && entry.descriptor) {
|
|
677
|
+
Object.defineProperty(entry.target, entry.prop, entry.descriptor);
|
|
678
|
+
} else {
|
|
679
|
+
delete entry.target[entry.prop];
|
|
680
|
+
}
|
|
681
|
+
} catch {
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
delete window[key];
|
|
685
|
+
}).catch(() => {
|
|
686
|
+
});
|
|
687
|
+
};
|
|
688
|
+
var expandScrollableContentInContext = async (context, options = {}) => {
|
|
689
|
+
return await context.evaluate(async ({
|
|
690
|
+
expandedAttr,
|
|
691
|
+
maxHeight,
|
|
692
|
+
preloadLazyContent,
|
|
693
|
+
preloadSettleMs,
|
|
694
|
+
visibleOnly
|
|
695
|
+
}) => {
|
|
696
|
+
const root = document.documentElement;
|
|
697
|
+
const body = document.body;
|
|
698
|
+
const scrollingElement = document.scrollingElement || root || body;
|
|
699
|
+
const viewportWidth = window.innerWidth || root?.clientWidth || body?.clientWidth || 1;
|
|
700
|
+
const viewportHeight = window.innerHeight || root?.clientHeight || body?.clientHeight || 1;
|
|
594
701
|
const scrollableOverflow = /* @__PURE__ */ new Set(["auto", "scroll", "overlay"]);
|
|
595
702
|
const clippingOverflow = /* @__PURE__ */ new Set(["hidden", "clip"]);
|
|
703
|
+
const candidates = [];
|
|
704
|
+
const changed = [];
|
|
705
|
+
const push = (el) => {
|
|
706
|
+
if (el && !candidates.includes(el)) candidates.push(el);
|
|
707
|
+
};
|
|
708
|
+
push(scrollingElement);
|
|
709
|
+
push(root);
|
|
710
|
+
push(body);
|
|
711
|
+
document.querySelectorAll("*").forEach(push);
|
|
596
712
|
const isDocumentElement = (el) => el === root || el === body || el === scrollingElement;
|
|
713
|
+
const delayMs = (ms) => new Promise((resolve) => setTimeout(resolve, Math.max(0, Number(ms) || 0)));
|
|
714
|
+
const styleValue = (el, prop) => el.style.getPropertyValue(prop) || "";
|
|
715
|
+
const stylePriority = (el, prop) => el.style.getPropertyPriority(prop) || "";
|
|
597
716
|
const isVisible = (el, style, rect) => {
|
|
598
717
|
if (!el || !style || !rect) return false;
|
|
599
|
-
if (style.display === "none" || style.visibility === "hidden" || style.visibility === "collapse")
|
|
600
|
-
|
|
601
|
-
}
|
|
602
|
-
if (Number(style.opacity) === 0) return false;
|
|
718
|
+
if (style.display === "none" || style.visibility === "hidden" || style.visibility === "collapse") return false;
|
|
719
|
+
if (Number(style.opacity || 1) === 0) return false;
|
|
603
720
|
return rect.width > 0 && rect.height > 0;
|
|
604
721
|
};
|
|
722
|
+
const intersectsViewport = (rect) => {
|
|
723
|
+
if (!rect || rect.width <= 0 || rect.height <= 0) return false;
|
|
724
|
+
return rect.bottom > 0 && rect.right > 0 && rect.top < viewportHeight && rect.left < viewportWidth;
|
|
725
|
+
};
|
|
726
|
+
const isVisibleInViewport = (el, style, rect) => isVisible(el, style, rect) && intersectsViewport(rect);
|
|
605
727
|
const hasOverflowValue = (style, values) => {
|
|
606
|
-
const overflowY = String(style?.overflowY || "").toLowerCase();
|
|
607
728
|
const overflow = String(style?.overflow || "").toLowerCase();
|
|
608
|
-
|
|
729
|
+
const overflowY = String(style?.overflowY || "").toLowerCase();
|
|
730
|
+
return values.has(overflow) || values.has(overflowY);
|
|
609
731
|
};
|
|
610
732
|
const isLargeVerticalClipper = (el, rect) => {
|
|
611
733
|
if (!el || !rect) return false;
|
|
612
|
-
const
|
|
613
|
-
const visibleHeight = Math.ceil(rect.height || 0);
|
|
614
|
-
const clientHeight = Math.ceil(el.clientHeight || 0);
|
|
615
|
-
const boxHeight = Math.max(visibleHeight, clientHeight);
|
|
734
|
+
const boxHeight = Math.max(Math.ceil(rect.height || 0), Math.ceil(el.clientHeight || 0));
|
|
616
735
|
return boxHeight >= Math.max(320, viewportHeight * 0.45);
|
|
617
736
|
};
|
|
618
|
-
const hasLargeVerticalOverflow = (el, rect) => {
|
|
619
|
-
if (!isLargeVerticalClipper(el, rect)) return false;
|
|
620
|
-
const viewportHeight = window.innerHeight || 0;
|
|
621
|
-
const scrollHeight = Math.ceil(el.scrollHeight || 0);
|
|
622
|
-
const clientHeight = Math.ceil(el.clientHeight || 0);
|
|
623
|
-
const overflowDelta = scrollHeight - clientHeight;
|
|
624
|
-
return overflowDelta >= Math.max(160, viewportHeight * 0.18);
|
|
625
|
-
};
|
|
626
|
-
const rememberOriginalBoxStyles = (el, { includeDocumentElement = false } = {}) => {
|
|
627
|
-
if (!el || !includeDocumentElement && isDocumentElement(el) || el.classList.contains(className)) {
|
|
628
|
-
return;
|
|
629
|
-
}
|
|
630
|
-
el.dataset.pkOrigOverflow = el.style.overflow;
|
|
631
|
-
el.dataset.pkOrigOverflowPriority = el.style.getPropertyPriority("overflow") || "";
|
|
632
|
-
el.dataset.pkOrigHeight = el.style.height;
|
|
633
|
-
el.dataset.pkOrigHeightPriority = el.style.getPropertyPriority("height") || "";
|
|
634
|
-
el.dataset.pkOrigMinHeight = el.style.minHeight;
|
|
635
|
-
el.dataset.pkOrigMinHeightPriority = el.style.getPropertyPriority("min-height") || "";
|
|
636
|
-
el.dataset.pkOrigMaxHeight = el.style.maxHeight;
|
|
637
|
-
el.dataset.pkOrigMaxHeightPriority = el.style.getPropertyPriority("max-height") || "";
|
|
638
|
-
el.classList.add(className);
|
|
639
|
-
};
|
|
640
|
-
const expandDocumentElementsToHeight = (height) => {
|
|
641
|
-
if (!expandDocumentElements) return;
|
|
642
|
-
const targetHeight = Math.ceil(Number(height) || 0);
|
|
643
|
-
if (targetHeight <= 0) return;
|
|
644
|
-
[root, body, scrollingElement].forEach((el) => {
|
|
645
|
-
if (!el) return;
|
|
646
|
-
rememberOriginalBoxStyles(el, { includeDocumentElement: true });
|
|
647
|
-
el.style.setProperty("overflow", "visible", "important");
|
|
648
|
-
el.style.setProperty("height", `${targetHeight}px`, "important");
|
|
649
|
-
el.style.setProperty("min-height", `${targetHeight}px`, "important");
|
|
650
|
-
el.style.setProperty("max-height", "none", "important");
|
|
651
|
-
});
|
|
652
|
-
};
|
|
653
737
|
const isScrollableY = (el, style, rect) => {
|
|
654
|
-
if (!el || el.scrollHeight <= el.clientHeight +
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
if (
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
if (hasOverflowValue(style, scrollableOverflow)) {
|
|
661
|
-
return true;
|
|
662
|
-
}
|
|
663
|
-
return hasOverflowValue(style, clippingOverflow) && hasLargeVerticalOverflow(el, rect);
|
|
738
|
+
if (!el || Math.ceil(el.scrollHeight || 0) <= Math.ceil(el.clientHeight || 0) + 2) return false;
|
|
739
|
+
if (isDocumentElement(el)) return true;
|
|
740
|
+
if (hasOverflowValue(style, scrollableOverflow)) return true;
|
|
741
|
+
if (!hasOverflowValue(style, clippingOverflow)) return false;
|
|
742
|
+
const overflowDelta = Math.ceil(el.scrollHeight || 0) - Math.ceil(el.clientHeight || 0);
|
|
743
|
+
return isLargeVerticalClipper(el, rect) && overflowDelta >= Math.max(160, viewportHeight * 0.18);
|
|
664
744
|
};
|
|
665
|
-
const
|
|
666
|
-
if (!el) return
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
745
|
+
const rememberBox = (el) => {
|
|
746
|
+
if (!el || el.getAttribute(expandedAttr) === "1") return;
|
|
747
|
+
el.setAttribute(expandedAttr, "1");
|
|
748
|
+
el.dataset.pkSsOverflow = styleValue(el, "overflow");
|
|
749
|
+
el.dataset.pkSsOverflowPriority = stylePriority(el, "overflow");
|
|
750
|
+
el.dataset.pkSsOverflowX = styleValue(el, "overflow-x");
|
|
751
|
+
el.dataset.pkSsOverflowXPriority = stylePriority(el, "overflow-x");
|
|
752
|
+
el.dataset.pkSsOverflowY = styleValue(el, "overflow-y");
|
|
753
|
+
el.dataset.pkSsOverflowYPriority = stylePriority(el, "overflow-y");
|
|
754
|
+
el.dataset.pkSsHeight = styleValue(el, "height");
|
|
755
|
+
el.dataset.pkSsHeightPriority = stylePriority(el, "height");
|
|
756
|
+
el.dataset.pkSsMinHeight = styleValue(el, "min-height");
|
|
757
|
+
el.dataset.pkSsMinHeightPriority = stylePriority(el, "min-height");
|
|
758
|
+
el.dataset.pkSsMaxHeight = styleValue(el, "max-height");
|
|
759
|
+
el.dataset.pkSsMaxHeightPriority = stylePriority(el, "max-height");
|
|
760
|
+
el.dataset.pkSsWidth = styleValue(el, "width");
|
|
761
|
+
el.dataset.pkSsWidthPriority = stylePriority(el, "width");
|
|
762
|
+
el.dataset.pkSsBoxSizing = styleValue(el, "box-sizing");
|
|
763
|
+
el.dataset.pkSsBoxSizingPriority = stylePriority(el, "box-sizing");
|
|
764
|
+
el.dataset.pkSsAlignSelf = styleValue(el, "align-self");
|
|
765
|
+
el.dataset.pkSsAlignSelfPriority = stylePriority(el, "align-self");
|
|
766
|
+
el.dataset.pkSsJustifySelf = styleValue(el, "justify-self");
|
|
767
|
+
el.dataset.pkSsJustifySelfPriority = stylePriority(el, "justify-self");
|
|
768
|
+
el.dataset.pkSsScrollTop = String(Math.max(0, Math.round(el.scrollTop || 0)));
|
|
769
|
+
el.dataset.pkSsScrollLeft = String(Math.max(0, Math.round(el.scrollLeft || 0)));
|
|
770
|
+
changed.push(el);
|
|
678
771
|
};
|
|
679
|
-
const
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
if (!el || isDocumentElement(el)) return;
|
|
683
|
-
const scrollHeight = Math.ceil(el.scrollHeight || 0);
|
|
684
|
-
if (scrollHeight <= 0) return;
|
|
685
|
-
rememberOriginalBoxStyles(el);
|
|
772
|
+
const setExpandedBox = (el, { height, width }) => {
|
|
773
|
+
if (!el) return;
|
|
774
|
+
rememberBox(el);
|
|
686
775
|
el.style.setProperty("overflow", "visible", "important");
|
|
687
|
-
el.style.setProperty("
|
|
688
|
-
el.style.setProperty("min-height", `${scrollHeight}px`, "important");
|
|
776
|
+
el.style.setProperty("overflow-y", "visible", "important");
|
|
689
777
|
el.style.setProperty("max-height", "none", "important");
|
|
778
|
+
if (height > 0) {
|
|
779
|
+
const cssHeight = `${Math.ceil(height)}px`;
|
|
780
|
+
el.style.setProperty("height", cssHeight, "important");
|
|
781
|
+
el.style.setProperty("min-height", cssHeight, "important");
|
|
782
|
+
}
|
|
783
|
+
if (!isDocumentElement(el) && width > 0) {
|
|
784
|
+
el.style.setProperty("box-sizing", "border-box", "important");
|
|
785
|
+
el.style.setProperty("width", `${Math.ceil(width)}px`, "important");
|
|
786
|
+
el.style.setProperty("align-self", "flex-start", "important");
|
|
787
|
+
el.style.setProperty("justify-self", "start", "important");
|
|
788
|
+
}
|
|
789
|
+
el.scrollTop = 0;
|
|
790
|
+
el.scrollLeft = 0;
|
|
690
791
|
};
|
|
691
792
|
const expandClippingAncestors = (el) => {
|
|
692
793
|
for (let node = el?.parentElement; node && !isDocumentElement(node); node = node.parentElement) {
|
|
693
794
|
const style = window.getComputedStyle(node);
|
|
694
795
|
const rect = node.getBoundingClientRect();
|
|
695
796
|
if (!isVisible(node, style, rect)) continue;
|
|
696
|
-
const
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
rememberOriginalBoxStyles(node);
|
|
797
|
+
const clips = hasOverflowValue(style, scrollableOverflow) || hasOverflowValue(style, clippingOverflow);
|
|
798
|
+
if (!clips) continue;
|
|
799
|
+
rememberBox(node);
|
|
700
800
|
node.style.setProperty("overflow", "visible", "important");
|
|
801
|
+
node.style.setProperty("overflow-y", "visible", "important");
|
|
701
802
|
node.style.setProperty("max-height", "none", "important");
|
|
702
|
-
expandedAncestors.push(node);
|
|
703
803
|
}
|
|
704
804
|
};
|
|
705
|
-
const
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
805
|
+
const preloadCandidates = [];
|
|
806
|
+
for (const el of candidates) {
|
|
807
|
+
const style = window.getComputedStyle(el);
|
|
808
|
+
const rect = isDocumentElement(el) ? { top: 0, left: 0, width: viewportWidth, height: viewportHeight } : el.getBoundingClientRect();
|
|
809
|
+
if (visibleOnly && !isDocumentElement(el) && !isVisibleInViewport(el, style, rect)) continue;
|
|
810
|
+
if (!isScrollableY(el, style, rect)) continue;
|
|
811
|
+
preloadCandidates.push(el);
|
|
812
|
+
}
|
|
813
|
+
if (preloadLazyContent && preloadCandidates.length > 0) {
|
|
814
|
+
preloadCandidates.forEach((el) => {
|
|
815
|
+
rememberBox(el);
|
|
816
|
+
const maxTop = Math.max(0, Math.ceil((el.scrollHeight || 0) - (el.clientHeight || viewportHeight || 0)));
|
|
817
|
+
if (isDocumentElement(el)) {
|
|
818
|
+
window.scrollTo(window.scrollX || 0, maxTop);
|
|
819
|
+
} else {
|
|
820
|
+
el.scrollTop = maxTop;
|
|
821
|
+
el.dispatchEvent(new Event("scroll", { bubbles: true }));
|
|
822
|
+
}
|
|
823
|
+
});
|
|
824
|
+
window.dispatchEvent(new Event("scroll"));
|
|
825
|
+
await delayMs(preloadSettleMs);
|
|
826
|
+
}
|
|
827
|
+
let targetHeight = viewportHeight;
|
|
828
|
+
let scrollableCount = 0;
|
|
829
|
+
let expandedCount = 0;
|
|
830
|
+
for (const el of preloadCandidates) {
|
|
831
|
+
const rect = isDocumentElement(el) ? { top: 0, left: 0, width: viewportWidth, height: viewportHeight } : el.getBoundingClientRect();
|
|
832
|
+
const scrollHeight = Math.ceil(el.scrollHeight || 0);
|
|
833
|
+
const clientHeight = Math.ceil(el.clientHeight || viewportHeight || 0);
|
|
834
|
+
if (scrollHeight <= clientHeight + 2) continue;
|
|
835
|
+
scrollableCount += 1;
|
|
836
|
+
if (isDocumentElement(el)) {
|
|
837
|
+
targetHeight = Math.max(targetHeight, scrollHeight);
|
|
838
|
+
el.scrollTop = 0;
|
|
839
|
+
continue;
|
|
717
840
|
}
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
841
|
+
const documentTop = Math.max(0, Math.ceil(rect.top + (window.scrollY || window.pageYOffset || 0)));
|
|
842
|
+
const width = Math.max(1, Math.ceil(rect.width || el.clientWidth || viewportWidth || 1));
|
|
843
|
+
targetHeight = Math.max(targetHeight, documentTop + scrollHeight);
|
|
844
|
+
expandClippingAncestors(el);
|
|
845
|
+
setExpandedBox(el, { height: scrollHeight, width });
|
|
846
|
+
expandedCount += 1;
|
|
847
|
+
}
|
|
848
|
+
for (let pass = 0; pass < 2; pass += 1) {
|
|
849
|
+
for (const el of changed) {
|
|
850
|
+
if (!el || isDocumentElement(el)) continue;
|
|
851
|
+
const height = Math.ceil(el.scrollHeight || 0);
|
|
852
|
+
if (height > Math.ceil(el.clientHeight || 0) + 2) {
|
|
853
|
+
el.style.setProperty("height", `${height}px`, "important");
|
|
854
|
+
el.style.setProperty("min-height", `${height}px`, "important");
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
const measureDocumentHeight = () => Math.max(
|
|
859
|
+
viewportHeight,
|
|
860
|
+
Math.ceil(root?.scrollHeight || 0),
|
|
861
|
+
Math.ceil(body?.scrollHeight || 0),
|
|
862
|
+
Math.ceil(scrollingElement?.scrollHeight || 0)
|
|
863
|
+
);
|
|
864
|
+
const measureExpandedBottom = () => {
|
|
865
|
+
let bottom = viewportHeight;
|
|
721
866
|
const scrollY = window.scrollY || window.pageYOffset || 0;
|
|
722
|
-
|
|
867
|
+
document.querySelectorAll(`[${expandedAttr}="1"]`).forEach((el) => {
|
|
723
868
|
if (!el || isDocumentElement(el)) return;
|
|
724
|
-
if (!el.classList.contains(className) && !el.closest(`.${className}`)) return;
|
|
725
|
-
const style = window.getComputedStyle(el);
|
|
726
869
|
const rect = el.getBoundingClientRect();
|
|
727
|
-
if (!
|
|
728
|
-
|
|
870
|
+
if (!rect || rect.width <= 0 || rect.height <= 0) return;
|
|
871
|
+
bottom = Math.max(bottom, Math.ceil(rect.bottom + scrollY));
|
|
729
872
|
});
|
|
730
|
-
return
|
|
731
|
-
};
|
|
732
|
-
const measureDocumentScrollHeight = () => {
|
|
733
|
-
return Math.max(
|
|
734
|
-
maxHeight,
|
|
735
|
-
Math.ceil(root?.scrollHeight || 0),
|
|
736
|
-
Math.ceil(body?.scrollHeight || 0),
|
|
737
|
-
Math.ceil(scrollingElement?.scrollHeight || 0)
|
|
738
|
-
);
|
|
873
|
+
return bottom;
|
|
739
874
|
};
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
if (
|
|
744
|
-
|
|
745
|
-
}
|
|
746
|
-
if (!isScrollableY(el, style, rect)) {
|
|
747
|
-
return;
|
|
748
|
-
}
|
|
749
|
-
const neededHeight = measureNeededHeight(el);
|
|
750
|
-
if (neededHeight <= 0) {
|
|
751
|
-
return;
|
|
752
|
-
}
|
|
753
|
-
if (neededHeight > maxHeight) {
|
|
754
|
-
maxHeight = neededHeight;
|
|
755
|
-
}
|
|
756
|
-
scrollableElements.push(el);
|
|
757
|
-
if (isDocumentElement(el)) {
|
|
758
|
-
return;
|
|
759
|
-
}
|
|
760
|
-
expandClippingAncestors(el);
|
|
761
|
-
if (forceScrollableHeight) {
|
|
762
|
-
expandElementToScrollHeight(el);
|
|
763
|
-
return;
|
|
764
|
-
}
|
|
765
|
-
rememberOriginalBoxStyles(el);
|
|
766
|
-
el.style.overflow = "visible";
|
|
767
|
-
el.style.height = "auto";
|
|
768
|
-
el.style.maxHeight = "none";
|
|
875
|
+
targetHeight = Math.max(targetHeight, measureDocumentHeight(), measureExpandedBottom());
|
|
876
|
+
targetHeight = Math.min(Math.max(1, Math.ceil(targetHeight)), Math.max(1, Math.ceil(maxHeight || targetHeight)));
|
|
877
|
+
[root, body, scrollingElement].forEach((el) => {
|
|
878
|
+
if (!el) return;
|
|
879
|
+
setExpandedBox(el, { height: targetHeight, width: 0 });
|
|
769
880
|
});
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
881
|
+
window.scrollTo(0, 0);
|
|
882
|
+
document.querySelectorAll(`[${expandedAttr}="1"]`).forEach((el) => {
|
|
883
|
+
if (!isDocumentElement(el)) {
|
|
884
|
+
el.scrollTop = 0;
|
|
885
|
+
el.scrollLeft = 0;
|
|
775
886
|
}
|
|
776
887
|
});
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
}
|
|
784
|
-
return Math.ceil(maxHeight);
|
|
888
|
+
window.dispatchEvent(new Event("scroll"));
|
|
889
|
+
await delayMs(preloadSettleMs);
|
|
890
|
+
return {
|
|
891
|
+
height: Math.max(targetHeight, measureDocumentHeight(), measureExpandedBottom()),
|
|
892
|
+
scrollableCount,
|
|
893
|
+
expandedCount
|
|
894
|
+
};
|
|
785
895
|
}, {
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
896
|
+
expandedAttr: EXPANDED_ATTR,
|
|
897
|
+
maxHeight: toPositiveInteger(options.maxHeight, DEFAULT_MAX_HEIGHT),
|
|
898
|
+
preloadLazyContent: options.preloadLazyContent !== false,
|
|
899
|
+
preloadSettleMs: Math.max(0, Math.min(500, Number(options.preloadSettleMs ?? options.settleMs ?? 120) || 0)),
|
|
900
|
+
visibleOnly: options.visibleOnly !== false
|
|
901
|
+
}).catch((error) => {
|
|
902
|
+
logger.warning(`\u5C55\u5F00\u6EDA\u52A8\u5BB9\u5668\u5931\u8D25: ${error?.message || error}`);
|
|
903
|
+
return { height: 0, scrollableCount: 0, expandedCount: 0 };
|
|
790
904
|
});
|
|
791
905
|
};
|
|
792
|
-
var
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
const bottom = Number.parseFloat(style.bottom);
|
|
823
|
-
const hasTopRule = Number.isFinite(top) && top <= Math.max(160, viewportHeight * 0.25);
|
|
824
|
-
const hasBottomRule = Number.isFinite(bottom) && bottom <= Math.max(160, viewportHeight * 0.25);
|
|
825
|
-
const isNearViewportTop = rect.top <= edgeThreshold;
|
|
826
|
-
const isNearViewportBottom = rect.bottom >= viewportHeight - edgeThreshold;
|
|
827
|
-
const edge = (hasBottomRule || isNearViewportBottom) && !(hasTopRule || isNearViewportTop) ? "bottom" : "top";
|
|
828
|
-
if (position === "fixed" && edge === "top" && !hasTopRule && !isNearViewportTop) return;
|
|
829
|
-
if (position === "fixed" && edge === "bottom" && !hasBottomRule && !isNearViewportBottom) return;
|
|
830
|
-
if (position === "sticky" && !hasTopRule && !hasBottomRule && !isNearViewportTop && !isNearViewportBottom) return;
|
|
831
|
-
candidates.push({ el, position, edge });
|
|
832
|
-
});
|
|
833
|
-
const candidateSet = new Set(candidates.map(({ el }) => el));
|
|
834
|
-
const topLevelCandidates = candidates.filter(({ el }) => {
|
|
835
|
-
for (let parent = el.parentElement; parent; parent = parent.parentElement) {
|
|
836
|
-
if (candidateSet.has(parent)) return false;
|
|
906
|
+
var expandFrameElement = async (frame, height) => {
|
|
907
|
+
const handle = await frame.frameElement?.().catch(() => null);
|
|
908
|
+
if (!handle) return false;
|
|
909
|
+
try {
|
|
910
|
+
return await handle.evaluate((el, { expandedAttr, targetHeight }) => {
|
|
911
|
+
if (!el || targetHeight <= 0) return false;
|
|
912
|
+
if (el.getAttribute(expandedAttr) !== "1") {
|
|
913
|
+
el.setAttribute(expandedAttr, "1");
|
|
914
|
+
el.dataset.pkSsOverflow = el.style.getPropertyValue("overflow") || "";
|
|
915
|
+
el.dataset.pkSsOverflowPriority = el.style.getPropertyPriority("overflow") || "";
|
|
916
|
+
el.dataset.pkSsOverflowX = el.style.getPropertyValue("overflow-x") || "";
|
|
917
|
+
el.dataset.pkSsOverflowXPriority = el.style.getPropertyPriority("overflow-x") || "";
|
|
918
|
+
el.dataset.pkSsOverflowY = el.style.getPropertyValue("overflow-y") || "";
|
|
919
|
+
el.dataset.pkSsOverflowYPriority = el.style.getPropertyPriority("overflow-y") || "";
|
|
920
|
+
el.dataset.pkSsHeight = el.style.getPropertyValue("height") || "";
|
|
921
|
+
el.dataset.pkSsHeightPriority = el.style.getPropertyPriority("height") || "";
|
|
922
|
+
el.dataset.pkSsMinHeight = el.style.getPropertyValue("min-height") || "";
|
|
923
|
+
el.dataset.pkSsMinHeightPriority = el.style.getPropertyPriority("min-height") || "";
|
|
924
|
+
el.dataset.pkSsMaxHeight = el.style.getPropertyValue("max-height") || "";
|
|
925
|
+
el.dataset.pkSsMaxHeightPriority = el.style.getPropertyPriority("max-height") || "";
|
|
926
|
+
el.dataset.pkSsWidth = el.style.getPropertyValue("width") || "";
|
|
927
|
+
el.dataset.pkSsWidthPriority = el.style.getPropertyPriority("width") || "";
|
|
928
|
+
el.dataset.pkSsBoxSizing = el.style.getPropertyValue("box-sizing") || "";
|
|
929
|
+
el.dataset.pkSsBoxSizingPriority = el.style.getPropertyPriority("box-sizing") || "";
|
|
930
|
+
el.dataset.pkSsAlignSelf = el.style.getPropertyValue("align-self") || "";
|
|
931
|
+
el.dataset.pkSsAlignSelfPriority = el.style.getPropertyPriority("align-self") || "";
|
|
932
|
+
el.dataset.pkSsJustifySelf = el.style.getPropertyValue("justify-self") || "";
|
|
933
|
+
el.dataset.pkSsJustifySelfPriority = el.style.getPropertyPriority("justify-self") || "";
|
|
934
|
+
el.dataset.pkSsScrollTop = String(Math.max(0, Math.round(el.scrollTop || 0)));
|
|
935
|
+
el.dataset.pkSsScrollLeft = String(Math.max(0, Math.round(el.scrollLeft || 0)));
|
|
837
936
|
}
|
|
937
|
+
el.style.setProperty("overflow", "visible", "important");
|
|
938
|
+
el.style.setProperty("overflow-y", "visible", "important");
|
|
939
|
+
el.style.setProperty("height", `${Math.ceil(targetHeight)}px`, "important");
|
|
940
|
+
el.style.setProperty("min-height", `${Math.ceil(targetHeight)}px`, "important");
|
|
941
|
+
el.style.setProperty("max-height", "none", "important");
|
|
942
|
+
el.style.setProperty("align-self", "flex-start", "important");
|
|
943
|
+
el.style.setProperty("justify-self", "start", "important");
|
|
838
944
|
return true;
|
|
945
|
+
}, { expandedAttr: EXPANDED_ATTR, targetHeight: Math.ceil(height) });
|
|
946
|
+
} finally {
|
|
947
|
+
await handle.dispose?.().catch(() => {
|
|
839
948
|
});
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
el.
|
|
851
|
-
}
|
|
852
|
-
|
|
853
|
-
if (position === "fixed") {
|
|
854
|
-
const deltaY = edge === "bottom" ? safeTargetHeight - viewportHeight - scrollY : -scrollY;
|
|
855
|
-
if (Math.abs(deltaY) > 1) {
|
|
856
|
-
el.style.setProperty("translate", `0 ${Math.round(deltaY)}px`, "important");
|
|
857
|
-
}
|
|
858
|
-
return;
|
|
949
|
+
}
|
|
950
|
+
};
|
|
951
|
+
var restoreExpandedStateInContext = async (context) => {
|
|
952
|
+
await context.evaluate(({ expandedAttr }) => {
|
|
953
|
+
const hasOwn2 = (source, key) => Object.prototype.hasOwnProperty.call(source, key);
|
|
954
|
+
const restoreStyle = (el, prop, valueKey, priorityKey) => {
|
|
955
|
+
if (!hasOwn2(el.dataset, valueKey)) return;
|
|
956
|
+
const value = el.dataset[valueKey] || "";
|
|
957
|
+
const priority = el.dataset[priorityKey] || "";
|
|
958
|
+
if (value) {
|
|
959
|
+
el.style.setProperty(prop, value, priority);
|
|
960
|
+
} else {
|
|
961
|
+
el.style.removeProperty(prop);
|
|
859
962
|
}
|
|
860
|
-
el.
|
|
861
|
-
el.
|
|
862
|
-
|
|
963
|
+
delete el.dataset[valueKey];
|
|
964
|
+
delete el.dataset[priorityKey];
|
|
965
|
+
};
|
|
966
|
+
document.querySelectorAll(`[${expandedAttr}="1"]`).forEach((el) => {
|
|
967
|
+
restoreStyle(el, "overflow", "pkSsOverflow", "pkSsOverflowPriority");
|
|
968
|
+
restoreStyle(el, "overflow-x", "pkSsOverflowX", "pkSsOverflowXPriority");
|
|
969
|
+
restoreStyle(el, "overflow-y", "pkSsOverflowY", "pkSsOverflowYPriority");
|
|
970
|
+
restoreStyle(el, "height", "pkSsHeight", "pkSsHeightPriority");
|
|
971
|
+
restoreStyle(el, "min-height", "pkSsMinHeight", "pkSsMinHeightPriority");
|
|
972
|
+
restoreStyle(el, "max-height", "pkSsMaxHeight", "pkSsMaxHeightPriority");
|
|
973
|
+
restoreStyle(el, "width", "pkSsWidth", "pkSsWidthPriority");
|
|
974
|
+
restoreStyle(el, "box-sizing", "pkSsBoxSizing", "pkSsBoxSizingPriority");
|
|
975
|
+
restoreStyle(el, "align-self", "pkSsAlignSelf", "pkSsAlignSelfPriority");
|
|
976
|
+
restoreStyle(el, "justify-self", "pkSsJustifySelf", "pkSsJustifySelfPriority");
|
|
977
|
+
if (hasOwn2(el.dataset, "pkSsScrollTop")) {
|
|
978
|
+
el.scrollTop = Math.max(0, Math.round(Number(el.dataset.pkSsScrollTop) || 0));
|
|
979
|
+
delete el.dataset.pkSsScrollTop;
|
|
980
|
+
}
|
|
981
|
+
if (hasOwn2(el.dataset, "pkSsScrollLeft")) {
|
|
982
|
+
el.scrollLeft = Math.max(0, Math.round(Number(el.dataset.pkSsScrollLeft) || 0));
|
|
983
|
+
delete el.dataset.pkSsScrollLeft;
|
|
984
|
+
}
|
|
985
|
+
el.removeAttribute(expandedAttr);
|
|
863
986
|
});
|
|
864
|
-
|
|
987
|
+
window.dispatchEvent(new Event("scroll"));
|
|
865
988
|
}, {
|
|
866
|
-
|
|
867
|
-
|
|
989
|
+
expandedAttr: EXPANDED_ATTR
|
|
990
|
+
}).catch(() => {
|
|
868
991
|
});
|
|
869
992
|
};
|
|
870
|
-
var
|
|
871
|
-
await
|
|
872
|
-
const
|
|
873
|
-
const
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
el.style.setProperty("position", el.dataset.pkOrigPosition || "", el.dataset.pkOrigPositionPriority || "");
|
|
882
|
-
delete el.dataset.pkOrigPosition;
|
|
883
|
-
delete el.dataset.pkOrigPositionPriority;
|
|
884
|
-
}
|
|
885
|
-
if (hasOwn2(el.dataset, "pkOrigTop")) {
|
|
886
|
-
el.style.setProperty("top", el.dataset.pkOrigTop || "", el.dataset.pkOrigTopPriority || "");
|
|
887
|
-
delete el.dataset.pkOrigTop;
|
|
888
|
-
delete el.dataset.pkOrigTopPriority;
|
|
889
|
-
}
|
|
890
|
-
if (hasOwn2(el.dataset, "pkOrigBottom")) {
|
|
891
|
-
el.style.setProperty("bottom", el.dataset.pkOrigBottom || "", el.dataset.pkOrigBottomPriority || "");
|
|
892
|
-
delete el.dataset.pkOrigBottom;
|
|
893
|
-
delete el.dataset.pkOrigBottomPriority;
|
|
894
|
-
}
|
|
895
|
-
if (hasOwn2(el.dataset, "pkOrigTranslate")) {
|
|
896
|
-
el.style.setProperty("translate", el.dataset.pkOrigTranslate || "", el.dataset.pkOrigTranslatePriority || "");
|
|
897
|
-
delete el.dataset.pkOrigTranslate;
|
|
898
|
-
delete el.dataset.pkOrigTranslatePriority;
|
|
899
|
-
}
|
|
900
|
-
delete el.dataset.pkAffixedAdjusted;
|
|
901
|
-
const stillExpanded = expansionKeys.some((key) => hasOwn2(el.dataset, key));
|
|
902
|
-
if (!stillExpanded) {
|
|
903
|
-
el.classList.remove(className);
|
|
904
|
-
}
|
|
993
|
+
var resetScrollPositionInContext = async (context) => {
|
|
994
|
+
await context.evaluate(() => {
|
|
995
|
+
const root = document.documentElement;
|
|
996
|
+
const body = document.body;
|
|
997
|
+
const scrollingElement = document.scrollingElement || root || body;
|
|
998
|
+
window.scrollTo(0, 0);
|
|
999
|
+
[root, body, scrollingElement].forEach((el) => {
|
|
1000
|
+
if (!el) return;
|
|
1001
|
+
el.scrollTop = 0;
|
|
1002
|
+
el.scrollLeft = 0;
|
|
1003
|
+
el.dispatchEvent(new Event("scroll", { bubbles: true }));
|
|
905
1004
|
});
|
|
906
|
-
|
|
1005
|
+
window.dispatchEvent(new Event("scroll"));
|
|
1006
|
+
}).catch(() => {
|
|
1007
|
+
});
|
|
907
1008
|
};
|
|
908
1009
|
var prepareExpandedFullPageScreenshot = async (page, options = {}) => {
|
|
909
|
-
const originalViewport = await resolveCurrentViewportSize(page);
|
|
910
1010
|
const maxHeight = toPositiveInteger(options.maxHeight, DEFAULT_MAX_HEIGHT);
|
|
911
|
-
const
|
|
912
|
-
const
|
|
913
|
-
const
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
await page.setViewportSize({
|
|
924
|
-
width: originalViewport.width,
|
|
925
|
-
height: targetHeight
|
|
1011
|
+
const settleMs = Math.max(0, Number(options.settleMs ?? DEFAULT_SETTLE_MS) || 0);
|
|
1012
|
+
const frames = typeof page.frames === "function" ? page.frames() : [page.mainFrame?.()].filter(Boolean);
|
|
1013
|
+
const mainFrame = page.mainFrame?.() || frames[0] || null;
|
|
1014
|
+
let targetHeight = 0;
|
|
1015
|
+
let scrollableCount = 0;
|
|
1016
|
+
let expandedCount = 0;
|
|
1017
|
+
for (const frame of frames) {
|
|
1018
|
+
if (!frame || frame === mainFrame) continue;
|
|
1019
|
+
const state2 = await expandScrollableContentInContext(frame, {
|
|
1020
|
+
...options,
|
|
1021
|
+
maxHeight,
|
|
1022
|
+
settleMs
|
|
926
1023
|
});
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
1024
|
+
if (state2.height > 0) {
|
|
1025
|
+
await expandFrameElement(frame, Math.min(state2.height, maxHeight)).catch(() => false);
|
|
1026
|
+
await resetScrollPositionInContext(frame);
|
|
1027
|
+
}
|
|
1028
|
+
targetHeight = Math.max(targetHeight, state2.height || 0);
|
|
1029
|
+
scrollableCount += state2.scrollableCount || 0;
|
|
1030
|
+
expandedCount += state2.expandedCount || 0;
|
|
1031
|
+
}
|
|
1032
|
+
if (mainFrame) {
|
|
1033
|
+
const state2 = await expandScrollableContentInContext(mainFrame, {
|
|
1034
|
+
...options,
|
|
1035
|
+
maxHeight,
|
|
1036
|
+
settleMs
|
|
931
1037
|
});
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
1038
|
+
targetHeight = Math.max(targetHeight, state2.height || 0);
|
|
1039
|
+
scrollableCount += state2.scrollableCount || 0;
|
|
1040
|
+
expandedCount += state2.expandedCount || 0;
|
|
1041
|
+
await resetScrollPositionInContext(mainFrame);
|
|
1042
|
+
}
|
|
1043
|
+
targetHeight = Math.min(Math.max(1, Math.ceil(targetHeight || 0)), maxHeight);
|
|
1044
|
+
if (settleMs > 0) await (0, import_delay.default)(settleMs);
|
|
1045
|
+
logger.info(`\u5C55\u5F00\u5F0F\u957F\u622A\u56FE\u76EE\u6807: height=${targetHeight}, scrollables=${scrollableCount}, expanded=${expandedCount}`);
|
|
936
1046
|
return {
|
|
937
|
-
originalViewport,
|
|
938
|
-
maxScrollHeight,
|
|
939
1047
|
targetHeight,
|
|
940
|
-
|
|
941
|
-
|
|
1048
|
+
frames,
|
|
1049
|
+
settleMs
|
|
942
1050
|
};
|
|
943
1051
|
};
|
|
944
1052
|
var restoreExpandedFullPageScreenshot = async (page, state2 = {}) => {
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
document.documentElement,
|
|
949
|
-
document.body,
|
|
950
|
-
document.scrollingElement
|
|
951
|
-
].filter((el) => el?.classList?.contains(className)));
|
|
952
|
-
targets.forEach((el) => {
|
|
953
|
-
el.style.setProperty("overflow", el.dataset.pkOrigOverflow || "", el.dataset.pkOrigOverflowPriority || "");
|
|
954
|
-
el.style.setProperty("height", el.dataset.pkOrigHeight || "", el.dataset.pkOrigHeightPriority || "");
|
|
955
|
-
el.style.setProperty("min-height", el.dataset.pkOrigMinHeight || "", el.dataset.pkOrigMinHeightPriority || "");
|
|
956
|
-
el.style.setProperty("max-height", el.dataset.pkOrigMaxHeight || "", el.dataset.pkOrigMaxHeightPriority || "");
|
|
957
|
-
delete el.dataset.pkOrigOverflow;
|
|
958
|
-
delete el.dataset.pkOrigOverflowPriority;
|
|
959
|
-
delete el.dataset.pkOrigHeight;
|
|
960
|
-
delete el.dataset.pkOrigHeightPriority;
|
|
961
|
-
delete el.dataset.pkOrigMinHeight;
|
|
962
|
-
delete el.dataset.pkOrigMinHeightPriority;
|
|
963
|
-
delete el.dataset.pkOrigMaxHeight;
|
|
964
|
-
delete el.dataset.pkOrigMaxHeightPriority;
|
|
965
|
-
el.classList.remove(className);
|
|
966
|
-
});
|
|
967
|
-
}, EXPANDED_SCROLLABLE_CLASS);
|
|
968
|
-
if (state2?.originalViewport && state2?.viewportResized) {
|
|
969
|
-
await page.setViewportSize(state2.originalViewport);
|
|
1053
|
+
const frames = Array.isArray(state2.frames) ? [...state2.frames].reverse() : typeof page.frames === "function" ? [...page.frames()].reverse() : [];
|
|
1054
|
+
for (const frame of frames) {
|
|
1055
|
+
await restoreExpandedStateInContext(frame);
|
|
970
1056
|
}
|
|
971
1057
|
};
|
|
972
|
-
var
|
|
973
|
-
const
|
|
974
|
-
const
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
const maxClipHeight = Math.round(toPositiveNumber(options.maxClipHeight, 0));
|
|
978
|
-
const fallbackOptions = {
|
|
979
|
-
type: type === "webp" ? "png" : type,
|
|
980
|
-
fullPage
|
|
981
|
-
};
|
|
982
|
-
if (quality !== void 0 && fallbackOptions.type === "jpeg") {
|
|
983
|
-
fallbackOptions.quality = quality;
|
|
984
|
-
}
|
|
985
|
-
if (timeout > 0) {
|
|
986
|
-
fallbackOptions.timeout = timeout;
|
|
987
|
-
}
|
|
1058
|
+
var captureExpandedFullPageScreenshotOnce = async (page, options = {}) => {
|
|
1059
|
+
const originalViewport = await resolveCurrentViewportSize(page).catch(() => null);
|
|
1060
|
+
const originalMainScroll = await readMainFrameScroll(page);
|
|
1061
|
+
let state2 = null;
|
|
1062
|
+
let viewportResized = false;
|
|
988
1063
|
try {
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
fromSurface: true,
|
|
1000
|
-
captureBeyondViewport: fullPage,
|
|
1001
|
-
optimizeForSpeed: true
|
|
1002
|
-
};
|
|
1003
|
-
if (quality !== void 0) {
|
|
1004
|
-
captureParams.quality = quality;
|
|
1005
|
-
}
|
|
1006
|
-
if (fullPage) {
|
|
1007
|
-
captureParams.clip = buildFullPageClip(metrics, viewport, maxClipHeight);
|
|
1008
|
-
}
|
|
1009
|
-
const result = await session.send("Page.captureScreenshot", captureParams);
|
|
1010
|
-
if (!result || typeof result.data !== "string" || !result.data) {
|
|
1011
|
-
throw new Error("CDP returned empty screenshot data");
|
|
1012
|
-
}
|
|
1013
|
-
return Buffer.from(result.data, "base64");
|
|
1014
|
-
} finally {
|
|
1015
|
-
if (typeof session.detach === "function") {
|
|
1016
|
-
await session.detach().catch(() => {
|
|
1017
|
-
});
|
|
1018
|
-
}
|
|
1064
|
+
await freezeViewportMetrics(page);
|
|
1065
|
+
state2 = await prepareExpandedFullPageScreenshot(page, options);
|
|
1066
|
+
const targetHeight = Math.max(1, Math.ceil(state2.targetHeight || options.maxHeight || DEFAULT_MAX_HEIGHT));
|
|
1067
|
+
if (originalViewport?.width && originalViewport?.height && targetHeight > Math.ceil(originalViewport.height) + 1) {
|
|
1068
|
+
await page.setViewportSize({
|
|
1069
|
+
width: Math.ceil(originalViewport.width),
|
|
1070
|
+
height: targetHeight
|
|
1071
|
+
});
|
|
1072
|
+
viewportResized = true;
|
|
1073
|
+
if (state2.settleMs > 0) await (0, import_delay.default)(state2.settleMs);
|
|
1019
1074
|
}
|
|
1020
|
-
} catch (error) {
|
|
1021
|
-
logger.warning(`CDP \u622A\u56FE\u5931\u8D25\uFF0C\u56DE\u9000 page.screenshot: ${error?.message || error}`);
|
|
1022
|
-
return await page.screenshot(fallbackOptions);
|
|
1023
|
-
}
|
|
1024
|
-
};
|
|
1025
|
-
var captureExpandedFullPageScreenshot = async (page, options = {}) => {
|
|
1026
|
-
const state2 = await prepareExpandedFullPageScreenshot(page, options);
|
|
1027
|
-
try {
|
|
1028
1075
|
return await capturePageScreenshot(page, {
|
|
1076
|
+
...options,
|
|
1029
1077
|
fullPage: true,
|
|
1030
|
-
|
|
1031
|
-
quality: options.quality,
|
|
1032
|
-
timeout: options.timeout,
|
|
1033
|
-
maxClipHeight: state2.targetHeight
|
|
1078
|
+
maxClipHeight: targetHeight
|
|
1034
1079
|
});
|
|
1035
1080
|
} finally {
|
|
1036
|
-
await restoreAffixedElementsForExpandedScreenshot(page).catch((error) => {
|
|
1037
|
-
logger.warning(`\u79FB\u52A8\u7AEF\u5438\u9644\u5143\u7D20\u6062\u590D\u5931\u8D25: ${error?.message || error}`);
|
|
1038
|
-
});
|
|
1039
1081
|
if (options.restore) {
|
|
1040
1082
|
await restoreExpandedFullPageScreenshot(page, state2);
|
|
1083
|
+
if (viewportResized && originalViewport?.width && originalViewport?.height) {
|
|
1084
|
+
await page.setViewportSize({
|
|
1085
|
+
width: Math.ceil(originalViewport.width),
|
|
1086
|
+
height: Math.ceil(originalViewport.height)
|
|
1087
|
+
}).catch((error) => {
|
|
1088
|
+
logger.warning(`\u6062\u590D viewport \u5931\u8D25: ${error?.message || error}`);
|
|
1089
|
+
});
|
|
1090
|
+
}
|
|
1041
1091
|
}
|
|
1092
|
+
await restoreViewportMetrics(page);
|
|
1093
|
+
if (options.restore) {
|
|
1094
|
+
await restoreMainFrameScroll(page, originalMainScroll);
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1097
|
+
};
|
|
1098
|
+
var isDarkBlankPixel = ({ r, g, b }) => {
|
|
1099
|
+
const max = Math.max(r, g, b);
|
|
1100
|
+
const min = Math.min(r, g, b);
|
|
1101
|
+
return max <= 34 && max - min <= 10;
|
|
1102
|
+
};
|
|
1103
|
+
var analyzeScreenshotBuffer = async (buffer) => {
|
|
1104
|
+
const image = await import_jimp.Jimp.read(buffer);
|
|
1105
|
+
const { width, height } = image.bitmap;
|
|
1106
|
+
const stepY = Math.max(1, Math.floor(height / 900));
|
|
1107
|
+
const stepX = Math.max(1, Math.floor(width / 420));
|
|
1108
|
+
let darkRows = 0;
|
|
1109
|
+
let loadingLikeRows = 0;
|
|
1110
|
+
let rows = 0;
|
|
1111
|
+
for (let y = 0; y < height; y += stepY) {
|
|
1112
|
+
rows += 1;
|
|
1113
|
+
let dark = 0;
|
|
1114
|
+
let edge = 0;
|
|
1115
|
+
let count = 0;
|
|
1116
|
+
let prev = null;
|
|
1117
|
+
for (let x = 0; x < width; x += stepX) {
|
|
1118
|
+
const rgba = (0, import_jimp.intToRGBA)(image.getPixelColor(x, y));
|
|
1119
|
+
count += 1;
|
|
1120
|
+
if (isDarkBlankPixel(rgba)) dark += 1;
|
|
1121
|
+
if (prev) {
|
|
1122
|
+
const diff = Math.abs(rgba.r - prev.r) + Math.abs(rgba.g - prev.g) + Math.abs(rgba.b - prev.b);
|
|
1123
|
+
if (diff > 45) edge += 1;
|
|
1124
|
+
}
|
|
1125
|
+
prev = rgba;
|
|
1126
|
+
}
|
|
1127
|
+
const darkRatio = dark / Math.max(1, count);
|
|
1128
|
+
const edgeRatio = edge / Math.max(1, count - 1);
|
|
1129
|
+
if (darkRatio >= 0.965 && edgeRatio <= 0.012) darkRows += 1;
|
|
1130
|
+
if (darkRatio >= 0.88 && edgeRatio <= 0.025) loadingLikeRows += 1;
|
|
1131
|
+
}
|
|
1132
|
+
const sampledRows = Math.max(1, rows);
|
|
1133
|
+
return {
|
|
1134
|
+
width,
|
|
1135
|
+
height,
|
|
1136
|
+
darkBlankRowsRatio: darkRows / sampledRows,
|
|
1137
|
+
loadingLikeRowsRatio: loadingLikeRows / sampledRows
|
|
1138
|
+
};
|
|
1139
|
+
};
|
|
1140
|
+
var resolveScreenshotQualityIssue = (analysis) => {
|
|
1141
|
+
if (!analysis) return null;
|
|
1142
|
+
if (analysis.loadingLikeRowsRatio >= 0.92 || analysis.darkBlankRowsRatio >= 0.72) {
|
|
1143
|
+
return "loading-like-dark-screenshot";
|
|
1042
1144
|
}
|
|
1145
|
+
return null;
|
|
1146
|
+
};
|
|
1147
|
+
var captureExpandedFullPageScreenshot = async (page, options = {}) => {
|
|
1148
|
+
const attempts = Math.max(1, toPositiveInteger(options.qualityRetryAttempts, DEFAULT_QUALITY_RETRY_ATTEMPTS));
|
|
1149
|
+
const retryDelayMs = Math.max(0, Number(options.qualityRetryDelayMs ?? DEFAULT_QUALITY_RETRY_DELAY_MS) || 0);
|
|
1150
|
+
let lastBuffer = null;
|
|
1151
|
+
let lastAnalysis = null;
|
|
1152
|
+
let lastIssue = null;
|
|
1153
|
+
for (let attempt = 1; attempt <= attempts; attempt += 1) {
|
|
1154
|
+
const buffer = await captureExpandedFullPageScreenshotOnce(page, options);
|
|
1155
|
+
const analysis = await analyzeScreenshotBuffer(buffer).catch((error) => {
|
|
1156
|
+
logger.warning(`\u622A\u56FE\u8D28\u91CF\u5206\u6790\u5931\u8D25: ${error?.message || error}`);
|
|
1157
|
+
return null;
|
|
1158
|
+
});
|
|
1159
|
+
const issue = resolveScreenshotQualityIssue(analysis);
|
|
1160
|
+
lastBuffer = buffer;
|
|
1161
|
+
lastAnalysis = analysis;
|
|
1162
|
+
lastIssue = issue;
|
|
1163
|
+
if (!issue) return buffer;
|
|
1164
|
+
if (attempt < attempts && retryDelayMs > 0) {
|
|
1165
|
+
logger.warning(`\u622A\u56FE\u7591\u4F3C\u5F02\u5E38(${issue})\uFF0C\u7B49\u5F85\u540E\u91CD\u8BD5: attempt=${attempt}/${attempts}`);
|
|
1166
|
+
await (0, import_delay.default)(retryDelayMs);
|
|
1167
|
+
}
|
|
1168
|
+
}
|
|
1169
|
+
if (lastIssue && lastAnalysis) {
|
|
1170
|
+
logger.warning(
|
|
1171
|
+
`\u622A\u56FE\u8D28\u91CF\u4ECD\u5F02\u5E38(${lastIssue})\uFF0C\u8FD4\u56DE\u6700\u4F73\u53EF\u5F97\u7ED3\u679C: dark=${lastAnalysis.darkBlankRowsRatio.toFixed(2)}, loading=${lastAnalysis.loadingLikeRowsRatio.toFixed(2)}`
|
|
1172
|
+
);
|
|
1173
|
+
}
|
|
1174
|
+
return lastBuffer;
|
|
1043
1175
|
};
|
|
1044
1176
|
|
|
1045
1177
|
// src/errors.js
|
|
@@ -1489,6 +1621,9 @@ var ProxyMeterRuntime = {
|
|
|
1489
1621
|
getProxyMeterSnapshot
|
|
1490
1622
|
};
|
|
1491
1623
|
|
|
1624
|
+
// src/internals/constants.js
|
|
1625
|
+
var PageRuntimeStateKey = "__playwright_toolkit_runtime_state__";
|
|
1626
|
+
|
|
1492
1627
|
// src/runtime-env.js
|
|
1493
1628
|
var BROWSER_PROFILE_SCHEMA_VERSION = 1;
|
|
1494
1629
|
var SUPPORTED_CLOAK_FINGERPRINT_PLATFORMS = /* @__PURE__ */ new Set(["linux", "macos", "windows"]);
|
|
@@ -9068,11 +9203,11 @@ var resolveScreenshotWatermarkifyMeta = async (page, options = {}) => {
|
|
|
9068
9203
|
watermark: options.watermark,
|
|
9069
9204
|
strip: options.strip,
|
|
9070
9205
|
stripLogoSrc,
|
|
9071
|
-
device:
|
|
9206
|
+
device: resolvePageDevice(page)
|
|
9072
9207
|
};
|
|
9073
9208
|
};
|
|
9074
9209
|
var buildFontFamily = () => 'MiSans, "SF Pro Display", "PingFang SC", "Helvetica Neue", Arial, sans-serif';
|
|
9075
|
-
var
|
|
9210
|
+
var resolvePageDevice = (page) => normalizeDevice(page?.[PageRuntimeStateKey]?.device);
|
|
9076
9211
|
var findStripSegment = (segments, kind) => {
|
|
9077
9212
|
const found = Array.isArray(segments) ? segments.find((segment) => segment?.kind === kind) : null;
|
|
9078
9213
|
return found && typeof found === "object" ? found : {};
|
|
@@ -9724,7 +9859,7 @@ var watermarkifyScreenshotBuffer = async (buffer, meta, page = null, options = {
|
|
|
9724
9859
|
};
|
|
9725
9860
|
|
|
9726
9861
|
// src/internals/compression.js
|
|
9727
|
-
var
|
|
9862
|
+
var import_jimp2 = require("jimp");
|
|
9728
9863
|
var logger15 = createInternalLogger("Compression");
|
|
9729
9864
|
var DEFAULT_SCREENSHOT_MAX_BYTES = 5 * 1024 * 1024;
|
|
9730
9865
|
var DEFAULT_SCREENSHOT_OUTPUT_TYPE = "jpeg";
|
|
@@ -9791,10 +9926,10 @@ var encodeJpeg = async (sourceImage, compression, scale, quality) => {
|
|
|
9791
9926
|
image.resize({
|
|
9792
9927
|
w: width,
|
|
9793
9928
|
h: height,
|
|
9794
|
-
mode:
|
|
9929
|
+
mode: import_jimp2.ResizeStrategy.BILINEAR
|
|
9795
9930
|
});
|
|
9796
9931
|
}
|
|
9797
|
-
const buffer = await image.getBuffer(
|
|
9932
|
+
const buffer = await image.getBuffer(import_jimp2.JimpMime.jpeg, { quality });
|
|
9798
9933
|
return {
|
|
9799
9934
|
buffer,
|
|
9800
9935
|
bytes: getBase64BytesFromBuffer(buffer),
|
|
@@ -9806,7 +9941,7 @@ var encodeJpeg = async (sourceImage, compression, scale, quality) => {
|
|
|
9806
9941
|
};
|
|
9807
9942
|
};
|
|
9808
9943
|
var compressImageBuffer = async (buffer, compression) => {
|
|
9809
|
-
const sourceImage = await
|
|
9944
|
+
const sourceImage = await import_jimp2.Jimp.read(buffer);
|
|
9810
9945
|
const maxQuality = toJpegQuality(compression.quality);
|
|
9811
9946
|
const minQuality = Math.min(maxQuality, toJpegQuality(compression.minQuality));
|
|
9812
9947
|
let quality = maxQuality;
|