/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at . */ // Tests that the editor scrolls correctly when pausing on location that // requires horizontal scrolling. "use strict"; add_task(async function testHorizontalScrolling() { // Ensure having the default fixed height, as it can impact the number of displayed lines await pushPref("devtools.toolbox.footer.height", 250); // Also set a precise size for side panels, as it can impact the number of displayed columns await pushPref("devtools.debugger.start-panel-size", 300); await pushPref("devtools.debugger.end-panel-size", 300); // Strengthen the test by ensuring we always use the same Firefox window size. // Note that the inner size is the important one as that's the final space available for DevTools. // The outer size will be different based on OS/Environment. const expectedWidth = 1280; const expectedHeight = 1040; if ( window.innerWidth != expectedWidth || window.innerHeight != expectedHeight ) { info("Resize the top level window to match the expected size"); const onResize = once(window, "resize"); const deltaW = window.outerWidth - window.innerWidth; const deltaH = window.outerHeight - window.innerHeight; const originalWidth = window.outerWidth; const originalHeight = window.outerHeight; window.resizeTo(expectedWidth + deltaW, expectedHeight + deltaH); await onResize; registerCleanupFunction(() => { window.resizeTo(originalWidth, originalHeight); }); } is(window.innerWidth, expectedWidth); const dbg = await initDebugger( "doc-editor-scroll.html", "scroll.js", "long.js" ); await selectSource(dbg, "horizontal-scroll.js"); const editor = getCMEditor(dbg); const global = editor.codeMirror.contentDOM.ownerGlobal; const font = new global.FontFace( "Ahem", "url(chrome://mochitests/content/browser/devtools/client/debugger/test/mochitest/examples/Ahem.ttf)" ); const loadedFont = await font.load(); global.document.fonts.add(loadedFont); is(global.devicePixelRatio, 1); is(global.browsingContext.top.window.devicePixelRatio, 1); global.browsingContext.top.overrideDPPX = 1; is(global.browsingContext.fullZoom, 1); is(global.browsingContext.textZoom, 1); // /!\ Change the Codemirror font to use a fixed font across all OSes // and always have the same number of characters displayed. // Note that this devtools mono makes the "o" characters almost invisible. editor.codeMirror.contentDOM.style.fontFamily = "Ahem"; editor.codeMirror.contentDOM.style.fontSize = "10px"; editor.codeMirror.contentDOM.style.lineHeight = "15px"; editor.codeMirror.contentDOM.style.fontWeight = "normal"; editor.codeMirror.contentDOM.style.fontStyle = "normal"; editor.codeMirror.contentDOM.style.fontStretch = "normal"; is(global.getComputedStyle(editor.codeMirror.contentDOM).fontFamily, "Ahem"); await wait(1000); is( Math.round(editor.codeMirror.dom.getBoundingClientRect().width), 679, "Sanity check to ensure we have a fixed editor width, so that we have the expected displayed columns" ); // All the following methods lookup for first/last visible position in the current viewport. // Also note that the element at the returned position may only be partially visible. function getFirstVisibleColumn() { const { x, y } = editor.codeMirror.dom.getBoundingClientRect(); const gutterWidth = editor.codeMirror.dom.querySelector(".cm-gutters").clientWidth; // This is hardcoded to match the second line, which is around 20px from the top. // Also append the gutter width as it would pick hidden columns displayed behind it const pos = editor.codeMirror.posAtCoords({ x: x + gutterWidth + 2, y: y + 20, }); // /!\ the column is 0-based while lines are 1-based return pos - editor.codeMirror.state.doc.lineAt(pos).from; } function getLastVisibleColumn() { const { x, y, width } = editor.codeMirror.dom.getBoundingClientRect(); // This is hardcoded to match the second line, which is around 20px from the top const pos = editor.codeMirror.posAtCoords({ x: x + width, y: y + 20 }); // /!\ the column is 0-based while lines are 1-based return pos - editor.codeMirror.state.doc.lineAt(pos).from; } info("Pause in middle of the screen, we should not scroll on pause"); await addBreakpoint(dbg, "horizontal-scroll.js", 2, 25); invokeInTab("horizontal"); await waitForPaused(dbg); const lastColumn = getLastVisibleColumn(); is(lastColumn, 55); ok( isScrolledPositionVisible(dbg, 2, 1), "The 2nd line, first column is visible" ); ok( isScrolledPositionVisible(dbg, 2, lastColumn - 1), "The 2nd line, column before the last is visible" ); ok( !isScrolledPositionVisible(dbg, 2, lastColumn), "The last column is hidden" ); info("Step to the last visible column, the editor shouldn't scroll"); // There is one breakable position every two column. // We can see the column breakpoint for the column after the last visible one. // Setting a breakpoint on that column and pausing shouldn't cause to scroll the viewport. await addBreakpoint(dbg, "horizontal-scroll.js", 2, lastColumn); await resume(dbg); await waitForPaused(dbg); is(getLastVisibleColumn(), lastColumn, "We did not scroll horizontaly"); ok( isScrolledPositionVisible(dbg, 2, lastColumn - 1), "The column before the last column is still visible" ); ok( !isScrolledPositionVisible(dbg, 2, lastColumn), "The last colunm is still hidden" ); info( "Step to the next column, and the editor should scroll it into the center" ); info("Step into the next breakable column, the editor should now scroll"); // Set a breakpoint on the first column that would cause a scroll // (there is one breakable position every two column) await addBreakpoint(dbg, "horizontal-scroll.js", 2, lastColumn + 2); await resume(dbg); await waitForPaused(dbg); const lastColumn2 = getLastVisibleColumn(); is(lastColumn2, 74); ok( isScrolledPositionVisible(dbg, 2, lastColumn2), "The new last column is visible" ); ok( !isScrolledPositionVisible(dbg, 2, lastColumn2 + 1), "The column after the last is hidden" ); const firstColumn = getFirstVisibleColumn(); is(firstColumn, 30); ok( !isScrolledPositionVisible(dbg, 2, firstColumn), "The new first column is partially visible and considered hidden" ); ok( isScrolledPositionVisible(dbg, 2, firstColumn + 1), "The column after the first visible is visible" ); await resume(dbg); }); // Tests the limit of the no of column breakpoint markers in a minified source with a long line. add_task(async function testColumnBreakpointsLimitAfterHorizontalScroll() { // Keep the layout consistent await pushPref("devtools.debugger.end-panel-size", 300); const dbg = await initDebugger( "doc-large-sources.html", "codemirror-bundle.js" ); info("Select the minified bundle and add a breakpoint"); await selectSource(dbg, "codemirror-bundle.js"); await addBreakpoint(dbg, "codemirror-bundle.js", 1); let columnBreakpointMarkers = await waitForAllElements( dbg, "columnBreakpoints" ); is( columnBreakpointMarkers.length, 100, "We have the expected limit of column breakpoint markers on the minified source" ); info("Scroll horizintally far to the right of the file"); await scrollEditorIntoView(dbg, 0, 300000); columnBreakpointMarkers = findAllElements(dbg, "columnBreakpoints"); is( columnBreakpointMarkers.length, 0, "There are no column breakpoint marker as the source has horizontally scrolled the viewport over the limit" ); info("Scroll back to the start of the line"); await scrollEditorIntoView(dbg, 0, 0); columnBreakpointMarkers = await waitForAllElements(dbg, "columnBreakpoints"); is( columnBreakpointMarkers.length, 100, "We still have the expected limit of column breakpoint markers on the minified source" ); });