fix(chromium): reland support selectAll on macos (#3038)

Co-authored-by: Joel Einbinder <joel.einbinde@gmail.com>
This commit is contained in:
Joel Einbinder 2020-07-21 16:33:46 -07:00 committed by GitHub
parent 3c151d8f1d
commit f4b7ed5542
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 80 additions and 11 deletions

View File

@ -40,6 +40,7 @@ export class CRBrowser extends BrowserBase {
_backgroundPages = new Map<string, CRPage>(); _backgroundPages = new Map<string, CRPage>();
_serviceWorkers = new Map<string, CRServiceWorker>(); _serviceWorkers = new Map<string, CRServiceWorker>();
_devtools?: CRDevTools; _devtools?: CRDevTools;
_isMac = false;
private _tracingRecording = false; private _tracingRecording = false;
private _tracingPath: string | null = ''; private _tracingPath: string | null = '';
@ -50,6 +51,8 @@ export class CRBrowser extends BrowserBase {
const browser = new CRBrowser(connection, options); const browser = new CRBrowser(connection, options);
browser._devtools = devtools; browser._devtools = devtools;
const session = connection.rootSession; const session = connection.rootSession;
const version = await session.send('Browser.getVersion');
browser._isMac = version.userAgent.includes('Macintosh');
if (!options.persistent) { if (!options.persistent) {
await session.send('Target.setAutoAttach', { autoAttach: true, waitForDebuggerOnStart: true, flatten: true }); await session.send('Target.setAutoAttach', { autoAttach: true, waitForDebuggerOnStart: true, flatten: true });
return browser; return browser;

View File

@ -18,6 +18,8 @@
import * as input from '../input'; import * as input from '../input';
import * as types from '../types'; import * as types from '../types';
import { CRSession } from './crConnection'; import { CRSession } from './crConnection';
import { macEditingCommands } from '../macEditingCommands';
import { helper } from '../helper';
function toModifiersMask(modifiers: Set<types.KeyboardModifier>): number { function toModifiersMask(modifiers: Set<types.KeyboardModifier>): number {
let mask = 0; let mask = 0;
@ -33,18 +35,36 @@ function toModifiersMask(modifiers: Set<types.KeyboardModifier>): number {
} }
export class RawKeyboardImpl implements input.RawKeyboard { export class RawKeyboardImpl implements input.RawKeyboard {
private _client: CRSession; constructor(
private _client: CRSession,
private _isMac: boolean,
) { }
constructor(client: CRSession) { _commandsForCode(code: string, modifiers: Set<types.KeyboardModifier>) {
this._client = client; if (!this._isMac)
return [];
const parts = [];
for (const modifier of (['Shift', 'Control', 'Alt', 'Meta']) as types.KeyboardModifier[]) {
if (modifiers.has(modifier))
parts.push(modifier);
}
parts.push(code);
const shortcut = parts.join('+');
let commands = macEditingCommands[shortcut] || [];
if (helper.isString(commands))
commands = [commands];
// remove the trailing : to match the Chromium command names.
return commands.map(c => c.substring(0, c.length - 1));
} }
async keydown(modifiers: Set<types.KeyboardModifier>, code: string, keyCode: number, keyCodeWithoutLocation: number, key: string, location: number, autoRepeat: boolean, text: string | undefined): Promise<void> { async keydown(modifiers: Set<types.KeyboardModifier>, code: string, keyCode: number, keyCodeWithoutLocation: number, key: string, location: number, autoRepeat: boolean, text: string | undefined): Promise<void> {
const commands = this._commandsForCode(code, modifiers);
await this._client.send('Input.dispatchKeyEvent', { await this._client.send('Input.dispatchKeyEvent', {
type: text ? 'keyDown' : 'rawKeyDown', type: text ? 'keyDown' : 'rawKeyDown',
modifiers: toModifiersMask(modifiers), modifiers: toModifiersMask(modifiers),
windowsVirtualKeyCode: keyCodeWithoutLocation, windowsVirtualKeyCode: keyCodeWithoutLocation,
code, code,
commands,
key, key,
text, text,
unmodifiedText: text, unmodifiedText: text,

View File

@ -65,7 +65,7 @@ export class CRPage implements PageDelegate {
constructor(client: CRSession, targetId: string, browserContext: CRBrowserContext, opener: CRPage | null, hasUIWindow: boolean) { constructor(client: CRSession, targetId: string, browserContext: CRBrowserContext, opener: CRPage | null, hasUIWindow: boolean) {
this._targetId = targetId; this._targetId = targetId;
this._opener = opener; this._opener = opener;
this.rawKeyboard = new RawKeyboardImpl(client); this.rawKeyboard = new RawKeyboardImpl(client, browserContext._browser._isMac);
this.rawMouse = new RawMouseImpl(client); this.rawMouse = new RawMouseImpl(client);
this._pdf = new CRPDF(client); this._pdf = new CRPDF(client);
this._coverage = new CRCoverage(client); this._coverage = new CRCoverage(client);

View File

@ -17,7 +17,6 @@
export const macEditingCommands: {[key: string]: string|string[]} = { export const macEditingCommands: {[key: string]: string|string[]} = {
'Backspace': 'deleteBackward:', 'Backspace': 'deleteBackward:',
'Tab': 'insertTab:',
'Enter': 'insertNewline:', 'Enter': 'insertNewline:',
'NumpadEnter': 'insertNewline:', 'NumpadEnter': 'insertNewline:',
'Escape': 'cancelOperation:', 'Escape': 'cancelOperation:',
@ -34,7 +33,6 @@ export const macEditingCommands: {[key: string]: string|string[]} = {
'Shift+Backspace': 'deleteBackward:', 'Shift+Backspace': 'deleteBackward:',
'Shift+Enter': 'insertNewline:', 'Shift+Enter': 'insertNewline:',
'Shift+NumpadEnter': 'insertNewline:', 'Shift+NumpadEnter': 'insertNewline:',
'Shift+Tab': 'insertBacktab:',
'Shift+Escape': 'cancelOperation:', 'Shift+Escape': 'cancelOperation:',
'Shift+ArrowUp': 'moveUpAndModifySelection:', 'Shift+ArrowUp': 'moveUpAndModifySelection:',
'Shift+ArrowDown': 'moveDownAndModifySelection:', 'Shift+ArrowDown': 'moveDownAndModifySelection:',
@ -87,7 +85,6 @@ export const macEditingCommands: {[key: string]: string|string[]} = {
'Shift+Control+ArrowLeft': 'moveToLeftEndOfLineAndModifySelection:', 'Shift+Control+ArrowLeft': 'moveToLeftEndOfLineAndModifySelection:',
'Shift+Control+ArrowRight': 'moveToRightEndOfLineAndModifySelection:', 'Shift+Control+ArrowRight': 'moveToRightEndOfLineAndModifySelection:',
'Alt+Backspace': 'deleteWordBackward:', 'Alt+Backspace': 'deleteWordBackward:',
'Alt+Tab': 'insertTabIgnoringFieldEditor:',
'Alt+Enter': 'insertNewlineIgnoringFieldEditor:', 'Alt+Enter': 'insertNewlineIgnoringFieldEditor:',
'Alt+NumpadEnter': 'insertNewlineIgnoringFieldEditor:', 'Alt+NumpadEnter': 'insertNewlineIgnoringFieldEditor:',
'Alt+Escape': 'complete:', 'Alt+Escape': 'complete:',
@ -99,7 +96,6 @@ export const macEditingCommands: {[key: string]: string|string[]} = {
'Alt+PageUp': 'pageUp:', 'Alt+PageUp': 'pageUp:',
'Alt+PageDown': 'pageDown:', 'Alt+PageDown': 'pageDown:',
'Shift+Alt+Backspace': 'deleteWordBackward:', 'Shift+Alt+Backspace': 'deleteWordBackward:',
'Shift+Alt+Tab': 'insertTabIgnoringFieldEditor:',
'Shift+Alt+Enter': 'insertNewlineIgnoringFieldEditor:', 'Shift+Alt+Enter': 'insertNewlineIgnoringFieldEditor:',
'Shift+Alt+NumpadEnter': 'insertNewlineIgnoringFieldEditor:', 'Shift+Alt+NumpadEnter': 'insertNewlineIgnoringFieldEditor:',
'Shift+Alt+Escape': 'complete:', 'Shift+Alt+Escape': 'complete:',

View File

@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
const {FFOX, CHROMIUM, LINUX, WEBKIT} = testOptions; const {FFOX, CHROMIUM, LINUX, WEBKIT, MAC} = testOptions;
describe('Page.focus', function() { describe('Page.focus', function() {
it('should work', async function({page, server}) { it('should work', async function({page, server}) {
@ -44,7 +44,7 @@ describe('Page.focus', function() {
expect(focused).toBe(true); expect(focused).toBe(true);
expect(blurred).toBe(true); expect(blurred).toBe(true);
}); });
it.fail(WEBKIT && !LINUX)('should traverse focus', async function({page, server}) { it('should traverse focus', async function({page}) {
await page.setContent(`<input id="i1"><input id="i2">`); await page.setContent(`<input id="i1"><input id="i2">`);
let focused = false; let focused = false;
await page.exposeFunction('focusEvent', () => focused = true); await page.exposeFunction('focusEvent', () => focused = true);
@ -59,4 +59,45 @@ describe('Page.focus', function() {
expect(await page.$eval('#i1', e => e.value)).toBe('First'); expect(await page.$eval('#i1', e => e.value)).toBe('First');
expect(await page.$eval('#i2', e => e.value)).toBe('Last'); expect(await page.$eval('#i2', e => e.value)).toBe('Last');
}); });
it('should traverse focus in all directions', async function({page}) {
await page.setContent(`<input value="1"><input value="2"><input value="3">`);
await page.keyboard.press('Tab');
expect(await page.evaluate(() => document.activeElement.value)).toBe('1');
await page.keyboard.press('Tab');
expect(await page.evaluate(() => document.activeElement.value)).toBe('2');
await page.keyboard.press('Tab');
expect(await page.evaluate(() => document.activeElement.value)).toBe('3');
await page.keyboard.press('Shift+Tab');
expect(await page.evaluate(() => document.activeElement.value)).toBe('2');
await page.keyboard.press('Shift+Tab');
expect(await page.evaluate(() => document.activeElement.value)).toBe('1');
});
// Chromium and WebKit both have settings for tab traversing all links, but
// it is only on by default in WebKit.
it.skip(!MAC || !WEBKIT)('should traverse only form elements', async function({page}) {
await page.setContent(`
<input id="input-1">
<button id="button">buttton</button>
<a href id="link">link</a>
<input id="input-2">
`);
await page.keyboard.press('Tab');
expect(await page.evaluate(() => document.activeElement.id)).toBe('input-1');
await page.keyboard.press('Tab');
expect(await page.evaluate(() => document.activeElement.id)).toBe('input-2');
await page.keyboard.press('Shift+Tab');
expect(await page.evaluate(() => document.activeElement.id)).toBe('input-1');
await page.keyboard.press('Alt+Tab');
expect(await page.evaluate(() => document.activeElement.id)).toBe('button');
await page.keyboard.press('Alt+Tab');
expect(await page.evaluate(() => document.activeElement.id)).toBe('link');
await page.keyboard.press('Alt+Tab');
expect(await page.evaluate(() => document.activeElement.id)).toBe('input-2');
await page.keyboard.press('Alt+Shift+Tab');
expect(await page.evaluate(() => document.activeElement.id)).toBe('link');
await page.keyboard.press('Alt+Shift+Tab');
expect(await page.evaluate(() => document.activeElement.id)).toBe('button');
await page.keyboard.press('Alt+Shift+Tab');
expect(await page.evaluate(() => document.activeElement.id)).toBe('input-1');
});
}); });

View File

@ -290,7 +290,7 @@ describe('Keyboard', function() {
await textarea.type('👹 Tokyo street Japan 🇯🇵'); await textarea.type('👹 Tokyo street Japan 🇯🇵');
expect(await frame.$eval('textarea', textarea => textarea.value)).toBe('👹 Tokyo street Japan 🇯🇵'); expect(await frame.$eval('textarea', textarea => textarea.value)).toBe('👹 Tokyo street Japan 🇯🇵');
}); });
it.skip(CHROMIUM && MAC)('should handle selectAll', async ({page, server}) => { it('should handle selectAll', async ({page, server}) => {
await page.goto(server.PREFIX + '/input/textarea.html'); await page.goto(server.PREFIX + '/input/textarea.html');
const textarea = await page.$('textarea'); const textarea = await page.$('textarea');
await textarea.type('some text'); await textarea.type('some text');
@ -318,6 +318,15 @@ describe('Keyboard', function() {
await page.keyboard.press('Backspace'); await page.keyboard.press('Backspace');
expect(await page.$eval('textarea', textarea => textarea.value)).toBe('some tex'); expect(await page.$eval('textarea', textarea => textarea.value)).toBe('some tex');
}); });
it.skip(!MAC)('should support MacOS shortcuts', async ({page, server}) => {
await page.goto(server.PREFIX + '/input/textarea.html');
const textarea = await page.$('textarea');
await textarea.type('some text');
// select one word backwards
await page.keyboard.press('Shift+Control+Alt+KeyB');
await page.keyboard.press('Backspace');
expect(await page.$eval('textarea', textarea => textarea.value)).toBe('some ');
});
it('should press the meta key', async ({page}) => { it('should press the meta key', async ({page}) => {
const lastEvent = await captureLastKeydown(page); const lastEvent = await captureLastKeydown(page);
await page.keyboard.press('Meta'); await page.keyboard.press('Meta');