Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 6 additions & 39 deletions src/accessibility.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,48 +14,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import * as dom from './dom';

export type SerializedAXNode = {
role: string,
name: string,
value?: string|number,
description?: string,

keyshortcuts?: string,
roledescription?: string,
valuetext?: string,

disabled?: boolean,
expanded?: boolean,
focused?: boolean,
modal?: boolean,
multiline?: boolean,
multiselectable?: boolean,
readonly?: boolean,
required?: boolean,
selected?: boolean,

checked?: boolean|'mixed',
pressed?: boolean|'mixed',

level?: number,
valuemin?: number,
valuemax?: number,

autocomplete?: string,
haspopup?: string,
invalid?: string,
orientation?: string,

children?: SerializedAXNode[]
};
import * as dom from './dom';
import * as types from './types';

export interface AXNode {
isInteresting(insideControl: boolean): boolean;
isLeafNode(): boolean;
isControl(): boolean;
serialize(): SerializedAXNode;
serialize(): types.SerializedAXNode;
children(): Iterable<AXNode>;
}

Expand All @@ -68,7 +35,7 @@ export class Accessibility {
async snapshot(options: {
interestingOnly?: boolean;
root?: dom.ElementHandle;
} = {}): Promise<SerializedAXNode | null> {
} = {}): Promise<types.SerializedAXNode | null> {
const {
interestingOnly = true,
root = null,
Expand Down Expand Up @@ -98,8 +65,8 @@ function collectInterestingNodes(collection: Set<AXNode>, node: AXNode, insideCo
collectInterestingNodes(collection, child, insideControl);
}

function serializeTree(node: AXNode, whitelistedNodes?: Set<AXNode>): SerializedAXNode[] {
const children: SerializedAXNode[] = [];
function serializeTree(node: AXNode, whitelistedNodes?: Set<AXNode>): types.SerializedAXNode[] {
const children: types.SerializedAXNode[] = [];
for (const child of node.children())
children.push(...serializeTree(child, whitelistedNodes));

Expand Down
17 changes: 9 additions & 8 deletions src/chromium/crAccessibility.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { CRSession } from './crConnection';
import { Protocol } from './protocol';
import * as dom from '../dom';
import * as accessibility from '../accessibility';
import * as types from '../types';

export async function getAccessibilityTree(client: CRSession, needle?: dom.ElementHandle): Promise<{tree: accessibility.AXNode, needle: accessibility.AXNode | null}> {
const {nodes} = await client.send('Accessibility.getFullAXTree');
Expand Down Expand Up @@ -198,7 +199,7 @@ class CRAXNode implements accessibility.AXNode {
return this.isLeafNode() && !!this._name;
}

serialize(): accessibility.SerializedAXNode {
serialize(): types.SerializedAXNode {
const properties: Map<string, number | string | boolean> = new Map();
for (const property of this._payload.properties || [])
properties.set(property.name.toLowerCase(), property.value.value);
Expand All @@ -209,12 +210,12 @@ class CRAXNode implements accessibility.AXNode {
if (this._payload.description)
properties.set('description', this._payload.description.value);

const node: {[x in keyof accessibility.SerializedAXNode]: any} = {
const node: {[x in keyof types.SerializedAXNode]: any} = {
role: this._role,
name: this._payload.name ? (this._payload.name.value || '') : ''
};

const userStringProperties: Array<keyof accessibility.SerializedAXNode> = [
const userStringProperties: Array<keyof types.SerializedAXNode> = [
'value',
'description',
'keyshortcuts',
Expand All @@ -227,7 +228,7 @@ class CRAXNode implements accessibility.AXNode {
node[userStringProperty] = properties.get(userStringProperty);
}

const booleanProperties: Array<keyof accessibility.SerializedAXNode> = [
const booleanProperties: Array<keyof types.SerializedAXNode> = [
'disabled',
'expanded',
'focused',
Expand All @@ -249,7 +250,7 @@ class CRAXNode implements accessibility.AXNode {
node[booleanProperty] = value;
}

const tristateProperties: Array<keyof accessibility.SerializedAXNode> = [
const tristateProperties: Array<keyof types.SerializedAXNode> = [
'checked',
'pressed',
];
Expand All @@ -259,7 +260,7 @@ class CRAXNode implements accessibility.AXNode {
const value = properties.get(tristateProperty);
node[tristateProperty] = value === 'mixed' ? 'mixed' : value === 'true' ? true : false;
}
const numericalProperties: Array<keyof accessibility.SerializedAXNode> = [
const numericalProperties: Array<keyof types.SerializedAXNode> = [
'level',
'valuemax',
'valuemin',
Expand All @@ -269,7 +270,7 @@ class CRAXNode implements accessibility.AXNode {
continue;
node[numericalProperty] = properties.get(numericalProperty);
}
const tokenProperties: Array<keyof accessibility.SerializedAXNode> = [
const tokenProperties: Array<keyof types.SerializedAXNode> = [
'autocomplete',
'haspopup',
'invalid',
Expand All @@ -281,7 +282,7 @@ class CRAXNode implements accessibility.AXNode {
continue;
node[tokenProperty] = value;
}
return node as accessibility.SerializedAXNode;
return node as types.SerializedAXNode;
}

static createTree(client: CRSession, payloads: Protocol.Accessibility.AXNode[]): CRAXNode {
Expand Down
7 changes: 1 addition & 6 deletions src/console.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,7 @@

import * as js from './javascript';
import * as util from 'util';

export type ConsoleMessageLocation = {
url?: string,
lineNumber?: number,
columnNumber?: number,
};
import { ConsoleMessageLocation } from './types';

export class ConsoleMessage {
private _type: string;
Expand Down
15 changes: 8 additions & 7 deletions src/firefox/ffAccessibility.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import * as accessibility from '../accessibility';
import { FFSession } from './ffConnection';
import { Protocol } from './protocol';
import * as dom from '../dom';
import * as types from '../types';

export async function getAccessibilityTree(session: FFSession, needle?: dom.ElementHandle): Promise<{tree: accessibility.AXNode, needle: accessibility.AXNode | null}> {
const objectId = needle ? needle._objectId : undefined;
Expand Down Expand Up @@ -198,12 +199,12 @@ class FFAXNode implements accessibility.AXNode {
return this.isLeafNode() && !!this._name.trim();
}

serialize(): accessibility.SerializedAXNode {
const node: {[x in keyof accessibility.SerializedAXNode]: any} = {
serialize(): types.SerializedAXNode {
const node: {[x in keyof types.SerializedAXNode]: any} = {
role: FFRoleToARIARole.get(this._role) || this._role,
name: this._name || ''
};
const userStringProperties: Array<keyof accessibility.SerializedAXNode & keyof Protocol.Accessibility.AXTree> = [
const userStringProperties: Array<keyof types.SerializedAXNode & keyof Protocol.Accessibility.AXTree> = [
'name',
'value',
'description',
Expand All @@ -216,7 +217,7 @@ class FFAXNode implements accessibility.AXNode {
continue;
node[userStringProperty] = this._payload[userStringProperty];
}
const booleanProperties: Array<keyof accessibility.SerializedAXNode & keyof Protocol.Accessibility.AXTree> = [
const booleanProperties: Array<keyof types.SerializedAXNode & keyof Protocol.Accessibility.AXTree> = [
'disabled',
'expanded',
'focused',
Expand All @@ -235,7 +236,7 @@ class FFAXNode implements accessibility.AXNode {
continue;
node[booleanProperty] = value;
}
const tristateProperties: Array<keyof accessibility.SerializedAXNode & keyof Protocol.Accessibility.AXTree> = [
const tristateProperties: Array<keyof types.SerializedAXNode & keyof Protocol.Accessibility.AXTree> = [
'checked',
'pressed',
];
Expand All @@ -245,15 +246,15 @@ class FFAXNode implements accessibility.AXNode {
const value = this._payload[tristateProperty];
node[tristateProperty] = value;
}
const numericalProperties: Array<keyof accessibility.SerializedAXNode & keyof Protocol.Accessibility.AXTree> = [
const numericalProperties: Array<keyof types.SerializedAXNode & keyof Protocol.Accessibility.AXTree> = [
'level'
];
for (const numericalProperty of numericalProperties) {
if (!(numericalProperty in this._payload))
continue;
node[numericalProperty] = this._payload[numericalProperty];
}
const tokenProperties: Array<keyof accessibility.SerializedAXNode & keyof Protocol.Accessibility.AXTree> = [
const tokenProperties: Array<keyof types.SerializedAXNode & keyof Protocol.Accessibility.AXTree> = [
'autocomplete',
'haspopup',
'invalid',
Expand Down
4 changes: 2 additions & 2 deletions src/page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import { TimeoutSettings } from './timeoutSettings';
import * as types from './types';
import { Events } from './events';
import { BrowserContext, BrowserContextBase } from './browserContext';
import { ConsoleMessage, ConsoleMessageLocation } from './console';
import { ConsoleMessage } from './console';
import * as accessibility from './accessibility';
import { EventEmitter } from 'events';
import { FileChooser } from './fileChooser';
Expand Down Expand Up @@ -279,7 +279,7 @@ export class Page extends EventEmitter {
await PageBinding.dispatch(this, payload, context);
}

_addConsoleMessage(type: string, args: js.JSHandle[], location: ConsoleMessageLocation, text?: string) {
_addConsoleMessage(type: string, args: js.JSHandle[], location: types.ConsoleMessageLocation, text?: string) {
const message = new ConsoleMessage(type, text, args, location);
const intercepted = this._frameManager.interceptConsoleMessage(message);
if (intercepted || !this.listenerCount(Events.Page.Console))
Expand Down
17 changes: 17 additions & 0 deletions src/rpc/channels.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ export interface PageChannel extends Channel {
on(event: 'requestFinished', callback: (params: RequestChannel) => void): this;
on(event: 'requestFailed', callback: (params: RequestChannel) => void): this;
on(event: 'close', callback: () => void): this;
on(event: 'console', callback: (params: ConsoleMessageChannel) => void): this;

setDefaultNavigationTimeoutNoReply(params: { timeout: number }): void;
setDefaultTimeoutNoReply(params: { timeout: number }): Promise<void>;
Expand All @@ -82,6 +83,20 @@ export interface PageChannel extends Channel {
setNetworkInterceptionEnabled(params: { enabled: boolean }): Promise<void>;
screenshot(params: { options?: types.ScreenshotOptions }): Promise<Buffer>;
close(params: { options?: { runBeforeUnload?: boolean } }): Promise<void>;

// Input
keyboardDown(params: { key: string }): Promise<void>;
keyboardUp(params: { key: string }): Promise<void>;
keyboardInsertText(params: { text: string }): Promise<void>;
keyboardType(params: { text: string, options?: { delay?: number } }): Promise<void>;
keyboardPress(params: { key: string, options?: { delay?: number } }): Promise<void>;
mouseMove(params: { x: number, y: number, options?: { steps?: number } }): Promise<void>;
mouseDown(params: { options?: { button?: types.MouseButton, clickCount?: number } }): Promise<void>;
mouseUp(params: { options?: { button?: types.MouseButton, clickCount?: number } }): Promise<void>;
mouseClick(params: { x: number, y: number, options?: { delay?: number, button?: types.MouseButton, clickCount?: number } }): Promise<void>;

// A11Y
accessibilitySnapshot(params: { options: { interestingOnly?: boolean, root?: ElementHandleChannel } }): Promise<types.SerializedAXNode | null>;
}

export interface FrameChannel extends Channel {
Expand Down Expand Up @@ -173,3 +188,5 @@ export interface ResponseChannel extends Channel {
finished(): Promise<Error | null>;
}

export interface ConsoleMessageChannel extends Channel {
}
33 changes: 33 additions & 0 deletions src/rpc/client/accessibility.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/**
* Copyright 2017 Google Inc. All rights reserved.
* Modifications copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import { PageChannel } from '../channels';
import { ElementHandle } from './elementHandle';
import * as types from '../../types';

export class Accessibility {
private _channel: PageChannel;

constructor(channel: PageChannel) {
this._channel = channel;
}

snapshot(options: { interestingOnly?: boolean; root?: ElementHandle } = {}): Promise<types.SerializedAXNode | null> {
const root = options.root ? options.root._elementChannel : undefined;
return this._channel.accessibilitySnapshot({ options: { interestingOnly: options.interestingOnly, root } });
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't the recursive handle->guid unwrapper handle the root?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I need to go from ElementHandle to ElementHandleChannel here, recursive wrapper goes Channel -> guid

}
}
64 changes: 64 additions & 0 deletions src/rpc/client/console.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/**
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import * as util from 'util';
import { ConsoleMessageLocation } from '../../types';
import { JSHandle } from './jsHandle';
import { ConsoleMessageChannel, JSHandleChannel } from '../channels';
import { ChannelOwner } from './channelOwner';
import { Connection } from '../connection';

export class ConsoleMessage extends ChannelOwner<ConsoleMessageChannel> {
private _type: string = '';
private _text: string = '';
private _args: JSHandle[] = [];
private _location: ConsoleMessageLocation = {};

static from(request: ConsoleMessageChannel): ConsoleMessage {
return request._object;
}

constructor(connection: Connection, channel: ConsoleMessageChannel) {
super(connection, channel);
}

_initialize(params: { type: string, text: string, args: JSHandleChannel[], location: ConsoleMessageLocation }) {
this._type = params.type;
this._text = params.text;
this._args = params.args.map(JSHandle.from);
this._location = params.location;
}

type(): string {
return this._type;
}

text(): string {
return this._text;
}

args(): JSHandle[] {
return this._args;
}

location(): ConsoleMessageLocation {
return this._location;
}

[util.inspect.custom]() {
return this.text();
}
}
2 changes: 1 addition & 1 deletion src/rpc/client/elementHandle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import { FuncOn, JSHandle, convertArg } from './jsHandle';
import { Connection } from '../connection';

export class ElementHandle<T extends Node = Node> extends JSHandle<T> {
private _elementChannel: ElementHandleChannel;
readonly _elementChannel: ElementHandleChannel;

static from(handle: ElementHandleChannel): ElementHandle {
return handle._object;
Expand Down
Loading