chrome-devtools-frontend
Version:
Chrome DevTools UI
251 lines (229 loc) • 8 kB
text/typescript
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import * as SDK from '../../../../core/sdk/sdk.js';
import * as CssOverviewModule from '../../../../panels/css_overview/css_overview.js';
import * as UI from '../../legacy.js';
const computedArrayFontSizeIndex = 6;
function getPxMultiplier(): number {
return 1;
}
async function getEmMultiplier(isFontSizeProperty?: boolean): Promise<number> {
const selectedNode = UI.Context.Context.instance().flavor(SDK.DOMModel.DOMNode);
let currentFontSize;
if (selectedNode?.parentNode && selectedNode.nodeName() !== 'HTML') {
const [model] =
SDK.TargetManager.TargetManager.instance().models(CssOverviewModule.CSSOverviewModel.CSSOverviewModel);
const fontSizeNodeId = isFontSizeProperty ? selectedNode.parentNode.id : selectedNode.id;
const computedFontSize = await model.getComputedStyleForNode(fontSizeNodeId).then(findFontSizeValue);
const computedFontSizeValue = computedFontSize.replace(/[a-z]/g, '');
currentFontSize = parseFloat(computedFontSizeValue);
} else {
currentFontSize = 16;
}
return currentFontSize;
}
async function getRemMultiplier(): Promise<number> {
const selectedNode = UI.Context.Context.instance().flavor(SDK.DOMModel.DOMNode);
const htmlNode = findHtmlNode(selectedNode);
if (!htmlNode || !htmlNode.id) {
return 16;
}
const [model] =
SDK.TargetManager.TargetManager.instance().models(CssOverviewModule.CSSOverviewModel.CSSOverviewModel);
const computedRootFontSize = await model.getComputedStyleForNode(htmlNode.id).then(findFontSizeValue);
const rootFontSizeValue = computedRootFontSize.replace(/[a-z]/g, '');
const rootFontSize = parseFloat(rootFontSizeValue);
return rootFontSize;
}
async function getPercentMultiplier(isFontSizeProperty?: boolean): Promise<number> {
const emMultiplier = await getEmMultiplier(isFontSizeProperty);
const percMultiplier = emMultiplier / 100;
return percMultiplier;
}
async function getVhMultiplier(): Promise<number> {
const viewportObject = await getViewportObject();
if (!viewportObject) {
return 1;
}
const viewportHeight = viewportObject.height;
const vhMultiplier = viewportHeight / 100;
return vhMultiplier;
}
async function getVwMultiplier(): Promise<number> {
const viewportObject = await getViewportObject();
if (!viewportObject) {
return 1;
}
const viewportWidth = viewportObject.width;
const vwMultiplier = viewportWidth / 100;
return vwMultiplier;
}
async function getVminMultiplier(): Promise<number> {
const viewportObject = await getViewportObject();
if (!viewportObject) {
return 1;
}
const viewportWidth = viewportObject.width;
const viewportHeight = viewportObject.height;
const minViewportSize = Math.min(viewportWidth, viewportHeight);
const vminMultiplier = minViewportSize / 100;
return vminMultiplier;
}
async function getVmaxMultiplier(): Promise<number> {
const viewportObject = await getViewportObject();
if (!viewportObject) {
return 1;
}
const viewportWidth = viewportObject.width;
const viewportHeight = viewportObject.height;
const maxViewportSize = Math.max(viewportWidth, viewportHeight);
const vmaxMultiplier = maxViewportSize / 100;
return vmaxMultiplier;
}
function getCmMultiplier(): number {
return 37.795;
}
function getMmMultiplier(): number {
return 3.7795;
}
function getInMultiplier(): number {
return 96;
}
function getPtMultiplier(): number {
return 4 / 3;
}
function getPcMultiplier(): number {
return 16;
}
function findFontSizeValue(computedObject: {
computedStyle: Array<{
name: string,
value: string,
}>,
getError: () => void,
}): string {
const computedArray: Array<{
name: string,
value: string,
}> = computedObject.computedStyle;
let index = computedArrayFontSizeIndex;
if (computedArray[index].name && computedArray[index].name !== 'font-size') {
for (let i = 0; i < computedArray.length; i++) {
if (computedArray[i].name === 'font-size') {
index = i;
break;
}
}
}
return computedArray[index].value;
}
function findHtmlNode(selectedNode: SDK.DOMModel.DOMNode|null): SDK.DOMModel.DOMNode|null {
let node: SDK.DOMModel.DOMNode|(SDK.DOMModel.DOMNode | null) = selectedNode;
while (node && node.nodeName() !== 'HTML') {
if (node.parentNode) {
node = node.parentNode;
} else {
break;
}
}
return node;
}
const widthEvaluateParams = {
expression: 'window.innerWidth',
includeCommandLineAPI: false,
silent: true,
returnByValue: false,
generatePreview: false,
userGesture: false,
awaitPromise: true,
throwOnSideEffect: false,
disableBreaks: true,
replMode: false,
allowUnsafeEvalBlockedByCSP: false,
};
const heightEvaluateParams = {
expression: 'window.innerHeight',
includeCommandLineAPI: false,
silent: true,
returnByValue: false,
generatePreview: false,
userGesture: false,
awaitPromise: true,
throwOnSideEffect: false,
disableBreaks: true,
replMode: false,
allowUnsafeEvalBlockedByCSP: false,
};
async function getViewportObject(): Promise<{
width: number,
height: number,
}|null> {
const currentExecutionContext = UI.Context.Context.instance().flavor(SDK.RuntimeModel.ExecutionContext);
let width, height;
if (currentExecutionContext) {
const widthObject = await currentExecutionContext.evaluate(widthEvaluateParams, false, false);
const heightObject = await currentExecutionContext.evaluate(heightEvaluateParams, false, false);
if ('error' in widthObject || 'error' in heightObject) {
return null;
}
if (widthObject.object) {
width = widthObject.object.value;
}
if (heightObject.object) {
height = heightObject.object.value;
}
}
if (width === undefined || height === undefined) {
const selectedNode = UI.Context.Context.instance().flavor(SDK.DOMModel.DOMNode);
if (!selectedNode) {
return null;
}
const pageLayout = await selectedNode.domModel().target().pageAgent().invoke_getLayoutMetrics();
const zoom = pageLayout.visualViewport.zoom ? pageLayout.visualViewport.zoom : 1;
height = pageLayout.visualViewport.clientHeight / zoom;
width = pageLayout.visualViewport.clientWidth / zoom;
}
return {width, height};
}
const unitConversionMap = new Map<string, (isFontSize?: boolean) => number | Promise<number>>();
unitConversionMap.set('px', getPxMultiplier);
unitConversionMap.set('em', getEmMultiplier);
unitConversionMap.set('rem', getRemMultiplier);
unitConversionMap.set('%', getPercentMultiplier);
unitConversionMap.set('vh', getVhMultiplier);
unitConversionMap.set('vw', getVwMultiplier);
unitConversionMap.set('vmin', getVminMultiplier);
unitConversionMap.set('vmax', getVmaxMultiplier);
unitConversionMap.set('cm', getCmMultiplier);
unitConversionMap.set('mm', getMmMultiplier);
unitConversionMap.set('in', getInMultiplier);
unitConversionMap.set('pt', getPtMultiplier);
unitConversionMap.set('pc', getPcMultiplier);
export async function getUnitConversionMultiplier(
prevUnit: string, newUnit: string, isFontSize?: boolean): Promise<number> {
if (prevUnit === '') {
prevUnit = 'em';
}
if (newUnit === '') {
newUnit = 'em';
}
let prevUnitMultiplier, newUnitMultiplier;
const prevUnitFunction = unitConversionMap.get(prevUnit);
const newUnitFunction = unitConversionMap.get(newUnit);
if (prevUnitFunction && newUnitFunction) {
if (prevUnit === 'em' || prevUnit === '%') {
prevUnitMultiplier = await prevUnitFunction(isFontSize);
} else {
prevUnitMultiplier = await prevUnitFunction();
}
if (newUnit === 'em' || newUnit === '%') {
newUnitMultiplier = await newUnitFunction(isFontSize);
} else {
newUnitMultiplier = await newUnitFunction();
}
} else {
return 1;
}
return prevUnitMultiplier / newUnitMultiplier;
}