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