UNPKG

chrome-devtools-frontend

Version:
147 lines (117 loc) • 6.4 kB
// Copyright 2023 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 Common from '../../../../core/common/common.js'; import * as Root from '../../../../core/root/root.js'; import * as TextUtils from '../../../../models/text_utils/text_utils.js'; import { dispatchInputEvent, dispatchPasteEvent, renderElementIntoDOM, } from '../../../../testing/DOMHelpers.js'; import {describeWithEnvironment} from '../../../../testing/EnvironmentHelpers.js'; import {expectCall} from '../../../../testing/ExpectStubCall.js'; import * as Buttons from '../../../components/buttons/buttons.js'; import * as UI from '../../legacy.js'; import * as SourceFrame from './source_frame.js'; describeWithEnvironment('SourceFrame', () => { let setting: Common.Settings.Setting<boolean>; beforeEach(() => { setting = Common.Settings.Settings.instance().createSetting( 'disable-self-xss-warning', false, Common.Settings.SettingStorageType.SYNCED); setting.set(false); }); async function createSourceFrame(content: string): Promise<SourceFrame.SourceFrame.SourceFrameImpl> { const sourceFrame = new SourceFrame.SourceFrame.SourceFrameImpl(async () => TextUtils.ContentData.EMPTY_TEXT_CONTENT_DATA); await sourceFrame.setContent(content); return sourceFrame; } async function pasteIntoSourceFrame(): Promise<{codeMirror: HTMLDivElement, dataTransfer: DataTransfer}> { const sourceFrame = await createSourceFrame('Example'); const codeMirror = sourceFrame.element.querySelector('devtools-text-editor')?.shadowRoot?.querySelector('.cm-content') || null; assert.instanceOf(codeMirror, HTMLDivElement); const dataTransfer = new DataTransfer(); dataTransfer.setData('text/plain', 'foo'); dispatchPasteEvent(codeMirror, {clipboardData: dataTransfer, bubbles: true}); await new Promise(resolve => setTimeout(resolve, 0)); return {codeMirror, dataTransfer}; } it('finds string by simple regex', async () => { const sourceFrame = await createSourceFrame('Example'); sourceFrame.performSearch( new UI.SearchableView.SearchConfig('xa', /* caseSensitive */ false, /* isRegex */ true), true); sourceFrame.jumpToSearchResult(0); assert.strictEqual(sourceFrame.textEditor.state.selection.main.from, 1); assert.strictEqual(sourceFrame.textEditor.state.selection.main.to, 3); }); it('does not match always-empty match regex', async () => { const sourceFrame = await createSourceFrame('Example'); sourceFrame.performSearch( new UI.SearchableView.SearchConfig('Z*', /* caseSensitive */ false, /* isRegex */ true), true); // Check that the search did not find anything. // We do this in a roundabout way: select the whole document, then try to select // the search result and finally verify that the whole document selection // has not changed. const dummySelection = sourceFrame.textEditor.createSelection({lineNumber: 0, columnNumber: 0}, {lineNumber: 0, columnNumber: 7}); sourceFrame.textEditor.dispatch({selection: dummySelection}); sourceFrame.jumpToSearchResult(1); assert.strictEqual(sourceFrame.textEditor.state.selection.main.from, 0); assert.strictEqual(sourceFrame.textEditor.state.selection.main.to, 7); }); it('skips empty matches and finds non-empty match by regex', async () => { const sourceFrame = await createSourceFrame('Example'); sourceFrame.performSearch( new UI.SearchableView.SearchConfig('a?', /* caseSensitive */ false, /* isRegex */ true), true); sourceFrame.jumpToSearchResult(0); assert.strictEqual(sourceFrame.textEditor.state.selection.main.from, 2); assert.strictEqual(sourceFrame.textEditor.state.selection.main.to, 3); }); it('shows self-XSS warning which the user can disable', async () => { const {codeMirror, dataTransfer} = await pasteIntoSourceFrame(); const dialogShadowRoot = document.body.querySelector<HTMLDivElement>('[data-devtools-glass-pane]') ?.shadowRoot?.querySelector('.widget') ?.shadowRoot || null; assert.isNotNull(dialogShadowRoot); const title = dialogShadowRoot.querySelector('.title'); assert.instanceOf(title, HTMLDivElement); assert.strictEqual(title.textContent, 'Do you trust this code?'); const input = dialogShadowRoot.querySelector('input'); assert.instanceOf(input, HTMLInputElement); input.value = 'allow pasting'; dispatchInputEvent(input, {inputType: 'insertText', data: 'allow pasting', bubbles: true, composed: true}); const allowButton = dialogShadowRoot.querySelector('.button')?.children[0]; assert.deepEqual(allowButton?.textContent, 'Allow'); assert.instanceOf(allowButton, Buttons.Button.Button); allowButton.click(); await new Promise(resolve => setTimeout(resolve, 0)); assert.isTrue(setting.get()); dispatchPasteEvent(codeMirror, {clipboardData: dataTransfer, bubbles: true}); await new Promise(resolve => setTimeout(resolve, 0)); const dialogContainer = document.body.querySelector<HTMLDivElement>('[data-devtools-glass-pane]'); assert.isNull(dialogContainer); }); it('does not show self-XSS warning when disabled via command line', async () => { const stub = sinon.stub(Root.Runtime.Runtime, 'queryParam'); stub.withArgs('disableSelfXssWarnings').returns('true'); await pasteIntoSourceFrame(); const dialogContainer = document.body.querySelector<HTMLDivElement>('[data-devtools-glass-pane]'); assert.isNull(dialogContainer); stub.restore(); }); it('disassembles wasm', async () => { const contentData = new TextUtils.ContentData.ContentData( 'AGFzbQEAAAABBQFgAAF/AwIBAAcHAQNiYXIAAAoGAQQAQQILACQEbmFtZQAQD3Nob3ctd2FzbS0yLndhdAEGAQADYmFyAgMBAAA=', true, 'application/wasm'); const sourceFrame = new SourceFrame.SourceFrame.SourceFrameImpl(async () => contentData); const setContentStub = sinon.stub(sourceFrame, 'setContent'); renderElementIntoDOM(sourceFrame); const content = await expectCall(setContentStub); assert.strictEqual( content.toString(), '(module\n (func $bar (;0;) (export \"bar\") (result i32)\n i32.const 2\n )\n)'); sourceFrame.detach(); }); });