UNPKG

chrome-devtools-frontend

Version:
188 lines (156 loc) • 6.08 kB
// Copyright 2026 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. import type * as Protocol from '../../generated/protocol.js'; /** * Functions in this file are serialized via `.toString()` and evaluated in the * inspected page context over CDP (e.g. `Runtime.callFunctionOn`). Because they * execute in the inspected browser environment, they use standard DOM types. */ export function scrollListenerInPage(this: HTMLElement|Document, id: number, reportScrollPositionBindingName: string, scrollListenerNameInPage: string): void { if ('scrollingElement' in this && !this.scrollingElement) { return; } const scrollingElement = ('scrollingElement' in this ? this.scrollingElement : this) as HTMLElement; // @ts-expect-error We're setting a custom field on `Element` or `Document` for retaining the function on the page. this[scrollListenerNameInPage] = () => { // @ts-expect-error `reportScrollPosition` binding is injected to the page before calling the function. globalThis[reportScrollPositionBindingName]( JSON.stringify({scrollTop: scrollingElement.scrollTop, scrollLeft: scrollingElement.scrollLeft, id})); }; // @ts-expect-error We've already defined the function used below. this.addEventListener('scroll', this[scrollListenerNameInPage], true); } export function removeScrollListenerInPage(this: HTMLElement|Document, scrollListenerNameInPage: string): void { // @ts-expect-error We've already set this custom field while adding scroll listener. this.removeEventListener('scroll', this[scrollListenerNameInPage]); // @ts-expect-error We've already set this custom field while adding scroll listener. delete this[scrollListenerNameInPage]; } export function scrollTopInPage(this: Element|Document): number { if ('scrollingElement' in this) { if (!this.scrollingElement) { return 0; } return this.scrollingElement.scrollTop; } return this.scrollTop; } export function scrollLeftInPage(this: Element|Document): number { if ('scrollingElement' in this) { if (!this.scrollingElement) { return 0; } return this.scrollingElement.scrollLeft; } return this.scrollLeft; } export function setScrollTopInPage(this: Element|Document, offsetInPage: number): void { if ('scrollingElement' in this) { if (!this.scrollingElement) { return; } this.scrollingElement.scrollTop = offsetInPage; } else { this.scrollTop = offsetInPage; } } export function setScrollLeftInPage(this: Element|Document, offsetInPage: number): void { if ('scrollingElement' in this) { if (!this.scrollingElement) { return; } this.scrollingElement.scrollLeft = offsetInPage; } else { this.scrollLeft = offsetInPage; } } export function verticalScrollRangeInPage(this: Element|Document): number { if ('scrollingElement' in this) { if (!this.scrollingElement) { return 0; } return this.scrollingElement.scrollHeight - this.scrollingElement.clientHeight; } return this.scrollHeight - this.clientHeight; } export function horizontalScrollRangeInPage(this: Element|Document): number { if ('scrollingElement' in this) { if (!this.scrollingElement) { return 0; } return this.scrollingElement.scrollWidth - this.scrollingElement.clientWidth; } return this.scrollWidth - this.clientWidth; } export function toggleClassAndInjectStyleRule(this: Element, pseudoElementName: string|null, hidden: boolean): void { const classNamePrefix = '__web-inspector-hide'; const classNameSuffix = '-shortcut__'; const styleTagId = '__web-inspector-hide-shortcut-style__'; const pseudoElementNameEscaped = pseudoElementName ? pseudoElementName.replace(/[\(\)\:]/g, '_') : ''; const className = classNamePrefix + pseudoElementNameEscaped + classNameSuffix; this.classList.toggle(className, hidden); let localRoot: Element|HTMLHeadElement = this; while (localRoot.parentNode) { localRoot = (localRoot.parentNode as Element); } if (localRoot.nodeType === Node.DOCUMENT_NODE) { localRoot = document.head; } let style = localRoot.querySelector('style#' + styleTagId); if (!style) { const selectors = []; selectors.push('.__web-inspector-hide-shortcut__'); selectors.push('.__web-inspector-hide-shortcut__ *'); const selector = selectors.join(', '); const ruleBody = ' visibility: hidden !important;'; const rule = '\n' + selector + '\n{\n' + ruleBody + '\n}\n'; style = document.createElement('style'); style.id = styleTagId; style.textContent = rule; localRoot.appendChild(style); } // In addition to putting them on the element we want to hide, we will // also add pseudo element classes to the style element to keep track of // which pseudo elements we have style rules for. if (pseudoElementName && !style.classList.contains(className)) { style.classList.add(className); style.textContent = `.${className}${pseudoElementName}, ${style.textContent}`; } } export function scrollIntoViewInPage(this: Element): void { this.scrollIntoViewIfNeeded(true); } export function focusInPage(this: HTMLElement): void { this.focus(); } export function toStringForClipboard(this: Object, data: { subtype: string|undefined, indent: string, }): string|undefined { const subtype = data.subtype; const indent = data.indent; if (subtype === 'node') { return this instanceof Element ? this.outerHTML : undefined; } if (subtype && typeof this === 'undefined') { return String(subtype); } try { return JSON.stringify(this, null, indent); } catch { return String(this); } } export function saveVariable(this: Window, value: Protocol.Runtime.CallArgument): string { const prefix = 'temp'; let index = 1; while ((prefix + index) in this) { ++index; } const name = prefix + index; // @ts-expect-error Assignment to global object this[name] = value; return name; }