rebrowser-playwright-core
Version:
A drop-in replacement for playwright-core patched with rebrowser-patches. It allows to pass modern automation detection tests.
171 lines (168 loc) • 6.91 kB
JavaScript
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.FrameSelectors = void 0;
var _selectorParser = require("../utils/isomorphic/selectorParser");
var _utils = require("../utils");
/**
* 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.
*/
class FrameSelectors {
constructor(frame) {
this.frame = void 0;
this.frame = frame;
}
_parseSelector(selector, options) {
const strict = typeof (options === null || options === void 0 ? void 0 : options.strict) === 'boolean' ? options.strict : !!this.frame._page.context()._options.strictSelectors;
return this.frame._page.context().selectors().parseSelector(selector, strict);
}
async query(selector, options, scope) {
const resolved = await this.resolveInjectedForSelector(selector, options, scope);
// Be careful, |this.frame| can be different from |resolved.frame|.
if (!resolved) return null;
const handle = await resolved.injected.evaluateHandle((injected, {
info,
scope
}) => {
return injected.querySelector(info.parsed, scope || document, info.strict);
}, {
info: resolved.info,
scope: resolved.scope
});
const elementHandle = handle.asElement();
if (!elementHandle) {
handle.dispose();
return null;
}
return adoptIfNeeded(elementHandle, await resolved.frame._mainContext());
}
async queryArrayInMainWorld(selector, scope) {
const resolved = await this.resolveInjectedForSelector(selector, {
mainWorld: true
}, scope);
// Be careful, |this.frame| can be different from |resolved.frame|.
if (!resolved) throw new Error(`Failed to find frame for selector "${selector}"`);
return await resolved.injected.evaluateHandle((injected, {
info,
scope
}) => {
return injected.querySelectorAll(info.parsed, scope || document);
}, {
info: resolved.info,
scope: resolved.scope
});
}
async queryCount(selector) {
const resolved = await this.resolveInjectedForSelector(selector);
// Be careful, |this.frame| can be different from |resolved.frame|.
if (!resolved) throw new Error(`Failed to find frame for selector "${selector}"`);
return await resolved.injected.evaluate((injected, {
info
}) => {
return injected.querySelectorAll(info.parsed, document).length;
}, {
info: resolved.info
});
}
async queryAll(selector, scope) {
const resolved = await this.resolveInjectedForSelector(selector, {}, scope);
// Be careful, |this.frame| can be different from |resolved.frame|.
if (!resolved) return [];
const arrayHandle = await resolved.injected.evaluateHandle((injected, {
info,
scope
}) => {
return injected.querySelectorAll(info.parsed, scope || document);
}, {
info: resolved.info,
scope: resolved.scope
});
const properties = await arrayHandle.getProperties();
arrayHandle.dispose();
// Note: adopting elements one by one may be slow. If we encounter the issue here,
// we might introduce 'useMainContext' option or similar to speed things up.
const targetContext = await resolved.frame._mainContext();
const result = [];
for (const property of properties.values()) {
const elementHandle = property.asElement();
if (elementHandle) result.push(adoptIfNeeded(elementHandle, targetContext));else property.dispose();
}
return Promise.all(result);
}
async resolveFrameForSelector(selector, options = {}, scope) {
let frame = this.frame;
const frameChunks = (0, _selectorParser.splitSelectorByFrame)(selector);
for (const chunk of frameChunks) {
(0, _selectorParser.visitAllSelectorParts)(chunk, (part, nested) => {
if (nested && part.name === 'internal:control' && part.body === 'enter-frame') {
const locator = (0, _utils.asLocator)(this.frame._page.attribution.playwright.options.sdkLanguage, selector);
throw new _selectorParser.InvalidSelectorError(`Frame locators are not allowed inside composite locators, while querying "${locator}"`);
}
});
}
for (let i = 0; i < frameChunks.length - 1; ++i) {
const info = this._parseSelector(frameChunks[i], options);
const context = await frame._context(info.world);
const injectedScript = await context.injectedScript();
const handle = await injectedScript.evaluateHandle((injected, {
info,
scope,
selectorString
}) => {
const element = injected.querySelector(info.parsed, scope || document, info.strict);
if (element && element.nodeName !== 'IFRAME' && element.nodeName !== 'FRAME') throw injected.createStacklessError(`Selector "${selectorString}" resolved to ${injected.previewNode(element)}, <iframe> was expected`);
return element;
}, {
info,
scope: i === 0 ? scope : undefined,
selectorString: (0, _selectorParser.stringifySelector)(info.parsed)
});
const element = handle.asElement();
if (!element) return null;
const maybeFrame = await frame._page._delegate.getContentFrame(element);
element.dispose();
if (!maybeFrame) return null;
frame = maybeFrame;
}
// If we end up in the different frame, we should start from the frame root, so throw away the scope.
if (frame !== this.frame) scope = undefined;
return {
frame,
info: frame.selectors._parseSelector(frameChunks[frameChunks.length - 1], options),
scope
};
}
async resolveInjectedForSelector(selector, options, scope) {
const resolved = await this.resolveFrameForSelector(selector, options, scope);
// Be careful, |this.frame| can be different from |resolved.frame|.
if (!resolved) return;
const context = await resolved.frame._context(options !== null && options !== void 0 && options.mainWorld ? 'main' : resolved.info.world);
const injected = await context.injectedScript();
return {
injected,
info: resolved.info,
frame: resolved.frame,
scope: resolved.scope
};
}
}
exports.FrameSelectors = FrameSelectors;
async function adoptIfNeeded(handle, context) {
if (handle._context === context) return handle;
const adopted = await handle._page._delegate.adoptElementHandle(handle, context);
handle.dispose();
return adopted;
}
;