@skrillex1224/playwright-toolkit 3.0.26 → 3.0.28
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 +1 -1
- package/dist/browser.js.map +1 -1
- package/dist/index.cjs +486 -983
- package/dist/index.cjs.map +4 -4
- package/dist/index.js +489 -986
- 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
|
]
|
|
@@ -496,9 +496,6 @@ function createInternalLogger(moduleName, explicitLogger) {
|
|
|
496
496
|
var import_delay = __toESM(require("delay"), 1);
|
|
497
497
|
var import_jimp = require("jimp");
|
|
498
498
|
|
|
499
|
-
// src/internals/constants.js
|
|
500
|
-
var PageRuntimeStateKey = "__playwright_toolkit_runtime_state__";
|
|
501
|
-
|
|
502
499
|
// src/internals/viewport.js
|
|
503
500
|
var toPositiveInt = (value) => {
|
|
504
501
|
const number = Math.round(Number(value) || 0);
|
|
@@ -529,35 +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
|
-
var STITCH_SCROLL_TARGET_ATTR = "data-pk-stitch-scroll-target";
|
|
537
529
|
var DEFAULT_MAX_HEIGHT = 8e3;
|
|
538
|
-
var DEFAULT_SETTLE_MS =
|
|
539
|
-
var
|
|
540
|
-
var DEFAULT_STITCH_SETTLE_MS = 120;
|
|
541
|
-
var DEFAULT_STITCH_OVERLAP_PX = 24;
|
|
542
|
-
var MIN_VIRTUALIZED_SCROLL_RATIO = 2.2;
|
|
543
|
-
var MIN_SPARSE_SCROLL_RATIO = 4;
|
|
544
|
-
var MIN_STITCH_VISIBLE_HEIGHT_PX = 120;
|
|
545
|
-
var MIN_STITCH_VISIBLE_HEIGHT_RATIO = 0.22;
|
|
546
|
-
var MIN_STITCH_OUTPUT_HEIGHT_RATIO = 0.35;
|
|
547
|
-
var MIN_STITCH_OUTPUT_VIEWPORT_RATIO = 1.15;
|
|
548
|
-
var MOBILE_VIEWPORT_WIDTH_THRESHOLD = 520;
|
|
549
|
-
var DEFAULT_QUALITY_RETRY_ATTEMPTS = 2;
|
|
530
|
+
var DEFAULT_SETTLE_MS = 260;
|
|
531
|
+
var DEFAULT_QUALITY_RETRY_ATTEMPTS = 4;
|
|
550
532
|
var DEFAULT_QUALITY_RETRY_DELAY_MS = 2500;
|
|
551
|
-
var
|
|
552
|
-
var
|
|
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;
|
|
553
537
|
var toPositiveNumber = (value, fallback = 0) => {
|
|
554
538
|
const n = Number(value);
|
|
555
539
|
if (!Number.isFinite(n) || n <= 0) return fallback;
|
|
556
540
|
return n;
|
|
557
541
|
};
|
|
558
542
|
var toPositiveInteger = (value, fallback = 0) => {
|
|
559
|
-
const
|
|
560
|
-
return
|
|
543
|
+
const n = Math.round(Number(value) || 0);
|
|
544
|
+
return n > 0 ? n : fallback;
|
|
561
545
|
};
|
|
562
546
|
var normalizeType = (value) => {
|
|
563
547
|
const raw = String(value || "png").trim().toLowerCase();
|
|
@@ -572,964 +556,585 @@ var normalizeQuality = (value, type) => {
|
|
|
572
556
|
if (rounded < 0 || rounded > 100) return void 0;
|
|
573
557
|
return rounded;
|
|
574
558
|
};
|
|
575
|
-
var resolvePageDevice = async (page) => {
|
|
576
|
-
const declared = normalizeDevice(page?.[PageRuntimeStateKey]?.device);
|
|
577
|
-
if (declared === Device.Mobile) return Device.Mobile;
|
|
578
|
-
const viewport = await resolveCurrentViewportSize(page).catch(() => null);
|
|
579
|
-
const viewportWidth = Math.round(Number(viewport?.width) || 0);
|
|
580
|
-
if (viewportWidth > 0 && viewportWidth <= MOBILE_VIEWPORT_WIDTH_THRESHOLD) {
|
|
581
|
-
return Device.Mobile;
|
|
582
|
-
}
|
|
583
|
-
return declared;
|
|
584
|
-
};
|
|
585
|
-
var resolveStitchMode = (options = {}) => {
|
|
586
|
-
const raw = options.stitch ?? options.stitching ?? options.strategy;
|
|
587
|
-
if (raw === false || raw === "off" || raw === "none" || raw === "single") return "off";
|
|
588
|
-
if (raw === true || raw === "force" || raw === "stitched") return "force";
|
|
589
|
-
return "auto";
|
|
590
|
-
};
|
|
591
559
|
var buildFullPageClip = (metrics, viewport, maxClipHeight) => {
|
|
592
560
|
const contentSize = metrics && typeof metrics === "object" ? metrics.contentSize || null : null;
|
|
593
561
|
const width = Math.max(1, Math.ceil(viewport.width || contentSize?.width || 1));
|
|
594
562
|
let height = Math.max(1, Math.ceil(contentSize?.height || viewport.height || 1));
|
|
595
|
-
if (maxClipHeight > 0)
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
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
|
|
604
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
|
+
}
|
|
605
612
|
};
|
|
606
|
-
var
|
|
607
|
-
return await page.evaluate((
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
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: []
|
|
635
|
+
};
|
|
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
|
+
}
|
|
618
652
|
};
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
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;
|
|
623
701
|
const scrollableOverflow = /* @__PURE__ */ new Set(["auto", "scroll", "overlay"]);
|
|
624
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);
|
|
625
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) || "";
|
|
626
716
|
const isVisible = (el, style, rect) => {
|
|
627
717
|
if (!el || !style || !rect) return false;
|
|
628
|
-
if (style.display === "none" || style.visibility === "hidden" || style.visibility === "collapse")
|
|
629
|
-
|
|
630
|
-
}
|
|
631
|
-
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;
|
|
632
720
|
return rect.width > 0 && rect.height > 0;
|
|
633
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);
|
|
634
727
|
const hasOverflowValue = (style, values) => {
|
|
635
|
-
const overflowY = String(style?.overflowY || "").toLowerCase();
|
|
636
728
|
const overflow = String(style?.overflow || "").toLowerCase();
|
|
637
|
-
|
|
729
|
+
const overflowY = String(style?.overflowY || "").toLowerCase();
|
|
730
|
+
return values.has(overflow) || values.has(overflowY);
|
|
638
731
|
};
|
|
639
732
|
const isLargeVerticalClipper = (el, rect) => {
|
|
640
733
|
if (!el || !rect) return false;
|
|
641
|
-
const
|
|
642
|
-
const visibleHeight = Math.ceil(rect.height || 0);
|
|
643
|
-
const clientHeight = Math.ceil(el.clientHeight || 0);
|
|
644
|
-
const boxHeight = Math.max(visibleHeight, clientHeight);
|
|
734
|
+
const boxHeight = Math.max(Math.ceil(rect.height || 0), Math.ceil(el.clientHeight || 0));
|
|
645
735
|
return boxHeight >= Math.max(320, viewportHeight * 0.45);
|
|
646
736
|
};
|
|
647
|
-
const hasLargeVerticalOverflow = (el, rect) => {
|
|
648
|
-
if (!isLargeVerticalClipper(el, rect)) return false;
|
|
649
|
-
const viewportHeight = window.innerHeight || 0;
|
|
650
|
-
const scrollHeight = Math.ceil(el.scrollHeight || 0);
|
|
651
|
-
const clientHeight = Math.ceil(el.clientHeight || 0);
|
|
652
|
-
const overflowDelta = scrollHeight - clientHeight;
|
|
653
|
-
return overflowDelta >= Math.max(160, viewportHeight * 0.18);
|
|
654
|
-
};
|
|
655
|
-
const rememberOriginalBoxStyles = (el, { includeDocumentElement = false } = {}) => {
|
|
656
|
-
if (!el || !includeDocumentElement && isDocumentElement(el) || el.classList.contains(className)) {
|
|
657
|
-
return;
|
|
658
|
-
}
|
|
659
|
-
el.dataset.pkOrigOverflow = el.style.overflow;
|
|
660
|
-
el.dataset.pkOrigOverflowPriority = el.style.getPropertyPriority("overflow") || "";
|
|
661
|
-
el.dataset.pkOrigHeight = el.style.height;
|
|
662
|
-
el.dataset.pkOrigHeightPriority = el.style.getPropertyPriority("height") || "";
|
|
663
|
-
el.dataset.pkOrigMinHeight = el.style.minHeight;
|
|
664
|
-
el.dataset.pkOrigMinHeightPriority = el.style.getPropertyPriority("min-height") || "";
|
|
665
|
-
el.dataset.pkOrigMaxHeight = el.style.maxHeight;
|
|
666
|
-
el.dataset.pkOrigMaxHeightPriority = el.style.getPropertyPriority("max-height") || "";
|
|
667
|
-
el.classList.add(className);
|
|
668
|
-
};
|
|
669
|
-
const expandDocumentElementsToHeight = (height) => {
|
|
670
|
-
if (!expandDocumentElements) return;
|
|
671
|
-
const targetHeight = Math.ceil(Number(height) || 0);
|
|
672
|
-
if (targetHeight <= 0) return;
|
|
673
|
-
[root, body, scrollingElement].forEach((el) => {
|
|
674
|
-
if (!el) return;
|
|
675
|
-
rememberOriginalBoxStyles(el, { includeDocumentElement: true });
|
|
676
|
-
el.style.setProperty("overflow", "visible", "important");
|
|
677
|
-
el.style.setProperty("height", `${targetHeight}px`, "important");
|
|
678
|
-
el.style.setProperty("min-height", `${targetHeight}px`, "important");
|
|
679
|
-
el.style.setProperty("max-height", "none", "important");
|
|
680
|
-
});
|
|
681
|
-
};
|
|
682
737
|
const isScrollableY = (el, style, rect) => {
|
|
683
|
-
if (!el || el.scrollHeight <= el.clientHeight +
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
if (
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
if (hasOverflowValue(style, scrollableOverflow)) {
|
|
690
|
-
return true;
|
|
691
|
-
}
|
|
692
|
-
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);
|
|
693
744
|
};
|
|
694
|
-
const
|
|
695
|
-
if (!el) return
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
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);
|
|
707
771
|
};
|
|
708
|
-
const
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
if (!el || isDocumentElement(el)) return;
|
|
712
|
-
const scrollHeight = Math.ceil(el.scrollHeight || 0);
|
|
713
|
-
if (scrollHeight <= 0) return;
|
|
714
|
-
rememberOriginalBoxStyles(el);
|
|
772
|
+
const setExpandedBox = (el, { height, width }) => {
|
|
773
|
+
if (!el) return;
|
|
774
|
+
rememberBox(el);
|
|
715
775
|
el.style.setProperty("overflow", "visible", "important");
|
|
716
|
-
el.style.setProperty("
|
|
717
|
-
el.style.setProperty("min-height", `${scrollHeight}px`, "important");
|
|
776
|
+
el.style.setProperty("overflow-y", "visible", "important");
|
|
718
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;
|
|
719
791
|
};
|
|
720
792
|
const expandClippingAncestors = (el) => {
|
|
721
793
|
for (let node = el?.parentElement; node && !isDocumentElement(node); node = node.parentElement) {
|
|
722
794
|
const style = window.getComputedStyle(node);
|
|
723
795
|
const rect = node.getBoundingClientRect();
|
|
724
796
|
if (!isVisible(node, style, rect)) continue;
|
|
725
|
-
const
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
rememberOriginalBoxStyles(node);
|
|
797
|
+
const clips = hasOverflowValue(style, scrollableOverflow) || hasOverflowValue(style, clippingOverflow);
|
|
798
|
+
if (!clips) continue;
|
|
799
|
+
rememberBox(node);
|
|
729
800
|
node.style.setProperty("overflow", "visible", "important");
|
|
801
|
+
node.style.setProperty("overflow-y", "visible", "important");
|
|
730
802
|
node.style.setProperty("max-height", "none", "important");
|
|
731
|
-
expandedAncestors.push(node);
|
|
732
803
|
}
|
|
733
804
|
};
|
|
734
|
-
const
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
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;
|
|
746
840
|
}
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
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;
|
|
750
866
|
const scrollY = window.scrollY || window.pageYOffset || 0;
|
|
751
|
-
|
|
867
|
+
document.querySelectorAll(`[${expandedAttr}="1"]`).forEach((el) => {
|
|
752
868
|
if (!el || isDocumentElement(el)) return;
|
|
753
|
-
if (!el.classList.contains(className) && !el.closest(`.${className}`)) return;
|
|
754
|
-
const style = window.getComputedStyle(el);
|
|
755
869
|
const rect = el.getBoundingClientRect();
|
|
756
|
-
if (!
|
|
757
|
-
|
|
870
|
+
if (!rect || rect.width <= 0 || rect.height <= 0) return;
|
|
871
|
+
bottom = Math.max(bottom, Math.ceil(rect.bottom + scrollY));
|
|
758
872
|
});
|
|
759
|
-
return
|
|
873
|
+
return bottom;
|
|
760
874
|
};
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
Math.ceil(scrollingElement?.scrollHeight || 0)
|
|
767
|
-
);
|
|
768
|
-
};
|
|
769
|
-
candidates.forEach((el) => {
|
|
770
|
-
const style = window.getComputedStyle(el);
|
|
771
|
-
const rect = el.getBoundingClientRect();
|
|
772
|
-
if (visibleOnly && !isVisible(el, style, rect)) {
|
|
773
|
-
return;
|
|
774
|
-
}
|
|
775
|
-
if (!isScrollableY(el, style, rect)) {
|
|
776
|
-
return;
|
|
777
|
-
}
|
|
778
|
-
const neededHeight = measureNeededHeight(el);
|
|
779
|
-
if (neededHeight <= 0) {
|
|
780
|
-
return;
|
|
781
|
-
}
|
|
782
|
-
if (neededHeight > maxHeight) {
|
|
783
|
-
maxHeight = neededHeight;
|
|
784
|
-
}
|
|
785
|
-
scrollableElements.push(el);
|
|
786
|
-
if (isDocumentElement(el)) {
|
|
787
|
-
return;
|
|
788
|
-
}
|
|
789
|
-
expandClippingAncestors(el);
|
|
790
|
-
if (forceScrollableHeight) {
|
|
791
|
-
expandElementToScrollHeight(el);
|
|
792
|
-
return;
|
|
793
|
-
}
|
|
794
|
-
rememberOriginalBoxStyles(el);
|
|
795
|
-
el.style.overflow = "visible";
|
|
796
|
-
el.style.height = "auto";
|
|
797
|
-
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 });
|
|
798
880
|
});
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
881
|
+
window.scrollTo(0, 0);
|
|
882
|
+
document.querySelectorAll(`[${expandedAttr}="1"]`).forEach((el) => {
|
|
883
|
+
if (!isDocumentElement(el)) {
|
|
884
|
+
el.scrollTop = 0;
|
|
885
|
+
el.scrollLeft = 0;
|
|
804
886
|
}
|
|
805
887
|
});
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
}
|
|
813
|
-
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
|
+
};
|
|
814
895
|
}, {
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
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 };
|
|
819
904
|
});
|
|
820
905
|
};
|
|
821
|
-
var
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
const bottom = Number.parseFloat(style.bottom);
|
|
852
|
-
const hasTopRule = Number.isFinite(top) && top <= Math.max(160, viewportHeight * 0.25);
|
|
853
|
-
const hasBottomRule = Number.isFinite(bottom) && bottom <= Math.max(160, viewportHeight * 0.25);
|
|
854
|
-
const isNearViewportTop = rect.top <= edgeThreshold;
|
|
855
|
-
const isNearViewportBottom = rect.bottom >= viewportHeight - edgeThreshold;
|
|
856
|
-
const edge = (hasBottomRule || isNearViewportBottom) && !(hasTopRule || isNearViewportTop) ? "bottom" : "top";
|
|
857
|
-
if (position === "fixed" && edge === "top" && !hasTopRule && !isNearViewportTop) return;
|
|
858
|
-
if (position === "fixed" && edge === "bottom" && !hasBottomRule && !isNearViewportBottom) return;
|
|
859
|
-
if (position === "sticky" && !hasTopRule && !hasBottomRule && !isNearViewportTop && !isNearViewportBottom) return;
|
|
860
|
-
candidates.push({ el, position, edge });
|
|
861
|
-
});
|
|
862
|
-
const candidateSet = new Set(candidates.map(({ el }) => el));
|
|
863
|
-
const topLevelCandidates = candidates.filter(({ el }) => {
|
|
864
|
-
for (let parent = el.parentElement; parent; parent = parent.parentElement) {
|
|
865
|
-
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)));
|
|
866
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");
|
|
867
944
|
return true;
|
|
945
|
+
}, { expandedAttr: EXPANDED_ATTR, targetHeight: Math.ceil(height) });
|
|
946
|
+
} finally {
|
|
947
|
+
await handle.dispose?.().catch(() => {
|
|
868
948
|
});
|
|
869
|
-
|
|
870
|
-
if (!hasOwn2(el.dataset, "pkAffixedAdjusted")) {
|
|
871
|
-
el.dataset.pkAffixedAdjusted = "1";
|
|
872
|
-
el.dataset.pkOrigPosition = el.style.getPropertyValue("position") || "";
|
|
873
|
-
el.dataset.pkOrigPositionPriority = el.style.getPropertyPriority("position") || "";
|
|
874
|
-
el.dataset.pkOrigTop = el.style.getPropertyValue("top") || "";
|
|
875
|
-
el.dataset.pkOrigTopPriority = el.style.getPropertyPriority("top") || "";
|
|
876
|
-
el.dataset.pkOrigBottom = el.style.getPropertyValue("bottom") || "";
|
|
877
|
-
el.dataset.pkOrigBottomPriority = el.style.getPropertyPriority("bottom") || "";
|
|
878
|
-
el.dataset.pkOrigTranslate = el.style.getPropertyValue("translate") || "";
|
|
879
|
-
el.dataset.pkOrigTranslatePriority = el.style.getPropertyPriority("translate") || "";
|
|
880
|
-
}
|
|
881
|
-
el.classList.add(className);
|
|
882
|
-
if (position === "fixed") {
|
|
883
|
-
const deltaY = edge === "bottom" ? safeTargetHeight - viewportHeight - scrollY : -scrollY;
|
|
884
|
-
if (Math.abs(deltaY) > 1) {
|
|
885
|
-
el.style.setProperty("translate", `0 ${Math.round(deltaY)}px`, "important");
|
|
886
|
-
}
|
|
887
|
-
return;
|
|
888
|
-
}
|
|
889
|
-
el.style.setProperty("position", "relative", "important");
|
|
890
|
-
el.style.setProperty("top", "auto", "important");
|
|
891
|
-
el.style.setProperty("bottom", "auto", "important");
|
|
892
|
-
});
|
|
893
|
-
return topLevelCandidates.length;
|
|
894
|
-
}, {
|
|
895
|
-
className: EXPANDED_SCROLLABLE_CLASS,
|
|
896
|
-
targetHeight: options.targetHeight
|
|
897
|
-
});
|
|
949
|
+
}
|
|
898
950
|
};
|
|
899
|
-
var
|
|
900
|
-
await
|
|
951
|
+
var restoreExpandedStateInContext = async (context) => {
|
|
952
|
+
await context.evaluate(({ expandedAttr }) => {
|
|
901
953
|
const hasOwn2 = (source, key) => Object.prototype.hasOwnProperty.call(source, key);
|
|
902
|
-
const
|
|
903
|
-
|
|
904
|
-
"
|
|
905
|
-
"
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
el.style.setProperty("position", el.dataset.pkOrigPosition || "", el.dataset.pkOrigPositionPriority || "");
|
|
911
|
-
delete el.dataset.pkOrigPosition;
|
|
912
|
-
delete el.dataset.pkOrigPositionPriority;
|
|
913
|
-
}
|
|
914
|
-
if (hasOwn2(el.dataset, "pkOrigTop")) {
|
|
915
|
-
el.style.setProperty("top", el.dataset.pkOrigTop || "", el.dataset.pkOrigTopPriority || "");
|
|
916
|
-
delete el.dataset.pkOrigTop;
|
|
917
|
-
delete el.dataset.pkOrigTopPriority;
|
|
918
|
-
}
|
|
919
|
-
if (hasOwn2(el.dataset, "pkOrigBottom")) {
|
|
920
|
-
el.style.setProperty("bottom", el.dataset.pkOrigBottom || "", el.dataset.pkOrigBottomPriority || "");
|
|
921
|
-
delete el.dataset.pkOrigBottom;
|
|
922
|
-
delete el.dataset.pkOrigBottomPriority;
|
|
923
|
-
}
|
|
924
|
-
if (hasOwn2(el.dataset, "pkOrigTranslate")) {
|
|
925
|
-
el.style.setProperty("translate", el.dataset.pkOrigTranslate || "", el.dataset.pkOrigTranslatePriority || "");
|
|
926
|
-
delete el.dataset.pkOrigTranslate;
|
|
927
|
-
delete el.dataset.pkOrigTranslatePriority;
|
|
928
|
-
}
|
|
929
|
-
delete el.dataset.pkAffixedAdjusted;
|
|
930
|
-
const stillExpanded = expansionKeys.some((key) => hasOwn2(el.dataset, key));
|
|
931
|
-
if (!stillExpanded) {
|
|
932
|
-
el.classList.remove(className);
|
|
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);
|
|
933
962
|
}
|
|
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);
|
|
934
986
|
});
|
|
935
|
-
|
|
987
|
+
window.dispatchEvent(new Event("scroll"));
|
|
988
|
+
}, {
|
|
989
|
+
expandedAttr: EXPANDED_ATTR
|
|
990
|
+
}).catch(() => {
|
|
991
|
+
});
|
|
936
992
|
};
|
|
937
|
-
var
|
|
938
|
-
|
|
993
|
+
var resetScrollPositionInContext = async (context) => {
|
|
994
|
+
await context.evaluate(() => {
|
|
995
|
+
const root = document.documentElement;
|
|
939
996
|
const body = document.body;
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
top: Number.POSITIVE_INFINITY,
|
|
948
|
-
right: 0,
|
|
949
|
-
bottom: 0,
|
|
950
|
-
nodes: 0
|
|
951
|
-
};
|
|
952
|
-
const isVisible = (el, style, rect) => {
|
|
953
|
-
if (!el || !style || !rect) return false;
|
|
954
|
-
if (style.display === "none" || style.visibility === "hidden" || style.visibility === "collapse") return false;
|
|
955
|
-
if (Number(style.opacity) === 0) return false;
|
|
956
|
-
return rect.width > 1 && rect.height > 1;
|
|
957
|
-
};
|
|
958
|
-
const hasFixedAncestor = (el) => {
|
|
959
|
-
for (let node = el; node && node.nodeType === 1; node = node.parentElement) {
|
|
960
|
-
const position = String(window.getComputedStyle(node).position || "").toLowerCase();
|
|
961
|
-
if (position === "fixed") {
|
|
962
|
-
return true;
|
|
963
|
-
}
|
|
964
|
-
}
|
|
965
|
-
return false;
|
|
966
|
-
};
|
|
967
|
-
const parseRgbColor = (value) => {
|
|
968
|
-
const raw = String(value || "").trim();
|
|
969
|
-
const match = raw.match(/rgba?\(([^)]+)\)/i);
|
|
970
|
-
if (!match) return null;
|
|
971
|
-
const parts = match[1].replace(/\//g, " ").split(/[,\s]+/).map((part) => part.trim()).filter(Boolean);
|
|
972
|
-
if (parts.length < 3) return null;
|
|
973
|
-
const normalizeChannel = (part) => {
|
|
974
|
-
if (part.endsWith("%")) {
|
|
975
|
-
return Math.max(0, Math.min(255, Number.parseFloat(part) * 2.55));
|
|
976
|
-
}
|
|
977
|
-
return Math.max(0, Math.min(255, Number.parseFloat(part)));
|
|
978
|
-
};
|
|
979
|
-
const alpha = parts.length >= 4 ? parts[3].endsWith("%") ? Number.parseFloat(parts[3]) / 100 : Number.parseFloat(parts[3]) : 1;
|
|
980
|
-
return {
|
|
981
|
-
r: normalizeChannel(parts[0]),
|
|
982
|
-
g: normalizeChannel(parts[1]),
|
|
983
|
-
b: normalizeChannel(parts[2]),
|
|
984
|
-
a: Number.isFinite(alpha) ? Math.max(0, Math.min(1, alpha)) : 1
|
|
985
|
-
};
|
|
986
|
-
};
|
|
987
|
-
const colorLuminance = (color) => {
|
|
988
|
-
const channel = (value) => {
|
|
989
|
-
const ratio = Math.max(0, Math.min(255, value)) / 255;
|
|
990
|
-
return ratio <= 0.03928 ? ratio / 12.92 : ((ratio + 0.055) / 1.055) ** 2.4;
|
|
991
|
-
};
|
|
992
|
-
return 0.2126 * channel(color.r) + 0.7152 * channel(color.g) + 0.0722 * channel(color.b);
|
|
993
|
-
};
|
|
994
|
-
const contrastRatio = (a, b) => {
|
|
995
|
-
const l1 = colorLuminance(a);
|
|
996
|
-
const l2 = colorLuminance(b);
|
|
997
|
-
const lighter = Math.max(l1, l2);
|
|
998
|
-
const darker = Math.min(l1, l2);
|
|
999
|
-
return (lighter + 0.05) / (darker + 0.05);
|
|
1000
|
-
};
|
|
1001
|
-
const resolveBackgroundColor = (el) => {
|
|
1002
|
-
for (let node = el; node && node.nodeType === 1; node = node.parentElement) {
|
|
1003
|
-
const background = parseRgbColor(window.getComputedStyle(node).backgroundColor);
|
|
1004
|
-
if (background && background.a > 0.2) {
|
|
1005
|
-
return background;
|
|
1006
|
-
}
|
|
1007
|
-
}
|
|
1008
|
-
return { r: 255, g: 255, b: 255, a: 1 };
|
|
1009
|
-
};
|
|
1010
|
-
const isLowInformationTextPaint = (el, style) => {
|
|
1011
|
-
const color = parseRgbColor(style?.color);
|
|
1012
|
-
if (!color) return false;
|
|
1013
|
-
const opacity = Number(style.opacity);
|
|
1014
|
-
if (color.a <= 0.08 || Number.isFinite(opacity) && opacity <= 0.18) {
|
|
1015
|
-
return true;
|
|
1016
|
-
}
|
|
1017
|
-
const max = Math.max(color.r, color.g, color.b);
|
|
1018
|
-
const min = Math.min(color.r, color.g, color.b);
|
|
1019
|
-
if (!(min >= 224 && max - min <= 24 && color.a >= 0.85)) {
|
|
1020
|
-
return false;
|
|
1021
|
-
}
|
|
1022
|
-
const background = resolveBackgroundColor(el);
|
|
1023
|
-
return colorLuminance(background) > 0.76 && contrastRatio(color, background) < 1.35;
|
|
1024
|
-
};
|
|
1025
|
-
const addRect = (rect) => {
|
|
1026
|
-
if (!rect || rect.width <= 1 || rect.height <= 1) return;
|
|
1027
|
-
const left = Math.max(0, Math.floor(rect.left + scrollX));
|
|
1028
|
-
const top = Math.max(0, Math.floor(rect.top + scrollY));
|
|
1029
|
-
const right = Math.ceil(rect.right + scrollX);
|
|
1030
|
-
const bottom = Math.ceil(rect.bottom + scrollY);
|
|
1031
|
-
if (right <= left || bottom <= top) return;
|
|
1032
|
-
bounds.left = Math.min(bounds.left, left);
|
|
1033
|
-
bounds.top = Math.min(bounds.top, top);
|
|
1034
|
-
bounds.right = Math.max(bounds.right, right);
|
|
1035
|
-
bounds.bottom = Math.max(bounds.bottom, bottom);
|
|
1036
|
-
bounds.nodes += 1;
|
|
1037
|
-
};
|
|
1038
|
-
const addElement = (el, { minArea = 12, text = false } = {}) => {
|
|
1039
|
-
if (!el || el.nodeType !== 1 || hasFixedAncestor(el)) return;
|
|
1040
|
-
const style = window.getComputedStyle(el);
|
|
1041
|
-
if (text && isLowInformationTextPaint(el, style)) return;
|
|
1042
|
-
const rects = Array.from(el.getClientRects ? el.getClientRects() : []);
|
|
1043
|
-
rects.forEach((rect) => {
|
|
1044
|
-
if (!isVisible(el, style, rect)) return;
|
|
1045
|
-
if (rect.width * rect.height < minArea) return;
|
|
1046
|
-
addRect(rect);
|
|
1047
|
-
});
|
|
1048
|
-
};
|
|
1049
|
-
const walker = document.createTreeWalker(body, NodeFilter.SHOW_TEXT);
|
|
1050
|
-
let visitedTextNodes = 0;
|
|
1051
|
-
while (walker.nextNode() && visitedTextNodes < 6e3) {
|
|
1052
|
-
const node = walker.currentNode;
|
|
1053
|
-
visitedTextNodes += 1;
|
|
1054
|
-
const text = String(node.nodeValue || "").replace(/\s+/g, " ").trim();
|
|
1055
|
-
if (text.length < 2) continue;
|
|
1056
|
-
addElement(node.parentElement, { minArea: 8, text: true });
|
|
1057
|
-
}
|
|
1058
|
-
document.querySelectorAll("img,video,canvas,svg,picture").forEach((el) => {
|
|
1059
|
-
addElement(el, { minArea: 900 });
|
|
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 }));
|
|
1060
1004
|
});
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
const paddingY = Math.max(24, Math.min(160, viewportHeight * 0.18));
|
|
1064
|
-
return {
|
|
1065
|
-
left: Math.max(0, Math.floor(bounds.left - paddingX)),
|
|
1066
|
-
top: Math.max(0, Math.floor(bounds.top - paddingY)),
|
|
1067
|
-
right: Math.ceil(bounds.right + paddingX),
|
|
1068
|
-
bottom: Math.ceil(bounds.bottom + paddingY),
|
|
1069
|
-
width: Math.max(1, Math.ceil(bounds.right - bounds.left)),
|
|
1070
|
-
height: Math.max(1, Math.ceil(bounds.bottom - bounds.top)),
|
|
1071
|
-
nodes: bounds.nodes,
|
|
1072
|
-
viewport: {
|
|
1073
|
-
width: Math.max(1, Math.ceil(viewportWidth || 1)),
|
|
1074
|
-
height: Math.max(1, Math.ceil(viewportHeight || 1))
|
|
1075
|
-
}
|
|
1076
|
-
};
|
|
1077
|
-
}).catch((error) => {
|
|
1078
|
-
logger.warning(`\u622A\u56FE\u5185\u5BB9\u8FB9\u754C\u6D4B\u91CF\u5931\u8D25: ${error?.message || error}`);
|
|
1079
|
-
return null;
|
|
1005
|
+
window.dispatchEvent(new Event("scroll"));
|
|
1006
|
+
}).catch(() => {
|
|
1080
1007
|
});
|
|
1081
1008
|
};
|
|
1082
1009
|
var prepareExpandedFullPageScreenshot = async (page, options = {}) => {
|
|
1083
|
-
const originalViewport = await resolveCurrentViewportSize(page);
|
|
1084
1010
|
const maxHeight = toPositiveInteger(options.maxHeight, DEFAULT_MAX_HEIGHT);
|
|
1085
|
-
const
|
|
1086
|
-
const
|
|
1087
|
-
const
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
if (contentBounds?.bottom > 0) {
|
|
1098
|
-
const contentBottom = Math.min(Math.ceil(contentBounds.bottom), maxHeight);
|
|
1099
|
-
const trailingGap = targetHeight - contentBottom;
|
|
1100
|
-
const minTrailingGap = Math.max(
|
|
1101
|
-
MIN_TRAILING_BLANK_GAP_PX,
|
|
1102
|
-
Math.floor(targetHeight * MIN_TRAILING_BLANK_GAP_RATIO),
|
|
1103
|
-
Math.floor(originalViewport.height * 0.45)
|
|
1104
|
-
);
|
|
1105
|
-
if (trailingGap >= minTrailingGap && contentBottom >= originalViewport.height * 0.65) {
|
|
1106
|
-
targetHeight = Math.max(1, contentBottom);
|
|
1107
|
-
logger.info(`\u957F\u622A\u56FE\u88C1\u6389\u4F4E\u4FE1\u606F\u5C3E\u90E8: contentBottom=${contentBottom}, originalHeight=${Math.min(maxScrollHeight, maxHeight)}`);
|
|
1108
|
-
}
|
|
1109
|
-
}
|
|
1110
|
-
let viewportResized = false;
|
|
1111
|
-
if (!preserveViewport) {
|
|
1112
|
-
await page.setViewportSize({
|
|
1113
|
-
width: originalViewport.width,
|
|
1114
|
-
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
|
|
1115
1023
|
});
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
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
|
|
1120
1037
|
});
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
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}`);
|
|
1125
1046
|
return {
|
|
1126
|
-
originalViewport,
|
|
1127
|
-
maxScrollHeight,
|
|
1128
1047
|
targetHeight,
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
viewportResized
|
|
1048
|
+
frames,
|
|
1049
|
+
settleMs
|
|
1132
1050
|
};
|
|
1133
1051
|
};
|
|
1134
1052
|
var restoreExpandedFullPageScreenshot = async (page, state2 = {}) => {
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
document.documentElement,
|
|
1139
|
-
document.body,
|
|
1140
|
-
document.scrollingElement
|
|
1141
|
-
].filter((el) => el?.classList?.contains(className)));
|
|
1142
|
-
targets.forEach((el) => {
|
|
1143
|
-
el.style.setProperty("overflow", el.dataset.pkOrigOverflow || "", el.dataset.pkOrigOverflowPriority || "");
|
|
1144
|
-
el.style.setProperty("height", el.dataset.pkOrigHeight || "", el.dataset.pkOrigHeightPriority || "");
|
|
1145
|
-
el.style.setProperty("min-height", el.dataset.pkOrigMinHeight || "", el.dataset.pkOrigMinHeightPriority || "");
|
|
1146
|
-
el.style.setProperty("max-height", el.dataset.pkOrigMaxHeight || "", el.dataset.pkOrigMaxHeightPriority || "");
|
|
1147
|
-
delete el.dataset.pkOrigOverflow;
|
|
1148
|
-
delete el.dataset.pkOrigOverflowPriority;
|
|
1149
|
-
delete el.dataset.pkOrigHeight;
|
|
1150
|
-
delete el.dataset.pkOrigHeightPriority;
|
|
1151
|
-
delete el.dataset.pkOrigMinHeight;
|
|
1152
|
-
delete el.dataset.pkOrigMinHeightPriority;
|
|
1153
|
-
delete el.dataset.pkOrigMaxHeight;
|
|
1154
|
-
delete el.dataset.pkOrigMaxHeightPriority;
|
|
1155
|
-
el.classList.remove(className);
|
|
1156
|
-
});
|
|
1157
|
-
}, EXPANDED_SCROLLABLE_CLASS);
|
|
1158
|
-
if (state2?.originalViewport && state2?.viewportResized) {
|
|
1159
|
-
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);
|
|
1160
1056
|
}
|
|
1161
1057
|
};
|
|
1162
|
-
var
|
|
1163
|
-
const
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
return null;
|
|
1168
|
-
}
|
|
1169
|
-
return await page.evaluate((config) => {
|
|
1170
|
-
const viewportWidth = window.innerWidth || document.documentElement?.clientWidth || document.body?.clientWidth || 0;
|
|
1171
|
-
const viewportHeight = window.innerHeight || document.documentElement?.clientHeight || document.body?.clientHeight || 0;
|
|
1172
|
-
const root = document.documentElement;
|
|
1173
|
-
const body = document.body;
|
|
1174
|
-
const scrollingElement = document.scrollingElement;
|
|
1175
|
-
const candidates = [];
|
|
1176
|
-
const seen = /* @__PURE__ */ new Set();
|
|
1177
|
-
const scrollableOverflow = /* @__PURE__ */ new Set(["auto", "scroll", "overlay"]);
|
|
1178
|
-
const clippingOverflow = /* @__PURE__ */ new Set(["hidden", "clip"]);
|
|
1179
|
-
const pushCandidate = (el2) => {
|
|
1180
|
-
if (!el2 || seen.has(el2)) return;
|
|
1181
|
-
seen.add(el2);
|
|
1182
|
-
candidates.push(el2);
|
|
1183
|
-
};
|
|
1184
|
-
pushCandidate(scrollingElement);
|
|
1185
|
-
pushCandidate(root);
|
|
1186
|
-
pushCandidate(body);
|
|
1187
|
-
document.querySelectorAll("*").forEach(pushCandidate);
|
|
1188
|
-
const isDocumentElement = (el2) => el2 === root || el2 === body || el2 === scrollingElement;
|
|
1189
|
-
const isVisible = (el2, style, rect) => {
|
|
1190
|
-
if (!el2 || !style || !rect) return false;
|
|
1191
|
-
if (style.display === "none" || style.visibility === "hidden" || style.visibility === "collapse") return false;
|
|
1192
|
-
if (Number(style.opacity) === 0) return false;
|
|
1193
|
-
return rect.width > 0 && rect.height > 0;
|
|
1194
|
-
};
|
|
1195
|
-
const hasOverflowValue = (style, values) => {
|
|
1196
|
-
const overflowY = String(style?.overflowY || "").toLowerCase();
|
|
1197
|
-
const overflow = String(style?.overflow || "").toLowerCase();
|
|
1198
|
-
return values.has(overflowY) || values.has(overflow);
|
|
1199
|
-
};
|
|
1200
|
-
const looksScrollable = (el2, style) => {
|
|
1201
|
-
if (!el2 || el2.scrollHeight <= el2.clientHeight + 1) return false;
|
|
1202
|
-
if (isDocumentElement(el2)) return true;
|
|
1203
|
-
return hasOverflowValue(style, scrollableOverflow) || hasOverflowValue(style, clippingOverflow);
|
|
1204
|
-
};
|
|
1205
|
-
const textHeightFor = (el2) => {
|
|
1206
|
-
const nodes = isDocumentElement(el2) ? document.querySelectorAll("body *") : el2.querySelectorAll("*");
|
|
1207
|
-
let textHeight = 0;
|
|
1208
|
-
let textNodes = 0;
|
|
1209
|
-
const maxNodes = 3500;
|
|
1210
|
-
for (const node of nodes) {
|
|
1211
|
-
if (textNodes >= maxNodes) break;
|
|
1212
|
-
const text = String(node.textContent || "").replace(/\s+/g, " ").trim();
|
|
1213
|
-
if (text.length < 2) continue;
|
|
1214
|
-
let childHasText = false;
|
|
1215
|
-
for (const child of node.children || []) {
|
|
1216
|
-
if (String(child.textContent || "").replace(/\s+/g, " ").trim().length >= 2) {
|
|
1217
|
-
childHasText = true;
|
|
1218
|
-
break;
|
|
1219
|
-
}
|
|
1220
|
-
}
|
|
1221
|
-
if (childHasText) continue;
|
|
1222
|
-
const style = window.getComputedStyle(node);
|
|
1223
|
-
const rect = node.getBoundingClientRect();
|
|
1224
|
-
if (!isVisible(node, style, rect)) continue;
|
|
1225
|
-
textNodes += 1;
|
|
1226
|
-
textHeight += Math.min(Math.ceil(rect.height || 0), 900);
|
|
1227
|
-
}
|
|
1228
|
-
return { textHeight, textNodes };
|
|
1229
|
-
};
|
|
1230
|
-
const resolveEdgeOcclusion = () => {
|
|
1231
|
-
let top = 0;
|
|
1232
|
-
let bottom = 0;
|
|
1233
|
-
const maxHeight = Math.max(48, viewportHeight * 0.45);
|
|
1234
|
-
document.querySelectorAll("*").forEach((el2) => {
|
|
1235
|
-
const style = window.getComputedStyle(el2);
|
|
1236
|
-
const position = String(style.position || "").toLowerCase();
|
|
1237
|
-
if (position !== "fixed" && position !== "sticky") return;
|
|
1238
|
-
const rect = el2.getBoundingClientRect();
|
|
1239
|
-
if (!isVisible(el2, style, rect)) return;
|
|
1240
|
-
if (rect.height <= 0 || rect.height > maxHeight) return;
|
|
1241
|
-
if (rect.width < viewportWidth * 0.35) return;
|
|
1242
|
-
if (rect.top <= Math.max(16, viewportHeight * 0.08)) {
|
|
1243
|
-
top = Math.max(top, Math.ceil(rect.bottom));
|
|
1244
|
-
}
|
|
1245
|
-
if (rect.bottom >= viewportHeight - Math.max(16, viewportHeight * 0.08)) {
|
|
1246
|
-
bottom = Math.max(bottom, Math.ceil(viewportHeight - rect.top));
|
|
1247
|
-
}
|
|
1248
|
-
});
|
|
1249
|
-
return {
|
|
1250
|
-
top: Math.max(0, Math.min(Math.ceil(top), Math.floor(viewportHeight * 0.45))),
|
|
1251
|
-
bottom: Math.max(0, Math.min(Math.ceil(bottom), Math.floor(viewportHeight * 0.45)))
|
|
1252
|
-
};
|
|
1253
|
-
};
|
|
1254
|
-
let best = null;
|
|
1255
|
-
candidates.forEach((el2) => {
|
|
1256
|
-
const style = window.getComputedStyle(el2);
|
|
1257
|
-
const rect = isDocumentElement(el2) ? {
|
|
1258
|
-
top: 0,
|
|
1259
|
-
bottom: viewportHeight,
|
|
1260
|
-
left: 0,
|
|
1261
|
-
right: viewportWidth,
|
|
1262
|
-
width: viewportWidth,
|
|
1263
|
-
height: viewportHeight
|
|
1264
|
-
} : el2.getBoundingClientRect();
|
|
1265
|
-
if (!isDocumentElement(el2) && !isVisible(el2, style, rect)) return;
|
|
1266
|
-
if (!looksScrollable(el2, style)) return;
|
|
1267
|
-
const scrollHeight = Math.ceil(el2.scrollHeight || 0);
|
|
1268
|
-
const clientHeight = Math.ceil(el2.clientHeight || viewportHeight || 0);
|
|
1269
|
-
if (scrollHeight < Math.max(900, clientHeight * config.minScrollRatio)) return;
|
|
1270
|
-
const visibleTop = Math.max(0, Math.round(rect.top || 0));
|
|
1271
|
-
const visibleBottom = Math.min(viewportHeight, Math.round(rect.bottom || viewportHeight));
|
|
1272
|
-
const visibleHeight = Math.max(0, visibleBottom - visibleTop);
|
|
1273
|
-
const minVisibleHeight = Math.max(
|
|
1274
|
-
config.minVisibleHeightPx,
|
|
1275
|
-
Math.floor(viewportHeight * config.minVisibleHeightRatio)
|
|
1276
|
-
);
|
|
1277
|
-
if (!config.force && !isDocumentElement(el2) && visibleHeight < minVisibleHeight) return;
|
|
1278
|
-
const { textHeight, textNodes } = textHeightFor(el2);
|
|
1279
|
-
const sparseRatio = scrollHeight / Math.max(1, textHeight);
|
|
1280
|
-
const sparse = config.force || textNodes > 0 && sparseRatio >= config.minSparseRatio && scrollHeight - textHeight > clientHeight;
|
|
1281
|
-
if (!sparse) return;
|
|
1282
|
-
const visibleWidth = Math.max(1, Math.ceil(rect.width || viewportWidth || 1));
|
|
1283
|
-
const score2 = scrollHeight * Math.min(visibleWidth, viewportWidth || visibleWidth) * Math.min(1, visibleHeight / Math.max(1, viewportHeight));
|
|
1284
|
-
if (!best || score2 > best.score) {
|
|
1285
|
-
best = {
|
|
1286
|
-
el: el2,
|
|
1287
|
-
score: score2,
|
|
1288
|
-
kind: isDocumentElement(el2) ? "document" : "element",
|
|
1289
|
-
scrollHeight,
|
|
1290
|
-
clientHeight,
|
|
1291
|
-
scrollTop: Math.max(0, Math.round(el2.scrollTop || 0)),
|
|
1292
|
-
rect: {
|
|
1293
|
-
top: Math.max(0, Math.round(rect.top || 0)),
|
|
1294
|
-
bottom: Math.min(viewportHeight, Math.round(rect.bottom || viewportHeight)),
|
|
1295
|
-
left: Math.max(0, Math.round(rect.left || 0)),
|
|
1296
|
-
width: Math.max(1, Math.round(rect.width || viewportWidth || 1)),
|
|
1297
|
-
height: Math.max(1, Math.round(rect.height || viewportHeight || 1))
|
|
1298
|
-
},
|
|
1299
|
-
visibleHeight,
|
|
1300
|
-
textHeight,
|
|
1301
|
-
textNodes,
|
|
1302
|
-
sparseRatio
|
|
1303
|
-
};
|
|
1304
|
-
}
|
|
1305
|
-
});
|
|
1306
|
-
if (!best) return null;
|
|
1307
|
-
document.querySelectorAll(`[${config.attrName}="1"]`).forEach((node) => {
|
|
1308
|
-
node.removeAttribute(config.attrName);
|
|
1309
|
-
});
|
|
1310
|
-
if (best.kind === "element") {
|
|
1311
|
-
best.el.setAttribute(config.attrName, "1");
|
|
1312
|
-
}
|
|
1313
|
-
const { el, score, ...target } = best;
|
|
1314
|
-
return {
|
|
1315
|
-
...target,
|
|
1316
|
-
viewport: {
|
|
1317
|
-
width: Math.max(1, Math.ceil(viewportWidth || best.rect.width || 1)),
|
|
1318
|
-
height: Math.max(1, Math.ceil(viewportHeight || best.rect.height || 1))
|
|
1319
|
-
},
|
|
1320
|
-
edgeOcclusion: resolveEdgeOcclusion()
|
|
1321
|
-
};
|
|
1322
|
-
}, {
|
|
1323
|
-
attrName: STITCH_SCROLL_TARGET_ATTR,
|
|
1324
|
-
force: stitchMode === "force",
|
|
1325
|
-
minScrollRatio: MIN_VIRTUALIZED_SCROLL_RATIO,
|
|
1326
|
-
minSparseRatio: MIN_SPARSE_SCROLL_RATIO,
|
|
1327
|
-
minVisibleHeightPx: MIN_STITCH_VISIBLE_HEIGHT_PX,
|
|
1328
|
-
minVisibleHeightRatio: MIN_STITCH_VISIBLE_HEIGHT_RATIO
|
|
1329
|
-
}).catch((error) => {
|
|
1330
|
-
logger.warning(`\u865A\u62DF\u6EDA\u52A8\u622A\u56FE\u63A2\u6D4B\u5931\u8D25: ${error?.message || error}`);
|
|
1331
|
-
return null;
|
|
1332
|
-
});
|
|
1333
|
-
};
|
|
1334
|
-
var setStitchScrollTop = async (page, target, scrollTop) => {
|
|
1335
|
-
await page.evaluate(({ attrName, kind, top }) => {
|
|
1336
|
-
const el = kind === "document" ? document.scrollingElement || document.documentElement || document.body : document.querySelector(`[${attrName}="1"]`);
|
|
1337
|
-
if (!el) return;
|
|
1338
|
-
el.scrollTop = Math.max(0, Math.round(Number(top) || 0));
|
|
1339
|
-
el.dispatchEvent(new Event("scroll", { bubbles: true }));
|
|
1340
|
-
window.dispatchEvent(new Event("scroll"));
|
|
1341
|
-
}, {
|
|
1342
|
-
attrName: STITCH_SCROLL_TARGET_ATTR,
|
|
1343
|
-
kind: target.kind,
|
|
1344
|
-
top: scrollTop
|
|
1345
|
-
});
|
|
1346
|
-
};
|
|
1347
|
-
var cleanupStitchTarget = async (page) => {
|
|
1348
|
-
await page.evaluate((attrName) => {
|
|
1349
|
-
document.querySelectorAll(`[${attrName}="1"]`).forEach((node) => {
|
|
1350
|
-
node.removeAttribute(attrName);
|
|
1351
|
-
});
|
|
1352
|
-
}, STITCH_SCROLL_TARGET_ATTR).catch(() => {
|
|
1353
|
-
});
|
|
1354
|
-
};
|
|
1355
|
-
var cropImage = (image, crop) => image.clone().crop({
|
|
1356
|
-
x: Math.max(0, Math.round(crop.x || 0)),
|
|
1357
|
-
y: Math.max(0, Math.round(crop.y || 0)),
|
|
1358
|
-
w: Math.max(1, Math.round(crop.w || 1)),
|
|
1359
|
-
h: Math.max(1, Math.round(crop.h || 1))
|
|
1360
|
-
});
|
|
1361
|
-
var captureStitchedScrollableScreenshot = async (page, target, options = {}) => {
|
|
1362
|
-
const maxHeight = toPositiveInteger(options.maxHeight, DEFAULT_MAX_HEIGHT);
|
|
1363
|
-
const settleMs = Math.max(0, Number(options.stitchSettleMs ?? DEFAULT_STITCH_SETTLE_MS) || 0);
|
|
1364
|
-
const overlapPx = Math.max(0, Math.round(Number(options.stitchOverlapPx ?? DEFAULT_STITCH_OVERLAP_PX) || 0));
|
|
1365
|
-
const viewport = target.viewport || await resolveCurrentViewportSize(page);
|
|
1366
|
-
const rect = target.rect || {
|
|
1367
|
-
top: 0,
|
|
1368
|
-
bottom: viewport.height,
|
|
1369
|
-
left: 0,
|
|
1370
|
-
width: viewport.width,
|
|
1371
|
-
height: viewport.height
|
|
1372
|
-
};
|
|
1373
|
-
const edge = target.edgeOcclusion || { top: 0, bottom: 0 };
|
|
1374
|
-
const topCrop = Math.max(0, Math.min(rect.top, viewport.height - 1));
|
|
1375
|
-
const topOverlay = Math.max(topCrop, Math.min(edge.top || 0, viewport.height - 1));
|
|
1376
|
-
const bottomOverlay = Math.max(0, Math.min(edge.bottom || 0, viewport.height - topOverlay - 1));
|
|
1377
|
-
const middleCropY = Math.max(topCrop, topOverlay);
|
|
1378
|
-
const middleCropBottom = Math.max(
|
|
1379
|
-
middleCropY + 1,
|
|
1380
|
-
Math.min(rect.bottom || viewport.height, viewport.height - bottomOverlay)
|
|
1381
|
-
);
|
|
1382
|
-
const middleCropHeight = Math.max(1, middleCropBottom - middleCropY);
|
|
1383
|
-
const scrollStep = Math.max(120, middleCropHeight - overlapPx);
|
|
1384
|
-
const maxScrollTop = Math.max(0, Math.ceil(target.scrollHeight - target.clientHeight));
|
|
1385
|
-
const positions = [];
|
|
1386
|
-
for (let top = 0; top < maxScrollTop; top += scrollStep) {
|
|
1387
|
-
positions.push(Math.round(top));
|
|
1388
|
-
}
|
|
1389
|
-
if (!positions.includes(maxScrollTop)) {
|
|
1390
|
-
positions.push(maxScrollTop);
|
|
1391
|
-
}
|
|
1392
|
-
if (positions.length === 0) {
|
|
1393
|
-
positions.push(0);
|
|
1394
|
-
}
|
|
1395
|
-
const segments = [];
|
|
1396
|
-
let totalHeight = 0;
|
|
1397
|
-
let outputWidth = Math.max(1, Math.round(viewport.width || rect.width || 1));
|
|
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;
|
|
1398
1063
|
try {
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
const buffer = await capturePageScreenshot(page, {
|
|
1407
|
-
type: options.type || "png",
|
|
1408
|
-
quality: options.quality,
|
|
1409
|
-
timeout: options.timeout
|
|
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
|
|
1410
1071
|
});
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
const cropY = isFirst ? 0 : middleCropY;
|
|
1414
|
-
const cropBottom = isLast ? image.bitmap.height : middleCropBottom;
|
|
1415
|
-
let cropHeight = Math.max(1, Math.min(image.bitmap.height, cropBottom) - cropY);
|
|
1416
|
-
const remaining = maxHeight - totalHeight;
|
|
1417
|
-
if (cropHeight > remaining) {
|
|
1418
|
-
cropHeight = remaining;
|
|
1419
|
-
}
|
|
1420
|
-
segments.push(cropImage(image, {
|
|
1421
|
-
x: 0,
|
|
1422
|
-
y: cropY,
|
|
1423
|
-
w: outputWidth,
|
|
1424
|
-
h: cropHeight
|
|
1425
|
-
}));
|
|
1426
|
-
totalHeight += cropHeight;
|
|
1072
|
+
viewportResized = true;
|
|
1073
|
+
if (state2.settleMs > 0) await (0, import_delay.default)(state2.settleMs);
|
|
1427
1074
|
}
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1075
|
+
return await capturePageScreenshot(page, {
|
|
1076
|
+
...options,
|
|
1077
|
+
fullPage: true,
|
|
1078
|
+
maxClipHeight: targetHeight
|
|
1432
1079
|
});
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1080
|
+
} finally {
|
|
1081
|
+
if (options.restore) {
|
|
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
|
+
}
|
|
1437
1091
|
}
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
const expectedHeight = Math.min(maxHeight, Math.max(target.scrollHeight || 0, viewport.height || 0));
|
|
1442
|
-
const minPlausibleHeight = Math.max(
|
|
1443
|
-
Math.floor((viewport.height || rect.height || 0) * MIN_STITCH_OUTPUT_VIEWPORT_RATIO),
|
|
1444
|
-
Math.floor(expectedHeight * MIN_STITCH_OUTPUT_HEIGHT_RATIO)
|
|
1445
|
-
);
|
|
1446
|
-
if (positions.length > 1 && expectedHeight > (viewport.height || rect.height || 0) * 1.3 && totalHeight < minPlausibleHeight) {
|
|
1447
|
-
logger.warning(
|
|
1448
|
-
`\u865A\u62DF\u6EDA\u52A8\u5206\u6BB5\u622A\u56FE\u7ED3\u679C\u8FC7\u77ED\uFF0C\u964D\u7EA7\u666E\u901A\u957F\u622A\u56FE: segments=${segments.length}, height=${totalHeight}, expectedMin=${minPlausibleHeight}, scrollHeight=${target.scrollHeight}, rect=${rect.width}x${rect.height}@${rect.top}`
|
|
1449
|
-
);
|
|
1450
|
-
return null;
|
|
1092
|
+
await restoreViewportMetrics(page);
|
|
1093
|
+
if (options.restore) {
|
|
1094
|
+
await restoreMainFrameScroll(page, originalMainScroll);
|
|
1451
1095
|
}
|
|
1452
|
-
return await canvas.getBuffer(import_jimp.JimpMime.png);
|
|
1453
|
-
} finally {
|
|
1454
|
-
await setStitchScrollTop(page, target, target.scrollTop || 0).catch(() => {
|
|
1455
|
-
});
|
|
1456
|
-
await cleanupStitchTarget(page);
|
|
1457
1096
|
}
|
|
1458
1097
|
};
|
|
1459
|
-
var isLightBlankPixel = ({ r, g, b }) => {
|
|
1460
|
-
const max = Math.max(r, g, b);
|
|
1461
|
-
const min = Math.min(r, g, b);
|
|
1462
|
-
return max >= 238 && max - min <= 18;
|
|
1463
|
-
};
|
|
1464
1098
|
var isDarkBlankPixel = ({ r, g, b }) => {
|
|
1465
1099
|
const max = Math.max(r, g, b);
|
|
1466
1100
|
const min = Math.min(r, g, b);
|
|
1467
1101
|
return max <= 34 && max - min <= 10;
|
|
1468
1102
|
};
|
|
1469
|
-
var isLowInfoPixel = ({ r, g, b }) => {
|
|
1470
|
-
const max = Math.max(r, g, b);
|
|
1471
|
-
const min = Math.min(r, g, b);
|
|
1472
|
-
return max - min <= 12;
|
|
1473
|
-
};
|
|
1474
1103
|
var analyzeScreenshotBuffer = async (buffer) => {
|
|
1475
1104
|
const image = await import_jimp.Jimp.read(buffer);
|
|
1476
|
-
const width = image.bitmap
|
|
1477
|
-
const height = image.bitmap.height;
|
|
1105
|
+
const { width, height } = image.bitmap;
|
|
1478
1106
|
const stepY = Math.max(1, Math.floor(height / 900));
|
|
1479
1107
|
const stepX = Math.max(1, Math.floor(width / 420));
|
|
1480
|
-
const rows = [];
|
|
1481
|
-
let lightRows = 0;
|
|
1482
1108
|
let darkRows = 0;
|
|
1483
|
-
let lowInfoRows = 0;
|
|
1484
1109
|
let loadingLikeRows = 0;
|
|
1485
|
-
|
|
1110
|
+
let rows = 0;
|
|
1486
1111
|
for (let y = 0; y < height; y += stepY) {
|
|
1487
|
-
|
|
1112
|
+
rows += 1;
|
|
1488
1113
|
let dark = 0;
|
|
1489
|
-
let lowInfo = 0;
|
|
1490
1114
|
let edge = 0;
|
|
1491
1115
|
let count = 0;
|
|
1492
1116
|
let prev = null;
|
|
1493
1117
|
for (let x = 0; x < width; x += stepX) {
|
|
1494
1118
|
const rgba = (0, import_jimp.intToRGBA)(image.getPixelColor(x, y));
|
|
1495
1119
|
count += 1;
|
|
1496
|
-
if (isLightBlankPixel(rgba)) light += 1;
|
|
1497
1120
|
if (isDarkBlankPixel(rgba)) dark += 1;
|
|
1498
|
-
if (isLowInfoPixel(rgba)) lowInfo += 1;
|
|
1499
1121
|
if (prev) {
|
|
1500
1122
|
const diff = Math.abs(rgba.r - prev.r) + Math.abs(rgba.g - prev.g) + Math.abs(rgba.b - prev.b);
|
|
1501
1123
|
if (diff > 45) edge += 1;
|
|
1502
1124
|
}
|
|
1503
1125
|
prev = rgba;
|
|
1504
1126
|
}
|
|
1505
|
-
const
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
};
|
|
1512
|
-
rows.push(stat);
|
|
1513
|
-
const lightBlank = stat.lightRatio >= 0.965 && stat.edgeRatio <= 0.015;
|
|
1514
|
-
const darkBlank = stat.darkRatio >= 0.965 && stat.edgeRatio <= 0.012;
|
|
1515
|
-
const lowInfoBlank = stat.lowInfoRatio >= 0.965 && stat.edgeRatio <= 0.012;
|
|
1516
|
-
const loadingLike = stat.darkRatio >= 0.88 && stat.edgeRatio <= 0.025;
|
|
1517
|
-
if (lightBlank) lightRows += 1;
|
|
1518
|
-
if (darkBlank) darkRows += 1;
|
|
1519
|
-
if (lowInfoBlank) lowInfoRows += 1;
|
|
1520
|
-
if (loadingLike) loadingLikeRows += 1;
|
|
1521
|
-
rowFlags.push({ lightBlank, darkBlank, lowInfoBlank, loadingLike });
|
|
1522
|
-
}
|
|
1523
|
-
const sampledRows = Math.max(1, rows.length);
|
|
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);
|
|
1524
1133
|
return {
|
|
1525
|
-
image,
|
|
1526
1134
|
width,
|
|
1527
1135
|
height,
|
|
1528
|
-
lightBlankRowsRatio: lightRows / sampledRows,
|
|
1529
1136
|
darkBlankRowsRatio: darkRows / sampledRows,
|
|
1530
|
-
|
|
1531
|
-
loadingLikeRowsRatio: loadingLikeRows / sampledRows,
|
|
1532
|
-
rowFlags
|
|
1137
|
+
loadingLikeRowsRatio: loadingLikeRows / sampledRows
|
|
1533
1138
|
};
|
|
1534
1139
|
};
|
|
1535
1140
|
var resolveScreenshotQualityIssue = (analysis) => {
|
|
@@ -1539,109 +1144,6 @@ var resolveScreenshotQualityIssue = (analysis) => {
|
|
|
1539
1144
|
}
|
|
1540
1145
|
return null;
|
|
1541
1146
|
};
|
|
1542
|
-
var cropBufferToContentBounds = async (buffer, bounds, options = {}) => {
|
|
1543
|
-
if (!bounds || bounds.nodes <= 0) return buffer;
|
|
1544
|
-
const image = options.image || await import_jimp.Jimp.read(buffer);
|
|
1545
|
-
const width = image.bitmap.width;
|
|
1546
|
-
const height = image.bitmap.height;
|
|
1547
|
-
const safeLeft = Math.max(0, Math.min(width - 1, Math.floor(bounds.left || 0)));
|
|
1548
|
-
const safeRight = Math.max(safeLeft + 1, Math.min(width, Math.ceil(bounds.right || width)));
|
|
1549
|
-
const safeTop = Math.max(0, Math.min(height - 1, Math.floor(bounds.top || 0)));
|
|
1550
|
-
const safeBottom = Math.max(safeTop + 1, Math.min(height, Math.ceil(bounds.bottom || height)));
|
|
1551
|
-
const cropW = safeRight - safeLeft;
|
|
1552
|
-
const cropH = safeBottom - safeTop;
|
|
1553
|
-
const shouldCropX = cropW > 80 && cropW <= width * 0.82 && (safeLeft > width * 0.04 || width - safeRight > width * 0.04);
|
|
1554
|
-
const shouldCropY = cropH > 160 && cropH <= height * 0.88 && height - safeBottom > Math.max(320, height * 0.1);
|
|
1555
|
-
if (!shouldCropX && !shouldCropY) {
|
|
1556
|
-
return buffer;
|
|
1557
|
-
}
|
|
1558
|
-
const x = shouldCropX ? safeLeft : 0;
|
|
1559
|
-
const y = shouldCropY ? safeTop : 0;
|
|
1560
|
-
const w = shouldCropX ? cropW : width;
|
|
1561
|
-
const h = shouldCropY ? cropH : height;
|
|
1562
|
-
logger.info(`\u5185\u5BB9\u611F\u77E5\u88C1\u526A\u622A\u56FE: x=${x}, y=${y}, w=${w}, h=${h}, original=${width}x${height}`);
|
|
1563
|
-
return await cropImage(image, { x, y, w, h }).getBuffer(import_jimp.JimpMime.png);
|
|
1564
|
-
};
|
|
1565
|
-
var captureExpandedFullPageScreenshotOnce = async (page, options = {}) => {
|
|
1566
|
-
const stitchedTarget = await resolveVirtualizedScrollTarget(page, options);
|
|
1567
|
-
if (stitchedTarget) {
|
|
1568
|
-
const buffer = await captureStitchedScrollableScreenshot(page, stitchedTarget, options);
|
|
1569
|
-
if (buffer) {
|
|
1570
|
-
return buffer;
|
|
1571
|
-
}
|
|
1572
|
-
}
|
|
1573
|
-
const state2 = await prepareExpandedFullPageScreenshot(page, options);
|
|
1574
|
-
try {
|
|
1575
|
-
const buffer = await capturePageScreenshot(page, {
|
|
1576
|
-
fullPage: true,
|
|
1577
|
-
type: options.type || "png",
|
|
1578
|
-
quality: options.quality,
|
|
1579
|
-
timeout: options.timeout,
|
|
1580
|
-
maxClipHeight: state2.targetHeight
|
|
1581
|
-
});
|
|
1582
|
-
return await cropBufferToContentBounds(buffer, state2.contentBounds);
|
|
1583
|
-
} finally {
|
|
1584
|
-
await restoreAffixedElementsForExpandedScreenshot(page).catch((error) => {
|
|
1585
|
-
logger.warning(`\u79FB\u52A8\u7AEF\u5438\u9644\u5143\u7D20\u6062\u590D\u5931\u8D25: ${error?.message || error}`);
|
|
1586
|
-
});
|
|
1587
|
-
if (options.restore) {
|
|
1588
|
-
await restoreExpandedFullPageScreenshot(page, state2);
|
|
1589
|
-
}
|
|
1590
|
-
}
|
|
1591
|
-
};
|
|
1592
|
-
var capturePageScreenshot = async (page, options = {}) => {
|
|
1593
|
-
const fullPage = Boolean(options.fullPage);
|
|
1594
|
-
const type = fullPage ? FORCED_FULLPAGE_TYPE : normalizeType(options.type);
|
|
1595
|
-
const quality = fullPage ? FORCED_FULLPAGE_QUALITY : normalizeQuality(options.quality, type);
|
|
1596
|
-
const timeout = toPositiveNumber(options.timeout, DEFAULT_TIMEOUT_MS);
|
|
1597
|
-
const maxClipHeight = Math.round(toPositiveNumber(options.maxClipHeight, 0));
|
|
1598
|
-
const fallbackOptions = {
|
|
1599
|
-
type: type === "webp" ? "png" : type,
|
|
1600
|
-
fullPage
|
|
1601
|
-
};
|
|
1602
|
-
if (quality !== void 0 && fallbackOptions.type === "jpeg") {
|
|
1603
|
-
fallbackOptions.quality = quality;
|
|
1604
|
-
}
|
|
1605
|
-
if (timeout > 0) {
|
|
1606
|
-
fallbackOptions.timeout = timeout;
|
|
1607
|
-
}
|
|
1608
|
-
try {
|
|
1609
|
-
const context = page && typeof page.context === "function" ? page.context() : null;
|
|
1610
|
-
if (!context || typeof context.newCDPSession !== "function") {
|
|
1611
|
-
throw new Error("CDP session is not available");
|
|
1612
|
-
}
|
|
1613
|
-
const session = await context.newCDPSession(page);
|
|
1614
|
-
try {
|
|
1615
|
-
const metrics = await session.send("Page.getLayoutMetrics");
|
|
1616
|
-
const viewport = await resolveCurrentViewportSize(page);
|
|
1617
|
-
const captureParams = {
|
|
1618
|
-
format: type,
|
|
1619
|
-
fromSurface: true,
|
|
1620
|
-
captureBeyondViewport: fullPage,
|
|
1621
|
-
optimizeForSpeed: true
|
|
1622
|
-
};
|
|
1623
|
-
if (quality !== void 0) {
|
|
1624
|
-
captureParams.quality = quality;
|
|
1625
|
-
}
|
|
1626
|
-
if (fullPage) {
|
|
1627
|
-
captureParams.clip = buildFullPageClip(metrics, viewport, maxClipHeight);
|
|
1628
|
-
}
|
|
1629
|
-
const result = await session.send("Page.captureScreenshot", captureParams);
|
|
1630
|
-
if (!result || typeof result.data !== "string" || !result.data) {
|
|
1631
|
-
throw new Error("CDP returned empty screenshot data");
|
|
1632
|
-
}
|
|
1633
|
-
return Buffer.from(result.data, "base64");
|
|
1634
|
-
} finally {
|
|
1635
|
-
if (typeof session.detach === "function") {
|
|
1636
|
-
await session.detach().catch(() => {
|
|
1637
|
-
});
|
|
1638
|
-
}
|
|
1639
|
-
}
|
|
1640
|
-
} catch (error) {
|
|
1641
|
-
logger.warning(`CDP \u622A\u56FE\u5931\u8D25\uFF0C\u56DE\u9000 page.screenshot: ${error?.message || error}`);
|
|
1642
|
-
return await page.screenshot(fallbackOptions);
|
|
1643
|
-
}
|
|
1644
|
-
};
|
|
1645
1147
|
var captureExpandedFullPageScreenshot = async (page, options = {}) => {
|
|
1646
1148
|
const attempts = Math.max(1, toPositiveInteger(options.qualityRetryAttempts, DEFAULT_QUALITY_RETRY_ATTEMPTS));
|
|
1647
1149
|
const retryDelayMs = Math.max(0, Number(options.qualityRetryDelayMs ?? DEFAULT_QUALITY_RETRY_DELAY_MS) || 0);
|
|
@@ -1658,9 +1160,7 @@ var captureExpandedFullPageScreenshot = async (page, options = {}) => {
|
|
|
1658
1160
|
lastBuffer = buffer;
|
|
1659
1161
|
lastAnalysis = analysis;
|
|
1660
1162
|
lastIssue = issue;
|
|
1661
|
-
if (!issue)
|
|
1662
|
-
return buffer;
|
|
1663
|
-
}
|
|
1163
|
+
if (!issue) return buffer;
|
|
1664
1164
|
if (attempt < attempts && retryDelayMs > 0) {
|
|
1665
1165
|
logger.warning(`\u622A\u56FE\u7591\u4F3C\u5F02\u5E38(${issue})\uFF0C\u7B49\u5F85\u540E\u91CD\u8BD5: attempt=${attempt}/${attempts}`);
|
|
1666
1166
|
await (0, import_delay.default)(retryDelayMs);
|
|
@@ -1668,7 +1168,7 @@ var captureExpandedFullPageScreenshot = async (page, options = {}) => {
|
|
|
1668
1168
|
}
|
|
1669
1169
|
if (lastIssue && lastAnalysis) {
|
|
1670
1170
|
logger.warning(
|
|
1671
|
-
`\u622A\u56FE\u8D28\u91CF\u4ECD\u5F02\u5E38(${lastIssue})\uFF0C\u8FD4\u56DE\u6700\u4F73\u53EF\u5F97\u7ED3\u679C:
|
|
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)}`
|
|
1672
1172
|
);
|
|
1673
1173
|
}
|
|
1674
1174
|
return lastBuffer;
|
|
@@ -2121,6 +1621,9 @@ var ProxyMeterRuntime = {
|
|
|
2121
1621
|
getProxyMeterSnapshot
|
|
2122
1622
|
};
|
|
2123
1623
|
|
|
1624
|
+
// src/internals/constants.js
|
|
1625
|
+
var PageRuntimeStateKey = "__playwright_toolkit_runtime_state__";
|
|
1626
|
+
|
|
2124
1627
|
// src/runtime-env.js
|
|
2125
1628
|
var BROWSER_PROFILE_SCHEMA_VERSION = 1;
|
|
2126
1629
|
var SUPPORTED_CLOAK_FINGERPRINT_PLATFORMS = /* @__PURE__ */ new Set(["linux", "macos", "windows"]);
|
|
@@ -9700,11 +9203,11 @@ var resolveScreenshotWatermarkifyMeta = async (page, options = {}) => {
|
|
|
9700
9203
|
watermark: options.watermark,
|
|
9701
9204
|
strip: options.strip,
|
|
9702
9205
|
stripLogoSrc,
|
|
9703
|
-
device:
|
|
9206
|
+
device: resolvePageDevice(page)
|
|
9704
9207
|
};
|
|
9705
9208
|
};
|
|
9706
9209
|
var buildFontFamily = () => 'MiSans, "SF Pro Display", "PingFang SC", "Helvetica Neue", Arial, sans-serif';
|
|
9707
|
-
var
|
|
9210
|
+
var resolvePageDevice = (page) => normalizeDevice(page?.[PageRuntimeStateKey]?.device);
|
|
9708
9211
|
var findStripSegment = (segments, kind) => {
|
|
9709
9212
|
const found = Array.isArray(segments) ? segments.find((segment) => segment?.kind === kind) : null;
|
|
9710
9213
|
return found && typeof found === "object" ? found : {};
|