Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: remove more paths and url matches from the server side #3528

Merged
merged 1 commit into from
Aug 19, 2020
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
5 changes: 0 additions & 5 deletions src/browserContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,11 +111,6 @@ export abstract class BrowserContext extends EventEmitter {
this._doExposeBinding(binding);
}

async addInitScript(script: string | Function | { path?: string | undefined; content?: string | undefined; }, arg?: any): Promise<void> {
const source = await helper.evaluationScript(script, arg);
await this._doAddInitScript(source);
}

async grantPermissions(permissions: string[], options?: { origin?: string }) {
let origin = '*';
if (options && options.origin) {
Expand Down
7 changes: 3 additions & 4 deletions src/frames.ts
Original file line number Diff line number Diff line change
Expand Up @@ -469,18 +469,17 @@ export class Frame {
});
}

async waitForNavigation(options: types.WaitForNavigationOptions = {}): Promise<network.Response | null> {
async waitForNavigation(options: types.NavigateOptions = {}): Promise<network.Response | null> {
return runNavigationTask(this, options, async progress => {
const toUrl = typeof options.url === 'string' ? ` to "${options.url}"` : '';
const waitUntil = verifyLifecycle('waitUntil', options.waitUntil === undefined ? 'load' : options.waitUntil);
progress.log(`waiting for navigation${toUrl} until "${waitUntil}"`);
progress.log(`waiting for navigation until "${waitUntil}"`);

const navigationEvent: NavigationEvent = await helper.waitForEvent(progress, this._eventEmitter, kNavigationEvent, (event: NavigationEvent) => {
// Any failed navigation results in a rejection.
if (event.error)
return true;
progress.log(` navigated to "${this._url}"`);
return helper.urlMatches(this._url, options.url);
return true;
}).promise;
if (navigationEvent.error)
throw navigationEvent.error;
Expand Down
109 changes: 0 additions & 109 deletions src/helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,40 +41,6 @@ export type Listener = (...args: any[]) => void;
const isInDebugMode = !!getFromENV('PWDEBUG');

class Helper {

static evaluationString(fun: Function | string, ...args: any[]): string {
if (Helper.isString(fun)) {
assert(args.length === 0 || (args.length === 1 && args[0] === undefined), 'Cannot evaluate a string with arguments');
return fun;
}
return Helper.evaluationStringForFunctionBody(String(fun), ...args);
}

static evaluationStringForFunctionBody(functionBody: string, ...args: any[]): string {
return `(${functionBody})(${args.map(serializeArgument).join(',')})`;
function serializeArgument(arg: any): string {
if (Object.is(arg, undefined))
return 'undefined';
return JSON.stringify(arg);
}
}

static async evaluationScript(fun: Function | string | { path?: string, content?: string }, arg?: any, addSourceUrl: boolean = true): Promise<string> {
if (!helper.isString(fun) && typeof fun !== 'function') {
if (fun.content !== undefined) {
fun = fun.content;
} else if (fun.path !== undefined) {
let contents = await util.promisify(fs.readFile)(fun.path, 'utf8');
if (addSourceUrl)
contents += '//# sourceURL=' + fun.path.replace(/\n/g, '');
fun = contents;
} else {
throw new Error('Either path or content property must be present');
}
}
return helper.evaluationString(fun, arg);
}

static addEventListener(
emitter: EventEmitter,
eventName: (string | symbol),
Expand Down Expand Up @@ -117,62 +83,6 @@ class Helper {
return typeof obj === 'boolean' || obj instanceof Boolean;
}

static globToRegex(glob: string): RegExp {
const tokens = ['^'];
let inGroup;
for (let i = 0; i < glob.length; ++i) {
const c = glob[i];
if (escapeGlobChars.has(c)) {
tokens.push('\\' + c);
continue;
}
if (c === '*') {
const beforeDeep = glob[i - 1];
let starCount = 1;
while (glob[i + 1] === '*') {
starCount++;
i++;
}
const afterDeep = glob[i + 1];
const isDeep = starCount > 1 &&
(beforeDeep === '/' || beforeDeep === undefined) &&
(afterDeep === '/' || afterDeep === undefined);
if (isDeep) {
tokens.push('((?:[^/]*(?:\/|$))*)');
i++;
} else {
tokens.push('([^/]*)');
}
continue;
}

switch (c) {
case '?':
tokens.push('.');
break;
case '{':
inGroup = true;
tokens.push('(');
break;
case '}':
inGroup = false;
tokens.push(')');
break;
case ',':
if (inGroup) {
tokens.push('|');
break;
}
tokens.push('\\' + c);
break;
default:
tokens.push(c);
}
}
tokens.push('$');
return new RegExp(tokens.join(''));
}

static completeUserURL(urlString: string): string {
if (urlString.startsWith('localhost') || urlString.startsWith('127.0.0.1'))
urlString = 'http://' + urlString;
Expand All @@ -191,23 +101,6 @@ class Helper {
return { width: Math.floor(size.width + 1e-3), height: Math.floor(size.height + 1e-3) };
}

static urlMatches(urlString: string, match: types.URLMatch | undefined): boolean {
if (match === undefined || match === '')
return true;
if (helper.isString(match))
match = helper.globToRegex(match);
if (helper.isRegExp(match))
return match.test(urlString);
if (typeof match === 'string' && match === urlString)
return true;
const url = new URL(urlString);
if (typeof match === 'string')
return url.pathname === match;

assert(typeof match === 'function', 'url parameter should be string, RegExp or function');
return match(url);
}

// See https://joel.tools/microtasks/
static makeWaitForNextTask() {
if (parseInt(process.versions.node, 10) >= 11)
Expand Down Expand Up @@ -366,8 +259,6 @@ export async function mkdirIfNeeded(filePath: string) {
await mkdirAsync(path.dirname(filePath), {recursive: true}).catch(() => {});
}

const escapeGlobChars = new Set(['/', '$', '^', '+', '.', '(', ')', '=', '!', '|']);

export const helper = Helper;

const debugLoggerColorMap = {
Expand Down
4 changes: 2 additions & 2 deletions src/page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ export class Page extends EventEmitter {
async reload(options?: types.NavigateOptions): Promise<network.Response | null> {
const waitPromise = this.mainFrame().waitForNavigation(options);
await this._delegate.reload();
const response = waitPromise;
const response = await waitPromise;
await this._doSlowMo();
return response;
}
Expand Down Expand Up @@ -424,7 +424,7 @@ export class PageBinding {
constructor(name: string, playwrightFunction: frames.FunctionWithSource) {
this.name = name;
this.playwrightFunction = playwrightFunction;
this.source = helper.evaluationString(addPageBinding, name);
this.source = `(${addPageBinding.toString()})(${JSON.stringify(name)})`;
}

static async dispatch(page: Page, payload: string, context: dom.FrameExecutionContext) {
Expand Down
7 changes: 3 additions & 4 deletions src/rpc/client/browserContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,7 @@ import { Page, BindingCall } from './page';
import * as network from './network';
import { BrowserContextChannel, BrowserContextInitializer } from '../channels';
import { ChannelOwner } from './channelOwner';
import { helper } from '../../helper';
import { isUnderTest, deprecate } from './clientHelper';
import { isUnderTest, deprecate, evaluationScript, urlMatches } from './clientHelper';
import { Browser } from './browser';
import { Events } from './events';
import { TimeoutSettings } from '../../timeoutSettings';
Expand Down Expand Up @@ -68,7 +67,7 @@ export class BrowserContext extends ChannelOwner<BrowserContextChannel, BrowserC

_onRoute(route: network.Route, request: network.Request) {
for (const {url, handler} of this._routes) {
if (helper.urlMatches(request.url(), url)) {
if (urlMatches(request.url(), url)) {
handler(route, request);
return;
}
Expand Down Expand Up @@ -168,7 +167,7 @@ export class BrowserContext extends ChannelOwner<BrowserContextChannel, BrowserC

async addInitScript(script: Function | string | { path?: string, content?: string }, arg?: any): Promise<void> {
return this._wrapApiCall('browserContext.addInitScript', async () => {
const source = await helper.evaluationScript(script, arg);
const source = await evaluationScript(script, arg);
await this._channel.addInitScript({ source });
});
}
Expand Down
101 changes: 100 additions & 1 deletion src/rpc/client/clientHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,10 @@
* limitations under the License.
*/

import { isUnderTest as commonIsUnderTest } from '../../helper';
import { isUnderTest as commonIsUnderTest, helper } from '../../helper';
import * as types from './types';
import * as fs from 'fs';
import * as util from 'util';

const deprecatedHits = new Set();
export function deprecate(methodName: string, message: string) {
Expand All @@ -38,3 +40,100 @@ export function envObjectToArray(env: types.Env): { name: string, value: string
}
return result;
}

export async function evaluationScript(fun: Function | string | { path?: string, content?: string }, arg?: any, addSourceUrl: boolean = true): Promise<string> {
if (typeof fun === 'function') {
const source = fun.toString();
const argString = Object.is(arg, undefined) ? 'undefined' : JSON.stringify(arg);
return `(${source})(${argString})`;
}
if (arg !== undefined)
throw new Error('Cannot evaluate a string with arguments');
if (helper.isString(fun))
return fun;
if (fun.content !== undefined)
return fun.content;
if (fun.path !== undefined) {
let source = await util.promisify(fs.readFile)(fun.path, 'utf8');
if (addSourceUrl)
source += '//# sourceURL=' + fun.path.replace(/\n/g, '');
return source;
}
throw new Error('Either path or content property must be present');
}

export function urlMatches(urlString: string, match: types.URLMatch | undefined): boolean {
if (match === undefined || match === '')
return true;
if (helper.isString(match))
match = globToRegex(match);
if (helper.isRegExp(match))
return match.test(urlString);
if (typeof match === 'string' && match === urlString)
return true;
const url = new URL(urlString);
if (typeof match === 'string')
return url.pathname === match;

if (typeof match !== 'function')
throw new Error('url parameter should be string, RegExp or function');
return match(url);
}

const escapeGlobChars = new Set(['/', '$', '^', '+', '.', '(', ')', '=', '!', '|']);

export function globToRegex(glob: string): RegExp {
const tokens = ['^'];
let inGroup;
for (let i = 0; i < glob.length; ++i) {
const c = glob[i];
if (escapeGlobChars.has(c)) {
tokens.push('\\' + c);
continue;
}
if (c === '*') {
const beforeDeep = glob[i - 1];
let starCount = 1;
while (glob[i + 1] === '*') {
starCount++;
i++;
}
const afterDeep = glob[i + 1];
const isDeep = starCount > 1 &&
(beforeDeep === '/' || beforeDeep === undefined) &&
(afterDeep === '/' || afterDeep === undefined);
if (isDeep) {
tokens.push('((?:[^/]*(?:\/|$))*)');
i++;
} else {
tokens.push('([^/]*)');
}
continue;
}

switch (c) {
case '?':
tokens.push('.');
break;
case '{':
inGroup = true;
tokens.push('(');
break;
case '}':
inGroup = false;
tokens.push(')');
break;
case ',':
if (inGroup) {
tokens.push('|');
break;
}
tokens.push('\\' + c);
break;
default:
tokens.push(c);
}
}
tokens.push('$');
return new RegExp(tokens.join(''));
}
5 changes: 3 additions & 2 deletions src/rpc/client/frame.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
* limitations under the License.
*/

import { helper, assert } from '../../helper';
import { assert } from '../../helper';
import { FrameChannel, FrameInitializer, FrameNavigatedEvent, FrameGotoOptions, FrameWaitForSelectorOptions, FrameDispatchEventOptions, FrameSetContentOptions, FrameClickOptions, FrameDblclickOptions, FrameFillOptions, FrameFocusOptions, FrameTextContentOptions, FrameInnerTextOptions, FrameInnerHTMLOptions, FrameGetAttributeOptions, FrameHoverOptions, FrameSetInputFilesOptions, FrameTypeOptions, FramePressOptions, FrameCheckOptions, FrameUncheckOptions } from '../channels';
import { BrowserContext } from './browserContext';
import { ChannelOwner } from './channelOwner';
Expand All @@ -29,6 +29,7 @@ import { EventEmitter } from 'events';
import { Waiter } from './waiter';
import { Events } from './events';
import { LifecycleEvent, URLMatch, SelectOption, SelectOptionOptions, FilePayload, WaitForFunctionOptions, kLifecycleEvents } from './types';
import { urlMatches } from './clientHelper';

const fsReadFileAsync = util.promisify(fs.readFile.bind(fs));

Expand Down Expand Up @@ -123,7 +124,7 @@ export class Frame extends ChannelOwner<FrameChannel, FrameInitializer> {
if (event.error)
return true;
waiter.log(` navigated to "${event.url}"`);
return helper.urlMatches(event.url, options.url);
return urlMatches(event.url, options.url);
});
if (navigatedEvent.error) {
const e = new Error(navigatedEvent.error);
Expand Down
Loading