diff --git a/docs/api/index.md b/docs/api/index.md index f7e0017ae78..7066aa55202 100644 --- a/docs/api/index.md +++ b/docs/api/index.md @@ -1172,6 +1172,13 @@ Valid options to configure PDF generation via [Page.pdf()](./puppeteer.page.pdf. +[QueryOptions](./puppeteer.queryoptions.md) + + + + + + [RemoteAddress](./puppeteer.remoteaddress.md) diff --git a/docs/api/puppeteer.elementhandle.__.md b/docs/api/puppeteer.elementhandle.__.md index cd3585a0253..4ab3553a882 100644 --- a/docs/api/puppeteer.elementhandle.__.md +++ b/docs/api/puppeteer.elementhandle.__.md @@ -11,7 +11,8 @@ Queries the current element for all elements matching the given selector. ```typescript class ElementHandle { $$( - selector: Selector + selector: Selector, + options?: QueryOptions ): Promise>>>; } ``` @@ -43,6 +44,19 @@ Selector The selector to query for. + + + +options + + + +[QueryOptions](./puppeteer.queryoptions.md) + + + +_(Optional)_ + **Returns:** diff --git a/docs/api/puppeteer.elementhandle.md b/docs/api/puppeteer.elementhandle.md index a3a7f6f5d7a..a7df5f4eec8 100644 --- a/docs/api/puppeteer.elementhandle.md +++ b/docs/api/puppeteer.elementhandle.md @@ -105,7 +105,7 @@ Queries the current element for an element matching the given selector. -[$$(selector)](./puppeteer.elementhandle.__.md) +[$$(selector, options)](./puppeteer.elementhandle.__.md) diff --git a/docs/api/puppeteer.frame.__.md b/docs/api/puppeteer.frame.__.md index a148a0168a5..9b2bae55161 100644 --- a/docs/api/puppeteer.frame.__.md +++ b/docs/api/puppeteer.frame.__.md @@ -11,7 +11,8 @@ Queries the frame for all elements matching the given selector. ```typescript class Frame { $$( - selector: Selector + selector: Selector, + options?: QueryOptions ): Promise>>>; } ``` @@ -43,6 +44,19 @@ Selector [selector](https://pptr.dev/guides/page-interactions#query-selectors) to query page for. [CSS selectors](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors) can be passed as-is and a [Puppeteer-specific seletor syntax](https://pptr.dev/guides/page-interactions#p-selectors) allows quering by [text](https://pptr.dev/guides/page-interactions#text-selectors--p-text), [a11y role and name](https://pptr.dev/guides/page-interactions#aria-selectors--p-aria), and [xpath](https://pptr.dev/guides/page-interactions#xpath-selectors--p-xpath) and [combining these queries across shadow roots](https://pptr.dev/guides/page-interactions#-and--combinators). Alternatively, you can specify a selector type using a prefix [prefix](https://pptr.dev/guides/page-interactions#built-in-selectors). + + + +options + + + +[QueryOptions](./puppeteer.queryoptions.md) + + + +_(Optional)_ + **Returns:** diff --git a/docs/api/puppeteer.frame.md b/docs/api/puppeteer.frame.md index 2848b7d24c1..cc9d8f502cf 100644 --- a/docs/api/puppeteer.frame.md +++ b/docs/api/puppeteer.frame.md @@ -125,7 +125,7 @@ Queries the frame for an element matching the given selector. -[$$(selector)](./puppeteer.frame.__.md) +[$$(selector, options)](./puppeteer.frame.__.md) diff --git a/docs/api/puppeteer.page.__.md b/docs/api/puppeteer.page.__.md index 65befca7b2e..14002caddd4 100644 --- a/docs/api/puppeteer.page.__.md +++ b/docs/api/puppeteer.page.__.md @@ -11,7 +11,8 @@ Finds elements on the page that match the selector. If no elements match the sel ```typescript class Page { $$( - selector: Selector + selector: Selector, + options?: QueryOptions ): Promise>>>; } ``` @@ -43,6 +44,19 @@ Selector [selector](https://pptr.dev/guides/page-interactions#query-selectors) to query page for. [CSS selectors](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors) can be passed as-is and a [Puppeteer-specific seletor syntax](https://pptr.dev/guides/page-interactions#p-selectors) allows quering by [text](https://pptr.dev/guides/page-interactions#text-selectors--p-text), [a11y role and name](https://pptr.dev/guides/page-interactions#aria-selectors--p-aria), and [xpath](https://pptr.dev/guides/page-interactions#xpath-selectors--p-xpath) and [combining these queries across shadow roots](https://pptr.dev/guides/page-interactions#-and--combinators). Alternatively, you can specify a selector type using a prefix [prefix](https://pptr.dev/guides/page-interactions#built-in-selectors). + + + +options + + + +[QueryOptions](./puppeteer.queryoptions.md) + + + +_(Optional)_ + **Returns:** diff --git a/docs/api/puppeteer.page.md b/docs/api/puppeteer.page.md index 1e3f61cf7f3..acb15ab0b58 100644 --- a/docs/api/puppeteer.page.md +++ b/docs/api/puppeteer.page.md @@ -250,7 +250,7 @@ Shortcut for [Page.mainFrame().$(selector)](./puppeteer.frame._.md). -[$$(selector)](./puppeteer.page.__.md) +[$$(selector, options)](./puppeteer.page.__.md) diff --git a/docs/api/puppeteer.queryoptions.md b/docs/api/puppeteer.queryoptions.md new file mode 100644 index 00000000000..fb318cb4863 --- /dev/null +++ b/docs/api/puppeteer.queryoptions.md @@ -0,0 +1,55 @@ +--- +sidebar_label: QueryOptions +--- + +# QueryOptions interface + +#### Signature: + +```typescript +export interface QueryOptions +``` + +## Properties + + + +
+ +Property + + + +Modifiers + + + +Type + + + +Description + + + +Default + +
+ +isolate + + + + + +boolean + + + +Whether to run the query in isolation. When returning many elements from [Page.$$()](./puppeteer.page.__.md) or similar methods, it might be useful to turn off the isolation to improve performance. By default, the querying code will be executed in a separate sandbox realm. + + + +`true` + +
diff --git a/packages/puppeteer-core/src/api/ElementHandle.ts b/packages/puppeteer-core/src/api/ElementHandle.ts index 9c3fe0d5ab0..e6cff11c7ab 100644 --- a/packages/puppeteer-core/src/api/ElementHandle.ts +++ b/packages/puppeteer-core/src/api/ElementHandle.ts @@ -31,7 +31,11 @@ import type { MouseClickOptions, } from './Input.js'; import {JSHandle} from './JSHandle.js'; -import type {ScreenshotOptions, WaitForSelectorOptions} from './Page.js'; +import type { + QueryOptions, + ScreenshotOptions, + WaitForSelectorOptions, +} from './Page.js'; /** * @public @@ -371,8 +375,34 @@ export abstract class ElementHandle< * elements matching the given selector. */ @throwIfDisposed() - @ElementHandle.bindIsolatedHandle async $$( + selector: Selector, + options?: QueryOptions + ): Promise>>> { + if (options?.isolate === false) { + return await this.#$$impl(selector); + } + return await this.#$$(selector); + } + + /** + * Isolates {@link ElementHandle.$$} if needed. + * + * @internal + */ + @ElementHandle.bindIsolatedHandle + async #$$( + selector: Selector + ): Promise>>> { + return await this.#$$impl(selector); + } + + /** + * Implementation for {@link ElementHandle.$$}. + * + * @internal + */ + async #$$impl( selector: Selector ): Promise>>> { const {updatedSelector, QueryHandler} = diff --git a/packages/puppeteer-core/src/api/Frame.ts b/packages/puppeteer-core/src/api/Frame.ts index 412087de6e8..659b2f2919e 100644 --- a/packages/puppeteer-core/src/api/Frame.ts +++ b/packages/puppeteer-core/src/api/Frame.ts @@ -10,6 +10,7 @@ import type {ClickOptions, ElementHandle} from '../api/ElementHandle.js'; import type {HTTPResponse} from '../api/HTTPResponse.js'; import type { Page, + QueryOptions, WaitForSelectorOptions, WaitTimeoutOptions, } from '../api/Page.js'; @@ -564,11 +565,12 @@ export abstract class Frame extends EventEmitter { */ @throwIfDetached async $$( - selector: Selector + selector: Selector, + options?: QueryOptions ): Promise>>> { // eslint-disable-next-line rulesdir/use-using -- This is cached. const document = await this.#document(); - return await document.$$(selector); + return await document.$$(selector, options); } /** diff --git a/packages/puppeteer-core/src/api/Page.ts b/packages/puppeteer-core/src/api/Page.ts index edf7468a7a2..ef1ff9b23de 100644 --- a/packages/puppeteer-core/src/api/Page.ts +++ b/packages/puppeteer-core/src/api/Page.ts @@ -346,6 +346,21 @@ export interface ScreencastOptions { ffmpegPath?: string; } +/** + * @public + */ +export interface QueryOptions { + /** + * Whether to run the query in isolation. When returning many elements + * from {@link Page.$$} or similar methods, it might be useful to turn + * off the isolation to improve performance. By default, the querying + * code will be executed in a separate sandbox realm. + * + * @defaultValue `true` + */ + isolate: boolean; +} + /** * All the events that a page instance may emit. * @@ -1081,9 +1096,10 @@ export abstract class Page extends EventEmitter { * Shortcut for {@link Frame.$$ | Page.mainFrame().$$(selector) }. */ async $$( - selector: Selector + selector: Selector, + options?: QueryOptions ): Promise>>> { - return await this.mainFrame().$$(selector); + return await this.mainFrame().$$(selector, options); } /** diff --git a/test/src/queryselector.spec.ts b/test/src/queryselector.spec.ts index 13e20a4d1a9..c7f03314d18 100644 --- a/test/src/queryselector.spec.ts +++ b/test/src/queryselector.spec.ts @@ -167,6 +167,23 @@ describe('querySelector', function () { }); expect(await Promise.all(promises)).toEqual(['A', 'B']); }); + + it('should query existing elements without isolation', async () => { + const {page} = await getTestState(); + + await page.setContent('
A

B
'); + const elements = await page.$$('div', { + isolate: false, + }); + expect(elements).toHaveLength(2); + const promises = elements.map(element => { + return page.evaluate(e => { + return e.textContent; + }, element); + }); + expect(await Promise.all(promises)).toEqual(['A', 'B']); + }); + it('should return empty array if nothing is found', async () => { const {page, server} = await getTestState();